Skip to content

Commit cb6140f

Browse files
committed
remove char in 1.5.2, merge in upstream
2 parents a4142ad + e3143f6 commit cb6140f

File tree

17 files changed

+297
-57
lines changed

17 files changed

+297
-57
lines changed

ci/deps/actions-38-downstream_compat.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ dependencies:
3434
- gcsfs
3535
- jinja2
3636
- lxml
37-
- matplotlib
37+
- matplotlib>=3.6.1
3838
- numba
3939
- numexpr
4040
- openpyxl

ci/deps/circle-38-arm64.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ dependencies:
3333
- gcsfs
3434
- jinja2
3535
- lxml
36-
- matplotlib
36+
- matplotlib>=3.6.1
3737
- numba
3838
- numexpr
3939
- openpyxl

doc/source/user_guide/basics.rst

-6
Original file line numberDiff line numberDiff line change
@@ -1213,12 +1213,6 @@ With a DataFrame, you can simultaneously reindex the index and columns:
12131213
df
12141214
df.reindex(index=["c", "f", "b"], columns=["three", "two", "one"])
12151215
1216-
You may also use ``reindex`` with an ``axis`` keyword:
1217-
1218-
.. ipython:: python
1219-
1220-
df.reindex(["c", "f", "b"], axis="index")
1221-
12221216
Note that the ``Index`` objects containing the actual axis labels can be
12231217
**shared** between objects. So if we have a Series and a DataFrame, the
12241218
following can be done:

doc/source/whatsnew/v1.5.2.rst

-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ Bug fixes
2929
~~~~~~~~~
3030
- Bug in the Copy-on-Write implementation losing track of views in certain chained indexing cases (:issue:`48996`)
3131
- Fixed memory leak in :meth:`.Styler.to_excel` (:issue:`49751`)
32-
-
3332

3433
.. ---------------------------------------------------------------------------
3534
.. _whatsnew_152.other:

doc/source/whatsnew/v1.5.3.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Fixed regressions
2727
Bug fixes
2828
~~~~~~~~~
2929
- Bug in :meth:`.Styler.to_excel` leading to error when unrecognized ``border-style`` (e.g. ``"hair"``) provided to Excel writers (:issue:`48649`)
30+
- Bug when chaining several :meth:`.Styler.concat` calls, only the last styler was concatenated (:issue:`49207`)
3031
- Fixed bug when instantiating a :class:`DataFrame` subclass inheriting from ``typing.Generic`` that triggered a ``UserWarning`` on python 3.11 (:issue:`49649`)
3132
-
3233

@@ -36,7 +37,6 @@ Bug fixes
3637
Other
3738
~~~~~
3839
-
39-
-
4040

4141
.. ---------------------------------------------------------------------------
4242
.. _whatsnew_153.contributors:

doc/source/whatsnew/v2.0.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ Timezones
717717
^^^^^^^^^
718718
- Bug in :meth:`Series.astype` and :meth:`DataFrame.astype` with object-dtype containing multiple timezone-aware ``datetime`` objects with heterogeneous timezones to a :class:`DatetimeTZDtype` incorrectly raising (:issue:`32581`)
719719
- Bug in :func:`to_datetime` was failing to parse date strings with timezone name when ``format`` was specified with ``%Z`` (:issue:`49748`)
720-
-
720+
- Better error message when passing invalid values to ``ambiguous`` parameter in :meth:`Timestamp.tz_localize` (:issue:`49565`)
721721

722722
Numeric
723723
^^^^^^^

environment.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ dependencies:
3535
- gcsfs
3636
- jinja2
3737
- lxml
38-
- matplotlib
38+
- matplotlib>=3.6.1
3939
- numba>=0.53.1
4040
- numexpr>=2.8.0 # pin for "Run checks on imported code" job
4141
- openpyxl

pandas/_libs/tslibs/timestamps.pyx

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ construction requirements, we need to do object instantiation in python
66
(see Timestamp class below). This will serve as a C extension type that
77
shadows the python class, where we do any heavy lifting.
88
"""
9-
import warnings
109

10+
import warnings
1111
cimport cython
1212

1313
import numpy as np
@@ -1944,8 +1944,11 @@ default 'raise'
19441944
>>> pd.NaT.tz_localize()
19451945
NaT
19461946
"""
1947-
if ambiguous == "infer":
1948-
raise ValueError("Cannot infer offset with only one time.")
1947+
if not isinstance(ambiguous, bool) and ambiguous not in {"NaT", "raise"}:
1948+
raise ValueError(
1949+
"'ambiguous' parameter must be one of: "
1950+
"True, False, 'NaT', 'raise' (default)"
1951+
)
19491952

19501953
nonexistent_options = ("raise", "NaT", "shift_forward", "shift_backward")
19511954
if nonexistent not in nonexistent_options and not PyDelta_Check(nonexistent):

