{"record":{"id":"9c9992acc9c26607","repo":"roboflow/supervision","slug":"value-must-be-a-np-ndarray-or-a-list","errorCode":null,"errorMessage":"Value must be a np.ndarray or a list","messagePattern":"Value must be a np\\.ndarray or a list","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/supervision/key_points/core.py","lineNumber":1139,"sourceCode":"            from supervision import _cv2 as cv2\n            import supervision as sv\n            from ultralytics import YOLO\n\n            image = cv2.imread(\"<SOURCE_IMAGE_PATH>\")\n            model = YOLO('yolov8s.pt')\n\n            result = model(image)[0]\n            key_points = sv.KeyPoints.from_ultralytics(result)\n\n            key_points['class_name'] = [\n                 model.model.names[class_id]\n                 for class_id\n                 in key_points.class_id\n             ]\n            ```\n        \"\"\"\n        if not isinstance(value, (np.ndarray, list)):\n            raise TypeError(\"Value must be a np.ndarray or a list\")\n\n        if isinstance(value, list):\n            value = np.array(value)\n\n        self.data[key] = value\n\n    @classmethod\n    def empty(cls) -> KeyPoints:\n        \"\"\"\n        Create an empty KeyPoints object with no key points.\n\n        Returns:\n            An empty `sv.KeyPoints` object.\n\n        Examples:\n            ```pycon\n            >>> import supervision as sv\n            >>> key_points = sv.KeyPoints.empty()","sourceCodeStart":1121,"sourceCodeEnd":1157,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/key_points/core.py#L1121-L1157","documentation":"F1Score's kernel computes F1 = 2TP / (2TP + FP + FN) from an array whose last axis must be exactly 3 (TP, FP, FN). The guard rejects arrays whose final dimension differs — e.g. square class-confusion matrices or two-column tallies — protecting the arithmetic from silently wrong indexing.","triggerScenarios":"Passing a sv.ConfusionMatrix's (num_classes+1, num_classes+1) matrix, or a hand-built [TP, FP] array, to the module-level f1_score helper. Normally unreachable through sv.F1Score's public API, which constructs the 3-column stats itself.","commonSituations":"Importing internal helpers to compute F1 from a stored confusion matrix; misunderstanding that 'confusion matrix' here denotes the per-item TP/FP/FN tally.","solutions":["Build a (N, ..., 3) array: np.stack([tp, fp, fn], axis=-1)","Derive TP/FP/FN from a square matrix first (diagonal = TP, off-diagonal column/row sums = FP/FN) if that is what you have","Use the public sv.F1Score API instead of the internal helper"],"exampleFix":"# before\nf1_score(cm.matrix)  # square matrix -> ValueError\n\n# after\ntp = np.diag(cm.matrix).astype(np.float64)\nfp = cm.matrix.sum(axis=0) - tp\nfn = cm.matrix.sum(axis=1) - tp\nf1_score(np.stack([tp, fp, fn], axis=-1))  # (num_classes, 3)","handlingStrategy":"validation","validationCode":"def to_stats(tp, fp, fn) -> np.ndarray:\n    tp, fp, fn = (np.asarray(x, dtype=np.float64) for x in (tp, fp, fn))\n    assert tp.shape == fp.shape == fn.shape\n    return np.stack([tp, fp, fn], axis=-1)  # (..., 3)\n\nassert to_stats(tp, fp, fn).shape[-1] == 3","typeGuard":"def is_tpfpfn_array(arr: np.ndarray) -> bool:\n    \"\"\"True when the last axis holds exactly [TP, FP, FN].\"\"\"\n    return isinstance(arr, np.ndarray) and arr.ndim >= 1 and arr.shape[-1] == 3","tryCatchPattern":null,"preventionTips":["Convert square confusion matrices to per-class TP/FP/FN before calling F1 helpers","Use the public sv.F1Score API which builds the 3-column stats itself"],"tags":["metrics","f1-score","shape-validation","internal-api"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}