Skip to content

Commit e918b1c

Browse files
committed
Fixed a Styler.bar bug of missing value
1 parent 14e7b59 commit e918b1c

File tree

2 files changed

+44
-3
lines changed

2 files changed

+44
-3
lines changed

pandas/io/formats/style.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4081,9 +4081,43 @@ def css_calc(x, left: float, right: float, align: str, color: str | list | tuple
40814081
else:
40824082
return ret
40834083

4084+
def replace_pd_NA_with_np_nan(data_structure):
4085+
"""
4086+
Recursively replace pd.NA with np.nan in a nested list or array.
4087+
4088+
This function traverses a nested structure (like lists or numpy arrays)
4089+
and replaces occurrences of Pandas' NA values (pd.NA) with NumPy's NaN values (np.nan).
4090+
It handles nested lists and arrays, applying the replacement recursively.
4091+
4092+
Parameters:
4093+
data_structure (list, np.ndarray, or any): A nested list, numpy array, or any other object.
4094+
If it's a list or numpy array, the function will
4095+
process its elements recursively.
4096+
4097+
Returns:
4098+
list, np.ndarray, or any: A new object with the same structure as `data_structure`,
4099+
where all pd.NA values have been replaced with np.nan.
4100+
The type of the returned object is the same as the input.
4101+
4102+
Example:
4103+
>>> data = [1, pd.NA, [3, pd.NA, [pd.NA, 5]], pd.NA]
4104+
>>> replace_pd_NA_with_np_nan(data)
4105+
[1, nan, [3, nan, [nan, 5]], nan]
4106+
"""
4107+
if isinstance(data_structure, list):
4108+
# Process each item in the list recursively
4109+
return [replace_pd_NA_with_np_nan(element) for element in data_structure]
4110+
elif isinstance(data_structure, np.ndarray):
4111+
# Convert numpy array elements recursively
4112+
return np.array([replace_pd_NA_with_np_nan(element) for element in data_structure])
4113+
else:
4114+
# Replace pd.NA with np.nan for individual elements
4115+
return np.nan if pd.isna(data_structure) else data_structure
4116+
40844117
values = data.to_numpy()
4085-
left = np.nanmin(values) if vmin is None else vmin
4086-
right = np.nanmax(values) if vmax is None else vmax
4118+
np_values = replace_pd_NA_with_np_nan(values)
4119+
left = np.nanmin(np_values) if vmin is None else vmin
4120+
right = np.nanmax(np_values) if vmax is None else vmax
40874121
z: float = 0 # adjustment to translate data
40884122

40894123
if align == "mid":

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import numpy as np
22
import pytest
33

4-
from pandas import DataFrame
4+
from pandas import DataFrame, NA
55

66
pytest.importorskip("jinja2")
77

@@ -328,3 +328,10 @@ def test_bar_invalid_color_type_error_raises():
328328
# Test that providing a color list with more than two elements raises a ValueError
329329
with pytest.raises(ValueError, match=msg):
330330
df.style.bar(color=['#d65f5f', '#5fba7d', '#abcdef']).to_html()
331+
332+
333+
def test_styler_bar_with_missing_values():
334+
df = DataFrame({"A": [1, 2, NA, 4]})
335+
expected_substring = "linear-gradient"
336+
html_output = df.style.bar(subset='A').to_html()
337+
assert expected_substring in html_output

0 commit comments

Comments
 (0)