Skip to content

ENH: GH11128 add weekday_name to DatetimeIndex and .dt #12803

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ These can be accessed like ``Series.dt.<property>``.
Series.dt.days_in_month
Series.dt.tz
Series.dt.freq
Series.dt.weekday_name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you move after .weekday


**Datetime Methods**

Expand Down Expand Up @@ -1487,6 +1488,7 @@ Time/Date Components
DatetimeIndex.is_year_start
DatetimeIndex.is_year_end
DatetimeIndex.inferred_freq
DatetimeIndex.weekday_name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here


Selecting
~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions doc/source/timeseries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ There are several time/date properties that one can access from ``Timestamp`` or
is_quarter_end,"Logical indicating if last day of quarter (defined by frequency)"
is_year_start,"Logical indicating if first day of year (defined by frequency)"
is_year_end,"Logical indicating if last day of year (defined by frequency)"
weekday_name,"The name of day in a week (ex: Friday)"

Furthermore, if you have a ``Series`` with datetimelike values, then you can access these properties via the ``.dt`` accessor, see the :ref:`docs <basics.dt_accessors>`

Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.18.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Other Enhancements
- ``pd.read_csv()`` now supports opening files using xz compression, via extension inference or explicit ``compression='xz'`` is specified; ``xz`` compressions is also supported by ``DataFrame.to_csv`` in the same way (:issue:`11852`)
- ``pd.read_msgpack()`` now always gives writeable ndarrays even when compression is used (:issue:`12359`).
- ``Index.take`` now handles ``allow_fill`` and ``fill_value`` consistently (:issue:`12631`)
- Added ``weekday_name`` as a component to ``DatetimeIndex`` and ``.dt`` accessor. (:issue:`11128`)

.. ipython:: python

Expand Down
3 changes: 2 additions & 1 deletion pandas/tests/series/test_datetime_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def test_dt_namespace_accessor(self):
ok_for_dt = ok_for_base + ['date', 'time', 'microsecond', 'nanosecond',
'is_month_start', 'is_month_end',
'is_quarter_start', 'is_quarter_end',
'is_year_start', 'is_year_end', 'tz']
'is_year_start', 'is_year_end', 'tz',
'weekday_name']
ok_for_dt_methods = ['to_period', 'to_pydatetime', 'tz_localize',
'tz_convert', 'normalize', 'strftime', 'round',
'floor', 'ceil']
Expand Down
11 changes: 10 additions & 1 deletion pandas/tseries/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ def f(self):

result = tslib.get_start_end_field(
values, field, self.freqstr, month_kw)
elif field in ['weekday_name']:
result = tslib.get_date_name_field(values, field)
return self._maybe_mask_results(result)
else:
result = tslib.get_date_field(values, field)

Expand Down Expand Up @@ -208,7 +211,7 @@ def _join_i8_wrapper(joinf, **kwargs):
'daysinmonth', 'date', 'time', 'microsecond',
'nanosecond', 'is_month_start', 'is_month_end',
'is_quarter_start', 'is_quarter_end', 'is_year_start',
'is_year_end', 'tz', 'freq']
'is_year_end', 'tz', 'freq', 'weekday_name']
_is_numeric_dtype = False
_infer_as_myclass = True

Expand Down Expand Up @@ -1564,6 +1567,12 @@ def _set_freq(self, value):
'dow',
"The day of the week with Monday=0, Sunday=6")
weekday = dayofweek

weekday_name = _field_accessor(
'weekday_name',
'weekday_name',
"The name of day in a week (ex: Friday)\n\n.. versionadded:: 0.18.1")

dayofyear = _field_accessor(
'dayofyear',
'doy',
Expand Down
2 changes: 1 addition & 1 deletion pandas/tseries/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_ops_properties(self):
'is_month_start', 'is_month_end',
'is_quarter_start',
'is_quarter_end', 'is_year_start',
'is_year_end'],
'is_year_end', 'weekday_name'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are addl tests in tests/series/test_datetime_values.py (just add the new propery name)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and they were added to in my original PR.

lambda x: isinstance(x, DatetimeIndex))

