Skip to content

Commit 33c3124

Browse files
authored
Merge branch 'master' into styler_apply
2 parents dc4114a + d558bce commit 33c3124

35 files changed

+845
-582
lines changed

asv_bench/benchmarks/rolling.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,11 @@ class Pairwise:
140140

141141
def setup(self, window, method, pairwise):
142142
N = 10 ** 4
143+
n_groups = 20
144+
groups = [i for _ in range(N // n_groups) for i in range(n_groups)]
143145
arr = np.random.random(N)
144146
self.df = pd.DataFrame(arr)
147+
self.df_group = pd.DataFrame({"A": groups, "B": arr}).groupby("A")
145148

146149
def time_pairwise(self, window, method, pairwise):
147150
if window is None:
@@ -150,6 +153,13 @@ def time_pairwise(self, window, method, pairwise):
150153
r = self.df.rolling(window=window)
151154
getattr(r, method)(self.df, pairwise=pairwise)
152155

156+
def time_groupby(self, window, method, pairwise):
157+
if window is None:
158+
r = self.df_group.expanding()
159+
else:
160+
r = self.df_group.rolling(window=window)
161+
getattr(r, method)(self.df, pairwise=pairwise)
162+
153163

154164
class Quantile:
155165
params = (

doc/source/user_guide/style.ipynb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,7 @@
181181
"\n",
182182
"styles = [\n",
183183
" hover(),\n",
184-
" {'selector': \"th\", 'props': [(\"font-size\", \"150%\"),\n",
185-
" (\"text-align\", \"center\")]}\n",
184+
" {'selector': \"th\", 'props': [(\"font-size\", \"150%\"), (\"text-align\", \"center\")]}\n",
186185
"]\n",
187186
"\n",
188187
"df.style.set_table_styles(styles)"
@@ -225,7 +224,7 @@
225224
"cell_type": "markdown",
226225
"metadata": {},
227226
"source": [
228-
"We can also chain all of the above by setting the `overwrite` argument to `False` so that it preserves previous settings."
227+
"We can also chain all of the above by setting the `overwrite` argument to `False` so that it preserves previous settings. We also show the CSS string input rather than the list of tuples."
229228
]
230229
},
231230
{
@@ -239,13 +238,13 @@
239238
" set_table_styles(styles).\\\n",
240239
" set_table_styles({\n",
241240
" 'A': [{'selector': '',\n",
242-
" 'props': [('color', 'red')]}],\n",
241+
" 'props': 'color:red;'}],\n",
243242
" 'B': [{'selector': 'td',\n",
244-
" 'props': [('color', 'blue')]}]\n",
243+
" 'props': 'color:blue;'}]\n",
245244
" }, axis=0, overwrite=False).\\\n",
246245
" set_table_styles({\n",
247246
" 3: [{'selector': 'td',\n",
248-
" 'props': [('color', 'green')]}]\n",
247+
" 'props': 'color:green;font-weight:bold;'}]\n",
249248
" }, axis=1, overwrite=False)\n",
250249
"s"
251250
]

doc/source/whatsnew/v1.2.2.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Fixed regressions
2121
- Fixed regression in :meth:`~DataFrame.to_pickle` failing to create bz2/xz compressed pickle files with ``protocol=5`` (:issue:`39002`)
2222
- Fixed regression in :func:`pandas.testing.assert_series_equal` and :func:`pandas.testing.assert_frame_equal` always raising ``AssertionError`` when comparing extension dtypes (:issue:`39410`)
2323
- Fixed regression in :meth:`~DataFrame.to_csv` opening ``codecs.StreamWriter`` in binary mode instead of in text mode and ignoring user-provided ``mode`` (:issue:`39247`)
24+
- Fixed regression in :meth:`core.window.rolling.Rolling.count` where the ``min_periods`` argument would be set to ``0`` after the operation (:issue:`39554`)
2425
-
2526

2627
.. ---------------------------------------------------------------------------
@@ -31,6 +32,7 @@ Bug fixes
3132
~~~~~~~~~
3233

3334
- :func:`pandas.read_excel` error message when a specified ``sheetname`` does not exist is now uniform across engines (:issue:`39250`)
35+
- Fixed bug in :func:`pandas.read_excel` producing incorrect results when the engine ``openpyxl`` is used and the excel file is missing or has incorrect dimension information; the fix requires ``openpyxl`` >= 3.0.0, prior versions may still fail (:issue:`38956`, :issue:`39001`)
3436
-
3537

3638
.. ---------------------------------------------------------------------------

doc/source/whatsnew/v1.3.0.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,12 @@ Other enhancements
5353
- :meth:`DataFrame.apply` can now accept non-callable DataFrame properties as strings, e.g. ``df.apply("size")``, which was already the case for :meth:`Series.apply` (:issue:`39116`)
5454
- :meth:`Series.apply` can now accept list-like or dictionary-like arguments that aren't lists or dictionaries, e.g. ``ser.apply(np.array(["sum", "mean"]))``, which was already the case for :meth:`DataFrame.apply` (:issue:`39140`)
5555
- :meth:`DataFrame.plot.scatter` can now accept a categorical column as the argument to ``c`` (:issue:`12380`, :issue:`31357`)
56-
- :meth:`.Styler.set_tooltips` allows on hover tooltips to be added to styled HTML dataframes. (:issue:`39317`)
57-
- :meth:`.Styler.apply` now more consistently accepts ndarray function returns, i.e. in all cases for ``axis`` is ``0, 1 or None``. (:issue:`39359`)
56+
- :meth:`.Styler.set_tooltips` allows on hover tooltips to be added to styled HTML dataframes (:issue:`35643`)
57+
- :meth:`.Styler.set_tooltips_class` and :meth:`.Styler.set_table_styles` amended to optionally allow certain css-string input arguments (:issue:`39564`)
5858
- :meth:`Series.loc.__getitem__` and :meth:`Series.loc.__setitem__` with :class:`MultiIndex` now raising helpful error message when indexer has too many dimensions (:issue:`35349`)
5959
- :meth:`pandas.read_stata` and :class:`StataReader` support reading data from compressed files.
6060

61+
6162
.. ---------------------------------------------------------------------------
6263
6364
.. _whatsnew_130.notable_bug_fixes:
@@ -250,7 +251,8 @@ Performance improvements
250251
- Performance improvement in :meth:`Series.mean` for nullable data types (:issue:`34814`)
251252
- Performance improvement in :meth:`Series.isin` for nullable data types (:issue:`38340`)
252253
- Performance improvement in :meth:`DataFrame.corr` for method=kendall (:issue:`28329`)
253-
- Performance improvement in :meth:`core.window.Rolling.corr` and :meth:`core.window.Rolling.cov` (:issue:`39388`)
254+
- Performance improvement in :meth:`core.window.rolling.Rolling.corr` and :meth:`core.window.rolling.Rolling.cov` (:issue:`39388`)
255+
- Performance improvement in :meth:`core.window.rolling.RollingGroupby.corr`, :meth:`core.window.expanding.ExpandingGroupby.corr`, :meth:`core.window.expanding.ExpandingGroupby.corr` and :meth:`core.window.expanding.ExpandingGroupby.cov` (:issue:`39591`)
254256

255257
.. ---------------------------------------------------------------------------
256258
@@ -280,6 +282,7 @@ Datetimelike
280282
- Bug in :class:`Categorical` incorrectly typecasting ``datetime`` object to ``Timestamp`` (:issue:`38878`)
281283
- Bug in comparisons between :class:`Timestamp` object and ``datetime64`` objects just outside the implementation bounds for nanosecond ``datetime64`` (:issue:`39221`)
282284
- Bug in :meth:`Timestamp.round`, :meth:`Timestamp.floor`, :meth:`Timestamp.ceil` for values near the implementation bounds of :class:`Timestamp` (:issue:`39244`)
285+
- Bug in :meth:`Timedelta.round`, :meth:`Timedelta.floor`, :meth:`Timedelta.ceil` for values near the implementation bounds of :class:`Timedelta` (:issue:`38964`)
283286
- Bug in :func:`date_range` incorrectly creating :class:`DatetimeIndex` containing ``NaT`` instead of raising ``OutOfBoundsDatetime`` in corner cases (:issue:`24124`)
284287

285288
Timedelta
@@ -407,6 +410,8 @@ Groupby/resample/rolling
407410
- Bug in :meth:`.Resampler.aggregate` and :meth:`DataFrame.transform` raising ``TypeError`` instead of ``SpecificationError`` when missing keys had mixed dtypes (:issue:`39025`)
408411
- Bug in :meth:`.DataFrameGroupBy.idxmin` and :meth:`.DataFrameGroupBy.idxmax` with ``ExtensionDtype`` columns (:issue:`38733`)
409412
- Bug in :meth:`Series.resample` would raise when the index was a :class:`PeriodIndex` consisting of ``NaT`` (:issue:`39227`)
413+
- Bug in :meth:`core.window.rolling.RollingGroupby.corr` and :meth:`core.window.expanding.ExpandingGroupby.corr` where the groupby column would return 0 instead of ``np.nan`` when providing ``other`` that was longer than each group (:issue:`39591`)
414+
- Bug in :meth:`core.window.expanding.ExpandingGroupby.corr` and :meth:`core.window.expanding.ExpandingGroupby.cov` where 1 would be returned instead of ``np.nan`` when providing ``other`` that was longer than each group (:issue:`39591`)
410415

411416
Reshaping
412417
^^^^^^^^^

pandas/_libs/tslibs/fields.pyx

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,3 +636,154 @@ def get_locale_names(name_type: str, locale: object = None):
636636
"""
637637
with set_locale(locale, LC_TIME):
638638
return getattr(LocaleTime(), name_type)
639+
640+
641+
# ---------------------------------------------------------------------
642+
# Rounding
643+
644+
645+
class RoundTo:
646+
"""
647+
enumeration defining the available rounding modes
648+
649+
Attributes
650+
----------
651+
MINUS_INFTY
652+
round towards -∞, or floor [2]_
653+
PLUS_INFTY
654+
round towards +∞, or ceil [3]_
655+
NEAREST_HALF_EVEN
656+
round to nearest, tie-break half to even [6]_
657+
NEAREST_HALF_MINUS_INFTY
658+
round to nearest, tie-break half to -∞ [5]_
659+
NEAREST_HALF_PLUS_INFTY
660+
round to nearest, tie-break half to +∞ [4]_
661+
662+
663+
References
664+
----------
665+
.. [1] "Rounding - Wikipedia"
666+
https://en.wikipedia.org/wiki/Rounding
667+
.. [2] "Rounding down"
668+
https://en.wikipedia.org/wiki/Rounding#Rounding_down
669+
.. [3] "Rounding up"
670+
https://en.wikipedia.org/wiki/Rounding#Rounding_up
671+
.. [4] "Round half up"
672+
https://en.wikipedia.org/wiki/Rounding#Round_half_up
673+
.. [5] "Round half down"
674+
https://en.wikipedia.org/wiki/Rounding#Round_half_down
675+
.. [6] "Round half to even"
676+
https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
677+
"""
678+
@property
679+
def MINUS_INFTY(self) -> int:
680+
return 0
681+
682+
@property
683+
def PLUS_INFTY(self) -> int:
684+
return 1
685+
686+
@property
687+
def NEAREST_HALF_EVEN(self) -> int:
688+
return 2
689+
690+
@property
691+
def NEAREST_HALF_PLUS_INFTY(self) -> int:
692+
return 3
693+
694+
@property
695+
def NEAREST_HALF_MINUS_INFTY(self) -> int:
696+
return 4
697+
698+
699+
cdef inline ndarray[int64_t] _floor_int64(int64_t[:] values, int64_t unit):
700+
cdef:
701+
Py_ssize_t i, n = len(values)
702+
ndarray[int64_t] result = np.empty(n, dtype="i8")
703+
int64_t res, value
704+
705+
with cython.overflowcheck(True):
706+
for i in range(n):
707+
value = values[i]
708+
if value == NPY_NAT:
709+
res = NPY_NAT
710+
else:
711+
res = value - value % unit
712+
result[i] = res
713+
714+
return result
715+
716+
717+
cdef inline ndarray[int64_t] _ceil_int64(int64_t[:] values, int64_t unit):
718+
cdef:
719+
Py_ssize_t i, n = len(values)
720+
ndarray[int64_t] result = np.empty(n, dtype="i8")
721+
int64_t res, value
722+
723+
with cython.overflowcheck(True):
724+
for i in range(n):
725+
value = values[i]
726+
727+
if value == NPY_NAT:
728+
res = NPY_NAT
729+
else:
730+
remainder = value % unit
731+
if remainder == 0:
732+
res = value
733+
else:
734+
res = value + (unit - remainder)
735+
736+
result[i] = res
737+
738+
return result
739+
740+
741+
cdef inline ndarray[int64_t] _rounddown_int64(values, int64_t unit):
742+
return _ceil_int64(values - unit // 2, unit)
743+
744+
745+
cdef inline ndarray[int64_t] _roundup_int64(values, int64_t unit):
746+
return _floor_int64(values + unit // 2, unit)
747+
748+
749+
def round_nsint64(values: np.ndarray, mode: RoundTo, nanos) -> np.ndarray:
750+
"""
751+
Applies rounding mode at given frequency
752+
753+
Parameters
754+
----------
755+
values : np.ndarray[int64_t]`
756+
mode : instance of `RoundTo` enumeration
757+
nanos : np.int64
758+
Freq to round to, expressed in nanoseconds
759+
760+
Returns
761+
-------
762+
np.ndarray[int64_t]
763+
"""
764+
cdef:
765+
int64_t unit = nanos
766+
767+
if mode == RoundTo.MINUS_INFTY:
768+
return _floor_int64(values, unit)
769+
elif mode == RoundTo.PLUS_INFTY:
770+
return _ceil_int64(values, unit)
771+
elif mode == RoundTo.NEAREST_HALF_MINUS_INFTY:
772+
return _rounddown_int64(values, unit)
773+
elif mode == RoundTo.NEAREST_HALF_PLUS_INFTY:
774+
return _roundup_int64(values, unit)
775+
elif mode == RoundTo.NEAREST_HALF_EVEN:
776+
# for odd unit there is no need of a tie break
777+
if unit % 2:
778+
return _rounddown_int64(values, unit)
779+
quotient, remainder = np.divmod(values, unit)
780+
mask = np.logical_or(
781+
remainder > (unit // 2),
782+
np.logical_and(remainder == (unit // 2), quotient % 2)
783+
)
784+
quotient[mask] += 1
785+
return quotient * unit
786+
787+
# if/elif above should catch all rounding modes defined in enum 'RoundTo':
788+
# if flow of control arrives here, it is a bug
789+
raise ValueError("round_nsint64 called with an unrecognized rounding mode")

pandas/_libs/tslibs/timedeltas.pyx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ from pandas._libs.tslibs.util cimport (
4747
is_integer_object,
4848
is_timedelta64_object,
4949
)
50+
from pandas._libs.tslibs.fields import RoundTo, round_nsint64
5051

5152
# ----------------------------------------------------------------------
5253
# Constants
@@ -1297,14 +1298,18 @@ class Timedelta(_Timedelta):
12971298
object_state = self.value,
12981299
return (Timedelta, object_state)
12991300

1300-
def _round(self, freq, rounder):
1301+
@cython.cdivision(True)
1302+
def _round(self, freq, mode):
13011303
cdef:
1302-
int64_t result, unit
1304+
int64_t result, unit, remainder
1305+
ndarray[int64_t] arr
13031306

13041307
from pandas._libs.tslibs.offsets import to_offset
13051308
unit = to_offset(freq).nanos
1306-
result = unit * rounder(self.value / float(unit))
1307-
return Timedelta(result, unit='ns')
1309+
1310+
arr = np.array([self.value], dtype="i8")
1311+
result = round_nsint64(arr, mode, unit)[0]
1312+
return Timedelta(result, unit="ns")
13081313

13091314
def round(self, freq):
13101315
"""
@@ -1323,7 +1328,7 @@ class Timedelta(_Timedelta):
13231328
------
13241329
ValueError if the freq cannot be converted
13251330
"""
1326-
return self._round(freq, np.round)
1331+
return self._round(freq, RoundTo.NEAREST_HALF_EVEN)
13271332

13281333
def floor(self, freq):
13291334
"""
@@ -1334,7 +1339,7 @@ class Timedelta(_Timedelta):
13341339
freq : str
13351340
Frequency string indicating the flooring resolution.
13361341
"""
1337-
return self._round(freq, np.floor)
1342+
return self._round(freq, RoundTo.MINUS_INFTY)
13381343

13391344
def ceil(self, freq):
13401345
"""
@@ -1345,7 +1350,7 @@ class Timedelta(_Timedelta):
13451350
freq : str
13461351
Frequency string indicating the ceiling resolution.
13471352
"""
1348-
return self._round(freq, np.ceil)
1353+
return self._round(freq, RoundTo.PLUS_INFTY)
13491354

13501355
# ----------------------------------------------------------------
13511356
# Arithmetic Methods

0 commit comments

Comments
 (0)