|
6 | 6 | import numpy as np
|
7 | 7 | import pandas as pd
|
8 | 8 |
|
9 |
| -from xray import Variable, Dataset, DataArray |
| 9 | +from xray import Variable, Dataset, DataArray, indexing |
10 | 10 | from xray.variable import (Coordinate, as_variable, NumpyArrayAdapter,
|
11 |
| - PandasIndexAdapter) |
| 11 | + PandasIndexAdapter, _as_compatible_data) |
| 12 | +from xray.pycompat import PY3 |
12 | 13 |
|
13 | 14 | from . import TestCase, source_ndarray
|
14 | 15 |
|
@@ -36,32 +37,89 @@ def test_attrs(self):
|
36 | 37 | v.attrs['foo'] = 'baz'
|
37 | 38 | self.assertEqual(v.attrs['foo'], 'baz')
|
38 | 39 |
|
39 |
| - def test_0d_data(self): |
40 |
| - d = datetime(2000, 1, 1) |
41 |
| - for value, dtype in [(0, int), |
42 |
| - (np.float32(0.5), np.float32), |
43 |
| - ('foo', np.str_), |
44 |
| - (d, None), |
45 |
| - (np.datetime64(d), np.datetime64)]: |
| 40 | + def assertIndexedLikeNDArray(self, variable, expected_value0, |
| 41 | + expected_dtype=None): |
| 42 | + """Given a 1-dimensional variable, verify that the variable is indexed |
| 43 | + like a numpy.ndarray. |
| 44 | + """ |
| 45 | + self.assertEqual(variable[0].shape, ()) |
| 46 | + self.assertEqual(variable[0].ndim, 0) |
| 47 | + self.assertEqual(variable[0].size, 1) |
| 48 | + # test identity |
| 49 | + self.assertTrue(variable.equals(variable.copy())) |
| 50 | + self.assertTrue(variable.identical(variable.copy())) |
| 51 | + # check value is equal for both ndarray and Variable |
| 52 | + self.assertEqual(variable.values[0], expected_value0) |
| 53 | + self.assertEqual(variable[0].values, expected_value0) |
| 54 | + # check type or dtype is consistent for both ndarray and Variable |
| 55 | + if expected_dtype is None: |
| 56 | + # check output type instead of array dtype |
| 57 | + self.assertEqual(type(variable.values[0]), type(expected_value0)) |
| 58 | + self.assertEqual(type(variable[0].values), type(expected_value0)) |
| 59 | + else: |
| 60 | + self.assertEqual(variable.values[0].dtype, expected_dtype) |
| 61 | + self.assertEqual(variable[0].values.dtype, expected_dtype) |
| 62 | + |
| 63 | + def test_index_0d_int(self): |
| 64 | + for value, dtype in [(0, np.int_), |
| 65 | + (np.int32(0), np.int32)]: |
| 66 | + x = self.cls(['x'], [value]) |
| 67 | + self.assertIndexedLikeNDArray(x, value, dtype) |
| 68 | + |
| 69 | + def test_index_0d_float(self): |
| 70 | + for value, dtype in [(0.5, np.float_), |
| 71 | + (np.float32(0.5), np.float32)]: |
| 72 | + x = self.cls(['x'], [value]) |
| 73 | + self.assertIndexedLikeNDArray(x, value, dtype) |
| 74 | + |
| 75 | + def test_index_0d_string(self): |
| 76 | + for value, dtype in [('foo', np.dtype('U3' if PY3 else 'S3')), |
| 77 | + (u'foo', np.dtype('U3'))]: |
46 | 78 | x = self.cls(['x'], [value])
|
47 |
| - # check array properties |
48 |
| - self.assertEqual(x[0].shape, ()) |
49 |
| - self.assertEqual(x[0].ndim, 0) |
50 |
| - self.assertEqual(x[0].size, 1) |
51 |
| - # test identity |
52 |
| - self.assertTrue(x.equals(x.copy())) |
53 |
| - self.assertTrue(x.identical(x.copy())) |
54 |
| - # check value is equal for both ndarray and Variable |
55 |
| - self.assertEqual(x.values[0], value) |
56 |
| - self.assertEqual(x[0].values, value) |
57 |
| - # check type or dtype is consistent for both ndarray and Variable |
58 |
| - if dtype is None: |
59 |
| - # check output type instead of array dtype |
60 |
| - self.assertEqual(type(x.values[0]), type(value)) |
61 |
| - self.assertEqual(type(x[0].values), type(value)) |
62 |
| - else: |
63 |
| - assert np.issubdtype(x.values[0].dtype, dtype), (x.values[0].dtype, dtype) |
64 |
| - assert np.issubdtype(x[0].values.dtype, dtype), (x[0].values.dtype, dtype) |
| 79 | + self.assertIndexedLikeNDArray(x, value, dtype) |
| 80 | + |
| 81 | + def test_index_0d_datetime(self): |
| 82 | + d = datetime(2000, 1, 1) |
| 83 | + x = self.cls(['x'], [d]) |
| 84 | + self.assertIndexedLikeNDArray(x, d) |
| 85 | + |
| 86 | + x = self.cls(['x'], [np.datetime64(d)]) |
| 87 | + self.assertIndexedLikeNDArray(x, np.datetime64(d), 'datetime64[ns]') |
| 88 | + |
| 89 | + x = self.cls(['x'], pd.DatetimeIndex([d])) |
| 90 | + self.assertIndexedLikeNDArray(x, np.datetime64(d), 'datetime64[ns]') |
| 91 | + |
| 92 | + def test_index_0d_object(self): |
| 93 | + |
| 94 | + class HashableItemWrapper(object): |
| 95 | + def __init__(self, item): |
| 96 | + self.item = item |
| 97 | + |
| 98 | + def __eq__(self, other): |
| 99 | + return self.item == other.item |
| 100 | + |
| 101 | + def __hash__(self): |
| 102 | + return hash(self.item) |
| 103 | + |
| 104 | + def __repr__(self): |
| 105 | + return '%s(item=%r)' % (type(self).__name__, self.item) |
| 106 | + |
| 107 | + item = HashableItemWrapper((1, 2, 3)) |
| 108 | + x = self.cls('x', [item]) |
| 109 | + self.assertIndexedLikeNDArray(x, item) |
| 110 | + |
| 111 | + def test_index_and_concat_datetime(self): |
| 112 | + # regression test for #125 |
| 113 | + date_range = pd.date_range('2011-09-01', periods=10) |
| 114 | + for dates in [date_range, date_range.values, |
| 115 | + date_range.to_pydatetime()]: |
| 116 | + expected = self.cls('t', dates) |
| 117 | + for times in [[expected[i] for i in range(10)], |
| 118 | + [expected[i:(i + 1)] for i in range(10)], |
| 119 | + [expected[[i]] for i in range(10)]]: |
| 120 | + actual = Variable.concat(times, 't') |
| 121 | + self.assertEqual(expected.dtype, actual.dtype) |
| 122 | + self.assertArrayEqual(expected, actual) |
65 | 123 |
|
66 | 124 | def test_0d_time_data(self):
|
67 | 125 | # regression test for #105
|
@@ -229,6 +287,39 @@ def test_item(self):
|
229 | 287 | self.assertEqual(v.item(), 0)
|
230 | 288 | self.assertIs(type(v.item()), float)
|
231 | 289 |
|
| 290 | + def test_datetime64_conversion(self): |
| 291 | + # verify that datetime64 is always converted to ns precision with |
| 292 | + # sources preserved |
| 293 | + values = np.datetime64('2000-01-01T00') |
| 294 | + v = Variable([], values) |
| 295 | + self.assertEqual(v.dtype, np.dtype('datetime64[ns]')) |
| 296 | + self.assertEqual(v.values, values) |
| 297 | + self.assertEqual(v.values.dtype, np.dtype('datetime64[ns]')) |
| 298 | + |
| 299 | + values = pd.date_range('2000-01-01', periods=3).values.astype( |
| 300 | + 'datetime64[s]') |
| 301 | + v = Variable(['t'], values) |
| 302 | + self.assertEqual(v.dtype, np.dtype('datetime64[ns]')) |
| 303 | + self.assertArrayEqual(v.values, values) |
| 304 | + self.assertEqual(v.values.dtype, np.dtype('datetime64[ns]')) |
| 305 | + self.assertIsNot(source_ndarray(v.values), values) |
| 306 | + |
| 307 | + values = pd.date_range('2000-01-01', periods=3).values.copy() |
| 308 | + v = Variable(['t'], values) |
| 309 | + self.assertEqual(v.dtype, np.dtype('datetime64[ns]')) |
| 310 | + self.assertArrayEqual(v.values, values) |
| 311 | + self.assertEqual(v.values.dtype, np.dtype('datetime64[ns]')) |
| 312 | + self.assertIs(source_ndarray(v.values), values) |
| 313 | + |
| 314 | + def test_0d_str(self): |
| 315 | + v = Variable([], u'foo') |
| 316 | + self.assertEqual(v.dtype, np.dtype('U3')) |
| 317 | + self.assertEqual(v.values, 'foo') |
| 318 | + |
| 319 | + v = Variable([], np.string_('foo')) |
| 320 | + self.assertEqual(v.dtype, np.dtype('S3')) |
| 321 | + self.assertEqual(v.values, bytes('foo', 'ascii') if PY3 else 'foo') |
| 322 | + |
232 | 323 | def test_equals_and_identical(self):
|
233 | 324 | d = np.random.rand(10, 3)
|
234 | 325 | d[0, 0] = np.nan
|
@@ -463,3 +554,60 @@ def test_data(self):
|
463 | 554 | self.assertIsInstance(x._data, PandasIndexAdapter)
|
464 | 555 | with self.assertRaisesRegexp(TypeError, 'cannot be modified'):
|
465 | 556 | x[:] = 0
|
| 557 | + |
| 558 | + def test_avoid_index_dtype_inference(self): |
| 559 | + # verify our work-around for (pandas<0.14): |
| 560 | + # https://github.com/pydata/pandas/issues/6370 |
| 561 | + data = pd.date_range('2000-01-01', periods=3).to_pydatetime() |
| 562 | + t = Coordinate('t', data) |
| 563 | + self.assertArrayEqual(t.values[:2], data[:2]) |
| 564 | + self.assertArrayEqual(t[:2].values, data[:2]) |
| 565 | + self.assertArrayEqual(t.values[:2], data[:2]) |
| 566 | + self.assertArrayEqual(t[:2].values, data[:2]) |
| 567 | + self.assertEqual(t.dtype, object) |
| 568 | + self.assertEqual(t[:2].dtype, object) |
| 569 | + |
| 570 | + |
| 571 | +class TestAsCompatibleData(TestCase): |
| 572 | + def test_unchanged_types(self): |
| 573 | + types = (NumpyArrayAdapter, PandasIndexAdapter, |
| 574 | + indexing.LazilyIndexedArray) |
| 575 | + for t in types: |
| 576 | + for data in [np.arange(3), |
| 577 | + pd.date_range('2000-01-01', periods=3), |
| 578 | + pd.date_range('2000-01-01', periods=3).values]: |
| 579 | + x = t(data) |
| 580 | + self.assertIs(x, _as_compatible_data(x)) |
| 581 | + |
| 582 | + def test_converted_types(self): |
| 583 | + for input_array in [[[0, 1, 2]], pd.DataFrame([[0, 1, 2]])]: |
| 584 | + actual = _as_compatible_data(input_array) |
| 585 | + self.assertArrayEqual(np.asarray(input_array), actual) |
| 586 | + self.assertEqual(NumpyArrayAdapter, type(actual)) |
| 587 | + self.assertEqual(np.dtype(int), actual.dtype) |
| 588 | + |
| 589 | + def test_datetime(self): |
| 590 | + expected = np.datetime64('2000-01-01T00') |
| 591 | + actual = _as_compatible_data(expected) |
| 592 | + self.assertEqual(expected, actual) |
| 593 | + self.assertEqual(np.datetime64, type(actual)) |
| 594 | + self.assertEqual(np.dtype('datetime64[ns]'), actual.dtype) |
| 595 | + |
| 596 | + expected = np.array([np.datetime64('2000-01-01T00')]) |
| 597 | + actual = _as_compatible_data(expected) |
| 598 | + self.assertEqual(np.asarray(expected), actual) |
| 599 | + self.assertEqual(NumpyArrayAdapter, type(actual)) |
| 600 | + self.assertEqual(np.dtype('datetime64[ns]'), actual.dtype) |
| 601 | + |
| 602 | + expected = np.array([np.datetime64('2000-01-01T00', 'ns')]) |
| 603 | + actual = _as_compatible_data(expected) |
| 604 | + self.assertEqual(np.asarray(expected), actual) |
| 605 | + self.assertEqual(NumpyArrayAdapter, type(actual)) |
| 606 | + self.assertEqual(np.dtype('datetime64[ns]'), actual.dtype) |
| 607 | + self.assertIs(expected, source_ndarray(np.asarray(actual))) |
| 608 | + |
| 609 | + expected = pd.Timestamp('2000-01-01T00').to_datetime() |
| 610 | + actual = _as_compatible_data(expected) |
| 611 | + self.assertEqual(np.asarray(expected), actual) |
| 612 | + self.assertEqual(NumpyArrayAdapter, type(actual)) |
| 613 | + self.assertEqual(np.dtype('O'), actual.dtype) |
0 commit comments