Skip to content

Commit

Permalink
feat(python): add notebook html repr for Series (#5653)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-beedie committed Nov 28, 2022
1 parent 908a4c7 commit b3c2e4e
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 8 deletions.
14 changes: 10 additions & 4 deletions py-polars/polars/_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __enter__(self) -> None:
s = f"<{self.tag} "
for k, v in self.attributes.items():
s += f'{k}="{v}" '
s += ">"
s = f"{s.rstrip()}>"
self.elements.append(s)
else:
self.elements.append(f"<{self.tag}>")
Expand All @@ -46,13 +46,16 @@ def __init__(
df: DataFrame, # type: ignore[name-defined] # noqa: F821
max_cols: int = 75,
max_rows: int = 40,
from_series: bool = False,
):
self.df = df
self.elements: list[str] = []
self.max_cols = max_cols
self.max_rows = max_rows
self.series = from_series
self.row_idx: Iterable[int]
self.col_idx: Iterable[int]

if max_rows < df.height:
self.row_idx = (
list(range(0, max_rows // 2))
Expand All @@ -72,7 +75,11 @@ def __init__(

def write_header(self) -> None:
"""Write the header of an HTML table."""
self.elements.append(f"<small>shape: {self.df.shape}</small>")
shape = self.df.shape
if self.series:
shape = shape[:1]

self.elements.append(f"<small>shape: {shape}</small>")
with Tag(self.elements, "thead"):
with Tag(self.elements, "tr"):
columns = self.df.columns
Expand Down Expand Up @@ -122,8 +129,7 @@ class NotebookFormatter(HTMLFormatter):
"""
Class for formatting output data in HTML for display in Jupyter Notebooks.
This class is intended for functionality specific to DataFrame._repr_html_()
and DataFrame.to_html(notebook=True).
This class is intended for functionality specific to DataFrame._repr_html_().
"""

Expand Down
12 changes: 10 additions & 2 deletions py-polars/polars/internals/dataframe/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -1471,7 +1471,7 @@ def __deepcopy__(self: DF, memo: None = None) -> DF:
def _ipython_key_completions_(self) -> list[str]:
return self.columns

def _repr_html_(self) -> str:
def _repr_html_(self, **kwargs: Any) -> str:
"""
Format output data in HTML for display in Jupyter Notebooks.
Expand All @@ -1489,7 +1489,15 @@ def _repr_html_(self) -> str:
if max_rows < 0:
max_rows = self.shape[0]

return "\n".join(NotebookFormatter(self, max_cols, max_rows).render())
from_series = kwargs.get("from_series", False)
return "\n".join(
NotebookFormatter(
self,
max_cols=max_cols,
max_rows=max_rows,
from_series=from_series,
).render()
)

def to_arrow(self) -> pa.Table:
"""
Expand Down
4 changes: 4 additions & 0 deletions py-polars/polars/internals/series/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,10 @@ def __array_ufunc__(
f" `{method}`."
)

def _repr_html_(self) -> str:
"""Format output data in HTML for display in Jupyter Notebooks."""
return self.to_frame()._repr_html_(from_series=True)

def estimated_size(self, unit: SizeUnit = "b") -> int | float:
"""
Return an estimation of the total (heap) allocated size of the Series.
Expand Down
5 changes: 3 additions & 2 deletions py-polars/tests/unit/test_df.py
Original file line number Diff line number Diff line change
Expand Up @@ -1182,8 +1182,9 @@ def test_literal_series() -> None:


def test_to_html(df: pl.DataFrame) -> None:
# check if it does not panic/ error
df._repr_html_()
# check it does not panic/error, and appears to contain a table
html = df._repr_html_()
assert "<table" in html


def test_rows() -> None:
Expand Down
6 changes: 6 additions & 0 deletions py-polars/tests/unit/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -2395,6 +2395,12 @@ class XSeries(pl.Series):
assert str(n) in x_repr


def test_repr_html(df: pl.DataFrame) -> None:
# check it does not panic/error, and appears to contain a table
html = pl.Series("misc", [123, 456, 789])._repr_html_()
assert "<table" in html


def test_builtin_abs() -> None:
s = pl.Series("s", [-1, 0, 1, None])
assert abs(s).to_list() == [1, 0, 1, None]
Expand Down

0 comments on commit b3c2e4e

Please sign in to comment.