Skip to content

Commit e76c7d8

Browse files
committed
Force 'dim' to be OrderedDict for python 3.5
1 parent dcc3cfd commit e76c7d8

File tree

4 files changed

+66
-33
lines changed

4 files changed

+66
-33
lines changed

xarray/core/dataarray.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import functools
2+
import sys
23
import warnings
34
from collections import OrderedDict
45

@@ -1153,18 +1154,22 @@ def expand_dims(self, dim=None, axis=None, **dim_kwargs):
11531154
with length 1. If provided as a dict, then the keys are the new
11541155
dimensions and the values are either integers (giving the length of
11551156
the new dimensions) or sequence/ndarray (giving the coordinates of
1156-
the new dimensions).
1157+
the new dimensions). **WARNING** for python 3.5, if ``dim`` is
1158+
dict-like, then it must be an ``OrderedDict`` to ensure that the
1159+
order in which the dims are given is maintained.
11571160
axis : integer, list (or tuple) of integers, or None
11581161
Axis position(s) where new axis is to be inserted (position(s) on
11591162
the result array). If a list (or tuple) of integers is passed,
11601163
multiple axes are inserted. In this case, dim arguments should be
11611164
same length list. If axis=None is passed, all the axes will be
11621165
inserted to the start of the result array.
1163-
**dim_kwargs : `int or sequence/ndarray`
1166+
**dim_kwargs : int or sequence/ndarray
11641167
The keywords are arbitrary dimensions being inserted and the values
11651168
are either the lengths of the new dims (if int is given), or their
11661169
coordinates. Note, this is an alternative to passing a dict to the
1167-
dim kwarg and will only be used if dim is None.
1170+
dim kwarg and will only be used if dim is None. **WARNING** in
1171+
python 3.5, the order in which the dim kwargs are given will not be
1172+
maintained.
11681173
11691174
Returns
11701175
-------
@@ -1174,11 +1179,22 @@ def expand_dims(self, dim=None, axis=None, **dim_kwargs):
11741179
if isinstance(dim, int):
11751180
raise TypeError('dim should be str or sequence of strs or dict')
11761181
elif isinstance(dim, str):
1177-
dim = {dim: 1}
1182+
dim = OrderedDict(((dim, 1),))
11781183
elif isinstance(dim, (list, tuple)):
11791184
if len(dim) != len(set(dim)):
11801185
raise ValueError('dims should not contain duplicate values.')
1181-
dim = {d: 1 for d in dim}
1186+
dim = OrderedDict(((d, 1) for d in dim))
1187+
1188+
# TODO: get rid of the below code block when python 3.5 is no longer
1189+
# supported.
1190+
python36_plus = sys.version_info[0] == 3 and sys.version_info[1] > 5
1191+
not_ordereddict = dim is not None and not isinstance(dim, OrderedDict)
1192+
if not python36_plus and not_ordereddict:
1193+
raise TypeError("dim must be an OrderedDict for python <3.6")
1194+
# For python 3.5 dim, must be an OrderedDict. This allows kwargs to be
1195+
# used for python 3.5, but insertion order will not be maintained
1196+
dim_kwargs = OrderedDict(dim_kwargs)
1197+
11821198
dim = either_dict_or_kwargs(dim, dim_kwargs, 'expand_dims')
11831199
ds = self._to_temp_dataset().expand_dims(dim, axis)
11841200
return self._from_temp_dataset(ds)