def test_ops_properties_basic(self):
Expand Down
14 changes: 12 additions & 2 deletions pandas/tseries/tests/test_timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,7 @@ def test_nat_vector_field_access(self):
def test_nat_scalar_field_access(self):
fields = ['year', 'quarter', 'month', 'day', 'hour', 'minute',
'second', 'microsecond', 'nanosecond', 'week', 'dayofyear',
'days_in_month', 'daysinmonth', 'dayofweek']
'days_in_month', 'daysinmonth', 'dayofweek', 'weekday_name']
for field in fields:
result = getattr(NaT, field)
self.assertTrue(np.isnan(result))
Expand Down Expand Up @@ -1852,7 +1852,7 @@ def test_timestamp_fields(self):
fields = ['dayofweek', 'dayofyear', 'week', 'weekofyear', 'quarter',
'days_in_month', 'is_month_start', 'is_month_end',
'is_quarter_start', 'is_quarter_end', 'is_year_start',
'is_year_end']
'is_year_end', 'weekday_name']
for f in fields:
expected = getattr(idx, f)[-1]
result = getattr(Timestamp(idx[-1]), f)
Expand Down Expand Up @@ -3541,6 +3541,15 @@ def test_datetimeindex_accessors(self):
self.assertEqual(dti.is_year_end[0], False)
self.assertEqual(dti.is_year_end[364], True)

# GH 11128
self.assertEqual(dti.weekday_name[4], u'Monday')
self.assertEqual(dti.weekday_name[5], u'Tuesday')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a test for Timestamp (see where tests for .weekday are, prob in tseries/test_timeseries.py

self.assertEqual(dti.weekday_name[6], u'Wednesday')
self.assertEqual(dti.weekday_name[7], u'Thursday')
self.assertEqual(dti.weekday_name[8], u'Friday')
self.assertEqual(dti.weekday_name[9], u'Saturday')
self.assertEqual(dti.weekday_name[10], u'Sunday')

self.assertEqual(len(dti.year), 365)
self.assertEqual(len(dti.month), 365)
self.assertEqual(len(dti.day), 365)
Expand All @@ -3558,6 +3567,7 @@ def test_datetimeindex_accessors(self):
self.assertEqual(len(dti.is_quarter_end), 365)
self.assertEqual(len(dti.is_year_start), 365)
self.assertEqual(len(dti.is_year_end), 365)
self.assertEqual(len(dti.weekday_name), 365)

dti = DatetimeIndex(freq='BQ-FEB', start=datetime(1998, 1, 1),
periods=4)
Expand Down
39 changes: 38 additions & 1 deletion pandas/tslib.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,11 @@ class Timestamp(_Timestamp):
def dayofweek(self):
return self.weekday()

@property
def weekday_name(self):
out = get_date_name_field(np.array([self.value], dtype=np.int64), 'weekday_name')
return out[0]

@property
def dayofyear(self):
return self._get_field('doy')
Expand Down Expand Up @@ -667,7 +672,7 @@ class NaTType(_NaT):

fields = ['year', 'quarter', 'month', 'day', 'hour',
'minute', 'second', 'millisecond', 'microsecond', 'nanosecond',
'week', 'dayofyear', 'days_in_month', 'daysinmonth', 'dayofweek']
'week', 'dayofyear', 'days_in_month', 'daysinmonth', 'dayofweek', 'weekday_name']
for field in fields:
prop = property(fget=lambda self: np.nan)
setattr(NaTType, field, prop)
Expand Down Expand Up @@ -4390,6 +4395,38 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field, object freqstr=N

raise ValueError("Field %s not supported" % field)

@cython.wraparound(False)
@cython.boundscheck(False)
def get_date_name_field(ndarray[int64_t] dtindex, object field):
'''
Given a int64-based datetime index, return array of strings of date
name based on requested field (e.g. weekday_name)
'''
cdef:
_TSObject ts
Py_ssize_t i, count = 0
ndarray[object] out
pandas_datetimestruct dts
int dow

_dayname = np.array(
['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
dtype=np.str )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dtype=np.object_ is more consistent with what we use.


count = len(dtindex)
out = np.empty(count, dtype=object)

if field == 'weekday_name':
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = np.nan; continue

pandas_datetime_to_datetimestruct(dtindex[i], PANDAS_FR_ns, &dts)
dow = dayofweek(dts.year, dts.month, dts.day)
out[i] = _dayname[dow]
return out

raise ValueError("Field %s not supported" % field)


cdef inline int m8_weekday(int64_t val):
ts = convert_to_tsobject(val, None, None)
Expand Down