Skip to content

Commit a480df9

Browse files
committed
Switch the name of datetime components from 'time.month' to 'month'
This lets you write things like: counts = time.groupby('time.month').count() counts.sel(month=2) vs. counts.sel(**{'time.month': 2}) which is much more awkward CC jhamman
1 parent 40476f6 commit a480df9

File tree

5 files changed

+54
-25
lines changed

5 files changed

+54
-25
lines changed

doc/data-structures.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,14 @@ __ http://pandas.pydata.org/pandas-docs/stable/api.html#time-date-components
179179
foo['time.month']
180180
foo['time.dayofyear']
181181
182+
xray adds ``'season'`` to the list of datetime components supported by pandas:
183+
184+
.. ipython:: python
185+
186+
foo['time.season']
187+
188+
The set of valid seasons consists of 'DJF', 'MAM', 'JJA' and 'SON', labeled by
189+
the first letters of the corresponding months.
182190

183191
Dataset
184192
-------

doc/whats-new.rst

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ Breaking changes
3535
rhs = xray.DataArray([2, 3, 4], [('x', [1, 2, 3])])
3636
lhs + rhs
3737
38-
For :ref:`data construction and merging<merge>`, we align based on the
38+
:ref:`For dataset construction and merging<merge>`, we align based on the
3939
**union** of labels:
4040

4141
.. ipython:: python
4242
4343
xray.Dataset({'foo': lhs, 'bar': rhs})
4444
45-
For :ref:`update and __setitem__<update>`, we align based on the **original**
45+
:ref:`For update and __setitem__<update>`, we align based on the **original**
4646
object:
4747

4848
.. ipython:: python
@@ -76,15 +76,19 @@ Breaking changes
7676
7777
This functionality can be controlled through the ``compat`` option, which
7878
has also been added to the :py:class:`~xray.Dataset` constructor.
79-
- We have updated our use of the terms of "coordinates" and "variables". What
80-
were known in previous versions of xray as "coordinates" and "variables" are
81-
now referred to throughout the documentation as "coordinate variables" and
82-
"data variables". This brings xray in closer alignment to `CF Conventions`_.
83-
The only visible change besides the documentation is that ``Dataset.vars``
84-
has been renamed ``Dataset.data_vars``.
85-
- You will need to update your code if you have been ignoring deprecation
86-
warnings: methods and attributes that were deprecated in xray v0.3 or earlier
87-
(e.g., ``dimensions``, ``attributes```) have gone away.
79+
- Datetime shortcuts such as ``'time.month'`` now return a ``DataArray`` with
80+
the name ``'month'``, not ``'time.month'`` (:issue:`345`). This makes it
81+
easier to index the resulting arrays when they are used with ``groupby``:
82+
83+
.. ipython:: python
84+
85+
time = xray.DataArray(pd.date_range('2000-01-01', periods=365),
86+
dims='time', name='time')
87+
counts = time.groupby('time.month').count()
88+
counts.sel(month=2)
89+
90+
Previously, you would need to use something like
91+
``counts.sel(**{'time.month': 2}})``, which is much more awkward.
8892
- The ``season`` datetime shortcut now returns an array of string labels
8993
such `'DJF'`:
9094

@@ -94,6 +98,15 @@ Breaking changes
9498
ds['t.season']
9599
96100
Previously, it returned numbered seasons 1 through 4.
101+
- We have updated our use of the terms of "coordinates" and "variables". What
102+
were known in previous versions of xray as "coordinates" and "variables" are
103+
now referred to throughout the documentation as "coordinate variables" and
104+
"data variables". This brings xray in closer alignment to `CF Conventions`_.
105+
The only visible change besides the documentation is that ``Dataset.vars``
106+
has been renamed ``Dataset.data_vars``.
107+
- You will need to update your code if you have been ignoring deprecation
108+
warnings: methods and attributes that were deprecated in xray v0.3 or earlier
109+
(e.g., ``dimensions``, ``attributes```) have gone away.
97110

98111
.. _bottleneck: https://github.com/kwgoodman/bottleneck
99112