xarray/core/dataset.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2343,18 +2343,22 @@ def expand_dims(self, dim=None, axis=None, **dim_kwargs):
23432343
with length 1. If provided as a dict, then the keys are the new
23442344
dimensions and the values are either integers (giving the length of
23452345
the new dimensions) or sequence/ndarray (giving the coordinates of
2346-
the new dimensions).
2346+
the new dimensions). **WARNING** for python 3.5, if ``dim`` is
2347+
dict-like, then it must be an ``OrderedDict`` to ensure that the
2348+
order in which the dims are given is maintained.
23472349
axis : integer, list (or tuple) of integers, or None
23482350
Axis position(s) where new axis is to be inserted (position(s) on
23492351
the result array). If a list (or tuple) of integers is passed,
23502352
multiple axes are inserted. In this case, dim arguments should be
23512353
same length list. If axis=None is passed, all the axes will be
23522354
inserted to the start of the result array.
2353-
**dim_kwargs : `int or sequence/ndarray`
2355+
**dim_kwargs : int or sequence/ndarray
23542356
The keywords are arbitrary dimensions being inserted and the values
23552357
are either the lengths of the new dims (if int is given), or their
23562358
coordinates. Note, this is an alternative to passing a dict to the
2357-
dim kwarg and will only be used if dim is None.
2359+
dim kwarg and will only be used if dim is None. **WARNING** in
2360+
python 3.5, the order in which the dim kwargs are given will not be
2361+
maintained.
23582362
23592363
Returns
23602364
-------
@@ -2364,11 +2368,19 @@ def expand_dims(self, dim=None, axis=None, **dim_kwargs):
23642368
if isinstance(dim, int):
23652369
raise TypeError('dim should be str or sequence of strs or dict')
23662370
elif isinstance(dim, str):
2367-
dim = {dim: 1}
2371+
dim = OrderedDict(((dim, 1),))
23682372
elif isinstance(dim, (list, tuple)):
23692373
if len(dim) != len(set(dim)):
23702374
raise ValueError('dims should not contain duplicate values.')
2371-
dim = {d: 1 for d in dim}
2375+
dim = OrderedDict(((d, 1) for d in dim))
2376+
2377+
# TODO: get rid of the below code block when python 3.5 is no longer
2378+
# supported.
2379+
python36_plus = sys.version_info[0] == 3 and sys.version_info[1] > 5
2380+
not_ordereddict = dim is not None and not isinstance(dim, OrderedDict)
2381+
if not python36_plus and not_ordereddict:
2382+
raise TypeError("dim must be an OrderedDict for python <3.6")
2383+
23722384
dim = either_dict_or_kwargs(dim, dim_kwargs, 'expand_dims')
23732385

23742386
if axis is not None and not isinstance(axis, (list, tuple)):

xarray/tests/test_dataarray.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,11 +1332,11 @@ def test_expand_dims_error(self):
13321332
coords={'x': np.linspace(0.0, 1.0, 3)},
13331333
attrs={'key': 'entry'})
13341334
with pytest.raises(TypeError):
1335-
array.expand_dims({"new_dim": 3.2})
1335+
array.expand_dims(OrderedDict((("new_dim", 3.2),)))
13361336

13371337
# Attempt to use both dim and kwargs
13381338
with pytest.raises(ValueError):
1339-
array.expand_dims({"d": 4}, e=4)
1339+
array.expand_dims(OrderedDict((("d", 4),)), e=4)
13401340

