Skip to content

Commit 891785d

Browse files
committed
2 parents 6274120 + a022d7b commit 891785d

File tree

117 files changed

+2533
-1541
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+2533
-1541
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*.log
1313
*.swp
1414
*.pdb
15+
*.zip
1516
.project
1617
.pydevproject
1718
.settings

Diff for: .pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ repos:
2626
name: isort (cython)
2727
types: [cython]
2828
- repo: https://github.com./asottile/pyupgrade
29-
rev: v2.7.3
29+
rev: v2.7.4
3030
hooks:
3131
- id: pyupgrade
3232
args: [--py37-plus]

Diff for: asv_bench/benchmarks/categoricals.py

+43
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import string
2+
import sys
13
import warnings
24

35
import numpy as np
@@ -67,6 +69,47 @@ def time_existing_series(self):
6769
pd.Categorical(self.series)
6870

6971

72+
class AsType:
73+
def setup(self):
74+
N = 10 ** 5
75+
76+
random_pick = np.random.default_rng().choice
77+
78+
categories = {
79+
"str": list(string.ascii_letters),
80+
"int": np.random.randint(2 ** 16, size=154),
81+
"float": sys.maxsize * np.random.random((38,)),
82+
"timestamp": [
83+
pd.Timestamp(x, unit="s") for x in np.random.randint(2 ** 18, size=578)
84+
],
85+
}
86+
87+
self.df = pd.DataFrame(
88+
{col: random_pick(cats, N) for col, cats in categories.items()}
89+
)
90+
91+
for col in ("int", "float", "timestamp"):
92+
self.df[col + "_as_str"] = self.df[col].astype(str)
93+
94+
for col in self.df.columns:
95+
self.df[col] = self.df[col].astype("category")
96+
97+
def astype_str(self):
98+
[self.df[col].astype("str") for col in "int float timestamp".split()]
99+
100+
def astype_int(self):
101+
[self.df[col].astype("int") for col in "int_as_str timestamp".split()]
102+
103+
def astype_float(self):
104+
[
105+
self.df[col].astype("float")
106+
for col in "float_as_str int int_as_str timestamp".split()
107+
]
108+
109+
def astype_datetime(self):
110+
self.df["float"].astype(pd.DatetimeTZDtype(tz="US/Pacific"))
111+
112+
70113
class Concat:
71114
def setup(self):
72115
N = 10 ** 5

Diff for: asv_bench/benchmarks/rolling.py

+13
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,17 @@ def time_rolling_offset(self, method):
225225
getattr(self.groupby_roll_offset, method)()
226226

227227

228+
class GroupbyEWM:
229+
230+
params = ["cython", "numba"]
231+
param_names = ["engine"]
232+
233+
def setup(self, engine):
234+
df = pd.DataFrame({"A": range(50), "B": range(50)})
235+
self.gb_ewm = df.groupby("A").ewm(com=1.0)
236+
237+
def time_groupby_mean(self, engine):
238+
self.gb_ewm.mean(engine=engine)
239+
240+
228241
from .pandas_vb_common import setup # noqa: F401 isort:skip

Diff for: asv_bench/benchmarks/series_methods.py

+49
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,55 @@ def time_isin_long_series_long_values_floats(self):
9090
self.s_long_floats.isin(self.vals_long_floats)
9191

9292

