Skip to content

Commit a9a63e7

Browse files
authored
Merge branch 'master' into issue_43108
2 parents 0012423 + 0e92697 commit a9a63e7

File tree

12 files changed

+199
-45
lines changed

12 files changed

+199
-45
lines changed

doc/source/user_guide/options.rst

+19-15
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ and so passing in a substring will work - as long as it is unambiguous:
3838

3939
.. ipython:: python
4040
41-
pd.get_option("display.max_rows")
42-
pd.set_option("display.max_rows", 101)
43-
pd.get_option("display.max_rows")
44-
pd.set_option("max_r", 102)
45-
pd.get_option("display.max_rows")
41+
pd.get_option("display.chop_threshold")
42+
pd.set_option("display.chop_threshold", 2)
43+
pd.get_option("display.chop_threshold")
44+
pd.set_option("chop", 4)
45+
pd.get_option("display.chop_threshold")
4646
4747
4848
The following will **not work** because it matches multiple option names, e.g.
@@ -52,7 +52,7 @@ The following will **not work** because it matches multiple option names, e.g.
5252
:okexcept:
5353
5454
try:
55-
pd.get_option("column")
55+
pd.get_option("max")
5656
except KeyError as e:
5757
print(e)
5858
@@ -153,27 +153,27 @@ lines are replaced by an ellipsis.
153153
.. ipython:: python
154154
155155
df = pd.DataFrame(np.random.randn(7, 2))
156-
pd.set_option("max_rows", 7)
156+
pd.set_option("display.max_rows", 7)
157157
df
158-
pd.set_option("max_rows", 5)
158+
pd.set_option("display.max_rows", 5)
159159
df
160-
pd.reset_option("max_rows")
160+
pd.reset_option("display.max_rows")
161161
162162
Once the ``display.max_rows`` is exceeded, the ``display.min_rows`` options
163163
determines how many rows are shown in the truncated repr.
164164

165165
.. ipython:: python
166166
167-
pd.set_option("max_rows", 8)
168-
pd.set_option("min_rows", 4)
167+
pd.set_option("display.max_rows", 8)
168+
pd.set_option("display.min_rows", 4)
169169
# below max_rows -> all rows shown
170170
df = pd.DataFrame(np.random.randn(7, 2))
171171
df
172172
# above max_rows -> only min_rows (4) rows shown
173173
df = pd.DataFrame(np.random.randn(9, 2))
174174
df
175-
pd.reset_option("max_rows")
176-
pd.reset_option("min_rows")
175+
pd.reset_option("display.max_rows")
176+
pd.reset_option("display.min_rows")
177177
178178
``display.expand_frame_repr`` allows for the representation of
179179
dataframes to stretch across pages, wrapped over the full column vs row-wise.
@@ -193,13 +193,13 @@ dataframes to stretch across pages, wrapped over the full column vs row-wise.
193193
.. ipython:: python
194194
195195
df = pd.DataFrame(np.random.randn(10, 10))
196-
pd.set_option("max_rows", 5)
196+
pd.set_option("display.max_rows", 5)
197197
pd.set_option("large_repr", "truncate")
198198
df
199199
pd.set_option("large_repr", "info")
200200
df
201201
pd.reset_option("large_repr")
202-
pd.reset_option("max_rows")
202+
pd.reset_option("display.max_rows")
203203
204204
``display.max_colwidth`` sets the maximum width of columns. Cells
205205
of this length or longer will be truncated with an ellipsis.
@@ -491,6 +491,10 @@ styler.render.repr html Standard output format for
491491
Should be one of "html" or "latex".
492492
styler.render.max_elements 262144 Maximum number of datapoints that Styler will render
493493
trimming either rows, columns or both to fit.
494+
styler.render.max_rows None Maximum number of rows that Styler will render. By default
495+
this is dynamic based on ``max_elements``.
496+
styler.render.max_columns None Maximum number of columns that Styler will render. By default
497+
this is dynamic based on ``max_elements``.
494498
styler.render.encoding utf-8 Default encoding for output HTML or LaTeX files.
495499
styler.format.formatter None Object to specify formatting functions to ``Styler.format``.
496500
styler.format.na_rep None String representation for missing data.

