|
17 | 17 | import csv as _csv |
18 | 18 | import datetime as _dt |
19 | 19 | import math as _math |
| 20 | +import os |
20 | 21 |
|
21 | 22 | from . import _array_core as _ac |
22 | 23 |
|
@@ -280,7 +281,7 @@ def _binop(self, other, fn): |
280 | 281 | if isinstance(other, Series): |
281 | 282 | other = other._data |
282 | 283 | # A native array (marr/oarr) is a sequence, not a scalar: without this |
283 | | - # it was broadcast whole against each element, so |
| 284 | + # it was broadcast whole against each element, so |
284 | 285 | # produced a column OF ARRAYS instead of an elementwise sum. |
285 | 286 | if not isinstance(other, (str, bytes)) and hasattr(other, "__len__") and hasattr(other, "__iter__") and not isinstance(other, dict): |
286 | 287 | other = list(other) |
@@ -1551,9 +1552,7 @@ def sql_type(values): |
1551 | 1552 | for v in values: |
1552 | 1553 | if v is None or _isnan(v): |
1553 | 1554 | continue |
1554 | | - if isinstance(v, bool): |
1555 | | - seen.add("INTEGER") |
1556 | | - elif isinstance(v, int): |
| 1555 | + if isinstance(v, bool) or isinstance(v, int): |
1557 | 1556 | seen.add("INTEGER") |
1558 | 1557 | elif isinstance(v, float): |
1559 | 1558 | seen.add("REAL") |
@@ -2917,7 +2916,6 @@ def close(self): |
2917 | 2916 |
|
2918 | 2917 | class ExcelFile: |
2919 | 2918 | def __init__(self, path): |
2920 | | - import xml.etree.ElementTree as ET |
2921 | 2919 | import zipfile |
2922 | 2920 | self._path = path |
2923 | 2921 | zf = zipfile.ZipFile(path) |
@@ -3017,11 +3015,46 @@ def _assert_series_equal(left, right, **kw): |
3017 | 3015 | DataFrame({"v": list(right)}), **kw) |
3018 | 3016 |
|
3019 | 3017 |
|
3020 | | -class _TestingNamespace(object): |
| 3018 | +class _TestingNamespace: |
3021 | 3019 | """Mirrors `pandas.testing`.""" |
3022 | 3020 |
|
3023 | 3021 | assert_frame_equal = staticmethod(_assert_frame_equal) |
3024 | 3022 | assert_series_equal = staticmethod(_assert_series_equal) |
3025 | 3023 |
|
3026 | 3024 |
|
3027 | 3025 | testing = _TestingNamespace() |
| 3026 | + |
| 3027 | + |
| 3028 | +def coerce_frame(data, what: str = "data") -> DataFrame: |
| 3029 | + """Return a native DataFrame for whatever a caller hands an estimator. |
| 3030 | +
|
| 3031 | + Accepts a native DataFrame (returned as is), a pandas DataFrame or any |
| 3032 | + object exposing ``columns`` and column access, a path to a CSV file, |
| 3033 | + or a sequence of row mappings. The estimators index with native masks; |
| 3034 | + a pandas frame reaching them directly treats those masks as labels and |
| 3035 | + fails with a KeyError (found on the flagship's own bundled sample, |
| 3036 | + 2026-09-18), so every public entry point coerces here first. |
| 3037 | + """ |
| 3038 | + if isinstance(data, DataFrame): |
| 3039 | + return data |
| 3040 | + if isinstance(data, (str, os.PathLike)): |
| 3041 | + path = os.fspath(data) |
| 3042 | + if not os.path.exists(path): |
| 3043 | + raise FileNotFoundError(f"{what}: no such file {path!r}") |
| 3044 | + return read_csv(path) |
| 3045 | + if hasattr(data, "columns") and hasattr(data, "__getitem__"): |
| 3046 | + cols = list(data.columns) |
| 3047 | + out = {} |
| 3048 | + for c in cols: |
| 3049 | + v = data[c] |
| 3050 | + out[c] = list(v.tolist()) if hasattr(v, "tolist") else list(v) |
| 3051 | + return DataFrame(out) |
| 3052 | + if isinstance(data, dict): |
| 3053 | + return DataFrame(data) |
| 3054 | + if isinstance(data, (list, tuple)): |
| 3055 | + rows = list(data) |
| 3056 | + if rows and all(isinstance(r, dict) for r in rows): |
| 3057 | + cols = list(rows[0].keys()) |
| 3058 | + return DataFrame({c: [r.get(c) for r in rows] for c in cols}) |
| 3059 | + raise TypeError(f"{what} must be a DataFrame (native or pandas), a CSV path, " |
| 3060 | + f"a dict of columns or a list of row dicts, got {type(data).__name__}") |
0 commit comments