Repository: pydata/xarray

Issue:
BUG: _extract_nc4_variable_encoding raises when shuffle argument is set
I think we're missing the [`shuffle`](http://unidata.github.io/netcdf4-python/#section9) key from the valid encodings list below: 

https://github.com/pydata/xarray/blob/24643ecee2eab04d0f84c41715d753e829f448e6/xarray/backends/netCDF4_.py#L155-L156


```Python
var = xr.Variable(('x',), [1, 2, 3], {}, {'chunking': (2, 1)})
encoding = _extract_nc4_variable_encoding(var, raise_on_invalid=True)
```

```
variable = <xarray.Variable (x: 3)>
array([1, 2, 3]), raise_on_invalid = True, lsd_okay = True, backend = 'netCDF4'

    def _extract_nc4_variable_encoding(variable, raise_on_invalid=False,
                                       lsd_okay=True, backend='netCDF4'):
        encoding = variable.encoding.copy()

        safe_to_drop = set(['source', 'original_shape'])
        valid_encodings = set(['zlib', 'complevel', 'fletcher32', 'contiguous',
                               'chunksizes'])
        if lsd_okay:
            valid_encodings.add('least_significant_digit')

        if (encoding.get('chunksizes') is not None and
                (encoding.get('original_shape', variable.shape) !=
                    variable.shape) and not raise_on_invalid):
            del encoding['chunksizes']

        for k in safe_to_drop:
            if k in encoding:
                del encoding[k]

        if raise_on_invalid:
            invalid = [k for k in encoding if k not in valid_encodings]
            if invalid:
                raise ValueError('unexpected encoding parameters for %r backend: '
>                                ' %r' % (backend, invalid))
E               ValueError: unexpected encoding parameters for 'netCDF4' backend:  ['shuffle']

xarray/backends/netCDF4_.py:173: ValueError
```

Discussion:

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index f8c38c47..9dea215b 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -196,6 +196,7 @@ Bug fixes
 - ``:py:meth:`~xarray.Dataset.__init__` raises a ``MergeError`` if an
   coordinate shares a name with a dimension but is comprised of arbitrary
   dimensions(:issue:`1120`).
+
 - :py:func:`~xarray.open_rasterio` method now skips rasterio.crs -attribute if
   it is none.
   By `Leevi Annala <https://github.com/leevei>`_.
@@ -212,6 +213,10 @@ Bug fixes
   the first argument was a numpy variable (:issue:`1588`).
   By `Guido Imperiale <https://github.com/crusaderky>`_.
 
+- Fix ``netCDF4`` backend to properly roundtrip the ``shuffle`` encoding option
+  (:issue:`1606`).
+  By `Joe Hamman <https://github.com/jhamman>`_.
+
 - Fix bug when using ``pytest`` class decorators to skiping certain unittests.
   The previous behavior unintentionally causing additional tests to be skipped
   (:issue:`1531`). By `Joe Hamman <https://github.com/jhamman>`_.
diff --git a/xarray/backends/netCDF4_.py b/xarray/backends/netCDF4_.py
index f42fa2b1..238e9ab4 100644
--- a/xarray/backends/netCDF4_.py
+++ b/xarray/backends/netCDF4_.py
@@ -153,7 +153,7 @@ def _extract_nc4_variable_encoding(variable, raise_on_invalid=False,
 
     safe_to_drop = set(['source', 'original_shape'])
     valid_encodings = set(['zlib', 'complevel', 'fletcher32', 'contiguous',
-                           'chunksizes'])
+                           'chunksizes', 'shuffle'])
     if lsd_okay:
         valid_encodings.add('least_significant_digit')
 


Test patch:
diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py
index 828151d5..c7bf5349 100644
--- a/xarray/tests/test_backends.py
+++ b/xarray/tests/test_backends.py
@@ -729,6 +729,7 @@ class BaseNetCDF4Test(CFEncodedDataTest):
         data['var2'].encoding.update({'zlib': True,
                                       'chunksizes': (5, 5),
                                       'fletcher32': True,
+                                      'shuffle': True,
                                       'original_shape': data.var2.shape})
         with self.roundtrip(data) as actual:
             for k, v in iteritems(data['var2'].encoding):
@@ -1846,6 +1847,11 @@ class TestEncodingInvalid(TestCase):
         encoding = _extract_nc4_variable_encoding(var)
         self.assertEqual({}, encoding)
 
+        # regression test
+        var = xr.Variable(('x',), [1, 2, 3], {}, {'shuffle': True})
+        encoding = _extract_nc4_variable_encoding(var, raise_on_invalid=True)
+        self.assertEqual({'shuffle': True}, encoding)
+
     def test_extract_h5nc_encoding(self):
         # not supported with h5netcdf (yet)
         var = xr.Variable(('x',), [1, 2, 3], {},