|
15 | 15 | from typing import Any, List, Literal, Optional, Tuple, Type, Union |
16 | 16 |
|
17 | 17 | import numpy as np |
18 | | -from numpy import issubdtype, ndarray |
| 18 | +from numpy import ndarray |
19 | 19 | from pandas import DataFrame, Series |
20 | 20 | from pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy |
21 | 21 | from scipy.sparse import csr_matrix |
@@ -365,24 +365,42 @@ def strip_schema(obj): |
365 | 365 |
|
366 | 366 |
|
367 | 367 | def _dtype_to_schema(typ) -> JSON_TYPE: |
| 368 | + from lale.helpers import _safe_issubdtype |
| 369 | + |
368 | 370 | result: JSON_TYPE |
369 | | - if typ is bool or issubdtype(typ, np.bool_): |
| 371 | + # Handle pandas extension dtypes (e.g., StringDtype in pandas 3.x) |
| 372 | + # These are not np.dtype instances and have a 'name' attribute |
| 373 | + if hasattr(typ, "name") and not isinstance(typ, np.dtype): |
| 374 | + # Pandas extension dtype - check the name to determine the type |
| 375 | + dtype_name = str(typ.name).lower() |
| 376 | + if "string" in dtype_name or "str" in dtype_name: |
| 377 | + result = {"type": "string"} |
| 378 | + elif "int" in dtype_name: |
| 379 | + result = {"type": "integer"} |
| 380 | + elif "float" in dtype_name or "double" in dtype_name: |
| 381 | + result = {"type": "number"} |
| 382 | + elif "bool" in dtype_name: |
| 383 | + result = {"type": "boolean"} |
| 384 | + else: |
| 385 | + # Default to string for unknown extension dtypes |
| 386 | + result = {"type": "string"} |
| 387 | + elif typ is bool or _safe_issubdtype(typ, np.bool_): |
370 | 388 | result = {"type": "boolean"} |
371 | | - elif issubdtype(typ, np.unsignedinteger): |
| 389 | + elif _safe_issubdtype(typ, np.unsignedinteger): |
372 | 390 | result = {"type": "integer", "minimum": 0} |
373 | | - elif issubdtype(typ, np.integer): |
| 391 | + elif _safe_issubdtype(typ, np.integer): |
374 | 392 | result = {"type": "integer"} |
375 | | - elif issubdtype(typ, np.number): |
| 393 | + elif _safe_issubdtype(typ, np.number): |
376 | 394 | result = {"type": "number"} |
377 | | - elif issubdtype(typ, np.str_) or issubdtype(typ, np.bytes_): |
| 395 | + elif _safe_issubdtype(typ, np.str_) or _safe_issubdtype(typ, np.bytes_): |
378 | 396 | result = {"type": "string"} |
379 | 397 | elif isinstance(typ, np.dtype): |
380 | 398 | if typ.fields: |
381 | 399 | props = {k: _dtype_to_schema(t) for k, t in typ.fields.items()} |
382 | 400 | result = {"type": "object", "properties": props} |
383 | 401 | elif typ.shape: |
384 | 402 | result = _shape_and_dtype_to_schema(typ.shape, typ.subdtype) |
385 | | - elif issubdtype(typ, np.object_): |
| 403 | + elif _safe_issubdtype(typ, np.object_): |
386 | 404 | result = {"type": "string"} |
387 | 405 | else: |
388 | 406 | assert False, f"unexpected dtype {typ}" |
|
0 commit comments