xray/core/dataarray.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ def _new_from_dataset(cls, dataset, name):
195195
"""
196196
obj = object.__new__(cls)
197197
obj._dataset = dataset._copy_listed([name], keep_attrs=False)
198+
if name not in obj._dataset:
199+
# handle virtual variables
200+
try:
201+
_, name = name.split('.', 1)
202+
except Exception:
203+
raise KeyError(name)
198204
obj._name = name
199205
if name not in dataset._dims:
200206
obj._dataset._coord_names.discard(name)

xray/core/dataset.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,27 +137,27 @@ def _get_virtual_variable(variables, key):
137137
if not isinstance(key, basestring):
138138
raise KeyError(key)
139139

140-
split_key = key.split('.')
140+
split_key = key.split('.', 1)
141141
if len(split_key) != 2:
142142
raise KeyError(key)
143143

144-
ref_var_name, suffix = split_key
145-
ref_var = variables[ref_var_name]
144+
ref_name, var_name = split_key
145+
ref_var = variables[ref_name]
146146
if ref_var.ndim == 1:
147147
date = ref_var.to_index()
148148
elif ref_var.ndim == 0:
149149
date = pd.Timestamp(ref_var.values)
150150
else:
151151
raise KeyError(key)
152152

153-
if suffix == 'season':
153+
if var_name == 'season':
154154
# TODO: move 'season' into pandas itself
155155
seasons = np.array(['DJF', 'MAM', 'JJA', 'SON'])
156156
month = date.month
157157
data = seasons[(month // 3) % 4]
158158
else:
159-
data = getattr(date, suffix)
160-
return ref_var_name, variable.Variable(ref_var.dims, data)
159+
data = getattr(date, var_name)
160+
return ref_name, var_name, variable.Variable(ref_var.dims, data)
161161

162162

163163
def _as_dataset_variable(name, var):
@@ -624,10 +624,11 @@ def _copy_listed(self, names, keep_attrs=True):
624624
try:
625625
variables[name] = self._variables[name]
626626
except KeyError:
627-
ref_name, var = _get_virtual_variable(self._variables, name)
628-
variables[name] = var
627+
ref_name, var_name, var = _get_virtual_variable(
628+
self._variables, name)
629+
variables[var_name] = var
629630
if ref_name in self._coord_names:
630-
coord_names.add(name)
631+
coord_names.add(var_name)
631632

632633
needed_dims = set()
633634
for v in variables.values():
@@ -647,7 +648,7 @@ def __copy__(self):
647648
return self.copy(deep=False)
648649

649650
def __deepcopy__(self, memo=None):
650-
# memo does nothing but is required for compatability with
651+
# memo does nothing but is required for compatibility with
651652
# copy.deepcopy
652653
return self.copy(deep=True)
653654

xray/test/test_dataset.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -965,8 +965,9 @@ def test_getitem(self):
965965
def test_virtual_variables(self):
966966
# access virtual variables
967967
data = create_test_data()
968-
self.assertVariableEqual(data['time.dayofyear'],
969-
Variable('time', 1 + np.arange(20)))
968+
expected = DataArray(1 + np.arange(20), coords=[data['time']],
969+
dims='time', name='dayofyear')
970+
self.assertDataArrayIdentical(expected, data['time.dayofyear'])
970971
self.assertArrayEqual(data['time.month'].values,
971972
data.variables['time'].to_index().month)
972973
self.assertArrayEqual(data['time.season'].values, 'DJF')
@@ -975,7 +976,7 @@ def test_virtual_variables(self):
975976
self.assertArrayEqual(np.sin(data['time.dayofyear']),
976977
np.sin(1 + np.arange(20)))
977978
# ensure they become coordinates
978-
expected = Dataset({}, {'time.dayofyear': data['time.dayofyear']})
979+
expected = Dataset({}, {'dayofyear': data['time.dayofyear']})
979980
actual = data[['time.dayofyear']]
980981
self.assertDatasetEqual(expected, actual)
981982
# non-coordinate variables
@@ -1190,7 +1191,7 @@ def test_groupby_math_virtual(self):
11901191
grouped = ds.groupby('t.day')
11911192
actual = grouped - grouped.mean()
11921193
expected = Dataset({'x': ('t', [0, 0, 0])},
1193-
{'t': ds['t'], 't.day': ds['t.day']})
1194+
ds[['t', 't.day']])
11941195
self.assertDatasetIdentical(actual, expected)
11951196

11961197
def test_groupby_nan(self):

0 commit comments

Comments
 (0)