Skip to content

Commit

Permalink
feat(python): Change default number of rows printed in Notebooks for …
Browse files Browse the repository at this point in the history
…DataFrame/Series to 10 (#14536)
  • Loading branch information
stinodego committed Feb 26, 2024
1 parent b8e1f31 commit c00a2bf
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 61 deletions.
9 changes: 5 additions & 4 deletions py-polars/polars/dataframe/_html.py
Expand Up @@ -58,15 +58,16 @@ def __init__(
self.elements: list[str] = []
self.max_cols = max_cols
self.max_rows = max_rows
self.series = from_series
self.from_series = from_series
self.row_idx: Iterable[int]
self.col_idx: Iterable[int]

if max_rows < df.height:
half, rest = divmod(max_rows, 2)
self.row_idx = [
*list(range(max_rows // 2)),
*list(range(half + rest)),
-1,
*list(range(df.height - max_rows // 2, df.height)),
*list(range(df.height - half, df.height)),
]
else:
self.row_idx = range(df.height)
Expand Down Expand Up @@ -132,7 +133,7 @@ def render(self) -> list[str]:
):
# format frame/series shape with '_' thousand-separators
s = self.df.shape
shape = f"({s[0]:_},)" if self.series else f"({s[0]:_}, {s[1]:_})"
shape = f"({s[0]:_},)" if self.from_series else f"({s[0]:_}, {s[1]:_})"

self.elements.append(f"<small>shape: {shape}</small>")

Expand Down
12 changes: 6 additions & 6 deletions py-polars/polars/dataframe/frame.py
Expand Up @@ -1806,7 +1806,7 @@ def __deepcopy__(self, memo: None = None) -> Self:
def _ipython_key_completions_(self) -> list[str]:
return self.columns

def _repr_html_(self, **kwargs: Any) -> str:
def _repr_html_(self, *, _from_series: bool = False) -> str:
"""
Format output data in HTML for display in Jupyter Notebooks.
Expand All @@ -1818,18 +1818,18 @@ def _repr_html_(self, **kwargs: Any) -> str:
"""
max_cols = int(os.environ.get("POLARS_FMT_MAX_COLS", default=75))
if max_cols < 0:
max_cols = self.shape[1]
max_rows = int(os.environ.get("POLARS_FMT_MAX_ROWS", default=25))
max_cols = self.width

max_rows = int(os.environ.get("POLARS_FMT_MAX_ROWS", default=10))
if max_rows < 0:
max_rows = self.shape[0]
max_rows = self.height

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

Expand Down
2 changes: 1 addition & 1 deletion py-polars/polars/series/series.py
Expand Up @@ -1488,7 +1488,7 @@ def __array_ufunc__(

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

@deprecate_renamed_parameter("row", "index", version="0.19.3")
def item(self, index: int | None = None) -> Any:
Expand Down
26 changes: 0 additions & 26 deletions py-polars/tests/unit/dataframe/test_df.py
Expand Up @@ -1058,32 +1058,6 @@ def test_literal_series() -> None:
)


def test_to_html() -> None:
# check it does not panic/error, and appears to contain
# a reasonable table with suitably escaped html entities.
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"<bar>": ["a", "b", "c"],
"<baz": ["a", "b", "c"],
"spam>": ["a", "b", "c"],
}
)
html = df._repr_html_()
for match in (
"<table",
'class="dataframe"',
"<th>foo</th>",
"<th>&lt;bar&gt;</th>",
"<th>&lt;baz</th>",
"<th>spam&gt;</th>",
"<td>1</td>",
"<td>2</td>",
"<td>3</td>",
):
assert match in html, f"Expected to find {match!r} in html repr"


def test_rename(df: pl.DataFrame) -> None:
out = df.rename({"strings": "bars", "int": "foos"})
# check if we can select these new columns
Expand Down
79 changes: 79 additions & 0 deletions py-polars/tests/unit/dataframe/test_repr_html.py
@@ -0,0 +1,79 @@
import polars as pl


def test_repr_html() -> None:
# check it does not panic/error, and appears to contain
# a reasonable table with suitably escaped html entities.
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"<bar>": ["a", "b", "c"],
"<baz": ["a", "b", "c"],
"spam>": ["a", "b", "c"],
}
)
html = df._repr_html_()
for match in (
"<table",
'class="dataframe"',
"<th>foo</th>",
"<th>&lt;bar&gt;</th>",
"<th>&lt;baz</th>",
"<th>spam&gt;</th>",
"<td>1</td>",
"<td>2</td>",
"<td>3</td>",
):
assert match in html, f"Expected to find {match!r} in html repr"


def test_html_tables() -> None:
df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

# default: header contains names/dtypes
header = "<thead><tr><th>a</th><th>b</th><th>c</th></tr><tr><td>i64</td><td>i64</td><td>i64</td></tr></thead>"
assert header in df._repr_html_()

# validate that relevant config options are respected
with pl.Config(tbl_hide_column_names=True):
header = "<thead><tr><td>i64</td><td>i64</td><td>i64</td></tr></thead>"
assert header in df._repr_html_()

with pl.Config(tbl_hide_column_data_types=True):
header = "<thead><tr><th>a</th><th>b</th><th>c</th></tr></thead>"
assert header in df._repr_html_()

with pl.Config(
tbl_hide_column_data_types=True,
tbl_hide_column_names=True,
):
header = "<thead></thead>"
assert header in df._repr_html_()


def test_df_repr_html_max_rows_default() -> None:
df = pl.DataFrame({"a": range(50)})

html = df._repr_html_()

expected_rows = 10
assert html.count("<td>") - 2 == expected_rows


def test_df_repr_html_max_rows_odd() -> None:
df = pl.DataFrame({"a": range(50)})

with pl.Config(tbl_rows=9):
html = df._repr_html_()

expected_rows = 9
assert html.count("<td>") - 2 == expected_rows


def test_series_repr_html_max_rows_default() -> None:
s = pl.Series("a", range(50))

html = s._repr_html_()

expected_rows = 10
assert html.count("<td>") - 2 == expected_rows
24 changes: 0 additions & 24 deletions py-polars/tests/unit/test_config.py
Expand Up @@ -87,30 +87,6 @@ def test_hide_header_elements() -> None:
)


def test_html_tables() -> None:
df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

# default: header contains names/dtypes
header = "<thead><tr><th>a</th><th>b</th><th>c</th></tr><tr><td>i64</td><td>i64</td><td>i64</td></tr></thead>"
assert header in df._repr_html_()

# validate that relevant config options are respected
with pl.Config(tbl_hide_column_names=True):
header = "<thead><tr><td>i64</td><td>i64</td><td>i64</td></tr></thead>"
assert header in df._repr_html_()

with pl.Config(tbl_hide_column_data_types=True):
header = "<thead><tr><th>a</th><th>b</th><th>c</th></tr></thead>"
assert header in df._repr_html_()

with pl.Config(
tbl_hide_column_data_types=True,
tbl_hide_column_names=True,
):
header = "<thead></thead>"
assert header in df._repr_html_()


def test_set_tbl_cols() -> None:
df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

Expand Down

0 comments on commit c00a2bf

Please sign in to comment.