Skip to content

Commit

Permalink
update api
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoGorelli committed Mar 10, 2024
1 parent f8cb4a5 commit c1ba3b8
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 25 deletions.
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ Or just vendor it, it's only a bunch of pure-Python files.

There are three steps to writing dataframe-agnostic code using Narwhals:

1. use `narwhals.to_polars_api` to wrap a pandas, Polars, cuDF, or Modin dataframe
in the Polars API
2. use the subset of the Polars API defined in https://github.com/MarcoGorelli/narwhals/blob/main/narwhals/spec/__init__.py.
3. use `DataFrame.to_native` to return an object to the user in their original
1. use `narwhals.translate_frame` to wrap a pandas or Polars DataFrame to a Narwhals DataFrame
2. (optional) use `narwhals.get_namespace` to get a namespace object
3. use the subset of the Polars API defined in https://github.com/MarcoGorelli/narwhals/blob/main/narwhals/spec/__init__.py.
Some methods are only available if you called `narwhals.translate_frame` with `is_eager=True`
4. use `narwhals.to_native` to return an object to the user in their original
dataframe flavour. For example:

- if you started with pandas, you'll get pandas back
Expand All @@ -47,7 +48,7 @@ from typing import TypeVar
import pandas as pd
import polars as pl

from narwhals import translate_frame
from narwhals import translate_frame, get_namespace, to_native

AnyDataFrame = TypeVar("AnyDataFrame")

Expand All @@ -56,8 +57,10 @@ def my_agnostic_function(
suppliers_native: AnyDataFrame,
parts_native: AnyDataFrame,
) -> AnyDataFrame:
suppliers, pl = translate_frame(suppliers_native)
parts, _ = translate_frame(parts_native)
suppliers = translate_frame(suppliers_native)
parts = translate_frame(parts_native)
pl = get_namespace(suppliers)

result = (
suppliers.join(parts, left_on="city", right_on="city")
.filter(
Expand All @@ -70,7 +73,7 @@ def my_agnostic_function(
weight_max=pl.col("weight").max(),
)
)
return result.to_native()
return to_native(result)
```
You can pass in a pandas or Polars dataframe, the output will be the same!
Let's try it out:
Expand Down
2 changes: 2 additions & 0 deletions narwhals/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from narwhals.containers import is_polars
from narwhals.containers import is_series
from narwhals.translate import get_namespace
from narwhals.translate import to_native
from narwhals.translate import translate_any
from narwhals.translate import translate_frame
from narwhals.translate import translate_series
Expand All @@ -20,4 +21,5 @@
"is_pandas",
"get_implementation",
"get_namespace",
"to_native",
]
3 changes: 0 additions & 3 deletions narwhals/pandas_like/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,3 @@ def is_eager(self) -> bool:
@property
def is_lazy(self) -> bool:
return self._is_lazy

def to_native(self) -> Any:
return self._dataframe
3 changes: 0 additions & 3 deletions narwhals/pandas_like/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,6 @@ def series(self) -> Any:
def dtype(self) -> DType:
return translate_dtype(self._series.dtype)

def to_native(self) -> Any:
return self._series

def cast(
self,
dtype: DType, # type: ignore[override]
Expand Down
7 changes: 0 additions & 7 deletions narwhals/polars.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,6 @@ def __init__(self, series: pl.Series) -> None:
def alias(self, name: str) -> Self:
return self.__class__(self._series.alias(name))

def to_native(self) -> Any:
return self._series

@property
def name(self) -> str:
return self._series.name
Expand Down Expand Up @@ -523,10 +520,6 @@ def head(self, n: int) -> Self:
def unique(self, subset: list[str]) -> Self:
return self._from_dataframe(self._dataframe.unique(subset))

# --- public, non-Polars ---
def to_native(self) -> Any:
return self._dataframe

@property
def is_eager(self) -> bool:
return self._is_eager
Expand Down
4 changes: 0 additions & 4 deletions narwhals/spec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,10 +299,6 @@ def head(self, n: int) -> Self:
def unique(self, subset: list[str]) -> Self:
...

# --- public, non-Polars ---
def to_native(self) -> Any:
...

@property
def is_eager(self) -> bool:
...
Expand Down
20 changes: 20 additions & 0 deletions narwhals/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,31 @@ def get_namespace(obj: Any) -> Namespace:
raise NotImplementedError


def to_native(obj: Any) -> Any:
from narwhals.pandas_like.dataframe import PandasDataFrame
from narwhals.pandas_like.series import PandasSeries
from narwhals.polars import PolarsDataFrame
from narwhals.polars import PolarsSeries

if isinstance(obj, PandasDataFrame):
return obj._dataframe
if isinstance(obj, PandasSeries):
return obj._series
if isinstance(obj, PolarsDataFrame):
return obj._dataframe
if isinstance(obj, PolarsSeries):
return obj._series

msg = f"Expected Narwhals object, got {type(obj)}."
raise TypeError(msg)


__all__ = [
"translate_frame",
"translate_series",
"translate_any",
"get_pandas",
"get_polars",
"get_namespace",
"to_native",
]

0 comments on commit c1ba3b8

Please sign in to comment.