Repository: pydata/xarray

Issue:
`polyfit` with weights alters the DataArray in place
**What happened**:

After running `da.polyfit` on a DataArray with weights, the data has been overwritten

**What you expected to happen**:

I didn't see this documented anywhere, but I did not expect that creating a polyfit dataset would clobber the original data that I'm fitting to. The data isn't altered in the case of unweighted fitting, only weighted.

**Minimal Complete Verifiable Example**:

```python
In [2]: import xarray as xr; import numpy as np
In [3]: nz, ny, nx = (10, 20, 30)
In [4]: da = xr.DataArray(np.random.rand(nz, ny ,nz), dims=['z','y','x'])
In [6]: da.mean(), da.max()
Out[6]:
(<xarray.DataArray ()>
 array(0.4963857),
 <xarray.DataArray ()>
 array(0.99996494))

In [7]: pf = da.polyfit("z", deg=2) # This will not alter the data
In [9]: da.mean(), da.max()
Out[9]:
(<xarray.DataArray ()>
 array(0.4963857),
 <xarray.DataArray ()>
 array(0.99996494))

# Non-zero `w` argument alters the data
In [11]: pf = da.polyfit("z", deg=2, w=np.arange(nz))

In [12]: da.mean(), da.max()
Out[12]:
(<xarray.DataArray ()>
 array(2.24317611),
 <xarray.DataArray ()>
 array(8.95963569))

```

**Anything else we need to know?**:

I assume it's happening here
https://github.com/pydata/xarray/blob/da99a5664df4f5013c2f6b0e758394bec5e0bc80/xarray/core/dataset.py#L6805

My question is whether this is supposed to be the case to avoid copies? Or if it's accidental?


**Environment**:

<details><summary>Output of <tt>xr.show_versions()</tt></summary>

<!-- Paste the output here xr.show_versions() here -->
 xr.show_versions()

INSTALLED VERSIONS
------------------
commit: None
python: 3.8.6 | packaged by conda-forge | (default, Oct  7 2020, 18:42:56)
[Clang 10.0.1 ]
python-bits: 64
OS: Darwin
OS-release: 18.7.0
machine: x86_64
processor: i386
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: ('en_US', 'UTF-8')
libhdf5: 1.10.6
libnetcdf: 4.7.4

xarray: 0.19.0
pandas: 1.1.2
numpy: 1.20.2
scipy: 1.5.2
netCDF4: 1.5.4
pydap: None
h5netcdf: None
h5py: 3.2.1
Nio: None
zarr: 2.6.1
cftime: 1.2.1
nc_time_axis: None
PseudoNetCDF: None
rasterio: 1.2.1
cfgrib: None
iris: None
bottleneck: 1.3.2
dask: 2.14.0
distributed: 2.20.0
matplotlib: 3.3.0
cartopy: 0.18.0
seaborn: 0.10.1
numbagg: None
pint: 0.16.1
setuptools: 49.6.0.post20200814
pip: 21.1.2
conda: 4.8.4
pytest: 6.2.4
IPython: 7.18.1
sphinx: 3.5.1

</details>


Discussion:
malmans2: I don't think the small change in the `curvefit` doctest is caused by this PR.
I get the same error using main.

I've updated what's new in a separate commit, but looks like GH PRs are having difficulties at the moment.

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 99096267..5c0d3c3c 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -42,6 +42,8 @@ Performance
 
 Bug fixes
 ~~~~~~~~~
+- Fix bug where weighted ``polyfit`` were changing the original object (:issue:`5644`, :pull:`7900`).
+  By `Mattia Almansi <https://github.com/malmans2>`_.
 - Don't call ``CachingFileManager.__del__`` on interpreter shutdown (:issue:`7814`, :pull:`7880`).
   By `Justus Magin <https://github.com/keewis>`_.
 
diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py
index 81860bed..0f271e9d 100644
--- a/xarray/core/dataset.py
+++ b/xarray/core/dataset.py
@@ -7972,7 +7972,7 @@ class Dataset(
                 scale_da = scale
 
             if w is not None:
-                rhs *= w[:, np.newaxis]
+                rhs = rhs * w[:, np.newaxis]
 
             with warnings.catch_warnings():
                 if full:  # Copy np.polyfit behavior


Test patch:
diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py
index cc9220df..01c26ad6 100644
--- a/xarray/tests/test_dataset.py
+++ b/xarray/tests/test_dataset.py
@@ -6217,6 +6217,14 @@ class TestDataset:
         out = ds.polyfit("time", 2)
         assert len(out.data_vars) == 0
 
+    def test_polyfit_weighted(self) -> None:
+        # Make sure weighted polyfit does not change the original object (issue #5644)
+        ds = create_test_data(seed=1)
+        ds_copy = ds.copy(deep=True)
+
+        ds.polyfit("dim2", 2, w=np.arange(ds.sizes["dim2"]))
+        xr.testing.assert_identical(ds, ds_copy)
+
     def test_polyfit_warnings(self) -> None:
         ds = create_test_data(seed=1)