13411341
def test_expand_dims(self):
13421342
array = DataArray(np.random.randn(3, 4), dims=['x', 'dim_0'],
@@ -1403,10 +1403,6 @@ def test_expand_dims_with_scalar_coordinate(self):
14031403
assert_identical(array, roundtripped)
14041404

14051405
def test_expand_dims_with_greater_dim_size(self):
1406-
"""Python 3.6+ dicts keep insertion order, unlike Python 3.5 and
1407-
earlier. Therefore the following tests have to use ordered dicts to
1408-
pass for python 3.5 and earlier.
1409-
"""
14101406
array = DataArray(np.random.randn(3, 4), dims=['x', 'dim_0'],
14111407
coords={'x': np.linspace(0.0, 1.0, 3), 'z': 1.0},
14121408
attrs={'key': 'entry'})
@@ -1426,19 +1422,15 @@ def test_expand_dims_with_greater_dim_size(self):
14261422
assert_identical(expected, actual)
14271423

14281424
# Test with kwargs instead of passing dict to dim arg.
1429-
other_way = array.expand_dims(y=2, z=1, dim_1=['a', 'b', 'c'])
1430-
# Unfortunately, there is no way to maintain insertion order with
1431-
# kwargs in python 3.5 and earlier, so for now we have to ensure the
1432-
# dimensions of the expected result are in the same order as the actual
1433-
# result to allow the test to pass.
1434-
other_way_expected_coords = OrderedDict()
1435-
for dim in other_way.dims:
1436-
other_way_expected_coords[dim] = expected_coords[dim]
1425+
other_way = array.expand_dims(dim_1=['a', 'b', 'c'])
1426+
14371427
other_way_expected = DataArray(
1438-
array.values * np.ones(list(other_way.shape)),
1439-
coords=other_way_expected_coords,
1440-
dims=list(other_way_expected_coords.keys()),
1441-
attrs={'key': 'entry'}).drop(['y', 'dim_0'])
1428+
array.values * np.ones([3, 3, 4]),
1429+
coords={'dim_1': ['a', 'b', 'c'],
1430+
'x': np.linspace(0, 1, 3),
1431+
'dim_0': range(4), 'z': 1.0},
1432+
dims=['dim_1', 'x', 'dim_0'],
1433+
attrs={'key': 'entry'}).drop('dim_0')
14421434
assert_identical(other_way_expected, other_way)
14431435

14441436
def test_set_index(self):

xarray/tests/test_dataset.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,10 +2021,10 @@ def test_expand_dims_error(self):
20212021
'c': np.linspace(0, 1, 5)},
20222022
attrs={'key': 'entry'})
20232023
with raises_regex(TypeError, 'value of new dimension'):
2024-
original.expand_dims({"d": 3.2})
2024+
original.expand_dims(OrderedDict((("d", 3.2),)))
20252025

20262026
with raises_regex(ValueError, 'both keyword and positional'):
2027-
original.expand_dims({"d": 4}, e=4)
2027+
original.expand_dims(OrderedDict((("d", 4),)), e=4)
20282028

20292029
def test_expand_dims(self):
20302030
original = Dataset({'x': ('a', np.random.randn(3)),
@@ -2062,7 +2062,8 @@ def test_expand_dims(self):
20622062
# Test expanding one dimension to have size > 1 that doesn't have
20632063
# coordinates, and also expanding another dimension to have size > 1
20642064
# that DOES have coordinates.
2065-
actual = original.expand_dims({"d": 4, "e": ["l", "m", "n"]})
2065+
actual = original.expand_dims(
2066+
OrderedDict((("d", 4), ("e", ["l", "m", "n"]))))
20662067

20672068
expected = Dataset(
20682069
{'x': xr.DataArray(original['x'].values * np.ones([4, 3, 3]),
@@ -2081,8 +2082,20 @@ def test_expand_dims(self):
20812082
assert_identical(actual, expected)
20822083

20832084
# Test with kwargs instead of passing dict to dim arg.
2084-
other_way = original.expand_dims(d=4, e=["l", "m", "n"])
2085-
assert_identical(expected, other_way)
2085+
other_way = original.expand_dims(e=["l", "m", "n"])
2086+
other_way_expected = Dataset(
2087+
{'x': xr.DataArray(original['x'].values * np.ones([3, 3]),
2088+
coords=dict(e=['l', 'm', 'n'],
2089+
a=np.linspace(0, 1, 3)),
2090+
dims=['e', 'a']),
2091+
'y': xr.DataArray(original['y'].values * np.ones([3, 4, 3]),
2092+
coords=dict(e=['l', 'm', 'n'],
2093+
b=np.linspace(0, 1, 4),
2094+
a=np.linspace(0, 1, 3)),
2095+
dims=['e', 'b', 'a'])},
2096+
coords={'c': np.linspace(0, 1, 5)},
2097+
attrs={'key': 'entry'})
2098+
assert_identical(other_way_expected, other_way)
20862099

20872100
def test_set_index(self):
20882101
expected = create_test_multiindex()

0 commit comments

Comments
 (0)