Skip to content

Commit 11db555

Browse files
committed
Merge branch 'master' of https://github.com/pandas-dev/pandas into dlike8
2 parents b97ec96 + a0ca4d7 commit 11db555

File tree

15 files changed

+160
-376
lines changed

15 files changed

+160
-376
lines changed

doc/source/whatsnew/v0.24.0.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,7 @@ Other API Changes
688688
- :meth:`DataFrame.corr` and :meth:`Series.corr` now raise a ``ValueError`` along with a helpful error message instead of a ``KeyError`` when supplied with an invalid method (:issue:`22298`)
689689
- :meth:`shift` will now always return a copy, instead of the previous behaviour of returning self when shifting by 0 (:issue:`22397`)
690690
- Slicing a single row of a DataFrame with multiple ExtensionArrays of the same type now preserves the dtype, rather than coercing to object (:issue:`22784`)
691+
- :class:`DateOffset` attribute `_cacheable` and method `_should_cache` have been removed (:issue:`23118`)
691692

692693
.. _whatsnew_0240.deprecations:
693694

@@ -844,7 +845,7 @@ Numeric
844845
- Bug in :class:`DataFrame` multiplication between boolean dtype and integer returning ``object`` dtype instead of integer dtype (:issue:`22047`, :issue:`22163`)
845846
- Bug in :meth:`DataFrame.apply` where, when supplied with a string argument and additional positional or keyword arguments (e.g. ``df.apply('sum', min_count=1)``), a ``TypeError`` was wrongly raised (:issue:`22376`)
846847
- Bug in :meth:`DataFrame.astype` to extension dtype may raise ``AttributeError`` (:issue:`22578`)
847-
848+
- Bug in :class:`DataFrame` with ``timedelta64[ns]`` dtype arithmetic operations with ``ndarray`` with integer dtype incorrectly treating the narray as ``timedelta64[ns]`` dtype (:issue:`23114`)
848849

849850
Strings
850851
^^^^^^^

pandas/_libs/tslibs/offsets.pyx

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -282,11 +282,6 @@ class ApplyTypeError(TypeError):
282282
pass
283283

284284

285-
# TODO: unused. remove?
286-
class CacheableOffset(object):
287-
_cacheable = True
288-
289-
290285
# ---------------------------------------------------------------------
291286
# Base Classes
292287

