Skip to content

Commit

Permalink
feat: allow from_dict to not have native_namespace arg if all input…
Browse files Browse the repository at this point in the history
…s are already narwhals Series (#760)
  • Loading branch information
MarcoGorelli authored Aug 12, 2024
1 parent 54cd277 commit 5b0df48
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 4 deletions.
20 changes: 18 additions & 2 deletions narwhals/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def from_dict(
data: dict[str, Any],
schema: dict[str, DType] | Schema | None = None,
*,
native_namespace: ModuleType,
native_namespace: ModuleType | None = None,
) -> DataFrame[Any]:
"""
Instantiate DataFrame from dictionary.
Expand All @@ -64,7 +64,8 @@ def from_dict(
Arguments:
data: Dictionary to create DataFrame from.
schema: The DataFrame schema as Schema or dict of {name: type}.
native_namespace: The native library to use for DataFrame creation.
native_namespace: The native library to use for DataFrame creation. Only
necessary if inputs are not Narwhals Series.
Examples:
>>> import pandas as pd
Expand Down Expand Up @@ -97,6 +98,21 @@ def from_dict(
│ 2 ┆ 4 │
└─────┴─────┘
"""
from narwhals.series import Series
from narwhals.translate import to_native

if not data:
msg = "from_dict cannot be called with empty dictionary"
raise ValueError(msg)
if native_namespace is None:
for val in data.values():
if isinstance(val, Series):
native_namespace = val.__native_namespace__()
break
else:
msg = "Calling `from_dict` without `native_namespace` is only supported if all input values are already Narwhals Series"
raise TypeError(msg)
data = {key: to_native(value, strict=False) for key, value in data.items()}
implementation = Implementation.from_native_namespace(native_namespace)

if implementation is Implementation.POLARS:
Expand Down
5 changes: 3 additions & 2 deletions narwhals/stable/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -1473,7 +1473,7 @@ def from_dict(
data: dict[str, Any],
schema: dict[str, DType] | Schema | None = None,
*,
native_namespace: ModuleType,
native_namespace: ModuleType | None = None,
) -> DataFrame[Any]:
"""
Instantiate DataFrame from dictionary.
Expand All @@ -1485,7 +1485,8 @@ def from_dict(
Arguments:
data: Dictionary to create DataFrame from.
schema: The DataFrame schema as Schema or dict of {name: type}.
native_namespace: The native library to use for DataFrame creation.
native_namespace: The native library to use for DataFrame creation. Only
necessary if inputs are not Narwhals Series.
Examples:
>>> import pandas as pd
Expand Down
24 changes: 24 additions & 0 deletions tests/from_dict_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,27 @@ def test_from_dict_schema(constructor: Any, request: Any) -> None:
schema=schema, # type: ignore[arg-type]
)
assert result.collect_schema() == schema


def test_from_dict_without_namespace(constructor: Any) -> None:
df = nw.from_native(constructor({"a": [1, 2, 3], "b": [4, 5, 6]})).lazy().collect()
result = nw.from_dict({"c": df["a"], "d": df["b"]})
compare_dicts(result, {"c": [1, 2, 3], "d": [4, 5, 6]})


def test_from_dict_without_namespace_invalid(constructor: Any) -> None:
df = nw.from_native(constructor({"a": [1, 2, 3], "b": [4, 5, 6]})).lazy().collect()
with pytest.raises(TypeError, match="namespace"):
nw.from_dict({"c": nw.to_native(df["a"]), "d": nw.to_native(df["b"])})


def test_from_dict_one_native_one_narwhals(constructor: Any) -> None:
df = nw.from_native(constructor({"a": [1, 2, 3], "b": [4, 5, 6]})).lazy().collect()
result = nw.from_dict({"c": nw.to_native(df["a"]), "d": df["b"]})
expected = {"c": [1, 2, 3], "d": [4, 5, 6]}
compare_dicts(result, expected)


def test_from_dict_empty() -> None:
with pytest.raises(ValueError, match="empty"):
nw.from_dict({})

0 comments on commit 5b0df48

Please sign in to comment.