doc/source/whatsnew/v1.3.3.rst

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Fixed regressions
2727
- Fixed regression in :meth:`.Resampler.aggregate` when used after column selection would raise if ``func`` is a list of aggregation functions (:issue:`42905`)
2828
- Fixed regression in :meth:`DataFrame.corr` where Kendall correlation would produce incorrect results for columns with repeated values (:issue:`43401`)
2929
- Fixed regression in :meth:`DataFrame.groupby` where aggregation on columns with object types dropped results on those columns (:issue:`42395`, :issue:`43108`)
30+
- Fixed regression in :meth:`Series.fillna` raising ``TypeError`` when filling ``float`` ``Series`` with list-like fill value having a dtype which couldn't cast lostlessly (like ``float32`` filled with ``float64``) (:issue:`43424`)
31+
-
3032

3133
.. ---------------------------------------------------------------------------
3234

doc/source/whatsnew/v1.4.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Styler
7575
- Styling of indexing has been added, with :meth:`.Styler.apply_index` and :meth:`.Styler.applymap_index`. These mirror the signature of the methods already used to style data values, and work with both HTML and LaTeX format (:issue:`41893`).
7676
- :meth:`.Styler.bar` introduces additional arguments to control alignment and display (:issue:`26070`, :issue:`36419`), and it also validates the input arguments ``width`` and ``height`` (:issue:`42511`).
7777
- :meth:`.Styler.to_latex` introduces keyword argument ``environment``, which also allows a specific "longtable" entry through a separate jinja2 template (:issue:`41866`).
78-
- :meth:`.Styler.to_html` introduces keyword arguments ``sparse_index``, ``sparse_columns``, ``bold_headers``, ``caption`` (:issue:`41946`, :issue:`43149`).
78+
- :meth:`.Styler.to_html` introduces keyword arguments ``sparse_index``, ``sparse_columns``, ``bold_headers``, ``caption``, ``max_rows`` and ``max_columns`` (:issue:`41946`, :issue:`43149`, :issue:`42972`).
7979
- Keyword arguments ``level`` and ``names`` added to :meth:`.Styler.hide_index` and :meth:`.Styler.hide_columns` for additional control of visibility of MultiIndexes and index names (:issue:`25475`, :issue:`43404`, :issue:`43346`)
8080
- Global options have been extended to configure default ``Styler`` properties including formatting and encoding and mathjax options and LaTeX (:issue:`41395`)
8181
- Naive sparsification is now possible for LaTeX without the multirow package (:issue:`43369`)

pandas/core/array_algos/putmask.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,14 @@ def putmask_inplace(values: ArrayLike, mask: np.ndarray, value: Any) -> None:
4141
if lib.is_scalar(value) and isinstance(values, np.ndarray):
4242
value = convert_scalar_for_putitemlike(value, values.dtype)
4343

44-
if not isinstance(values, np.ndarray) or (
45-
values.dtype == object and not lib.is_scalar(value)
44+
if (
45+
not isinstance(values, np.ndarray)
46+
or (values.dtype == object and not lib.is_scalar(value))
47+
# GH#43424: np.putmask raises TypeError if we cannot cast between types with
48+
# rule = "safe", a stricter guarantee we may not have here
49+
or (
50+
isinstance(value, np.ndarray) and not np.can_cast(value.dtype, values.dtype)
51+
)
4652
):
4753
# GH#19266 using np.putmask gives unexpected results with listlike value
4854
if is_list_like(value) and len(value) == len(values):

pandas/core/config_init.py

+26
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,18 @@ def register_converter_cb(key):
769769
trimming will occur over columns, rows or both if needed.
770770
"""
771771

772+
styler_max_rows = """
773+
: int, optional
774+
The maximum number of rows that will be rendered. May still be reduced to
775+
satsify ``max_elements``, which takes precedence.
776+
"""
777+
778+
styler_max_columns = """
779+
: int, optional
780+
The maximum number of columns that will be rendered. May still be reduced to
781+
satsify ``max_elements``, which takes precedence.
782+
"""
783+
772784
styler_precision = """
773785
: int
774786
The precision for floats and complex numbers.
@@ -847,6 +859,20 @@ def register_converter_cb(key):
847859
validator=is_nonnegative_int,
848860
)
849861

