Repository: pydata/xarray

Issue:
Test failures with pandas 0.24.0
From a [recent build](https://travis-ci.org/pydata/xarray/jobs/484641077) on Travis-CI:
```
=================================== FAILURES ===================================
___________________ test_cf_timedelta[timedeltas7-days-nan] ____________________
timedeltas = numpy.datetime64('NaT'), units = 'days', numbers = array(nan)
    @pytest.mark.parametrize(
        ['timedeltas', 'units', 'numbers'],
        [('1D', 'days', np.int64(1)),
         (['1D', '2D', '3D'], 'days', np.array([1, 2, 3], 'int64')),
         ('1h', 'hours', np.int64(1)),
         ('1ms', 'milliseconds', np.int64(1)),
         ('1us', 'microseconds', np.int64(1)),
         (['NaT', '0s', '1s'], None, [np.nan, 0, 1]),
         (['30m', '60m'], 'hours', [0.5, 1.0]),
         (np.timedelta64('NaT', 'ns'), 'days', np.nan),
         (['NaT', 'NaT'], 'days', [np.nan, np.nan])])
    def test_cf_timedelta(timedeltas, units, numbers):
        timedeltas = pd.to_timedelta(timedeltas, box=False)
        numbers = np.array(numbers)
    
        expected = numbers
>       actual, _ = coding.times.encode_cf_timedelta(timedeltas, units)
xarray/tests/test_coding_times.py:550: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
timedeltas = numpy.datetime64('NaT'), units = 'days'
    def encode_cf_timedelta(timedeltas, units=None):
        if units is None:
            units = infer_timedelta_units(timedeltas)
    
        np_unit = _netcdf_to_numpy_timeunit(units)
>       num = 1.0 * timedeltas / np.timedelta64(1, np_unit)
E       TypeError: ufunc multiply cannot use operands with types dtype('float64') and dtype('<M8[ns]')
xarray/coding/times.py:379: TypeError
_____________________ TestDataArray.test_struct_array_dims _____________________
self = <xarray.tests.test_dataarray.TestDataArray object at 0x7fb508944a90>
    def test_struct_array_dims(self):
        """
        This test checks subraction of two DataArrays for the case
        when dimension is a structured array.
        """
        # GH837, GH861
        # checking array subraction when dims are the same
        p_data = np.array([('John', 180), ('Stacy', 150), ('Dick', 200)],
                          dtype=[('name', '|S256'), ('height', object)])
    
        p_data_1 = np.array([('John', 180), ('Stacy', 150), ('Dick', 200)],
                            dtype=[('name', '|S256'), ('height', object)])
    
        p_data_2 = np.array([('John', 180), ('Dick', 200)],
                            dtype=[('name', '|S256'), ('height', object)])
    
        weights_0 = DataArray([80, 56, 120], dims=['participant'],
                              coords={'participant': p_data})
    
        weights_1 = DataArray([81, 52, 115], dims=['participant'],
                              coords={'participant': p_data_1})
    
        actual = weights_1 - weights_0
    
        expected = DataArray([1, -4, -5], dims=['participant'],
                             coords={'participant': p_data})
    
        assert_identical(actual, expected)
    
        # checking array subraction when dims are not the same
        p_data_1 = np.array([('John', 180), ('Stacy', 151), ('Dick', 200)],
                            dtype=[('name', '|S256'), ('height', object)])
    
        weights_1 = DataArray([81, 52, 115], dims=['participant'],
                              coords={'participant': p_data_1})
    
        actual = weights_1 - weights_0
    
        expected = DataArray([1, -5], dims=['participant'],
                             coords={'participant': p_data_2})
    
>       assert_identical(actual, expected)
E       AssertionError: Left and right DataArray objects are not identical
E       
E       Differing values:
E       L
E           array([-5,  1])
E       R
E           array([ 1, -5])
E       Differing coordinates:
E       L * participant  (participant) object (b'Dick', 200) (b'John', 180)
E       R * participant  (participant) [('name', 'S256'), ('height', 'O')] (b'John', 180) (b'Dick', 200)
```

Discussion:

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index c408306f..184cee05 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -13,9 +13,9 @@ What's New
     import xarray as xr
     np.random.seed(123456)
 
-.. _whats-new.0.11.3:
+.. _whats-new.0.12.0:
 
-v0.11.3 (unreleased)
+v0.12.0 (unreleased)
 --------------------
 
 Breaking changes
@@ -47,14 +47,30 @@ Enhancements
 Bug fixes
 ~~~~~~~~~
 
+- Silenced warnings that appear when using pandas 0.24.
+  By `Stephan Hoyer <https://github.com/shoyer>`_
 - Interpolating via resample now internally specifies ``bounds_error=False``
   as an argument to ``scipy.interpolate.interp1d``, allowing for interpolation
   from higher frequencies to lower frequencies.  Datapoints outside the bounds
   of the original time coordinate are now filled with NaN (:issue:`2197`). By
   `Spencer Clark <https://github.com/spencerkclark>`_.
+
+.. _whats-new.0.11.3:
+
+v0.11.3 (26 January 2019)
+-------------------------
+
+Bug fixes
+~~~~~~~~~
+
 - Saving files with times encoded with reference dates with timezones
   (e.g. '2000-01-01T00:00:00-05:00') no longer raises an error
   (:issue:`2649`).  By `Spencer Clark <https://github.com/spencerkclark>`_.
+- Fixed performance regression with ``open_mfdataset`` (:issue:`2662`).
+  By `Tom Nicholas <http://github.com/TomNicholas>`_.
+- Fixed supplying an explicit dimension in the ``concat_dim`` argument to
+  to ``open_mfdataset`` (:issue:`2647`).
+  By `Ben Root <https://github.com/WeatherGod>`_.
 
 .. _whats-new.0.11.2:
 
diff --git a/xarray/core/coordinates.py b/xarray/core/coordinates.py
index 19e2d009..9347ba6b 100644
--- a/xarray/core/coordinates.py
+++ b/xarray/core/coordinates.py
@@ -1,4 +1,5 @@
-from collections import Mapping, OrderedDict
+import collections.abc
+from collections import OrderedDict
 from contextlib import contextmanager
 
 import pandas as pd
@@ -14,7 +15,7 @@ from .variable import Variable
 _THIS_ARRAY = ReprObject('<this-array>')
 
 
-class AbstractCoordinates(Mapping):
+class AbstractCoordinates(collections.abc.Mapping):
     def __getitem__(self, key):
         raise NotImplementedError
 
diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py
index 711dfcdc..0d1b9ebd 100644
--- a/xarray/core/dataset.py
+++ b/xarray/core/dataset.py
@@ -130,7 +130,7 @@ def merge_indexes(
         if isinstance(var_names, str):
             var_names = [var_names]
 
-        names, labels, levels = [], [], []  # type: (list, list, list)
+        names, codes, levels = [], [], []  # type: (list, list, list)
         current_index_variable = variables.get(dim)
 
         for n in var_names:
@@ -144,13 +144,18 @@ def merge_indexes(
         if current_index_variable is not None and append:
             current_index = current_index_variable.to_index()
             if isinstance(current_index, pd.MultiIndex):
+                try:
+                    current_codes = current_index.codes
+                except AttributeError:
+                    # fpr pandas<0.24
+                    current_codes = current_index.labels
                 names.extend(current_index.names)
-                labels.extend(current_index.labels)
+                codes.extend(current_codes)
                 levels.extend(current_index.levels)
             else:
                 names.append('%s_level_0' % dim)
                 cat = pd.Categorical(current_index.values, ordered=True)
-                labels.append(cat.codes)
+                codes.append(cat.codes)
                 levels.append(cat.categories)
 
         if not len(names) and len(var_names) == 1:
@@ -161,10 +166,10 @@ def merge_indexes(
                 names.append(n)
                 var = variables[n]
                 cat = pd.Categorical(var.values, ordered=True)
-                labels.append(cat.codes)
+                codes.append(cat.codes)
                 levels.append(cat.categories)
 
-            idx = pd.MultiIndex(labels=labels, levels=levels, names=names)
+            idx = pd.MultiIndex(levels, codes, names=names)
 
         vars_to_replace[dim] = IndexVariable(dim, idx)
         vars_to_remove.extend(var_names)


Test patch:
diff --git a/xarray/tests/test_coding_times.py b/xarray/tests/test_coding_times.py
index 24234d3b..863c0378 100644
--- a/xarray/tests/test_coding_times.py
+++ b/xarray/tests/test_coding_times.py
@@ -533,17 +533,22 @@ def test_infer_cftime_datetime_units(calendar, date_args, expected):
 
 @pytest.mark.parametrize(
     ['timedeltas', 'units', 'numbers'],
-    [('1D', 'days', np.int64(1)),
-     (['1D', '2D', '3D'], 'days', np.array([1, 2, 3], 'int64')),
-     ('1h', 'hours', np.int64(1)),
-     ('1ms', 'milliseconds', np.int64(1)),
-     ('1us', 'microseconds', np.int64(1)),
-     (['NaT', '0s', '1s'], None, [np.nan, 0, 1]),
-     (['30m', '60m'], 'hours', [0.5, 1.0]),
-     (np.timedelta64('NaT', 'ns'), 'days', np.nan),
-     (['NaT', 'NaT'], 'days', [np.nan, np.nan])])
+    [
+        ('1D', 'days', np.int64(1)),
+        (['1D', '2D', '3D'], 'days', np.array([1, 2, 3], 'int64')),
+        ('1h', 'hours', np.int64(1)),
+        ('1ms', 'milliseconds', np.int64(1)),
+        ('1us', 'microseconds', np.int64(1)),
+        (['NaT', '0s', '1s'], None, [np.nan, 0, 1]),
+        (['30m', '60m'], 'hours', [0.5, 1.0]),
+        ('NaT', 'days', np.nan),
+        (['NaT', 'NaT'], 'days', [np.nan, np.nan]),
+    ])
 def test_cf_timedelta(timedeltas, units, numbers):
-    timedeltas = pd.to_timedelta(timedeltas, box=False)
+    if timedeltas == 'NaT':
+        timedeltas = np.timedelta64('NaT', 'ns')
+    else:
+        timedeltas = pd.to_timedelta(timedeltas, box=False)
     numbers = np.array(numbers)
 
     expected = numbers
diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py
index 23e15aef..59d14d7c 100644
--- a/xarray/tests/test_dataarray.py
+++ b/xarray/tests/test_dataarray.py
@@ -122,21 +122,14 @@ class TestDataArray(object):
         """
         # GH837, GH861
         # checking array subraction when dims are the same
-        p_data = np.array([('John', 180), ('Stacy', 150), ('Dick', 200)],
+        # note: names need to be in sorted order to align consistently with
+        # pandas < 0.24 and >= 0.24.
+        p_data = np.array([('Abe', 180), ('Stacy', 150), ('Dick', 200)],
                           dtype=[('name', '|S256'), ('height', object)])
-
-        p_data_1 = np.array([('John', 180), ('Stacy', 150), ('Dick', 200)],
-                            dtype=[('name', '|S256'), ('height', object)])
-
-        p_data_2 = np.array([('John', 180), ('Dick', 200)],
-                            dtype=[('name', '|S256'), ('height', object)])
-
         weights_0 = DataArray([80, 56, 120], dims=['participant'],
                               coords={'participant': p_data})
-
         weights_1 = DataArray([81, 52, 115], dims=['participant'],
-                              coords={'participant': p_data_1})
-
+                              coords={'participant': p_data})
         actual = weights_1 - weights_0
 
         expected = DataArray([1, -4, -5], dims=['participant'],
@@ -145,31 +138,27 @@ class TestDataArray(object):
         assert_identical(actual, expected)
 
         # checking array subraction when dims are not the same
-        p_data_1 = np.array([('John', 180), ('Stacy', 151), ('Dick', 200)],
-                            dtype=[('name', '|S256'), ('height', object)])
-
+        p_data_alt = np.array([('Abe', 180), ('Stacy', 151), ('Dick', 200)],
+                              dtype=[('name', '|S256'), ('height', object)])
         weights_1 = DataArray([81, 52, 115], dims=['participant'],
-                              coords={'participant': p_data_1})
-
+                              coords={'participant': p_data_alt})
         actual = weights_1 - weights_0
 
         expected = DataArray([1, -5], dims=['participant'],
-                             coords={'participant': p_data_2})
+                             coords={'participant': p_data[[0, 2]]})
 
         assert_identical(actual, expected)
 
         # checking array subraction when dims are not the same and one
         # is np.nan
-        p_data_1 = np.array([('John', 180), ('Stacy', np.nan), ('Dick', 200)],
-                            dtype=[('name', '|S256'), ('height', object)])
-
+        p_data_nan = np.array([('Abe', 180), ('Stacy', np.nan), ('Dick', 200)],
+                              dtype=[('name', '|S256'), ('height', object)])
         weights_1 = DataArray([81, 52, 115], dims=['participant'],
-                              coords={'participant': p_data_1})
-
+                              coords={'participant': p_data_nan})
         actual = weights_1 - weights_0
 
         expected = DataArray([1, -5], dims=['participant'],
-                             coords={'participant': p_data_2})
+                             coords={'participant': p_data[[0, 2]]})
 
         assert_identical(actual, expected)
 
diff --git a/xarray/tests/test_utils.py b/xarray/tests/test_utils.py
index 5a5041b5..09152bac 100644
--- a/xarray/tests/test_utils.py
+++ b/xarray/tests/test_utils.py
@@ -74,7 +74,9 @@ def test_multiindex_from_product_levels():
     result = utils.multiindex_from_product_levels(
         [pd.Index(['b', 'a']), pd.Index([1, 3, 2])])
     np.testing.assert_array_equal(
-        result.labels, [[0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]])
+        # compat for pandas < 0.24
+        result.codes if hasattr(result, 'codes') else result.labels,
+        [[0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]])
     np.testing.assert_array_equal(result.levels[0], ['b', 'a'])
     np.testing.assert_array_equal(result.levels[1], [1, 3, 2])
 
@@ -86,7 +88,9 @@ def test_multiindex_from_product_levels_non_unique():
     result = utils.multiindex_from_product_levels(
         [pd.Index(['b', 'a']), pd.Index([1, 1, 2])])
     np.testing.assert_array_equal(
-        result.labels, [[0, 0, 0, 1, 1, 1], [0, 0, 1, 0, 0, 1]])
+        # compat for pandas < 0.24
+        result.codes if hasattr(result, 'codes') else result.labels,
+        [[0, 0, 0, 1, 1, 1], [0, 0, 1, 0, 0, 1]])
     np.testing.assert_array_equal(result.levels[0], ['b', 'a'])
     np.testing.assert_array_equal(result.levels[1], [1, 2])