Repository: pydata/xarray

Issue:
Xarray dimension interpolation strips coordinate attributes
<!-- Please include a self-contained copy-pastable example that generates the issue if possible.

Please be concise with code posted. See guidelines below on how to provide a good bug report:

- Craft Minimal Bug Reports: http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports
- Minimal Complete Verifiable Examples: https://stackoverflow.com/help/mcve

Bug reports that follow these guidelines are easier to diagnose, and so are often handled much more quickly.
-->

**What happened**: `DataArray.interp` strips the dimension coordinate attributes.

**What you expected to happen**: Preserved coordinate attributes.

**Minimal Complete Verifiable Example**:

Input:

```python
import xarray as xr
import numpy as np

data = xr.DataArray(
    np.random.rand(5),
    dims='x',
    coords={'x': ('x', np.arange(5), {'foo': 'bar'})}
)

print(data.x.attrs)  # initial attributes
print(data.sel(x=2).x.attrs)  # sel and isel preserve attributes
print(data.interp(x=2.5).x.attrs)  # interp does not preserve attributes
with xr.set_options(keep_attrs=True):
    print(data.interp(x=2.5).x.attrs)  # keep_attrs does nothing
```

Output:

```python
{'foo': 'bar'}
{'foo': 'bar'}
{}
{}
```

**Environment**:

<details><summary>Output of <tt>xr.show_versions()</tt></summary>

<!-- Paste the output here xr.show_versions() here -->

INSTALLED VERSIONS
------------------
commit: None
python: 3.8.3 | packaged by conda-forge | (default, Jun  1 2020, 17:43:00)
[GCC 7.5.0]
python-bits: 64
OS: Linux
OS-release: 3.10.0-957.27.2.el7.x86_64
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8
libhdf5: 1.10.6
libnetcdf: 4.7.4

xarray: 0.15.1
pandas: 1.0.4
numpy: 1.18.4
scipy: 1.4.1
netCDF4: 1.5.3
pydap: None
h5netcdf: None
h5py: None
Nio: None
zarr: None
cftime: 1.1.3
nc_time_axis: None
PseudoNetCDF: None
rasterio: None
cfgrib: None
iris: None
bottleneck: None
dask: 2.17.2
distributed: 2.18.0
matplotlib: 3.2.1
cartopy: 0.18.0
seaborn: None
numbagg: None
setuptools: 47.3.0.post20200616
pip: 20.1.1
conda: 4.8.3
pytest: None
IPython: 7.15.0
sphinx: None

</details>


Discussion:
pep8speaks: Hello @Illviljan! Thanks for updating this PR. We checked the lines you've touched for [PEP 8](https://www.python.org/dev/peps/pep-0008) issues, and found:



* In the file [`xarray/tests/test_interp.py`](https://github.com/pydata/xarray/blob/e6f35b130550f55f985e28efb7d8f4f92963ee12/xarray/tests/test_interp.py):

> [Line 885:1](https://github.com/pydata/xarray/blob/e6f35b130550f55f985e28efb7d8f4f92963ee12/xarray/tests/test_interp.py#L885): [E302](https://duckduckgo.com/?q=pep8%20E302) expected 2 blank lines, found 1


mathause: Ahh sorry - did not read the title good enough. So I guess the global and data_var attrs are already kept? Do you know if that is tested somewhere (no need to go look it up but if you happen to know).
Illviljan: Global and data_vars attrs are indeed already kept. I'm not sure if there are tests for those though. 
max-sixty: Looks good! Would you like to add a whatsnew entry @Illviljan ?
Illviljan: I've added a what's new entry. Was there any other actions here?
max-sixty: Sorry for the recent slow-down in reviews @Illviljan , and thank you for your recent PRs. I'm going to suggest we find a more robust system for ensuring PRs get prompt reviews in our next dev meeting.

Are people OK to merge this? While it brings up some broader issues, as long as this change doesn't cause those issues to be harder to resolve, this behavior seems good, as described by @dcherian :

> I think copying over `ds.coords["x"].attrs` for `ds.interp(x=[1,2,3])` is sensible.

(But I've been less involved in the details of `attrs`, so please tell me if this is rushing through narrower changes rather than addressing them more broadly)
max-sixty: Merging given previous approvals — we can handle any other concerns in a future PR

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 550b7c9b..6acb231b 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -121,6 +121,8 @@ Bug fixes
   By `Justus Magin <https://github.com/keewis>`_.
 - Decode values as signed if attribute `_Unsigned = "false"` (:issue:`4954`)
   By `Tobias Kölling <https://github.com/d70-t>`_.
+- Keep coords attributes when interpolating when the indexer is not a Variable. (:issue:`4239`, :issue:`4839` :pull:`5031`)
+  By `Jimmy Westling <https://github.com/illviljan>`_.
 - Ensure standard calendar dates encoded with a calendar attribute with some or
   all uppercase letters can be decoded or encoded to or from
   ``np.datetime64[ns]`` dates with or without ``cftime`` installed
diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py
index 3ae331da..ca8d8a28 100644
--- a/xarray/core/dataset.py
+++ b/xarray/core/dataset.py
@@ -2063,12 +2063,12 @@ class Dataset(DataWithCoords, DatasetArithmetic, Mapping):
                 else:
                     yield k, v
             elif isinstance(v, int):
-                yield k, Variable((), v)
+                yield k, Variable((), v, attrs=self.coords[k].attrs)
             elif isinstance(v, np.ndarray):
                 if v.ndim == 0:
-                    yield k, Variable((), v)
+                    yield k, Variable((), v, attrs=self.coords[k].attrs)
                 elif v.ndim == 1:
-                    yield k, IndexVariable((k,), v)
+                    yield k, IndexVariable((k,), v, attrs=self.coords[k].attrs)
                 else:
                     raise AssertionError()  # Already tested by _validate_indexers
             else:


Test patch:
diff --git a/xarray/tests/test_interp.py b/xarray/tests/test_interp.py
index b4e55948..97a6d236 100644
--- a/xarray/tests/test_interp.py
+++ b/xarray/tests/test_interp.py
@@ -881,3 +881,23 @@ def test_interp1d_bounds_error():
 
     # default is to fill with nans, so this should pass
     da.interp(time=3.5)
+
+
+@requires_scipy
+@pytest.mark.parametrize(
+    "x, expect_same_attrs",
+    [
+        (2.5, True),
+        (np.array([2.5, 5]), True),
+        (("x", np.array([0, 0.5, 1, 2]), dict(unit="s")), False),
+    ],
+)
+def test_coord_attrs(x, expect_same_attrs):
+    base_attrs = dict(foo="bar")
+    ds = xr.Dataset(
+        data_vars=dict(a=2 * np.arange(5)),
+        coords={"x": ("x", np.arange(5), base_attrs)},
+    )
+
+    has_same_attrs = ds.interp(x=x).x.attrs == base_attrs
+    assert expect_same_attrs == has_same_attrs