862+
cf.register_option(
863+
"render.max_rows",
864+
None,
865+
styler_max_rows,
866+
validator=is_nonnegative_int,
867+
)
868+
869+
cf.register_option(
870+
"render.max_columns",
871+
None,
872+
styler_max_columns,
873+
validator=is_nonnegative_int,
874+
)
875+
850876
cf.register_option("render.encoding", "utf-8", styler_encoding, validator=is_str)
851877

852878
cf.register_option("format.decimal", ".", styler_decimal, validator=is_str)

pandas/io/formats/style.py

+18
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,8 @@ def to_html(
885885
sparse_columns: bool | None = None,
886886
bold_headers: bool = False,
887887
caption: str | None = None,
888+
max_rows: int | None = None,
889+
max_columns: int | None = None,
888890
encoding: str | None = None,
889891
doctype_html: bool = False,
890892
exclude_styles: bool = False,
@@ -930,6 +932,20 @@ def to_html(
930932
caption : str, optional
931933
Set, or overwrite, the caption on Styler before rendering.
932934
935+
.. versionadded:: 1.4.0
936+
max_rows : int, optional
937+
The maximum number of rows that will be rendered. Defaults to
938+
``pandas.options.styler.render.max_rows/max_columns``.
939+
940+
.. versionadded:: 1.4.0
941+
max_columns : int, optional
942+
The maximum number of columns that will be rendered. Defaults to
943+
``pandas.options.styler.render.max_columns``, which is None.
944+
945+
Rows and columns may be reduced if the number of total elements is
946+
large. This value is set to ``pandas.options.styler.render.max_elements``,
947+
which is 262144 (18 bit browser rendering).
948+
933949
.. versionadded:: 1.4.0
934950
encoding : str, optional
935951
Character encoding setting for file output, and HTML meta tags.
@@ -981,6 +997,8 @@ def to_html(
981997
html = obj._render_html(
982998
sparse_index=sparse_index,
983999
sparse_columns=sparse_columns,
1000+
max_rows=max_rows,
1001+
max_cols=max_columns,
9841002
exclude_styles=exclude_styles,
9851003
encoding=encoding,
9861004
doctype_html=doctype_html,

pandas/io/formats/style_render.py

+45-5
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,21 @@ def __init__(
117117
tuple[int, int], Callable[[Any], str]
118118
] = defaultdict(lambda: partial(_default_formatter, precision=precision))
119119

120-
def _render_html(self, sparse_index: bool, sparse_columns: bool, **kwargs) -> str:
120+
def _render_html(
121+
self,
122+
sparse_index: bool,
123+
sparse_columns: bool,
124+
max_rows: int | None = None,
125+
max_cols: int | None = None,
126+
**kwargs,
127+
) -> str:
121128
"""
122129
Renders the ``Styler`` including all applied styles to HTML.
123130
Generates a dict with necessary kwargs passed to jinja2 template.
124131
"""
125132
self._compute()
126133
# TODO: namespace all the pandas keys
127-
d = self._translate(sparse_index, sparse_columns)
134+
d = self._translate(sparse_index, sparse_columns, max_rows, max_cols)
128135
d.update(kwargs)
129136
return self.template_html.render(
130137
**d,
@@ -166,7 +173,14 @@ def _compute(self):
166173
r = func(self)(*args, **kwargs)
167174
return r
168175

169-
def _translate(self, sparse_index: bool, sparse_cols: bool, blank: str = " "):
176+
def _translate(
177+
self,
178+
sparse_index: bool,
179+
sparse_cols: bool,
180+
max_rows: int | None = None,
181+
max_cols: int | None = None,
182+
blank: str = " ",
183+
):
170184
"""
171185
Process Styler data and settings into a dict for template rendering.
172186
@@ -181,6 +195,10 @@ def _translate(self, sparse_index: bool, sparse_cols: bool, blank: str = " 
181195
sparse_cols : bool
182196
Whether to sparsify the columns or print all hierarchical column elements.
183197
Upstream defaults are typically to `pandas.options.styler.sparse.columns`.
198+
blank : str
199+
Entry to top-left blank cells.
200+
max_rows, max_cols : int, optional
201+
Specific max rows and cols. max_elements always take precedence in render.
184202
185203
Returns
186204
-------
@@ -206,8 +224,14 @@ def _translate(self, sparse_index: bool, sparse_cols: bool, blank: str = " 
206224
}
207225

208226
max_elements = get_option("styler.render.max_elements")
227+
max_rows = max_rows if max_rows else get_option("styler.render.max_rows")
228+
max_cols = max_cols if max_cols else get_option("styler.render.max_columns")
209229
max_rows, max_cols = _get_trimming_maximums(
210-
len(self.data.index), len(self.data.columns), max_elements
230+
len(self.data.index),
231+
len(self.data.columns),
232+
max_elements,
233+
max_rows,
234+
max_cols,
211235
)
212236

213237
self.cellstyle_map_columns: DefaultDict[
@@ -832,7 +856,14 @@ def _element(
832856
}
833857

834858

835-
def _get_trimming_maximums(rn, cn, max_elements, scaling_factor=0.8):
859+
def _get_trimming_maximums(
860+
rn,
861+
cn,
862+
max_elements,
863+
max_rows=None,
864+
max_cols=None,
865+
scaling_factor=0.8,
866+
) -> tuple[int, int]:
836867
"""
837868
Recursively reduce the number of rows and columns to satisfy max elements.
838869
@@ -842,6 +873,10 @@ def _get_trimming_maximums(rn, cn, max_elements, scaling_factor=0.8):
842873
The number of input rows / columns
843874
max_elements : int
844875
The number of allowable elements
876+
max_rows, max_cols : int, optional
877+
Directly specify an initial maximum rows or columns before compression.
878+
scaling_factor : float
879+
Factor at which to reduce the number of rows / columns to fit.
845880
846881
Returns
847882
-------
@@ -855,6 +890,11 @@ def scale_down(rn, cn):
855890
else:
856891
return int(rn * scaling_factor), cn
857892

893+
if max_rows:
894+
rn = max_rows if rn > max_rows else rn
895+
if max_cols:
896+
cn = max_cols if cn > max_cols else cn
897+
858898
while rn * cn > max_elements:
859899
rn, cn = scale_down(rn, cn)
860900

pandas/tests/io/formats/style/test_html.py

+13
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,16 @@ def test_applymap_header_cell_ids(styler, index, columns):
452452
'<th id="T__level0_col0" class="col_heading level0 col0" >A</th>' in result
453453
) is columns
454454
assert ("#T__level0_col0 {\n attr: val;\n}" in result) is columns
455+
456+
457+
@pytest.mark.parametrize("rows", [True, False])
458+
@pytest.mark.parametrize("cols", [True, False])
459+
def test_maximums(styler_mi, rows, cols):
460+
result = styler_mi.to_html(
461+
max_rows=2 if rows else None,
462+
max_columns=2 if cols else None,
463+
)
464+
465+
assert ">5</td>" in result # [[0,1], [4,5]] always visible
466+
assert (">8</td>" in result) is not rows # first trimmed vertical element
467+
assert (">2</td>" in result) is not cols # first trimmed horizontal element

pandas/tests/io/formats/style/test_style.py

+41-13
Original file line numberDiff line numberDiff line change
@@ -150,28 +150,56 @@ def test_mi_styler_sparsify_options(mi_styler):
150150
assert html1 != html2
151151

152152

153-
def test_trimming_maximum():
154-
rn, cn = _get_trimming_maximums(100, 100, 100, scaling_factor=0.5)
155-
assert (rn, cn) == (12, 6)
156-
157-
rn, cn = _get_trimming_maximums(1000, 3, 750, scaling_factor=0.5)
158-
assert (rn, cn) == (250, 3)
153+
@pytest.mark.parametrize(
154+
"rn, cn, max_els, max_rows, max_cols, exp_rn, exp_cn",
155+
[
156+
(100, 100, 100, None, None, 12, 6), # reduce to (12, 6) < 100 elements
157+
(1000, 3, 750, None, None, 250, 3), # dynamically reduce rows to 250, keep cols
158+
(4, 1000, 500, None, None, 4, 125), # dynamically reduce cols to 125, keep rows
159+
(1000, 3, 750, 10, None, 10, 3), # overwrite above dynamics with max_row
160+
(4, 1000, 500, None, 5, 4, 5), # overwrite above dynamics with max_col
161+
(100, 100, 700, 50, 50, 25, 25), # rows cols below given maxes so < 700 elmts
162+
],
163+
)
164+
def test_trimming_maximum(rn, cn, max_els, max_rows, max_cols, exp_rn, exp_cn):
165+
rn, cn = _get_trimming_maximums(
166+
rn, cn, max_els, max_rows, max_cols, scaling_factor=0.5
167+
)
168+
assert (rn, cn) == (exp_rn, exp_cn)
159169

160170

161-
def test_render_trimming():
171+
@pytest.mark.parametrize(
172+
"option, val",
173+
[
174+
("styler.render.max_elements", 6),
175+
("styler.render.max_rows", 3),
176+
],
177+
)
178+
def test_render_trimming_rows(option, val):
179+
# test auto and specific trimming of rows
162180
df = DataFrame(np.arange(120).reshape(60, 2))
163-
with pd.option_context("styler.render.max_elements", 6):
181+
with pd.option_context(option, val):
164182
ctx = df.style._translate(True, True)
165183
assert len(ctx["head"][0]) == 3 # index + 2 data cols
166184
assert len(ctx["body"]) == 4 # 3 data rows + trimming row
167185
assert len(ctx["body"][0]) == 3 # index + 2 data cols
168186

169-
df = DataFrame(np.arange(120).reshape(12, 10))
170-
with pd.option_context("styler.render.max_elements", 6):
187+
188+
@pytest.mark.parametrize(
189+
"option, val",
190+
[
191+
("styler.render.max_elements", 6),
192+
("styler.render.max_columns", 2),
193+
],
194+
)
195+
def test_render_trimming_cols(option, val):
196+
# test auto and specific trimming of cols
197+
df = DataFrame(np.arange(30).reshape(3, 10))
198+
with pd.option_context(option, val):
171199
ctx = df.style._translate(True, True)
172-
assert len(ctx["head"][0]) == 4 # index + 2 data cols + trimming row
173-
assert len(ctx["body"]) == 4 # 3 data rows + trimming row
174-
assert len(ctx["body"][0]) == 4 # index + 2 data cols + trimming row
200+
assert len(ctx["head"][0]) == 4 # index + 2 data cols + trimming col
201+
assert len(ctx["body"]) == 3 # 3 data rows
202+
assert len(ctx["body"][0]) == 4 # index + 2 data cols + trimming col
175203

176204

177205
def test_render_trimming_mi():

0 commit comments

Comments
 (0)