Skip to content

Commit fc85b05

Browse files
committed
Merge branch 'master' into bug-is_matching_na
2 parents 94dc125 + 6635fa1 commit fc85b05

File tree

18 files changed

+325
-228
lines changed

18 files changed

+325
-228
lines changed

doc/source/user_guide/style.ipynb

+26-50
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,10 @@
140140
"metadata": {},
141141
"outputs": [],
142142
"source": [
143-
"s = df.style.set_table_attributes('class=\"table-cls\"')\n",
144-
"cls = pd.DataFrame(data=[['cls1', None], ['cls3', 'cls2 cls3']], index=[0,2], columns=['A', 'C'])\n",
145-
"s.set_td_classes(cls)"
143+
"css_classes = pd.DataFrame(data=[['cls1', None], ['cls3', 'cls2 cls3']], index=[0,2], columns=['A', 'C'])\n",
144+
"df.style.\\\n",
145+
" set_table_attributes('class=\"table-cls\"').\\\n",
146+
" set_td_classes(css_classes)"
146147
]
147148
},
148149
{
@@ -314,13 +315,10 @@
314315
"outputs": [],
315316
"source": [
316317
"def color_negative_red(val):\n",
317-
" \"\"\"\n",
318-
" Takes a scalar and returns a string with\n",
319-
" the css property `'color: red'` for negative\n",
320-
" strings, black otherwise.\n",
321-
" \"\"\"\n",
322-
" color = 'red' if val < 0 else 'black'\n",
323-
" return 'color: %s' % color"
318+
" \"\"\"Color negative scalars red.\"\"\"\n",
319+
" css = 'color: red;'\n",
320+
" if val < 0: return css\n",
321+
" return None"
324322
]
325323
},
326324
{
@@ -368,11 +366,9 @@
368366
"outputs": [],
369367
"source": [
370368
"def highlight_max(s):\n",
371-
" '''\n",
372-
" highlight the maximum in a Series yellow.\n",
373-
" '''\n",
374-
" is_max = s == s.max()\n",
375-
" return ['background-color: yellow' if v else '' for v in is_max]"
369+
" \"\"\"Highlight the maximum in a Series bold-orange.\"\"\"\n",
370+
" css = 'background-color: orange; font-weight: bold;'\n",
371+
" return np.where(s == np.nanmax(s.values), css, None)"
376372
]
377373
},
378374
{
@@ -384,11 +380,20 @@
384380
"df.style.apply(highlight_max)"
385381
]
386382
},
383+
{
384+
"cell_type": "code",
385+
"execution_count": null,
386+
"metadata": {},
387+
"outputs": [],
388+
"source": [
389+
"df.style.apply(highlight_max, axis=1)"
390+
]
391+
},
387392
{
388393
"cell_type": "markdown",
389394
"metadata": {},
390395
"source": [
391-
"In this case the input is a `Series`, one column at a time.\n",
396+
"In this case the input is a `Series`, one column (or row) at a time.\n",
392397
"Notice that the output shape of `highlight_max` matches the input shape, an array with `len(s)` items."
393398
]
394399
},
@@ -406,8 +411,8 @@
406411
"outputs": [],
407412
"source": [
408413
"def compare_col(s, comparator=None):\n",
409-
" attr = 'background-color: #00BFFF;'\n",
410-
" return np.where(s < comparator, attr, '')"
414+
" css = 'background-color: #00BFFF;'\n",
415+
" return np.where(s < comparator, css, None)"
411416
]
412417
},
413418
{
@@ -442,41 +447,12 @@
442447
"cell_type": "markdown",
443448
"metadata": {},
444449
"source": [
445-
"Above we used `Styler.apply` to pass in each column one at a time.\n",
450+
"Above we used `Styler.apply` to pass in each column (or row) one at a time.\n",
446451
"\n",
447452
"<span style=\"background-color: #DEDEBE\">*Debugging Tip*: If you're having trouble writing your style function, try just passing it into <code style=\"background-color: #DEDEBE\">DataFrame.apply</code>. Internally, <code style=\"background-color: #DEDEBE\">Styler.apply</code> uses <code style=\"background-color: #DEDEBE\">DataFrame.apply</code> so the result should be the same.</span>\n",
448453
"\n",
449454
"What if you wanted to highlight just the maximum value in the entire table?\n",
450-
"Use `.apply(function, axis=None)` to indicate that your function wants the entire table, not one column or row at a time. Let's try that next.\n",
451-
"\n",
452-
"We'll rewrite our `highlight-max` to handle either Series (from `.apply(axis=0 or 1)`) or DataFrames (from `.apply(axis=None)`). We'll also allow the color to be adjustable, to demonstrate that `.apply`, and `.applymap` pass along keyword arguments."
453-
]
454-
},
455-
{
456-
"cell_type": "code",
457-
"execution_count": null,
458-
"metadata": {},
459-
"outputs": [],
460-
"source": [
461-
"def highlight_max(data, color='yellow'):\n",
462-
" '''\n",
463-
" highlight the maximum in a Series or DataFrame\n",
464-
" '''\n",
465-
" attr = 'background-color: {}'.format(color)\n",
466-
" if data.ndim == 1: # Series from .apply(axis=0) or axis=1\n",
467-
" is_max = data == data.max()\n",
468-
" return [attr if v else '' for v in is_max]\n",
469-
" else: # from .apply(axis=None)\n",
470-
" is_max = data == data.max().max()\n",
471-
" return pd.DataFrame(np.where(is_max, attr, ''),\n",
472-
" index=data.index, columns=data.columns)"
473-
]
474-
},
475-
{
476-
"cell_type": "markdown",
477-
"metadata": {},
478-
"source": [
479-
"When using ``Styler.apply(func, axis=None)``, the function must return a DataFrame with the same index and column labels."
455+
"Use `.apply(function, axis=None)` to indicate that your function wants the entire table, not one column or row at a time. In this case the return must be a DataFrame or ndarray of the same shape as the input. Let's try that next. "
480456
]
481457
},
482458
{
@@ -485,7 +461,7 @@
485461
"metadata": {},
486462
"outputs": [],
487463
"source": [
488-
"s = df.style.apply(highlight_max, color='darkorange', axis=None)\n",
464+
"s = df.style.apply(highlight_max, axis=None)\n",
489465
"s"
490466
]
491467
},

