|
23 | 23 | from narwhals._expression_parsing import extract_compliant |
24 | 24 | from narwhals._expression_parsing import infer_kind |
25 | 25 | from narwhals._expression_parsing import is_scalar_like |
| 26 | +from narwhals.dependencies import is_narwhals_series |
26 | 27 | from narwhals.dependencies import is_numpy_array |
27 | 28 | from narwhals.dependencies import is_numpy_array_2d |
28 | 29 | from narwhals.expr import Expr |
29 | | -from narwhals.schema import Schema |
30 | 30 | from narwhals.series import Series |
31 | 31 | from narwhals.translate import from_native |
32 | 32 | from narwhals.translate import to_native |
|
50 | 50 |
|
51 | 51 | from narwhals._compliant import CompliantExpr |
52 | 52 | from narwhals._compliant import CompliantNamespace |
53 | | - from narwhals._pandas_like.series import PandasLikeSeries |
54 | 53 | from narwhals.dataframe import DataFrame |
55 | 54 | from narwhals.dataframe import LazyFrame |
56 | 55 | from narwhals.dtypes import DType |
| 56 | + from narwhals.schema import Schema |
57 | 57 | from narwhals.series import Series |
58 | | - from narwhals.typing import DTypeBackend |
59 | 58 | from narwhals.typing import IntoDataFrameT |
60 | 59 | from narwhals.typing import IntoExpr |
61 | 60 | from narwhals.typing import IntoFrameT |
@@ -329,91 +328,56 @@ def from_dict( |
329 | 328 | | 1 2 4 | |
330 | 329 | └──────────────────┘ |
331 | 330 | """ |
332 | | - return _from_dict_impl(data, schema, backend=backend) |
| 331 | + return _from_dict_impl(data, schema, backend=backend, version=Version.MAIN) |
333 | 332 |
|
334 | 333 |
|
335 | 334 | def _from_dict_impl( |
336 | 335 | data: Mapping[str, Any], |
337 | | - schema: Mapping[str, DType] | Schema | None = None, |
| 336 | + schema: Mapping[str, DType] | Schema | None, |
338 | 337 | *, |
339 | | - backend: ModuleType | Implementation | str | None = None, |
| 338 | + backend: ModuleType | Implementation | str | None, |
| 339 | + version: Version, |
340 | 340 | ) -> DataFrame[Any]: |
341 | | - from narwhals.series import Series |
342 | | - |
343 | 341 | if not data: |
344 | 342 | msg = "from_dict cannot be called with empty dictionary" |
345 | 343 | raise ValueError(msg) |
346 | 344 | if backend is None: |
347 | | - for val in data.values(): |
348 | | - if isinstance(val, Series): |
349 | | - native_namespace = val.__native_namespace__() |
350 | | - break |
351 | | - else: |
352 | | - msg = "Calling `from_dict` without `backend` is only supported if all input values are already Narwhals Series" |
353 | | - raise TypeError(msg) |
354 | | - data = {key: to_native(value, pass_through=True) for key, value in data.items()} |
355 | | - eager_backend = Implementation.from_native_namespace(native_namespace) |
356 | | - else: |
357 | | - eager_backend = Implementation.from_backend(backend) |
358 | | - native_namespace = eager_backend.to_native_namespace() |
359 | | - |
360 | | - supported_eager_backends = ( |
361 | | - Implementation.POLARS, |
362 | | - Implementation.PANDAS, |
363 | | - Implementation.PYARROW, |
364 | | - Implementation.MODIN, |
365 | | - Implementation.CUDF, |
366 | | - ) |
367 | | - if eager_backend is not None and eager_backend not in supported_eager_backends: |
368 | | - msg = f"Unsupported `backend` value.\nExpected one of {supported_eager_backends} or None, got: {eager_backend}." |
369 | | - raise ValueError(msg) |
370 | | - if eager_backend is Implementation.POLARS: |
371 | | - schema_pl = Schema(schema).to_polars() if schema else None |
372 | | - native_frame = native_namespace.from_dict(data, schema=schema_pl) |
373 | | - elif eager_backend.is_pandas_like(): |
374 | | - from narwhals._pandas_like.utils import align_and_extract_native |
375 | | - |
376 | | - aligned_data = {} |
377 | | - left_most_series = None |
378 | | - for key, native_series in data.items(): |
379 | | - if isinstance(native_series, native_namespace.Series): |
380 | | - compliant_series = from_native( |
381 | | - native_series, series_only=True |
382 | | - )._compliant_series |
383 | | - if left_most_series is None: |
384 | | - left_most_series = cast("PandasLikeSeries", compliant_series) |
385 | | - aligned_data[key] = native_series |
386 | | - else: |
387 | | - aligned_data[key] = align_and_extract_native( |
388 | | - left_most_series, compliant_series |
389 | | - )[1] |
390 | | - else: |
391 | | - aligned_data[key] = native_series |
392 | | - |
393 | | - native_frame = native_namespace.DataFrame.from_dict(aligned_data) |
394 | | - |
395 | | - if schema: |
396 | | - from narwhals._pandas_like.utils import get_dtype_backend |
397 | | - |
398 | | - it: Iterable[DTypeBackend] = ( |
399 | | - get_dtype_backend(native_type, eager_backend) |
400 | | - for native_type in native_frame.dtypes |
401 | | - ) |
402 | | - pd_schema = Schema(schema).to_pandas(it) |
403 | | - native_frame = native_frame.astype(pd_schema) |
404 | | - |
405 | | - elif eager_backend is Implementation.PYARROW: |
406 | | - pa_schema = Schema(schema).to_arrow() if schema is not None else schema |
407 | | - native_frame = native_namespace.table(data, schema=pa_schema) |
408 | | - else: # pragma: no cover |
| 345 | + data, backend = _from_dict_no_backend(data) |
| 346 | + implementation = Implementation.from_backend(backend) |
| 347 | + if is_eager_allowed(implementation): |
| 348 | + ns = _into_compliant_namespace(implementation, version) |
| 349 | + frame = ns._dataframe.from_dict(data, schema=schema, context=ns) |
| 350 | + return from_native(frame, eager_only=True) |
| 351 | + elif implementation is Implementation.UNKNOWN: # pragma: no cover |
| 352 | + native_namespace = implementation.to_native_namespace() |
409 | 353 | try: |
410 | 354 | # implementation is UNKNOWN, Narwhals extension using this feature should |
411 | 355 | # implement `from_dict` function in the top-level namespace. |
412 | 356 | native_frame = native_namespace.from_dict(data, schema=schema) |
413 | 357 | except AttributeError as e: |
414 | 358 | msg = "Unknown namespace is expected to implement `from_dict` function." |
415 | 359 | raise AttributeError(msg) from e |
416 | | - return from_native(native_frame, eager_only=True) |
| 360 | + return from_native(native_frame, eager_only=True) |
| 361 | + msg = ( |
| 362 | + f"Unsupported `backend` value.\nExpected one of " |
| 363 | + f"{Implementation.POLARS, Implementation.PANDAS, Implementation.PYARROW, Implementation.MODIN, Implementation.CUDF} " |
| 364 | + f"or None, got: {implementation}." |
| 365 | + ) |
| 366 | + raise ValueError(msg) |
| 367 | + |
| 368 | + |
| 369 | +def _from_dict_no_backend( |
| 370 | + data: Mapping[str, Series[Any] | Any], / |
| 371 | +) -> tuple[dict[str, Series[Any] | Any], ModuleType]: |
| 372 | + for val in data.values(): |
| 373 | + if is_narwhals_series(val): |
| 374 | + native_namespace = val.__native_namespace__() |
| 375 | + break |
| 376 | + else: |
| 377 | + msg = "Calling `from_dict` without `backend` is only supported if all input values are already Narwhals Series" |
| 378 | + raise TypeError(msg) |
| 379 | + data = {key: to_native(value, pass_through=True) for key, value in data.items()} |
| 380 | + return data, native_namespace |
417 | 381 |
|
418 | 382 |
|
419 | 383 | @deprecate_native_namespace(warn_version="1.31.0", required=True) |
|
0 commit comments