93+
class IsInLongSeriesLookUpDominates:
94+
params = [
95+
["int64", "int32", "float64", "float32", "object"],
96+
[5, 1000],
97+
["random_hits", "random_misses", "monotone_hits", "monotone_misses"],
98+
]
99+
param_names = ["dtype", "MaxNumber", "series_type"]
100+
101+
def setup(self, dtype, MaxNumber, series_type):
102+
N = 10 ** 7
103+
if series_type == "random_hits":
104+
np.random.seed(42)
105+
array = np.random.randint(0, MaxNumber, N)
106+
if series_type == "random_misses":
107+
np.random.seed(42)
108+
array = np.random.randint(0, MaxNumber, N) + MaxNumber
109+
if series_type == "monotone_hits":
110+
array = np.repeat(np.arange(MaxNumber), N // MaxNumber)
111+
if series_type == "monotone_misses":
112+
array = np.arange(N) + MaxNumber
113+
self.series = Series(array).astype(dtype)
114+
self.values = np.arange(MaxNumber).astype(dtype)
115+
116+
def time_isin(self, dtypes, MaxNumber, series_type):
117+
self.series.isin(self.values)
118+
119+
120+
class IsInLongSeriesValuesDominate:
121+
params = [
122+
["int64", "int32", "float64", "float32", "object"],
123+
["random", "monotone"],
124+
]
125+
param_names = ["dtype", "series_type"]
126+
127+
def setup(self, dtype, series_type):
128+
N = 10 ** 7
129+
if series_type == "random":
130+
np.random.seed(42)
131+
vals = np.random.randint(0, 10 * N, N)
132+
if series_type == "monotone":
133+
vals = np.arange(N)
134+
self.values = vals.astype(dtype)
135+
M = 10 ** 6 + 1
136+
self.series = Series(np.arange(M)).astype(dtype)
137+
138+
def time_isin(self, dtypes, series_type):
139+
self.series.isin(self.values)
140+
141+
93142
class NSort:
94143

95144
params = ["first", "last", "all"]

Diff for: ci/run_tests.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ PYTEST_CMD="${XVFB}pytest -m \"$PATTERN\" -n $PYTEST_WORKERS --dist=loadfile -s
2525
if [[ $(uname) != "Linux" && $(uname) != "Darwin" ]]; then
2626
# GH#37455 windows py38 build appears to be running out of memory
2727
# skip collection of window tests
28-
PYTEST_CMD="$PYTEST_CMD --ignore=pandas/tests/window/"
28+
PYTEST_CMD="$PYTEST_CMD --ignore=pandas/tests/window/ --ignore=pandas/tests/plotting/"
2929
fi
3030

3131
echo $PYTEST_CMD

Diff for: doc/source/ecosystem.rst

+10
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,16 @@ D-Tale integrates seamlessly with Jupyter notebooks, Python terminals, Kaggle
178178
& Google Colab. Here are some demos of the `grid <http://alphatechadmin.pythonanywhere.com/>`__
179179
and `chart-builder <http://alphatechadmin.pythonanywhere.com/charts/4?chart_type=surface&query=&x=date&z=Col0&agg=raw&cpg=false&y=%5B%22security_id%22%5D>`__.
180180

181+
`hvplot <https://hvplot.holoviz.org/index.html>`__
182+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
183+
184+
hvPlot is a high-level plotting API for the PyData ecosystem built on `HoloViews <https://holoviews.org/>`__.
185+
It can be loaded as a native pandas plotting backend via
186+
187+
.. code:: python
188+
189+
pd.set_option("plotting.backend", "hvplot")
190+
181191
.. _ecosystem.ide:
182192

183193
IDE

Diff for: doc/source/user_guide/computation.rst

+7
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,10 @@ parameter:
205205
- ``min`` : lowest rank in the group
206206
- ``max`` : highest rank in the group
207207
- ``first`` : ranks assigned in the order they appear in the array
208+
209+
.. _computation.windowing:
210+
211+
Windowing functions
212+
~~~~~~~~~~~~~~~~~~~
213+
214+
See :ref:`the window operations user guide <window.overview>` for an overview of windowing functions.

Diff for: doc/source/user_guide/indexing.rst

+34
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,40 @@ Mask
11581158
s.mask(s >= 0)
11591159
df.mask(df >= 0)
11601160
1161+
.. _indexing.np_where:
1162+
1163+
Setting with enlargement conditionally using :func:`numpy`
1164+
----------------------------------------------------------
1165+
1166+
An alternative to :meth:`~pandas.DataFrame.where` is to use :func:`numpy.where`.
1167+
Combined with setting a new column, you can use it to enlarge a dataframe where the
1168+
values are determined conditionally.
1169+
1170+
Consider you have two choices to choose from in the following dataframe. And you want to
1171+
set a new column color to 'green' when the second column has 'Z'. You can do the
1172+
following:
1173+
1174+
.. ipython:: python
1175+
1176+
df = pd.DataFrame({'col1': list('ABBC'), 'col2': list('ZZXY')})
1177+
df['color'] = np.where(df['col2'] == 'Z', 'green', 'red')
1178+
df
1179+
1180+
If you have multiple conditions, you can use :func:`numpy.select` to achieve that. Say
1181+
corresponding to three conditions there are three choice of colors, with a fourth color
1182+
as a fallback, you can do the following.
1183+
1184+
.. ipython:: python
1185+
1186+
conditions = [
1187+
(df['col2'] == 'Z') & (df['col1'] == 'A'),
1188+
(df['col2'] == 'Z') & (df['col1'] == 'B'),
1189+
(df['col1'] == 'B')
1190+
]
1191+
choices = ['yellow', 'blue', 'purple']
1192+
df['color'] = np.select(conditions, choices, default='black')
1193+
df
1194+
11611195
.. _indexing.query:
11621196

11631197
The :meth:`~pandas.DataFrame.query` Method

Diff for: doc/source/user_guide/window.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Concept Method Returned Object
4343
Rolling window ``rolling`` ``Rolling`` Yes Yes
4444
Weighted window ``rolling`` ``Window`` No No
4545
Expanding window ``expanding`` ``Expanding`` No Yes
46-
Exponentially Weighted window ``ewm`` ``ExponentialMovingWindow`` No No
46+
Exponentially Weighted window ``ewm`` ``ExponentialMovingWindow`` No Yes (as of version 1.2)
4747
============================= ================= =========================== =========================== ========================
4848

4949
As noted above, some operations support specifying a window based on a time offset:

Diff for: doc/source/whatsnew/v1.2.0.rst

+36-2
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,23 @@ example where the index name is preserved:
204204
The same is true for :class:`MultiIndex`, but the logic is applied separately on a
205205
level-by-level basis.
206206

207+
.. _whatsnew_120.groupby_ewm:
208+
209+
Groupby supports EWM operations directly
210+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
211+
212+
:class:`DataFrameGroupBy` now supports exponentially weighted window operations directly (:issue:`16037`).
213+
214+
.. ipython:: python
215+
216+
df = pd.DataFrame({'A': ['a', 'b', 'a', 'b'], 'B': range(4)})
217+
df
218+
df.groupby('A').ewm(com=1.0).mean()
219+
220+
Additionally ``mean`` supports execution via `Numba <https://numba.pydata.org/>`__ with
221+
the ``engine`` and ``engine_kwargs`` arguments. Numba must be installed as an optional dependency
222+
to use this feature.
223+
207224
.. _whatsnew_120.enhancements.other:
208225

209226
Other enhancements
@@ -458,7 +475,8 @@ Deprecations
458475
- :class:`Index` methods ``&``, ``|``, and ``^`` behaving as the set operations :meth:`Index.intersection`, :meth:`Index.union`, and :meth:`Index.symmetric_difference`, respectively, are deprecated and in the future will behave as pointwise boolean operations matching :class:`Series` behavior. Use the named set methods instead (:issue:`36758`)
459476
- :meth:`Categorical.is_dtype_equal` and :meth:`CategoricalIndex.is_dtype_equal` are deprecated, will be removed in a future version (:issue:`37545`)
460477
- :meth:`Series.slice_shift` and :meth:`DataFrame.slice_shift` are deprecated, use :meth:`Series.shift` or :meth:`DataFrame.shift` instead (:issue:`37601`)
461-
478+
- Partial slicing on unordered :class:`DatetimeIndexes` with keys, which are not in Index is deprecated and will be removed in a future version (:issue:`18531`)
479+
- The ``inplace`` parameter of :meth:`Categorical.remove_unused_categories` is deprecated and will be removed in a future version (:issue:`37643`)
462480

463481
.. ---------------------------------------------------------------------------
464482
@@ -482,7 +500,9 @@ Performance improvements
482500
- Reduced peak memory usage in :meth:`DataFrame.to_pickle` when using ``protocol=5`` in python 3.8+ (:issue:`34244`)
483501
- faster ``dir`` calls when many index labels, e.g. ``dir(ser)`` (:issue:`37450`)
484502
- Performance improvement in :class:`ExpandingGroupby` (:issue:`37064`)
503+
- Performance improvement in :meth:`Series.astype` and :meth:`DataFrame.astype` for :class:`Categorical` (:issue:`8628`)
485504
- Performance improvement in :meth:`pd.DataFrame.groupby` for ``float`` ``dtype`` (:issue:`28303`), changes of the underlying hash-function can lead to changes in float based indexes sort ordering for ties (e.g. :meth:`pd.Index.value_counts`)
505+
- Performance improvement in :meth:`pd.isin` for inputs with more than 1e6 elements
486506

487507
.. ---------------------------------------------------------------------------
488508
@@ -522,6 +542,7 @@ Timedelta
522542
- Bug in :class:`TimedeltaIndex`, :class:`Series`, and :class:`DataFrame` floor-division with ``timedelta64`` dtypes and ``NaT`` in the denominator (:issue:`35529`)
523543
- Bug in parsing of ISO 8601 durations in :class:`Timedelta`, :meth:`pd.to_datetime` (:issue:`37159`, fixes :issue:`29773` and :issue:`36204`)
524544
- Bug in :func:`to_timedelta` with a read-only array incorrectly raising (:issue:`34857`)
545+
- Bug in :class:`Timedelta` incorrectly truncating to sub-second portion of a string input when it has precision higher than nanoseconds (:issue:`36738`)
525546

526547
Timezones
527548
^^^^^^^^^
@@ -562,6 +583,8 @@ Strings
562583

563584
Interval
564585
^^^^^^^^
586+
587+
- Bug in :meth:`DataFrame.replace` and :meth:`Series.replace` where :class:`Interval` dtypes would be converted to object dtypes (:issue:`34871`)
565588
- Bug in :meth:`IntervalIndex.take` with negative indices and ``fill_value=None`` (:issue:`37330`)
566589
-
567590
-
@@ -584,6 +607,8 @@ Indexing
584607
- Bug in :meth:`DataFrame.loc` returned requested key plus missing values when ``loc`` was applied to single level from :class:`MultiIndex` (:issue:`27104`)
585608
- Bug in indexing on a :class:`Series` or :class:`DataFrame` with a :class:`CategoricalIndex` using a listlike indexer containing NA values (:issue:`37722`)
586609
- Bug in :meth:`DataFrame.xs` ignored ``droplevel=False`` for columns (:issue:`19056`)
610+
- Bug in :meth:`DataFrame.reindex` raising ``IndexingError`` wrongly for empty :class:`DataFrame` with ``tolerance`` not None or ``method="nearest"`` (:issue:`27315`)
611+
- Bug in indexing on a :class:`Series` or :class:`DataFrame` with a :class:`CategoricalIndex` using listlike indexer that contains elements that are in the index's ``categories`` but not in the index itself failing to raise ``KeyError`` (:issue:`37901`)
587612

588613
Missing
589614
^^^^^^^
@@ -597,6 +622,7 @@ MultiIndex
597622
- Bug in :meth:`DataFrame.xs` when used with :class:`IndexSlice` raises ``TypeError`` with message ``"Expected label or tuple of labels"`` (:issue:`35301`)
598623
- Bug in :meth:`DataFrame.reset_index` with ``NaT`` values in index raises ``ValueError`` with message ``"cannot convert float NaN to integer"`` (:issue:`36541`)
599624
- Bug in :meth:`DataFrame.combine_first` when used with :class:`MultiIndex` containing string and ``NaN`` values raises ``TypeError`` (:issue:`36562`)
625+
- Bug in :meth:`MultiIndex.drop` dropped ``NaN`` values when non existing key was given as input (:issue:`18853`)
600626

601627
I/O
602628
^^^
@@ -625,16 +651,23 @@ I/O
625651
- Bug in :func:`read_html` was raising a ``TypeError`` when supplying a ``pathlib.Path`` argument to the ``io`` parameter (:issue:`37705`)
626652
- :meth:`to_excel` and :meth:`to_markdown` support writing to fsspec URLs such as S3 and Google Cloud Storage (:issue:`33987`)
627653
- Bug in :meth:`read_fw` was not skipping blank lines (even with ``skip_blank_lines=True``) (:issue:`37758`)
654+
- :meth:`read_fwf` was inferring compression with ``compression=None`` which was not consistent with the other :meth:``read_*`` functions (:issue:`37909`)
655+
656+
Period
657+
^^^^^^
658+
659+
- Bug in :meth:`DataFrame.replace` and :meth:`Series.replace` where :class:`Period` dtypes would be converted to object dtypes (:issue:`34871`)
628660

629661
Plotting
630662
^^^^^^^^
631663

632664
- Bug in :meth:`DataFrame.plot` was rotating xticklabels when ``subplots=True``, even if the x-axis wasn't an irregular time series (:issue:`29460`)
633665
- Bug in :meth:`DataFrame.plot` where a marker letter in the ``style`` keyword sometimes causes a ``ValueError`` (:issue:`21003`)
634666
- Twinned axes were losing their tick labels which should only happen to all but the last row or column of 'externally' shared axes (:issue:`33819`)
667+
- Bug in :meth:`Series.plot` and :meth:`DataFrame.plot` was throwing :exc:`ValueError` with a :class:`Series` or :class:`DataFrame`
668+
indexed by a :class:`TimedeltaIndex` with a fixed frequency when x-axis lower limit was greater than upper limit (:issue:`37454`)
635669
- Bug in :meth:`DataFrameGroupBy.boxplot` when ``subplots=False``, a KeyError would raise (:issue:`16748`)
636670

637-
638671
Groupby/resample/rolling
639672
^^^^^^^^^^^^^^^^^^^^^^^^
640673

@@ -700,6 +733,7 @@ Other
700733
- Bug in :meth:`DataFrame.replace` and :meth:`Series.replace` incorrectly casting from ``PeriodDtype`` to object dtype (:issue:`34871`)
701734
- Fixed bug in metadata propagation incorrectly copying DataFrame columns as metadata when the column name overlaps with the metadata name (:issue:`37037`)
702735
- Fixed metadata propagation in the :class:`Series.dt`, :class:`Series.str` accessors, :class:`DataFrame.duplicated`, :class:`DataFrame.stack`, :class:`DataFrame.unstack`, :class:`DataFrame.pivot`, :class:`DataFrame.append`, :class:`DataFrame.diff`, :class:`DataFrame.applymap` and :class:`DataFrame.update` methods (:issue:`28283`) (:issue:`37381`)
736+
- Fixed metadata propagation when selecting columns from a DataFrame with ``DataFrame.__getitem__`` (:issue:`28283`)
703737
- Bug in :meth:`Index.union` behaving differently depending on whether operand is a :class:`Index` or other list-like (:issue:`36384`)
704738
- Passing an array with 2 or more dimensions to the :class:`Series` constructor now raises the more specific ``ValueError``, from a bare ``Exception`` previously (:issue:`35744`)
705739
- Bug in ``accessor.DirNamesMixin``, where ``dir(obj)`` wouldn't show attributes defined on the instance (:issue:`37173`).

Diff for: pandas/_libs/interval.pyx

+2-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ cdef class IntervalMixin:
179179
return (self.right == self.left) & (self.closed != 'both')
180180

181181
def _check_closed_matches(self, other, name='other'):
182-
"""Check if the closed attribute of `other` matches.
182+
"""
183+
Check if the closed attribute of `other` matches.
183184
184185
Note that 'left' and 'right' are considered different from 'both'.
185186

Diff for: pandas/_libs/src/klib/khash_python.h

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
// GH 13436 showed that _Py_HashDouble doesn't work well with khash
1717
// GH 28303 showed, that the simple xoring-version isn't good enough
1818
// See GH 36729 for evaluation of the currently used murmur2-hash version
19+
// An interesting alternative to expensive murmur2-hash would be to change
20+
// the probing strategy and use e.g. the probing strategy from CPython's
21+
// implementation of dicts, which shines for smaller sizes but is more
22+
// predisposed to superlinear running times (see GH 36729 for comparison)
23+
1924

2025
khint64_t PANDAS_INLINE asint64(double key) {
2126
khint64_t val;

0 commit comments

Comments
 (0)