Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
bbe6261
feat: start rough draft of narwhals support
machow Jul 25, 2024
12d39cf
Merge branch 'main' into feat-narwhals
schloerke Jul 26, 2024
57fe20f
add narwhals to deps
schloerke Jul 26, 2024
81058ea
lint
schloerke Jul 26, 2024
c86ec1e
Merge branch 'main' into machow-feat-narwhals
schloerke Jul 26, 2024
869aa7a
Fix typing issues
schloerke Jul 26, 2024
271ec5f
Rename fixtures
schloerke Jul 26, 2024
a5714e4
Partial for narwhals serialize frame. Still needs work. Need datetime…
schloerke Jul 26, 2024
f11769f
Merge branch 'main' into machow-feat-narwhals
schloerke Aug 26, 2024
db83bc7
add missing narwhals dep in config
schloerke Aug 26, 2024
1155868
Merge branch 'main' into machow-feat-narwhals
schloerke Aug 30, 2024
be6985a
Merge branch 'main' into machow-feat-narwhals
schloerke Sep 5, 2024
9318a5b
Disable timedelta dtype within pandas (as moving towards all narwhals…
schloerke Sep 5, 2024
42d0353
Scaffold out more dtypes
schloerke Sep 5, 2024
3e3c8c0
Use `orjson` instead of `json` package to serialize json. (Native dat…
schloerke Sep 5, 2024
ef2ee1c
Update tests, trying to support more dtypes (more to go). Disable non…
schloerke Sep 5, 2024
824f6b4
Merge branch 'main' into feat-narwhals
schloerke Sep 16, 2024
563bc52
Bump min version of narwhals to v1.8.0 for `nw.Series.scatter(rows, v…
schloerke Sep 16, 2024
861329a
Expose narwhals types of `DataFrame`, `DataFrameT`, `IntoDataFrame`, …
schloerke Sep 16, 2024
5172734
Add narwhals for types and integrate into _tbl_data.py
schloerke Sep 16, 2024
ce6cd35
Styles function will accept `IntoDataFrameT` type
schloerke Sep 16, 2024
3fea35c
Be explicit on type of object being inspected
schloerke Sep 16, 2024
dc8179f
Disable all pandas specific functions for data frame
schloerke Sep 16, 2024
002ba7e
DataGrid and DataTable can now accept `IntroDataFrameT`
schloerke Sep 16, 2024
2eacaa5
Upgrade `render.data_frame` to handle `IntoDataFrameT`. Add `._nw_dat…
schloerke Sep 16, 2024
32fe466
Update typing
schloerke Sep 16, 2024
00088be
Update tests to handle the new types
schloerke Sep 16, 2024
cf6b283
Fix port test to be more robust
schloerke Sep 16, 2024
9cf92d3
Add quick short circuit
schloerke Sep 17, 2024
cfb5ac7
Add TODO with hint from Marco
schloerke Sep 17, 2024
aa0d8dd
Fix html column bug
schloerke Sep 17, 2024
8b88353
Fix narwhals subset bugs
schloerke Sep 17, 2024
cf5a845
Do not convert data early
schloerke Sep 17, 2024
08e70ad
Update _data_frame.py
schloerke Sep 17, 2024
3b4be82
Fix another html column issue
schloerke Sep 17, 2024
1f8c9c6
lint
schloerke Sep 18, 2024
6273885
Update tests for narwhals. Note: py-htmltools is holding up PR's test…
schloerke Sep 18, 2024
815baa4
Merge branch 'main' into machow-feat-narwhals
schloerke Sep 23, 2024
d0282aa
Remove `SeriesLike` type
schloerke Sep 23, 2024
c9009e8
Tighten up code. Remove mention of singledispatch
schloerke Sep 23, 2024
e7d1fe8
Merge branch 'main' into machow-feat-narwhals
schloerke Sep 24, 2024
a0681b7
Use latest narwhals (1.8.3) and dev htmltools
schloerke Sep 24, 2024
d0772e4
Remove many TODOs
schloerke Sep 24, 2024
77d6085
Update _html.py
schloerke Sep 25, 2024
d66e1de
Update docs
schloerke Sep 25, 2024
a950d7d
Test more narwhals types. Add comment for file.
schloerke Sep 25, 2024
baed7f9
Add more cell types to data frame js. Add test app for all types and …
schloerke Sep 25, 2024
90276a3
Update app.py
schloerke Sep 27, 2024
246819f
If the cell value is `null`, display `""` for table cell value
schloerke Sep 27, 2024
0e57199
Test that `DataFrame.data()` returns the same type as render method
schloerke Sep 27, 2024
f4caa1f
Temporarily disable htmltools install
schloerke Sep 27, 2024
cb960c4
Install htmltools from `taglist_not_list` branch on GH
schloerke Sep 27, 2024
df72692
Add new narwhals types
schloerke Sep 27, 2024
6e22352
Update app.py
schloerke Sep 27, 2024
71ead9f
Update htmltools dep to include new TagList inheriting from UserList
schloerke Sep 30, 2024
f8d7c13
Add note about narwhals inspecting only the first sentence
schloerke Sep 30, 2024
46a1203
Be sure to return the cell text
schloerke Sep 30, 2024
da98bd8
Rename internal method `cell_contains_htmltoolslike` to `ui_must_be_p…
schloerke Sep 30, 2024
4fc66b3
Leverage htmltools's new HTML/UserString and TagList/UserList objects…
schloerke Sep 30, 2024
98d40e1
Update app to use html within the first row to tell narwhals it's an …
schloerke Sep 30, 2024
17c3b2a
Add `reactive_method()` and tests
schloerke Oct 1, 2024
f7c2e65
Utilize new `@reactive_calc_method` decorator in data frame class
schloerke Oct 1, 2024
5a47cc8
Merge branch 'main' into machow-feat-narwhals
schloerke Oct 1, 2024
ee2f485
Update _reactive_method.py
schloerke Oct 1, 2024
f817510
Update date warning when non-narwhals compatible data is received
schloerke Oct 1, 2024
8f68a4b
Update narwhals
schloerke Oct 1, 2024
758fd86
Change equality subclass checks to isinstance checks
schloerke Oct 1, 2024
f5e2c6a
Update test_render_data_frame_tbl_data.py
schloerke Oct 1, 2024
47b855c
Update test_render_data_frame_tbl_data.py
schloerke Oct 1, 2024
1a9aa96
Update _data_frame.py
schloerke Oct 1, 2024
a9d3c67
Add another future impl for reactive_value_method
schloerke Oct 1, 2024
a25a3d1
Restore odd skip as it is still required
schloerke Oct 1, 2024
fddd87f
Drop testing of python 3.8 (minimal changes)
schloerke Oct 1, 2024
a992b2f
Revert "chore: revert check where airmass skips on python 3.9 (#1708)"
schloerke Oct 1, 2024
12544b6
Update Makefile
schloerke Oct 1, 2024
6f28b06
Revert htmltools install as it isn't needed anymore
schloerke Oct 1, 2024
3f3b9be
Drop `is_into_data_frame`
schloerke Oct 2, 2024
bc99573
Code review
schloerke Oct 2, 2024
e0caacd
Remove unused `._type_hints`
schloerke Oct 2, 2024
3791a3b
Update _data_frame.py
schloerke Oct 3, 2024
e2444d1
Collect all JS deps of a data frame sep from the content. For each ce…
schloerke Oct 3, 2024
1eef355
Update test_render_data_frame_tbl_data.py
schloerke Oct 3, 2024
f5e9099
Try requiring py-htmltools directly. Do not install py-htmltools manu…
schloerke Oct 3, 2024
c397e82
Use htmltools github ref
schloerke Oct 3, 2024
3ce71f9
Remove small and unused narwals wrapper methods
schloerke Oct 3, 2024
1a6e917
Update Makefile
schloerke Oct 3, 2024
6142df3
Store the reactive calc on self directly. This adds a two-way pointer…
schloerke Oct 3, 2024
529f371
Move py-shinylive github install to pyproject
schloerke Oct 3, 2024
1a26d7d
Revert "Move py-shinylive github install to pyproject"
schloerke Oct 3, 2024
b29e037
Perform the install in a single command
schloerke Oct 3, 2024
1fcef38
Merge branch 'main' into feat-narwhals
schloerke Oct 3, 2024
1f46fb3
Un-export `render.[DataFrame, DataFrameT, IntoDataFrame, IntoDataFram…
schloerke Oct 3, 2024
6630f61
Add narwhals support for `@render.table`; Remove no longer necessary …
schloerke Oct 3, 2024
d1b53ff
Update CHANGELOG.md
schloerke Oct 3, 2024
9f2a29d
Fix test
schloerke Oct 3, 2024
a1a9e54
Add changelog for `@render.table`
schloerke Oct 3, 2024
6ee52a9
Set max version of narwhals to `<1.10.0`
schloerke Oct 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions shiny/render/_data_frame_utils/_tbl_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from functools import singledispatch
from typing import Any, List, Tuple, cast

import narwhals.stable.v1 as nw
from htmltools import TagNode

from ..._typing_extensions import TypeIs
Expand Down Expand Up @@ -184,7 +185,7 @@ def _(col: PlSeries) -> FrameDtype:

from ._html import col_contains_shiny_html

if col.dtype.is_(pl.String):
if col.dtype == pl.String():
if col_contains_shiny_html(col):
type_ = "html"
else:
Expand All @@ -203,6 +204,30 @@ def _(col: PlSeries) -> FrameDtype:
return {"type": type_}


@serialize_dtype.register
def _(col: nw.Series) -> FrameDtype:

from ._html import col_contains_shiny_html

if col.dtype == nw.String():
if col_contains_shiny_html(col):
type_ = "html"
else:
type_ = "string"
elif col.dtype.is_numeric():
type_ = "numeric"

elif col.dtype == nw.Categorical():
categories = col.cat.get_categories().to_list()
return {"type": "categorical", "categories": categories}
else:
type_ = "unknown"
if col_contains_shiny_html(col):
type_ = "html"

return {"type": type_}


# serialize_frame ----------------------------------------------------------------------


Expand All @@ -218,6 +243,8 @@ def _(data: PdDataFrame) -> FrameJson:
return serialize_frame_pd(data)


# TODO: test this
@serialize_frame.register(nw.DataFrame)
@serialize_frame.register
def _(data: PlDataFrame) -> FrameJson:
import json
Expand Down Expand Up @@ -308,6 +335,7 @@ def _(
return data.iloc[indx_rows, indx_cols]


@subset_frame.register(nw.DataFrame)
@subset_frame.register
def _(
data: PlDataFrame,
Expand All @@ -321,7 +349,7 @@ def _(
else slice(None)
)
indx_rows = rows if rows is not None else slice(None)
return data[indx_rows, indx_cols]
return data[indx_cols][indx_rows]


# get_frame_cell -----------------------------------------------------------------------
Expand All @@ -341,9 +369,10 @@ def _(data: PdDataFrame, row: int, col: int) -> Any:
)


@get_frame_cell.register(nw.DataFrame)
@get_frame_cell.register
def _(data: PlDataFrame, row: int, col: int) -> Any:
return data[row, col]
return data.item(row, col)


# shape --------------------------------------------------------------------------------
Expand All @@ -359,6 +388,7 @@ def _(data: PdDataFrame) -> Tuple[int, ...]:
return data.shape


@frame_shape.register(nw.DataFrame)
@frame_shape.register
def _(data: PlDataFrame) -> Tuple[int, ...]:
return data.shape
Expand All @@ -377,6 +407,7 @@ def _(data: PdDataFrame) -> PdDataFrame:
return data.copy()


@copy_frame.register(nw.DataFrame)
@copy_frame.register
def _(data: PlDataFrame) -> PlDataFrame:
return data.clone()
Expand All @@ -393,6 +424,7 @@ def _(data: PdDataFrame) -> List[str]:
return data.columns.to_list()


@frame_column_names.register(nw.DataFrame)
@frame_column_names.register
def _(data: PlDataFrame) -> List[str]:
return data.columns
1 change: 1 addition & 0 deletions tests/pytest/test_render_data_frame.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import narwhals as nw
import pandas as pd
import pytest

Expand Down
36 changes: 32 additions & 4 deletions tests/pytest/test_render_data_frame_tbl_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import datetime
from typing import Any, Union

import narwhals.stable.v1 as nw
import pandas as pd
import polars as pl
import polars.testing as pl_testing
Expand Down Expand Up @@ -53,6 +54,9 @@ class D:
params_frames = [
pytest.param(pd.DataFrame, id="pandas"),
pytest.param(pl.DataFrame, id="polars"),
pytest.param(
lambda d: nw.from_native(pl.DataFrame(d), eager_only=True), id="narwhals"
),
]

DataFrameLike: TypeAlias = Union[pd.DataFrame, pl.DataFrame]
Expand Down Expand Up @@ -93,6 +97,17 @@ def assert_frame_equal(
raise NotImplementedError(f"Unsupported data type: {type(src)}")


def assert_frame_equal2(
src: pd.DataFrame | pl.DataFrame,
target_dict: dict,
use_index: bool = False,
):
src = nw.to_native(src, strict=False)
target = nw.to_native(src, strict=False).__class__(target_dict)

assert_frame_equal(src, target, use_index)


# TODO: explicitly pass dtype= when doing Series construction
@pytest.mark.parametrize(
"ser, res_type",
Expand Down Expand Up @@ -126,6 +141,13 @@ def test_serialize_dtype(
],
res_type: str,
):
if isinstance(ser, pl.Series):
assert (
serialize_dtype(nw.from_native(ser, eager_only=True, allow_series=True))[
"type"
]
== res_type
)
assert serialize_dtype(ser)["type"] == res_type


Expand Down Expand Up @@ -158,9 +180,9 @@ def test_serialize_frame(df: DataFrameLike):
def test_subset_frame(df: DataFrameLike):
# TODO: this assumes subset_frame doesn't reset index
res = subset_frame(df, rows=[1], cols=["chr", "num"])
dst = df.__class__({"chr": ["b"], "num": [2]})
dst = {"chr": ["b"], "num": [2]}

assert_frame_equal(res, dst)
assert_frame_equal2(res, dst)


def test_get_frame_cell(df: DataFrameLike):
Expand All @@ -176,14 +198,20 @@ def test_copy_frame(df: DataFrameLike):
def test_subset_frame_rows_single(small_df: DataFrameLike):
res = subset_frame(small_df, rows=[1])

assert_frame_equal(res, small_df.__class__({"x": [2], "y": [4]}))
assert_frame_equal2(
res,
{"x": [2], "y": [4]},
)


def test_subset_frame_cols_single(small_df: DataFrameLike):
# TODO: include test of polars
res = subset_frame(small_df, cols=["y"])

assert_frame_equal(res, small_df.__class__({"y": [3, 4]}))
assert_frame_equal2(
res,
{"y": [3, 4]},
)


def test_shape(small_df: DataFrameLike):
Expand Down