Skip to content

ENH: Styler.bar height control #42483

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

Merged
merged 8 commits into from
Jul 13, 2021
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v1.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ enhancement2
Other enhancements
^^^^^^^^^^^^^^^^^^
- :meth:`Series.sample`, :meth:`DataFrame.sample`, and :meth:`.GroupBy.sample` now accept a ``np.random.Generator`` as input to ``random_state``. A generator will be more performant, especially with ``replace=False`` (:issue:`38100`)
- Additional options added to :meth:`.Styler.bar` to control alignment and display (:issue:`26070`)
- Additional options added to :meth:`.Styler.bar` to control alignment and display (:issue:`26070`, :issue:`36419`)
- :meth:`Series.ewm`, :meth:`DataFrame.ewm`, now support a ``method`` argument with a ``'table'`` option that performs the windowing operation over an entire :class:`DataFrame`. See :ref:`Window Overview <window.overview>` for performance and functional benefits (:issue:`42273`)
-

Expand Down
21 changes: 19 additions & 2 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -2052,6 +2052,7 @@ def bar(
vmin: float | None = None,
vmax: float | None = None,
props: str = "width: 10em;",
height: float = 100,
) -> Styler:
"""
Draw bar chart in the cell backgrounds.
Expand Down Expand Up @@ -2100,6 +2101,11 @@ def bar(
Maximum bar value, defining the right hand limit
of the bar drawing range, higher values are clipped to `vmax`.
When None (default): the maximum value of the data will be used.
height : float, default 100
The percentage height of the bar in the cell, centrally aligned, in [0,100].

.. versionadded:: 1.4.0

props : str, optional
The base CSS of the cell that is extended to add the bar chart. Defaults to
`"width: 10em;"`
Expand Down Expand Up @@ -2131,6 +2137,7 @@ def bar(
align=align,
colors=color,
width=width / 100,
height=height / 100,
vmin=vmin,
vmax=vmax,
base_css=props,
Expand Down Expand Up @@ -2791,6 +2798,7 @@ def _bar(
align: str | float | int | Callable,
colors: list[str],
width: float,
height: float,
vmin: float | None,
vmax: float | None,
base_css: str,
Expand All @@ -2808,6 +2816,9 @@ def _bar(
Two listed colors as string in valid CSS.
width : float in [0,1]
The percentage of the cell, measured from left, where drawn bars will reside.
height : float in [0,1]
The percentage of the cell's height where drawn bars will reside, centrally
aligned.
vmin : float, optional
Overwrite the minimum value of the window.
vmax : float, optional
Expand Down Expand Up @@ -2873,7 +2884,7 @@ def css_calc(x, left: float, right: float, align: str):

Notes
-----
Uses ``colors`` and ``width`` from outer scope.
Uses ``colors``, ``width`` and ``height`` from outer scope.
"""
if pd.isna(x):
return base_css
Expand Down Expand Up @@ -2911,7 +2922,13 @@ def css_calc(x, left: float, right: float, align: str):
else:
start, end = z_frac, (x - left) / (right - left)

return css_bar(start * width, end * width, color)
ret = css_bar(start * width, end * width, color)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens for height / width out of bounds? do we raise, is this tested?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently still renders, but will get bad results. height is a css artifact though so bad CSS just means it gets ignored by the browser.

What do you want to do? Leave as is? Raise on out-of-bounds? or auto trim bad values to be in the range?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think raising is appropriate (don't have to do here), prob needs a dedicated PR as likely need to audit all of these.

if height < 1 and "background: linear-gradient(" in ret:
return (
ret + f" no-repeat center; background-size: 100% {height * 100:.1f}%;"
)
else:
return ret

values = data.to_numpy()
left = np.nanmin(values) if vmin is None else vmin
Expand Down
16 changes: 16 additions & 0 deletions pandas/tests/io/formats/style/test_align.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,19 @@ def test_colors_mixed(align, exp):
data = DataFrame([[-1], [3]])
result = data.style.bar(align=align, color=["red", "green"])._compute().ctx
assert result == {(0, 0): exp[0], (1, 0): exp[1]}


def test_bar_align_height():
# test when keyword height is used 'no-repeat center' and 'background-size' present
data = DataFrame([[1], [2]])
result = data.style.bar(align="left", height=50)._compute().ctx
bg_s = "linear-gradient(90deg, #d65f5f 100.0%, transparent 100.0%) no-repeat center"
expected = {
(0, 0): [("width", "10em")],
(1, 0): [
("width", "10em"),
("background", bg_s),
("background-size", "100% 50.0%"),
],
}
assert result == expected