doc/source/whatsnew/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Version 1.2
2424
.. toctree::
2525
:maxdepth: 2
2626

27+
v1.2.3
2728
v1.2.2
2829
v1.2.1
2930
v1.2.0

doc/source/whatsnew/v1.2.2.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ Bug fixes
4646
Contributors
4747
~~~~~~~~~~~~
4848

49-
.. contributors:: v1.2.1..v1.2.2|HEAD
49+
.. contributors:: v1.2.1..v1.2.2

doc/source/whatsnew/v1.2.3.rst

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
.. _whatsnew_123:
2+
3+
What's new in 1.2.3 (March ??, 2021)
4+
------------------------------------
5+
6+
These are the changes in pandas 1.2.3. See :ref:`release` for a full changelog
7+
including other versions of pandas.
8+
9+
{{ header }}
10+
11+
.. ---------------------------------------------------------------------------
12+
13+
.. _whatsnew_123.regressions:
14+
15+
Fixed regressions
16+
~~~~~~~~~~~~~~~~~
17+
18+
-
19+
-
20+
21+
.. ---------------------------------------------------------------------------
22+
23+
.. _whatsnew_123.bug_fixes:
24+
25+
Bug fixes
26+
~~~~~~~~~
27+
28+
-
29+
-
30+
31+
.. ---------------------------------------------------------------------------
32+
33+
.. _whatsnew_123.other:
34+
35+
Other
36+
~~~~~
37+
38+
-
39+
-
40+
41+
.. ---------------------------------------------------------------------------
42+
43+
.. _whatsnew_123.contributors:
44+
45+
Contributors
46+
~~~~~~~~~~~~
47+
48+
.. contributors:: v1.2.2..v1.2.3|HEAD

