Skip to content

Commit 8f759b0

Browse files
Merge branch 'main' into pandas.io.stata.StataReader.data_label
2 parents d610f69 + aea1643 commit 8f759b0

File tree

10 files changed

+60
-34
lines changed

10 files changed

+60
-34
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ Bug fixes
544544

545545
Categorical
546546
^^^^^^^^^^^
547-
-
547+
- Bug in :func:`Series.apply` where ``nan`` was ignored for :class:`CategoricalDtype` (:issue:`59938`)
548548
-
549549

550550
Datetimelike
@@ -682,6 +682,7 @@ Sparse
682682
^^^^^^
683683
- Bug in :class:`SparseDtype` for equal comparison with na fill value. (:issue:`54770`)
684684
- Bug in :meth:`DataFrame.sparse.from_spmatrix` which hard coded an invalid ``fill_value`` for certain subtypes. (:issue:`59063`)
685+
- Bug in :meth:`DataFrame.sparse.to_dense` which ignored subclassing and always returned an instance of :class:`DataFrame` (:issue:`59913`)
685686

686687
ExtensionArray
687688
^^^^^^^^^^^^^^

pandas/core/apply.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@
3838
is_numeric_dtype,
3939
is_sequence,
4040
)
41-
from pandas.core.dtypes.dtypes import (
42-
CategoricalDtype,
43-
ExtensionDtype,
44-
)
41+
from pandas.core.dtypes.dtypes import ExtensionDtype
4542
from pandas.core.dtypes.generic import (
4643
ABCDataFrame,
4744
ABCNDFrame,
@@ -1465,14 +1462,7 @@ def curried(x):
14651462

14661463
else:
14671464
curried = func
1468-
1469-
# row-wise access
1470-
# apply doesn't have a `na_action` keyword and for backward compat reasons
1471-
# we need to give `na_action="ignore"` for categorical data.
1472-
# TODO: remove the `na_action="ignore"` when that default has been changed in
1473-
# Categorical (GH51645).
1474-
action = "ignore" if isinstance(obj.dtype, CategoricalDtype) else None
1475-
mapped = obj._map_values(mapper=curried, na_action=action)
1465+
mapped = obj._map_values(mapper=curried)
14761466

14771467
if len(mapped) and isinstance(mapped[0], ABCSeries):
14781468
# GH#43986 Need to do list(mapped) in order to get treated as nested

pandas/core/arrays/sparse/accessor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,10 +369,10 @@ def to_dense(self) -> DataFrame:
369369
1 1
370370
2 0
371371
"""
372-
from pandas import DataFrame
373-
374372
data = {k: v.array.to_dense() for k, v in self._parent.items()}
375-
return DataFrame(data, index=self._parent.index, columns=self._parent.columns)
373+
return self._parent._constructor(
374+
data, index=self._parent.index, columns=self._parent.columns
375+
)
376376

377377
def to_coo(self) -> spmatrix:
378378
"""

pandas/io/formats/html.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ def _write_cell(
195195
esc = {}
196196

197197
rs = pprint_thing(s, escape_chars=esc).strip()
198+
# replace spaces betweens strings with non-breaking spaces
199+
rs = rs.replace(" ", "  ")
198200

199201
if self.render_links and is_url(rs):
200202
rs_unescaped = pprint_thing(s, escape_chars={}).strip()

pandas/io/formats/style_render.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -906,9 +906,9 @@ def concatenated_visible_rows(obj):
906906
row_body_headers = [
907907
{
908908
**col,
909-
"display_value": col["display_value"]
910-
if col["is_visible"]
911-
else "",
909+
"display_value": (
910+
col["display_value"] if col["is_visible"] else ""
911+
),
912912
"cellstyle": self.ctx_index[r, c],
913913
}
914914
for c, col in enumerate(row[:index_levels])
@@ -2069,18 +2069,18 @@ def maybe_convert_css_to_tuples(style: CSSProperties) -> CSSList:
20692069
('border','1px solid red')]
20702070
"""
20712071
if isinstance(style, str):
2072-
s = style.split(";")
2073-
try:
2074-
return [
2075-
(x.split(":")[0].strip(), x.split(":")[1].strip())
2076-
for x in s
2077-
if x.strip() != ""
2078-
]
2079-
except IndexError as err:
2072+
if style and ":" not in style:
20802073
raise ValueError(
20812074
"Styles supplied as string must follow CSS rule formats, "
20822075
f"for example 'attr: val;'. '{style}' was given."
2083-
) from err
2076+
)
2077+
s = style.split(";")
2078+
return [
2079+
(x.split(":")[0].strip(), ":".join(x.split(":")[1:]).strip())
2080+
for x in s
2081+
if x.strip() != ""
2082+
]
2083+
20842084
return style
20852085

20862086

pandas/tests/apply/test_frame_apply.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,8 +741,9 @@ def test_apply_category_equalness(val):
741741

742742
result = df.a.apply(lambda x: x == val)
743743
expected = Series(
744-
[np.nan if pd.isnull(x) else x == val for x in df_values], name="a"
744+
[False if pd.isnull(x) else x == val for x in df_values], name="a"
745745
)
746+
# False since behavior of NaN for categorical dtype has been changed (GH 59966)
746747
tm.assert_series_equal(result, expected)
747748

748749

pandas/tests/apply/test_series_apply.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,10 @@ def test_apply_categorical_with_nan_values(series, by_row):
236236
with pytest.raises(AttributeError, match=msg):
237237
s.apply(lambda x: x.split("-")[0], by_row=by_row)
238238
return
239-
240-
result = s.apply(lambda x: x.split("-")[0], by_row=by_row)
239+
# NaN for cat dtype fixed in (GH 59966)
240+
result = s.apply(lambda x: x.split("-")[0] if pd.notna(x) else False, by_row=by_row)
241241
result = result.astype(object)
242-
expected = Series(["1", "1", np.nan], dtype="category")
242+
expected = Series(["1", "1", False], dtype="category")
243243
expected = expected.astype(object)
244244
tm.assert_series_equal(result, expected)
245245

pandas/tests/arrays/sparse/test_accessor.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,7 @@ def test_with_column_named_sparse(self):
252252
# https://github.com/pandas-dev/pandas/issues/30758
253253
df = pd.DataFrame({"sparse": pd.arrays.SparseArray([1, 2])})
254254
assert isinstance(df.sparse, pd.core.arrays.sparse.accessor.SparseFrameAccessor)
255+
256+
def test_subclassing(self):
257+
df = tm.SubclassedDataFrame({"sparse": pd.arrays.SparseArray([1, 2])})
258+
assert isinstance(df.sparse.to_dense(), tm.SubclassedDataFrame)

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -886,8 +886,19 @@ def test_maybe_convert_css_to_tuples(self):
886886
expected = []
887887
assert maybe_convert_css_to_tuples("") == expected
888888

889+
# issue #59623
890+
expected = [("a", "b"), ("c", "url('data:123')")]
891+
assert maybe_convert_css_to_tuples("a:b;c: url('data:123');") == expected
892+
893+
# if no value, return attr and empty string
894+
expected = [("a", ""), ("c", "")]
895+
assert maybe_convert_css_to_tuples("a:;c: ") == expected
896+
889897
def test_maybe_convert_css_to_tuples_err(self):
890-
msg = "Styles supplied as string must follow CSS rule formats"
898+
msg = (
899+
"Styles supplied as string must follow CSS rule formats, "
900+
"for example 'attr: val;'. 'err' was given."
901+
)
891902
with pytest.raises(ValueError, match=msg):
892903
maybe_convert_css_to_tuples("err")
893904

pandas/tests/io/formats/test_format.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,12 +375,29 @@ def test_repr_min_rows(self):
375375
(None, "{:.3f}", "None"),
376376
("", "{:.2f}", ""),
377377
(112345.6789, "{:6.3f}", "112345.679"),
378+
("foo foo", None, "foo      foo"),
379+
(" foo", None, "foo"),
380+
(
381+
"foo foo foo",
382+
None,
383+
"foo foo       foo",
384+
), # odd no.of spaces
385+
(
386+
"foo foo foo",
387+
None,
388+
"foo foo    foo",
389+
), # even no.of spaces
378390
],
379391
)
380392
def test_repr_float_formatting_html_output(
381393
self, data, format_option, expected_values
382394
):
383-
with option_context("display.float_format", format_option.format):
395+
if format_option is not None:
396+
with option_context("display.float_format", format_option.format):
397+
df = DataFrame({"A": [data]})
398+
html_output = df._repr_html_()
399+
assert expected_values in html_output
400+
else:
384401
df = DataFrame({"A": [data]})
385402
html_output = df._repr_html_()
386403
assert expected_values in html_output

0 commit comments

Comments
 (0)