pandas/core/indexes/interval.py

+17
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
is_number,
6060
is_object_dtype,
6161
is_scalar,
62+
is_signed_integer_dtype,
63+
is_unsigned_integer_dtype,
6264
)
6365
from pandas.core.dtypes.dtypes import IntervalDtype
6466
from pandas.core.dtypes.missing import is_valid_na_for_dtype
@@ -521,6 +523,7 @@ def _maybe_convert_i8(self, key):
521523
original = key
522524
if is_list_like(key):
523525
key = ensure_index(key)
526+
key = self._maybe_convert_numeric_to_64bit(key)
524527

525528
if not self._needs_i8_conversion(key):
526529
return original
@@ -566,6 +569,20 @@ def _maybe_convert_i8(self, key):
566569

567570
return key_i8
568571

572+
def _maybe_convert_numeric_to_64bit(self, idx: Index) -> Index:
573+
# IntervalTree only supports 64 bit numpy array
574+
dtype = idx.dtype
575+
if np.issubclass_(dtype.type, np.number):
576+
return idx
577+
elif is_signed_integer_dtype(dtype) and dtype != np.int64:
578+
return idx.astype(np.int64)
579+
elif is_unsigned_integer_dtype(dtype) and dtype != np.uint64:
580+
return idx.astype(np.uint64)
581+
elif is_float_dtype(dtype) and dtype != np.float64:
582+
return idx.astype(np.float64)
583+
else:
584+
return idx
585+
569586
def _searchsorted_monotonic(self, label, side: Literal["left", "right"] = "left"):
570587
if not self.is_non_overlapping_monotonic:
571588
raise KeyError(

pandas/io/formats/style.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,12 @@ def concat(self, other: Styler) -> Styler:
316316
inherited from the original Styler and not ``other``.
317317
- hidden columns and hidden index levels will be inherited from the
318318
original Styler
319+
- ``css`` will be inherited from the original Styler, and the value of
320+
keys ``data``, ``row_heading`` and ``row`` will be prepended with
321+
``foot0_``. If more concats are chained, their styles will be prepended
322+
with ``foot1_``, ''foot_2'', etc., and if a concatenated style have
323+
another concatanated style, the second style will be prepended with
324+
``foot{parent}_foot{child}_``.
319325
320326
A common use case is to concatenate user defined functions with
321327
``DataFrame.agg`` or with described statistics via ``DataFrame.describe``.
@@ -367,7 +373,7 @@ def concat(self, other: Styler) -> Styler:
367373
"number of index levels must be same in `other` "
368374
"as in `Styler`. See documentation for suggestions."
369375
)
370-
self.concatenated = other
376+
self.concatenated.append(other)
371377
return self
372378

373379
def _repr_html_(self) -> str | None:

pandas/io/formats/style_render.py