doc/source/whatsnew/v1.3.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ 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:`35643`)
56+
- :meth:`.Styler.set_tooltips` allows on hover tooltips to be added to styled HTML dataframes (:issue:`35643`, :issue:`21266`, :issue:`39317`)
5757
- :meth:`.Styler.set_tooltips_class` and :meth:`.Styler.set_table_styles` amended to optionally allow certain css-string input arguments (:issue:`39564`)
58+
- :meth:`.Styler.apply` now more consistently accepts ndarray function returns, i.e. in all cases for ``axis`` is ``0, 1 or None``. (:issue:`39359`)
5859
- :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`)
5960
- :meth:`pandas.read_stata` and :class:`StataReader` support reading data from compressed files.
6061

environment.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,4 @@ dependencies:
114114
- natsort # DataFrame.sort_values
115115
- pip:
116116
- git+https://github.com/pandas-dev/pydata-sphinx-theme.git@2488b7defbd3d753dd5fcfc890fc4a7e79d25103
117-
- git+https://github.com/numpy/numpydoc
117+
- numpydoc < 1.2 # 2021-02-09 1.2dev breaking CI

pandas/core/indexes/base.py

+7-12
Original file line numberDiff line numberDiff line change
@@ -3398,7 +3398,7 @@ def _get_fill_indexer(
33983398
else:
33993399
indexer = self._get_fill_indexer_searchsorted(target, method, limit)
34003400
if tolerance is not None and len(self):
3401-
indexer = self._filter_indexer_tolerance(target._values, indexer, tolerance)
3401+
indexer = self._filter_indexer_tolerance(target_values, indexer, tolerance)
34023402
return indexer
34033403

34043404
@final
@@ -3448,15 +3448,10 @@ def _get_nearest_indexer(self, target: Index, limit, tolerance) -> np.ndarray:
34483448
left_indexer = self.get_indexer(target, "pad", limit=limit)
34493449
right_indexer = self.get_indexer(target, "backfill", limit=limit)
34503450

3451-
target_values = target._values
3452-
# error: Unsupported left operand type for - ("ExtensionArray")
3453-
left_distances = np.abs(
3454-
self._values[left_indexer] - target_values # type: ignore[operator]
3455-
)
3456-
# error: Unsupported left operand type for - ("ExtensionArray")
3457-
right_distances = np.abs(
3458-
self._values[right_indexer] - target_values # type: ignore[operator]
3459-
)
3451+
target_values = target._get_engine_target()
3452+
own_values = self._get_engine_target()
3453+
left_distances = np.abs(own_values[left_indexer] - target_values)
3454+
right_distances = np.abs(own_values[right_indexer] - target_values)
34603455

34613456
op = operator.lt if self.is_monotonic_increasing else operator.le
34623457
indexer = np.where(
@@ -3475,8 +3470,8 @@ def _filter_indexer_tolerance(
34753470
indexer: np.ndarray,
34763471
tolerance,
34773472
) -> np.ndarray:
3478-
# error: Unsupported left operand type for - ("ExtensionArray")
3479-
distance = abs(self._values[indexer] - target) # type: ignore[operator]
3473+
own_values = self._get_engine_target()
3474+
distance = abs(own_values[indexer] - target)
34803475
return np.where(distance <= tolerance, indexer, -1)
34813476

34823477
# --------------------------------------------------------------------

pandas/core/indexes/datetimelike.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,7 @@ def take(self, indices, axis=0, allow_fill=True, fill_value=None, **kwargs):
231231

232232
def _convert_tolerance(self, tolerance, target):
233233
tolerance = np.asarray(to_timedelta(tolerance).to_numpy())
234-
235-
if target.size != tolerance.size and tolerance.size > 1:
236-
raise ValueError("list-like tolerance size must match target index size")
237-
return tolerance
234+
return super()._convert_tolerance(tolerance, target)
238235

239236
def tolist(self) -> List:
240237
"""

pandas/core/indexes/numeric.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,8 @@ def _shallow_copy(self, values, name: Hashable = lib.no_default):
119119
return super()._shallow_copy(values=values, name=name)
120120

121121
def _convert_tolerance(self, tolerance, target):
122-
tolerance = np.asarray(tolerance)
123-
if target.size != tolerance.size and tolerance.size > 1:
124-
raise ValueError("list-like tolerance size must match target index size")
122+
tolerance = super()._convert_tolerance(tolerance, target)
123+
125124
if not np.issubdtype(tolerance.dtype, np.number):
126125
if tolerance.ndim > 0:
127126
raise ValueError(

pandas/core/indexes/period.py

+11-29
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from pandas._libs.tslibs.parsing import DateParseError, parse_time_string
1212
from pandas._typing import Dtype, DtypeObj
1313
from pandas.errors import InvalidIndexError
14-
from pandas.util._decorators import cache_readonly, doc
14+
from pandas.util._decorators import doc
1515

1616
from pandas.core.dtypes.common import (
1717
is_bool_dtype,
@@ -324,10 +324,6 @@ def __contains__(self, key: Any) -> bool:
324324
except KeyError:
325325
return False
326326

327-
@cache_readonly
328-
def _int64index(self) -> Int64Index:
329-
return Int64Index._simple_new(self.asi8, name=self.name)
330-
331327
# ------------------------------------------------------------------------
332328
# Index Methods
333329

@@ -424,24 +420,18 @@ def inferred_type(self) -> str:
424420
# ------------------------------------------------------------------------
425421
# Indexing Methods
426422

427-
def _get_indexer(self, target: Index, method=None, limit=None, tolerance=None):
428-
429-
if not self._should_compare(target):
430-
return self._get_indexer_non_comparable(target, method, unique=True)
431-
432-
if isinstance(target, PeriodIndex):
433-
target = target._int64index # i.e. target.asi8
434-
self_index = self._int64index
435-
else:
436-
self_index = self
423+
def _convert_tolerance(self, tolerance, target):
424+
# Returned tolerance must be in dtype/units so that
425+
# `|self._get_engine_target() - target._engine_target()| <= tolerance`
426+
# is meaningful. Since PeriodIndex returns int64 for engine_target,
427+
# we may need to convert timedelta64 tolerance to int64.
428+
tolerance = super()._convert_tolerance(tolerance, target)
437429

438-
if tolerance is not None:
439-
tolerance = self._convert_tolerance(tolerance, target)
440-
if self_index is not self:
441-
# convert tolerance to i8
442-
tolerance = self._maybe_convert_timedelta(tolerance)
430+
if self.dtype == target.dtype:
431+
# convert tolerance to i8
432+
tolerance = self._maybe_convert_timedelta(tolerance)
443433

444-
return Index._get_indexer(self_index, target, method, limit, tolerance)
434+
return tolerance
445435

446436
def get_loc(self, key, method=None, tolerance=None):
447437
"""
@@ -579,14 +569,6 @@ def _get_string_slice(self, key: str):
579569
except KeyError as err:
580570
raise KeyError(key) from err
581571

582-
# ------------------------------------------------------------------------
583-
584-
def memory_usage(self, deep: bool = False) -> int:
585-
result = super().memory_usage(deep=deep)
586-
if hasattr(self, "_cache") and "_int64index" in self._cache:
587-
result += self._int64index.memory_usage(deep=deep)
588-
return result
589-
590572

591573
def period_range(
592574
start=None, end=None, periods: Optional[int] = None, freq=None, name=None

pandas/core/indexes/range.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from datetime import timedelta
44
import operator
55
from sys import getsizeof
6-
from typing import Any, Hashable, List, Optional, Tuple
6+
from typing import TYPE_CHECKING, Any, Hashable, List, Optional, Tuple
77
import warnings
88

99
import numpy as np
@@ -20,7 +20,6 @@
2020
ensure_python_int,
2121
is_float,
2222
is_integer,
23-
is_list_like,
2423
is_scalar,
2524
is_signed_integer_dtype,
2625
is_timedelta64_dtype,
@@ -35,6 +34,9 @@
3534
from pandas.core.indexes.numeric import Float64Index, Int64Index
3635
from pandas.core.ops.common import unpack_zerodim_and_defer
3736

37+
if TYPE_CHECKING:
38+
from pandas import Index
39+
3840
_empty_range = range(0)
3941

4042

@@ -368,8 +370,8 @@ def get_loc(self, key, method=None, tolerance=None):
368370
raise KeyError(key)
369371
return super().get_loc(key, method=method, tolerance=tolerance)
370372

371-
def _get_indexer(self, target, method=None, limit=None, tolerance=None):
372-
if com.any_not_none(method, tolerance, limit) or not is_list_like(target):
373+
def _get_indexer(self, target: Index, method=None, limit=None, tolerance=None):
374+
if com.any_not_none(method, tolerance, limit):
373375
return super()._get_indexer(
374376
target, method=method, tolerance=tolerance, limit=limit
375377
)
@@ -381,11 +383,11 @@ def _get_indexer(self, target, method=None, limit=None, tolerance=None):
381383
reverse = self._range[::-1]
382384
start, stop, step = reverse.start, reverse.stop, reverse.step
383385

384-
target_array = np.asarray(target)
385-
if not (is_signed_integer_dtype(target_array) and target_array.ndim == 1):
386+
if not is_signed_integer_dtype(target):
386387
# checks/conversions/roundings are delegated to general method
387388
return super()._get_indexer(target, method=method, tolerance=tolerance)
388389

390+
target_array = np.asarray(target)
389391
locs = target_array - start
390392
valid = (locs % step == 0) & (locs >= 0) & (target_array < stop)
391393
locs[~valid] = -1

0 commit comments

Comments
 (0)