{"record":{"id":"9affb2181372517e","repo":"pola-rs/polars","slug":"can-t-pass-a-series-with-missing-data-to-a-general","errorCode":null,"errorMessage":"can't pass a Series with missing data to a generalized ufunc, as it might give unexpected results. See https://docs.pola.rs/user-guide/expressions/missing-data/ for suggestions on how to remove or fill in missing data.","messagePattern":"can't pass a Series with missing data to a generalized ufunc, as it might give unexpected results\\. See https://docs\\.pola\\.rs/user-guide/expressions/missing-data/ for suggestions on how to remove or fill in missing data\\.","errorType":"exception","errorClass":"ComputeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/series/series.py","lineNumber":1682,"sourceCode":"                    dtype_char_minimum = dtype_ufunc\n                    break\n\n            # Override minimum dtype if requested.\n            dtype_char = (\n                np.dtype(kwargs.pop(\"dtype\")).char\n                if \"dtype\" in kwargs\n                else dtype_char_minimum\n            )\n\n            # Only generalized ufuncs have a signature set:\n            is_generalized_ufunc = bool(ufunc.signature)\n\n            if is_generalized_ufunc:\n                # Generalized ufuncs will operate on the whole array, so\n                # missing data can corrupt the results.\n                if self.has_nulls():\n                    msg = \"can't pass a Series with missing data to a generalized ufunc, as it might give unexpected results. See https://docs.pola.rs/user-guide/expressions/missing-data/ for suggestions on how to remove or fill in missing data.\"\n                    raise ComputeError(msg)\n                # If the input and output are the same size, e.g. \"(n)->(n)\" we\n                # can allocate ourselves and save a copy. If they're different,\n                # we let the ufunc do the allocation, since only it knows the\n                # output size.\n                assert ufunc.signature is not None  # pacify MyPy\n                ufunc_input, ufunc_output = ufunc.signature.split(\"->\")\n                if ufunc_output == \"()\":\n                    # If the result a scalar, just let the function do its\n                    # thing, no need for any song and dance involving\n                    # allocation:\n                    return ufunc(*args, dtype=dtype_char, **kwargs)\n                else:\n                    allocate_output = ufunc_input == ufunc_output\n            else:\n                allocate_output = True\n\n            f = get_ffi_func(\"apply_ufunc_<>\", numpy_char_code_to_dtype(dtype_char), s)\n","sourceCodeStart":1664,"sourceCodeEnd":1700,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/series/series.py#L1664-L1700","documentation":"Raised as polars ComputeError when a generalized ufunc (gufunc - a ufunc with a signature like '(n)->()' or '(m,n),(n,p)->(m,p)') is applied to a Series containing nulls. Gufuncs consume the whole backing buffer at once, so masked-out nulls would silently corrupt the math; Polars refuses instead of guessing how to handle missing values.","triggerScenarios":"`np.linalg.norm(s)` or `np.dot(a_s, b_s)` where either Series has nulls; `np.matmul(series_2d_view, other)` with missing data; any gufunc (ufunc.signature non-empty) dispatching through __array_ufunc__ while self.has_nulls() is true. Elementwise ufuncs are fine - nulls are re-masked afterwards.","commonSituations":"Linalg/feature math on real-world columns with missing values; joins or parses producing nulls that the developer forgot to handle; switching from elementwise ops (which tolerate nulls) to a gufunc like norm/dot and hitting the stricter rule.","solutions":["Drop nulls first: `s.drop_nulls()` before the gufunc (if the semantics allow omitting samples).","Fill nulls explicitly: `s.fill_null(0)` / `s.fill_null(strategy='mean')` / `s.interpolate()`.","Handle null-aware logic outside: split into `s.filter(s.is_not_null())` plus a mask, then stitch results back.","Catch polars.exceptions.ComputeError in generic numeric wrappers and surface a clearer message about missing data."],"exampleFix":"// before\ns = pl.Series([3.0, None, 4.0])\nnp.linalg.norm(s)  # ComputeError\n\n// after\nnp.linalg.norm(s.drop_nulls())\n# or\nnp.linalg.norm(s.fill_null(0.0))","handlingStrategy":"validation","validationCode":"def clean_for_gufunc(s: pl.Series, fill=None) -> pl.Series:\n    if s.has_nulls():\n        return s.fill_null(fill) if fill is not None else s.drop_nulls()\n    return s\n\nout = np.linalg.norm(clean_for_gufunc(s, fill=0.0))","typeGuard":"def is_gufunc_safe(s: pl.Series) -> bool:\n    return not s.has_nulls()","tryCatchPattern":"try:\n    out = np.linalg.norm(s)\nexcept pl.exceptions.ComputeError:\n    out = np.linalg.norm(s.drop_nulls())","preventionTips":["Check s.has_nulls() before any gufunc (signature != None) call: norm, dot, matmul.","Decide drop vs fill policy explicitly; never rely on gufuncs to ignore nulls.","Elementwise ufuncs tolerate nulls - the strict rule is gufunc-specific."],"tags":["polars","series","numpy","gufunc","nulls","compute-error","linalg"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}