@@ -296,8 +291,6 @@ class _BaseOffset(object):
296291
and will (after pickle errors are resolved) go into a cdef class.
297292
"""
298293
_typ = "dateoffset"
299-
_normalize_cache = True
300-
_cacheable = False
301294
_day_opt = None
302295
_attributes = frozenset(['n', 'normalize'])
303296

@@ -386,10 +379,6 @@ class _BaseOffset(object):
386379
# that allows us to use methods that can go in a `cdef class`
387380
return self * 1
388381

389-
# TODO: this is never true. fix it or get rid of it
390-
def _should_cache(self):
391-
return self.isAnchored() and self._cacheable
392-
393382
def __repr__(self):
394383
className = getattr(self, '_outputName', type(self).__name__)
395384

pandas/core/arrays/datetimes.py

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
resolution as libresolution)
1414

1515
from pandas.util._decorators import cache_readonly
16-
from pandas.errors import PerformanceWarning, AbstractMethodError
16+
from pandas.errors import PerformanceWarning
1717
from pandas import compat
1818

1919
from pandas.core.dtypes.common import (
@@ -268,27 +268,22 @@ def _generate_range(cls, start, end, periods, freq, tz=None,
268268
end, end.tz, start.tz, freq, tz
269269
)
270270
if freq is not None:
271-
if cls._use_cached_range(freq, _normalized, start, end):
272-
# Currently always False; never hit
273-
# Should be reimplemented as a part of GH#17914
274-
index = cls._cached_range(start, end, periods=periods,
275-
freq=freq)
276-
else:
277-
index = _generate_regular_range(cls, start, end, periods, freq)
278-
279-
if tz is not None and getattr(index, 'tz', None) is None:
280-
arr = conversion.tz_localize_to_utc(
281-
ensure_int64(index.values),
282-
tz, ambiguous=ambiguous)
283-
284-
index = cls(arr)
285-
286-
# index is localized datetime64 array -> have to convert
287-
# start/end as well to compare
288-
if start is not None:
289-
start = start.tz_localize(tz).asm8
290-
if end is not None:
291-
end = end.tz_localize(tz).asm8
271+
# TODO: consider re-implementing _cached_range; GH#17914
272+
index = _generate_regular_range(cls, start, end, periods, freq)
273+
274+
if tz is not None and getattr(index, 'tz', None) is None:
275+
arr = conversion.tz_localize_to_utc(
276+
ensure_int64(index.values),
277+
tz, ambiguous=ambiguous)
278+
279+
index = cls(arr)
280+
281+
# index is localized datetime64 array -> have to convert
282+
# start/end as well to compare
283+
if start is not None:
284+
start = start.tz_localize(tz).asm8
285+
if end is not None:
286+
end = end.tz_localize(tz).asm8
292287
else:
293288
# Create a linearly spaced date_range in local time
294289
arr = np.linspace(start.value, end.value, periods)
@@ -303,16 +298,6 @@ def _generate_range(cls, start, end, periods, freq, tz=None,
303298

304299
return cls._simple_new(index.values, freq=freq, tz=tz)
305300

306-
@classmethod
307-
def _use_cached_range(cls, freq, _normalized, start, end):
308-
# DatetimeArray is mutable, so is not cached
309-
return False
310-
311-
@classmethod
312-
def _cached_range(cls, start=None, end=None,
313-
periods=None, freq=None, **kwargs):
314-
raise AbstractMethodError(cls)
315-
316301
# -----------------------------------------------------------------
317302
# Descriptive Properties
318303

pandas/core/generic.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,10 @@ def __round__(self, decimals=0):
17951795
# ----------------------------------------------------------------------
17961796
# Array Interface
17971797

1798+
# This is also set in IndexOpsMixin
1799+
# GH#23114 Ensure ndarray.__op__(DataFrame) returns NotImplemented
1800+
__array_priority__ = 1000
1801+
17981802
def __array__(self, dtype=None):
17991803
return com.values_from_object(self)
18001804

pandas/core/indexes/datetimes.py

Lines changed: 11 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
DatelikeOps, TimelikeOps, DatetimeIndexOpsMixin,
4141
wrap_field_accessor, wrap_array_method)
4242
from pandas.tseries.offsets import (
43-
generate_range, CDay, prefix_mapping)
43+
CDay, prefix_mapping)
4444

4545
from pandas.core.tools.timedeltas import to_timedelta
4646
from pandas.util._decorators import Appender, cache_readonly, Substitution
@@ -317,13 +317,6 @@ def __new__(cls, data=None,
317317

318318
return subarr._deepcopy_if_needed(ref_to_data, copy)
319319

320-
@classmethod
321-
def _use_cached_range(cls, freq, _normalized, start, end):
322-
# Note: This always returns False
323-
return (freq._should_cache() and
324-
not (freq._normalize_cache and not _normalized) and
325-
_naive_in_cache_range(start, end))
326-
327320
def _convert_for_op(self, value):
328321
""" Convert value to be insertable to ndarray """
329322
if self._has_same_tz(value):
@@ -380,71 +373,6 @@ def tz(self, value):
380373
raise AttributeError("Cannot directly set timezone. Use tz_localize() "
381374
"or tz_convert() as appropriate")
382375

383-
@classmethod
384-
def _cached_range(cls, start=None, end=None, periods=None, freq=None,
385-
name=None):
386-
if start is None and end is None:
387-
# I somewhat believe this should never be raised externally
388-
raise TypeError('Must specify either start or end.')
389-
if start is not None:
390-
start = Timestamp(start)
391-
if end is not None:
392-
end = Timestamp(end)
393-
if (start is None or end is None) and periods is None:
394-
raise TypeError(
395-
'Must either specify period or provide both start and end.')
396-
397-
if freq is None:
398-
# This can't happen with external-facing code
399-
raise TypeError('Must provide freq.')
400-
401-
drc = _daterange_cache
402-
if freq not in _daterange_cache:
403-
xdr = generate_range(offset=freq, start=_CACHE_START,
404-
end=_CACHE_END)
405-
406-
arr = tools.to_datetime(list(xdr), box=False)
407-
408-
cachedRange = DatetimeIndex._simple_new(arr)
409-
cachedRange.freq = freq
410-
cachedRange = cachedRange.tz_localize(None)
411-
cachedRange.name = None
412-
drc[freq] = cachedRange
413-
else:
414-
cachedRange = drc[freq]
415-
416-
if start is None:
417-
if not isinstance(end, Timestamp):
418-
raise AssertionError('end must be an instance of Timestamp')
419-
420-
end = freq.rollback(end)
421-
422-
endLoc = cachedRange.get_loc(end) + 1
423-
startLoc = endLoc - periods
424-
elif end is None:
425-
if not isinstance(start, Timestamp):
426-
raise AssertionError('start must be an instance of Timestamp')
427-
428-
start = freq.rollforward(start)
429-
430-
startLoc = cachedRange.get_loc(start)
431-
endLoc = startLoc + periods
432-
else:
433-
if not freq.onOffset(start):
434-
start = freq.rollforward(start)
435-
436-
if not freq.onOffset(end):
437-
end = freq.rollback(end)
438-
439-
startLoc = cachedRange.get_loc(start)
440-
endLoc = cachedRange.get_loc(end) + 1
441-
442-
indexSlice = cachedRange[startLoc:endLoc]
443-
indexSlice.name = name
444-
indexSlice.freq = freq
445-
446-
return indexSlice
447-
448376
def _mpl_repr(self):
449377
# how to represent ourselves to matplotlib
450378
return libts.ints_to_pydatetime(self.asi8, self.tz)
@@ -802,22 +730,19 @@ def _fast_union(self, other):
802730
else:
803731
left, right = other, self
804732

805-
left_start, left_end = left[0], left[-1]
733+
left_end = left[-1]
806734
right_end = right[-1]
807735

808-
if not self.freq._should_cache():
809-
# concatenate dates
810-
if left_end < right_end:
811-
loc = right.searchsorted(left_end, side='right')
812-
right_chunk = right.values[loc:]
813-
dates = _concat._concat_compat((left.values, right_chunk))
814-
return self._shallow_copy(dates)
815-
else:
816-
return left
736+
# TODO: consider re-implementing freq._should_cache for fastpath
737+
738+
# concatenate dates
739+
if left_end < right_end:
740+
loc = right.searchsorted(left_end, side='right')
741+
right_chunk = right.values[loc:]
742+
dates = _concat._concat_compat((left.values, right_chunk))
743+
return self._shallow_copy(dates)
817744
else:
818-
return type(self)(start=left_start,
819-
end=max(left_end, right_end),
820-
freq=left.freq)
745+
return left
821746

822747
def _wrap_union_result(self, other, result):
823748
name = self.name if self.name == other.name else None
@@ -1694,21 +1619,6 @@ def cdate_range(start=None, end=None, periods=None, freq='C', tz=None,
16941619
closed=closed, **kwargs)
16951620

16961621

1697-
_CACHE_START = Timestamp(datetime(1950, 1, 1))
1698-
_CACHE_END = Timestamp(datetime(2030, 1, 1))
1699-
1700-
_daterange_cache = {}
1701-
1702-
1703-
def _naive_in_cache_range(start, end):
1704-
if start is None or end is None:
1705-
return False
1706-
else:
1707-
if start.tzinfo is not None or end.tzinfo is not None:
1708-
return False
1709-
return start > _CACHE_START and end < _CACHE_END
1710-
1711-
17121622
def _time_to_micros(time):
17131623
seconds = time.hour * 60 * 60 + 60 * time.minute + time.second
17141624
return 1000000 * seconds + time.microsecond

pandas/io/formats/style.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ class Styler(object):
6464
a unique identifier to avoid CSS collisions; generated automatically
6565
caption: str, default None
6666
caption to attach to the table
67+
cell_ids: bool, default True
68+
If True, each cell will have an ``id`` attribute in their HTML tag.
69+
The ``id`` takes the form ``T_<uuid>_row<num_row>_col<num_col>``
70+
where ``<uuid>`` is the unique identifier, ``<num_row>`` is the row
71+
number and ``<num_col>`` is the column number.
6772
6873
Attributes
6974
----------
@@ -112,7 +117,7 @@ class Styler(object):
112117
template = env.get_template("html.tpl")
113118

114119
def __init__(self, data, precision=None, table_styles=None, uuid=None,
115-
caption=None, table_attributes=None):
120+
caption=None, table_attributes=None, cell_ids=True):
116121
self.ctx = defaultdict(list)
117122
self._todo = []
118123

@@ -136,6 +141,7 @@ def __init__(self, data, precision=None, table_styles=None, uuid=None,
136141
self.table_attributes = table_attributes
137142
self.hidden_index = False
138143
self.hidden_columns = []
144+
self.cell_ids = cell_ids
139145

140146
# display_funcs maps (row, col) -> formatting function
141147

@@ -306,14 +312,16 @@ def format_attr(pair):
306312
cs.extend(cell_context.get("data", {}).get(r, {}).get(c, []))
307313
formatter = self._display_funcs[(r, c)]
308314
value = self.data.iloc[r, c]
309-
row_es.append({
310-
"type": "td",
311-
"value": value,
312-
"class": " ".join(cs),
313-
"id": "_".join(cs[1:]),
314-
"display_value": formatter(value),
315-
"is_visible": (c not in hidden_columns)
316-
})
315+
row_dict = {"type": "td",
316+
"value": value,
317+
"class": " ".join(cs),
318+
"display_value": formatter(value),
319+
"is_visible": (c not in hidden_columns)}
320+
# only add an id if the cell has a style
321+
if (self.cell_ids or
322+
not(len(ctx[r, c]) == 1 and ctx[r, c][0] == '')):
323+
row_dict["id"] = "_".join(cs[1:])
324+
row_es.append(row_dict)
317325
props = []
318326
for x in ctx[r, c]:
319327
# have to handle empty styles like ['']

pandas/io/formats/templates/html.tpl

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,17 @@
5050
{%- endblock thead %}
5151
{%- block tbody %}
5252
<tbody>
53-
{%- block before_rows %}{%- endblock before_rows %}
54-
{%- for r in body %}
55-
{%- block tr scoped %}
56-
<tr>
57-
{%- for c in r %}
58-
{%- if c.is_visible != False %}
59-
<{{ c.type }} id="T_{{ uuid }}{{ c.id }}" class="{{ c.class }}" {{ c.attributes|join(" ") }}>{{ c.display_value }}</{{ c.type }}>
60-
{%- endif %}
61-
{%- endfor %}
62-
</tr>
63-
{%- endblock tr %}
53+
{% block before_rows %}{% endblock before_rows %}
54+
{% for r in body %}
55+
{% block tr scoped %}
56+
<tr>
57+
{% for c in r %}
58+
{% if c.is_visible != False %}
59+
<{{ c.type }} {% if c.id is defined -%} id="T_{{ uuid }}{{ c.id }}" {%- endif %} class="{{ c.class }}" {{ c.attributes|join(" ") }}>{{ c.display_value }}</{{ c.type }}>
60+
{% endif %}
61+
{%- endfor %}
62+
</tr>
63+
{% endblock tr %}
6464
{%- endfor %}
6565
{%- block after_rows %}{%- endblock after_rows %}
6666
</tbody>

pandas/tests/arithmetic/test_numeric.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def test_numeric_arr_rdiv_tdscalar(self, three_days, numeric_idx, box):
156156
if box is not pd.Index and broken:
157157
# np.timedelta64(3, 'D') / 2 == np.timedelta64(1, 'D')
158158
raise pytest.xfail("timedelta64 not converted to nanos; "
159-
"Tick division not imlpemented")
159+
"Tick division not implemented")
160160

161161
expected = TimedeltaIndex(['3 Days', '36 Hours'])
162162

pandas/tests/arithmetic/test_object.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,6 @@ def test_objarr_radd_str_invalid(self, dtype, data, box):
140140
operator.sub, ops.rsub])
141141
def test_objarr_add_invalid(self, op, box):
142142
# invalid ops
143-
if box is pd.DataFrame and op is ops.radd:
144-
pytest.xfail(reason="DataFrame op incorrectly casts the np.array"
145-
"case to M8[ns]")
146143

147144
obj_ser = tm.makeObjectSeries()
148145
obj_ser.name = 'objects'

0 commit comments

Comments
 (0)