{"record":{"id":"cedcb108aa18d9a4","repo":"pandas-dev/pandas","slug":"is-not-a-partition-because-union-is-not-the-whole","errorCode":null,"errorMessage":"Is not a partition because union is not the whole.","messagePattern":"Is not a partition because union is not the whole\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/sparse/scipy_sparse.py","lineNumber":37,"sourceCode":"if TYPE_CHECKING:\n    from collections.abc import Iterable\n\n    import numpy as np\n    import scipy.sparse\n\n    from pandas._typing import (\n        IndexLabel,\n        npt,\n    )\n\n\ndef _check_is_partition(parts: Iterable, whole: Iterable) -> None:\n    whole = set(whole)\n    parts = [set(x) for x in parts]\n    if set.intersection(*parts) != set():\n        raise ValueError(\"Is not a partition because intersection is not null.\")\n    if set.union(*parts) != whole:\n        raise ValueError(\"Is not a partition because union is not the whole.\")\n\n\ndef _levels_to_axis(\n    ss,\n    levels: tuple[int] | list[int],\n    valid_ilocs: npt.NDArray[np.intp],\n    sort_labels: bool = False,\n) -> tuple[npt.NDArray[np.intp], list[IndexLabel]]:\n    \"\"\"\n    For a MultiIndexed sparse Series `ss`, return `ax_coords` and `ax_labels`,\n    where `ax_coords` are the coordinates along one of the two axes of the\n    destination sparse matrix, and `ax_labels` are the labels from `ss`' Index\n    which correspond to these coordinates.\n\n    Parameters\n    ----------\n    ss : Series\n    levels : tuple/list","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/sparse/scipy_sparse.py#L19-L55","documentation":"Thrown by _check_is_partition in pandas/core/arrays/sparse/scipy_sparse.py:37, the companion check to the intersection error. Here the union of row_levels and column_levels does not cover every level of the MultiIndex — at least one level is omitted from both groups. COO conversion needs to place every index level on exactly one axis, so an uncovered level is rejected.","triggerScenarios":"Calling ss.sparse.to_coo(row_levels=[0], column_levels=[1]) on a Series whose MultiIndex has 3 levels — level 2 is in neither group. Forgetting to list a level after extending the MultiIndex.","commonSituations":"Default row_levels=[0], column_levels=[1] used on a 3-level index without updating the call. Refactoring code that added a level to the MultiIndex but did not update to_coo arguments.","solutions":["List every level across the two groups: row_levels=[0], column_levels=[1,2] for a 3-level index.","Compute levels programmatically: row_levels=[0]; column_levels=[l for l in range(ss.index.nlevels) if l not in row_levels].","Verify coverage: assert set(row_levels) | set(column_levels) == set(range(ss.index.nlevels))."],"exampleFix":"// before\n# 3-level MultiIndex\nss.sparse.to_coo(row_levels=[0], column_levels=[1])  # level 2 uncovered\n\n// after\nss.sparse.to_coo(row_levels=[0], column_levels=[1, 2])","handlingStrategy":"validation","validationCode":"def covering_levels(row_levels, column_levels, nlevels):\n    missing = set(range(nlevels)) - (set(row_levels) | set(column_levels))\n    if missing:\n        raise ValueError(f'levels {missing} not assigned to any axis')\n    return row_levels, column_levels","typeGuard":"null","tryCatchPattern":"null","preventionTips":["After changing the MultiIndex, re-derive row_levels/column_levels programmatically.","Default to row_levels=[0] and column_levels=list(range(1, nlevels)) for the common case.","Add an assertion that the union equals the full level set before calling to_coo."],"tags":["sparse","scipy","coo","multiindex","partition"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}