+45-30
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def __init__(
119119
"blank": "blank",
120120
"foot": "foot",
121121
}
122-
self.concatenated: StylerRenderer | None = None
122+
self.concatenated: list[StylerRenderer] = []
123123
# add rendering variables
124124
self.hide_index_names: bool = False
125125
self.hide_column_names: bool = False
@@ -161,27 +161,34 @@ def _render(
161161
stylers for use within `_translate_latex`
162162
"""
163163
self._compute()
164-
dx = None
165-
if self.concatenated is not None:
166-
self.concatenated.hide_index_ = self.hide_index_
167-
self.concatenated.hidden_columns = self.hidden_columns
168-
self.concatenated.css = {
164+
dxs = []
165+
ctx_len = len(self.index)
166+
for i, concatenated in enumerate(self.concatenated):
167+
concatenated.hide_index_ = self.hide_index_
168+
concatenated.hidden_columns = self.hidden_columns
169+
foot = f"{self.css['foot']}{i}"
170+
concatenated.css = {
169171
**self.css,
170-
"data": f"{self.css['foot']}_{self.css['data']}",
171-
"row_heading": f"{self.css['foot']}_{self.css['row_heading']}",
172-
"row": f"{self.css['foot']}_{self.css['row']}",
173-
"foot": self.css["foot"],
172+
"data": f"{foot}_data",
173+
"row_heading": f"{foot}_row_heading",
174+
"row": f"{foot}_row",
175+
"foot": f"{foot}_foot",
174176
}
175-
dx = self.concatenated._render(
177+
dx = concatenated._render(
176178
sparse_index, sparse_columns, max_rows, max_cols, blank
177179
)
180+
dxs.append(dx)
178181

179-
for (r, c), v in self.concatenated.ctx.items():
180-
self.ctx[(r + len(self.index), c)] = v
181-
for (r, c), v in self.concatenated.ctx_index.items():
182-
self.ctx_index[(r + len(self.index), c)] = v
182+
for (r, c), v in concatenated.ctx.items():
183+
self.ctx[(r + ctx_len, c)] = v
184+
for (r, c), v in concatenated.ctx_index.items():
185+
self.ctx_index[(r + ctx_len, c)] = v
183186

184-
d = self._translate(sparse_index, sparse_columns, max_rows, max_cols, blank, dx)
187+
ctx_len += len(concatenated.index)
188+
189+
d = self._translate(
190+
sparse_index, sparse_columns, max_rows, max_cols, blank, dxs
191+
)
185192
return d
186193

187194
def _render_html(
@@ -258,7 +265,7 @@ def _translate(
258265
max_rows: int | None = None,
259266
max_cols: int | None = None,
260267
blank: str = " ",
261-
dx: dict | None = None,
268+
dxs: list[dict] | None = None,
262269
):
263270
"""
264271
Process Styler data and settings into a dict for template rendering.
@@ -278,15 +285,17 @@ def _translate(
278285
Specific max rows and cols. max_elements always take precedence in render.
279286
blank : str
280287
Entry to top-left blank cells.
281-
dx : dict
282-
The render dict of the concatenated Styler.
288+
dxs : list[dict]
289+
The render dicts of the concatenated Stylers.
283290
284291
Returns
285292
-------
286293
d : dict
287294
The following structure: {uuid, table_styles, caption, head, body,
288295
cellstyle, table_attributes}
289296
"""
297+
if dxs is None:
298+
dxs = []
290299
self.css["blank_value"] = blank
291300

292301
# construct render dict
@@ -340,10 +349,12 @@ def _translate(
340349
]
341350
d.update({k: map})
342351

343-
if dx is not None: # self.concatenated is not None
352+
for dx in dxs: # self.concatenated is not empty
344353
d["body"].extend(dx["body"]) # type: ignore[union-attr]
345354
d["cellstyle"].extend(dx["cellstyle"]) # type: ignore[union-attr]
346-
d["cellstyle_index"].extend(dx["cellstyle"]) # type: ignore[union-attr]
355+
d["cellstyle_index"].extend( # type: ignore[union-attr]
356+
dx["cellstyle_index"]
357+
)
347358

348359
table_attr = self.table_attributes
349360
if not get_option("styler.html.mathjax"):
@@ -847,23 +858,27 @@ def _translate_latex(self, d: dict, clines: str | None) -> None:
847858
for r, row in enumerate(d["head"])
848859
]
849860

850-
def concatenated_visible_rows(obj, n, row_indices):
861+
def _concatenated_visible_rows(obj, n, row_indices):
851862
"""
852863
Extract all visible row indices recursively from concatenated stylers.
853864
"""
854865
row_indices.extend(
855866
[r + n for r in range(len(obj.index)) if r not in obj.hidden_rows]
856867
)
857-
return (
858-
row_indices
859-
if obj.concatenated is None
860-
else concatenated_visible_rows(
861-
obj.concatenated, n + len(obj.index), row_indices
862-
)
863-
)
868+
n += len(obj.index)
869+
for concatenated in obj.concatenated:
870+
n = _concatenated_visible_rows(concatenated, n, row_indices)
871+
return n
872+
873+
def concatenated_visible_rows(obj):
874+
row_indices: list[int] = []
875+
_concatenated_visible_rows(obj, 0, row_indices)
876+
# TODO try to consolidate the concat visible rows
877+
# methods to a single function / recursion for simplicity
878+
return row_indices
864879

865880
body = []
866-
for r, row in zip(concatenated_visible_rows(self, 0, []), d["body"]):
881+
for r, row in zip(concatenated_visible_rows(self), d["body"]):
867882
# note: cannot enumerate d["body"] because rows were dropped if hidden
868883
# during _translate_body so must zip to acquire the true r-index associated
869884
# with the ctx obj which contains the cell styles.

pandas/tests/io/excel/test_writers.py

+9
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
_XlsxWriter,
3030
register_writer,
3131
)
32+
from pandas.io.excel._util import _writers
3233

3334

3435
@pytest.fixture
@@ -1351,3 +1352,11 @@ def test_excelwriter_fspath(self):
13511352
with tm.ensure_clean("foo.xlsx") as path:
13521353
with ExcelWriter(path) as writer:
13531354
assert os.fspath(writer) == str(path)
1355+
1356+
1357+
@pytest.mark.parametrize("klass", _writers.values())
1358+
def test_subclass_attr(klass):
1359+
# testing that subclasses of ExcelWriter don't have public attributes (issue 49602)
1360+
attrs_base = {name for name in dir(ExcelWriter) if not name.startswith("_")}
1361+
attrs_klass = {name for name in dir(klass) if not name.startswith("_")}
1362+
assert not attrs_base.symmetric_difference(attrs_klass)

0 commit comments

Comments
 (0)