Skip to content

Commit 41728a9

Browse files
FIX: boolean fields should still return array
1 parent 6317b6b commit 41728a9

File tree

4 files changed

+45
-15
lines changed

4 files changed

+45
-15
lines changed

doc/source/whatsnew/v0.20.0.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,8 @@ Accessing datetime fields of Index now return Index
478478
The several datetime-related attributes (see :ref:`here <timeseries.components>`
479479
for an overview) of DatetimeIndex, PeriodIndex and TimedeltaIndex previously
480480
returned numpy arrays, now they will return a new Index object (:issue:`15022`).
481+
Only in case of a boolean field, still a boolean array is returned to support
482+
boolean indexing.
481483

482484
Previous behaviour:
483485

@@ -498,7 +500,7 @@ New Behavior:
498500
This has the advantage that specific Index methods are still available on the
499501
result. On the other hand, this might have backward incompatibilities: e.g.
500502
compared to numpy arrays, Index objects are not mutable (values cannot be set
501-
by indexing). To get the original result, you can convert to a nunpy array
503+
by indexing). To get the original result, you can convert to a numpy array
502504
explicitly using ``np.asarray(idx.hour)``.
503505

504506
.. _whatsnew_0200.api_breaking.s3:

pandas/tests/indexes/datetimes/test_misc.py

+24-9
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def test_normalize(self):
172172
class TestDatetime64(tm.TestCase):
173173

174174
def test_datetimeindex_accessors(self):
175-
<<<<<<< f2831e2a2074e27e5cd3cfc0728d989742ee4680
175+
176176
dti_naive = DatetimeIndex(freq='D', start=datetime(1998, 1, 1),
177177
periods=365)
178178
# GH 13303
@@ -258,16 +258,31 @@ def test_datetimeindex_accessors(self):
258258

259259
dti.name = 'name'
260260

261-
for accessor in ['year', 'month', 'day', 'hour', 'minute', 'second',
262-
'microsecond', 'nanosecond', 'dayofweek', 'dayofyear',
263-
'weekofyear', 'quarter',
264-
'is_month_start', 'is_month_end',
261+
# non boolean accessors -> return Index
262+
for accessor in ['year', 'month', 'day', 'hour', 'minute',
263+
'second', 'microsecond', 'nanosecond',
264+
'dayofweek', 'dayofyear', 'weekofyear',
265+
'quarter', 'weekday_name']:
266+
res = getattr(dti, accessor)
267+
assert len(res) == 365
268+
assert isinstance(res, Index)
269+
assert res.name == 'name'
270+
271+
# boolean accessors -> return array
272+
for accessor in ['is_month_start', 'is_month_end',
265273
'is_quarter_start', 'is_quarter_end',
266-
'is_year_start', 'is_year_end', 'weekday_name']:
274+
'is_year_start', 'is_year_end']:
267275
res = getattr(dti, accessor)
268-
self.assertEqual(len(res), 365)
269-
self.assertIsInstance(res, Index)
270-
self.assertEqual(res.name, 'name')
276+
assert len(res) == 365
277+
assert isinstance(res, np.ndarray)
278+
279+
# test boolean indexing
280+
res = dti[dti.is_quarter_start]
281+
exp = dti[[0, 90, 181, 273]]
282+
tm.assert_index_equal(res, exp)
283+
res = dti[dti.is_leap_year]
284+
exp = DatetimeIndex([], freq='D', tz=dti.tz, name='name')
285+
tm.assert_index_equal(res, exp)
271286

272287
dti = DatetimeIndex(freq='BQ-FEB', start=datetime(1998, 1, 1),
273288
periods=4)

pandas/tests/scalar/test_timestamp.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -597,15 +597,26 @@ def test_nat_fields(self):
597597
def test_nat_vector_field_access(self):
598598
idx = DatetimeIndex(['1/1/2000', None, None, '1/4/2000'])
599599

600+
# non boolean fields
600601
fields = ['year', 'quarter', 'month', 'day', 'hour', 'minute',
601602
'second', 'microsecond', 'nanosecond', 'week', 'dayofyear',
602-
'days_in_month', 'is_leap_year']
603+
'days_in_month']
603604

604605
for field in fields:
605606
result = getattr(idx, field)
606607
expected = [getattr(x, field) for x in idx]
607608
self.assert_index_equal(result, pd.Index(expected))
608609

610+
# boolean fields
611+
fields = ['is_leap_year']
612+
# other boolean fields like 'is_month_start' and 'is_month_end'
613+
# not yet supported by NaT
614+
615+
for field in fields:
616+
result = getattr(idx, field)
617+
expected = [getattr(x, field) for x in idx]
618+
self.assert_numpy_array_equal(result, np.array(expected))
619+
609620
s = pd.Series(idx)
610621

611622
for field in fields:

pandas/tseries/index.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def f(self):
6464
if self.tz is not utc:
6565
values = self._local_timestamps()
6666

67+
# boolean accessors -> return array
6768
if field in ['is_month_start', 'is_month_end',
6869
'is_quarter_start', 'is_quarter_end',
6970
'is_year_start', 'is_year_end']:
@@ -73,14 +74,15 @@ def f(self):
7374

7475
result = libts.get_start_end_field(values, field, self.freqstr,
7576
month_kw)
76-
result = self._maybe_mask_results(result, convert='float64')
77+
return self._maybe_mask_results(result, convert='float64')
78+
elif field in ['is_leap_year']:
79+
# no need to mask NaT
80+
return libts.get_date_field(values, field)
7781

82+
# non-boolean accessors -> return Index
7883
elif field in ['weekday_name']:
7984
result = libts.get_date_name_field(values, field)
8085
result = self._maybe_mask_results(result)
81-
elif field in ['is_leap_year']:
82-
# no need to mask NaT
83-
result = libts.get_date_field(values, field)
8486
else:
8587
result = libts.get_date_field(values, field)
8688
result = self._maybe_mask_results(result, convert='float64')

0 commit comments

Comments
 (0)