html_url,issue_url,id,node_id,user,created_at,updated_at,author_association,body,reactions,performed_via_github_app,issue
https://github.com/pydata/xarray/issues/7005#issuecomment-1240646680,https://api.github.com/repos/pydata/xarray/issues/7005,1240646680,IC_kwDOAMm_X85J8sQY,1797906,2022-09-08T12:24:41Z,2022-09-08T12:24:41Z,NONE,"Hi @benbovy,

Thanks for the detailed response.

Yeah, that it was only raising for the second multi indexing map, does seem like a bug in that case, I'll leave the ticket open to track that.

I didn't stumble on the `set_levels` function while skimming though the docs, thanks. I've updated my function to make use of that. I'm hoping things should be safe with this, and I'm correctly replacing things in the correct order.

For anyone else who's looking to do the same, or for anyone to tell me what I'm doing is not safe, or there's a simpler way, here's the updated function:

```python
import numpy as np
import pandas as pd
import xarray as xr


def map_coords(ds, *, name, mapping):
    """"""
    Takes a xarray dataset's coordinate values and updates
    them with the given the provided mapping. In-place.

    Can handle both regular indices and multi-level indices.

    ds: An xr.Datset
    name: name of the coordinate to update
    mapping: dictionary, key of old value, value of new value.
    """"""
    # all attrs seem to get dropped on coords even if only
    # one is altered. Hold on to them and reapply after
    coord_attrs = {c: ds[c].attrs for c in ds.coords}

    if ds.indexes.is_multi(name):
        target = name
        parent = ds.indexes[target].name

        if target == parent:
            valid_targets = ds.indexes[parent].names
            raise ValueError(
                f""Can only map levels of a MultiIndex, not the MultiIndex ""
                f""itself. Target one of {valid_targets}"",
            )

        multi_index = ds.indexes[parent]
        level_values = dict(zip(multi_index.names, multi_index.levels))
        new_values = [mapping[v] for v in level_values[name]]
        ds.coords[parent] = multi_index.set_levels(new_values, level=target)
    else:
        old_values = ds.coords[name].values
        new_values = [mapping[v] for v in old_values]
        ds[name] = new_values

    # reapply attrs
    for coord, attrs in coord_attrs.items():
        ds[coord].attrs = attrs


midx = pd.MultiIndex.from_product([list(""abc""), [0, 1]], names=(""x_one"", ""x_two""))
midy = pd.MultiIndex.from_product([list(""abc""), [0, 1]], names=(""y_one"", ""y_two""))
mda = xr.DataArray(np.random.rand(6, 6, 3), [(""x"", midx), (""y"", midy), (""z"", range(3))])

map_coords(mda, name=""z"", mapping={0: ""zero"", 1: ""one"", 2: ""two""})
map_coords(mda, name=""x_one"", mapping={""a"": ""aa"", ""b"": ""bb"", ""c"": ""cc""})
map_coords(mda, name=""y_one"", mapping={""a"": ""aa"", ""b"": ""bb"", ""c"": ""cc""})

print(mda)
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",,1364911775
https://github.com/pydata/xarray/issues/7005#issuecomment-1240532341,https://api.github.com/repos/pydata/xarray/issues/7005,1240532341,IC_kwDOAMm_X85J8QV1,4160723,2022-09-08T10:32:31Z,2022-09-08T10:32:31Z,MEMBER,"Hi @jamesstidard,

> causes a ValueError I am struggling to interpret.

I agree that the error message is not very informative. This could be improved.

The problem in your example is located here:

```python
def map_coords(ds, *, name, mapping):
    ...
    ds.coords[name] = xr.DataArray(new_values, coords=coord.coords)
    ...
```

Since v2022.6.0, multi-index level coordinates are real coordinates. What happens is that when you update ""x_one"" it invalidates the multi-index ""x"" - ""x_one"" - ""x_two"" in the DataArray:

```python
...
map_coords(mda, name=""z"", mapping={0: ""zero"", 1: ""one"", 2: ""two""}). # success
map_coords(mda, name=""x_one"", mapping={""a"": ""aa"", ""b"": ""bb"", ""c"": ""cc""})  # success

# the 'x', 'x_one' and 'x_two' indexes are all equal
mda.xindexes[""x_one""].equals(mda.xindexes[""x""]) and mda.xindexes[""x_one""].equals(mda.xindexes[""x_two""])
# True

# the 'x' and 'x_two' indexes are identical, but not the 'x_one' index!
mda.xindexes[""x""] is mda.xindexes[""x_two""]
# True
mda.xindexes[""x""] is mda.xindexes[""x_one""]
# False
```

Which explains the alignment error when you further try to update the DataArray.

I'm actually surprised that it doesn't raise any error at that point, like this:

```python
mda = xr.DataArray(np.random.rand(6, 6, 3), [(""x"", midx), (""y"", midy), (""z"", range(3))])
mda.coords[""x_two""] = (""x"", range(6))
# ValueError: cannot set or update variable(s) 'x_two', which would corrupt the following
# index built from coordinates 'x', 'x_one', 'x_two':
# <xarray.core.indexes.PandasMultiIndex object at 0x1689cc190>
```

It seems like a bug.

Either way, you shouldn't try updating a single coordinate that is part of a multi-coordinate index, which is assumed immutable. Instead, you could use `reset_index`/`set_index` before/after updating the labels of one level coordinate. You could also replace the whole index and its coordinates at once (example below). I wouldn't rely too much on the latter option, though, as such special case for `pd.MultiIndex` might eventually be deprecated in Xarray.

```python
new_midx = midx.set_levels([""aa"", ""bb"", ""cc""], level=""x_one"")
mda.coords[""x""] = new_midx
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",,1364911775