Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrating Polars into ui.table and ui.aggrid #3969

Merged
merged 11 commits into from
Nov 13, 2024
29 changes: 29 additions & 0 deletions nicegui/elements/aggrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
if TYPE_CHECKING:
import pandas as pd

if importlib.util.find_spec('polars'):
optional_features.register('polars')
if TYPE_CHECKING:
import polars as pl


class AgGrid(Element,
component='aggrid.js',
Expand Down Expand Up @@ -85,6 +90,30 @@ def is_special_dtype(dtype):
**options,
}, theme=theme, auto_size_columns=auto_size_columns)

@classmethod
def from_polars(cls,
df: 'pl.DataFrame', *,
theme: str = 'balham',
auto_size_columns: bool = True,
options: Dict = {}) -> Self: # noqa: B006
"""Create an AG Grid from a Polars DataFrame.

If the DataFrame contains non-UTF-8 datatypes, they will be converted to strings.
To use a different conversion, convert the DataFrame manually before passing it to this method.

:param df: Polars DataFrame
:param theme: AG Grid theme (default: 'balham')
:param auto_size_columns: whether to automatically resize columns to fit the grid width (default: `True`)
:param options: dictionary of additional AG Grid options
:return: AG Grid element
"""
return cls({
'columnDefs': [{'field': str(col)} for col in df.columns],
'rowData': df.to_dicts(),
'suppressFieldDotNotation': True,
**options,
}, theme=theme, auto_size_columns=auto_size_columns)

@property
def options(self) -> Dict:
"""The options dictionary."""
Expand Down
79 changes: 76 additions & 3 deletions nicegui/elements/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
if TYPE_CHECKING:
import pandas as pd

if importlib.util.find_spec('polars'):
optional_features.register('polars')
if TYPE_CHECKING:
import polars as pl


class Table(FilterElement, component='table.js'):

Expand Down Expand Up @@ -133,7 +138,47 @@ def from_pandas(cls,
:param on_select: callback which is invoked when the selection changes
:return: table element
"""
rows, columns_from_df = cls._df_to_rows_and_columns(df)
rows, columns_from_df = cls._pandas_df_to_rows_and_columns(df)
table = cls(
rows=rows,
columns=columns or columns_from_df,
column_defaults=column_defaults,
row_key=row_key,
title=title,
selection=selection,
pagination=pagination,
on_select=on_select,
)
table._use_columns_from_df = columns is None
return table

@classmethod
def from_polars(cls,
df: 'pl.DataFrame', *,
columns: Optional[List[Dict]] = None,
column_defaults: Optional[Dict] = None,
row_key: str = 'id',
title: Optional[str] = None,
selection: Optional[Literal['single', 'multiple']] = None,
pagination: Optional[Union[int, dict]] = None,
on_select: Optional[Handler[TableSelectionEventArguments]] = None) -> Self:
"""Create a table from a Polars DataFrame.

Note:
If the DataFrame contains non-UTF-8 datatypes, they will be converted to strings.
To use a different conversion, convert the DataFrame manually before passing it to this method.

:param df: Polars DataFrame
:param columns: list of column objects (defaults to the columns of the dataframe)
:param column_defaults: optional default column properties
:param row_key: name of the column containing unique data identifying the row (default: "id")
:param title: title of the table
:param selection: selection type ("single" or "multiple"; default: `None`)
:param pagination: a dictionary correlating to a pagination object or number of rows per page (`None` hides the pagination, 0 means "infinite"; default: `None`).
:param on_select: callback which is invoked when the selection changes
:return: table element
"""
rows, columns_from_df = cls._polars_df_to_rows_and_columns(df)
table = cls(
rows=rows,
columns=columns or columns_from_df,
Expand Down Expand Up @@ -164,7 +209,31 @@ def update_from_pandas(self,
:param columns: list of column objects (defaults to the columns of the dataframe)
:param column_defaults: optional default column properties
"""
rows, columns_from_df = self._df_to_rows_and_columns(df)
rows, columns_from_df = self._pandas_df_to_rows_and_columns(df)
self._update_table(rows, columns_from_df, clear_selection, columns, column_defaults)

def update_from_polars(self,
df: 'pl.DataFrame', *,
clear_selection: bool = True,
columns: Optional[List[Dict]] = None,
column_defaults: Optional[Dict] = None) -> None:
"""Update the table from a Polars DataFrame.

:param df: Polars DataFrame
:param clear_selection: whether to clear the selection (default: True)
:param columns: list of column objects (defaults to the columns of the dataframe)
:param column_defaults: optional default column properties
"""
rows, columns_from_df = self._polars_df_to_rows_and_columns(df)
self._update_table(rows, columns_from_df, clear_selection, columns, column_defaults)

def _update_table(self,
rows: List[Dict],
columns_from_df: List[Dict],
clear_selection: bool,
columns: Optional[List[Dict]],
column_defaults: Optional[Dict]) -> None:
"""Helper function to update the table."""
self.rows[:] = rows
if column_defaults is not None:
self._column_defaults = column_defaults
Expand All @@ -175,7 +244,7 @@ def update_from_pandas(self,
self.update()

@staticmethod
def _df_to_rows_and_columns(df: 'pd.DataFrame') -> Tuple[List[Dict], List[Dict]]:
def _pandas_df_to_rows_and_columns(df: 'pd.DataFrame') -> Tuple[List[Dict], List[Dict]]:
import pandas as pd # pylint: disable=import-outside-toplevel

def is_special_dtype(dtype):
Expand All @@ -195,6 +264,10 @@ def is_special_dtype(dtype):

return df.to_dict('records'), [{'name': col, 'label': col, 'field': col} for col in df.columns]

@staticmethod
def _polars_df_to_rows_and_columns(df: 'pl.DataFrame') -> Tuple[List[Dict], List[Dict]]:
return df.to_dicts(), [{'name': col, 'label': col, 'field': col} for col in df.columns]

@property
def rows(self) -> List[Dict]:
"""List of rows."""
Expand Down
1 change: 1 addition & 0 deletions nicegui/optional_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
'pandas',
'pillow',
'plotly',
'polars',
'pyecharts',
'webview',
'sass',
Expand Down
Loading