diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index aeaafbc4c125d..7a72547156779 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -131,7 +131,7 @@ Timezones Numeric ^^^^^^^ -- +- Bug in :meth:`Series.corr` and :meth:`Series.cov` raising ``AttributeError`` for masked dtypes (:issue:`51422`) - Conversion diff --git a/pandas/core/nanops.py b/pandas/core/nanops.py index 60c0d04ef28b0..43e44c7882cca 100644 --- a/pandas/core/nanops.py +++ b/pandas/core/nanops.py @@ -1610,6 +1610,9 @@ def nancorr( if len(a) < min_periods: return np.nan + a = _ensure_numeric(a) + b = _ensure_numeric(b) + f = get_corr_func(method) return f(a, b) @@ -1668,6 +1671,9 @@ def nancov( if len(a) < min_periods: return np.nan + a = _ensure_numeric(a) + b = _ensure_numeric(b) + return np.cov(a, b, ddof=ddof)[0, 1] diff --git a/pandas/core/series.py b/pandas/core/series.py index 894c33dd618c6..badde08c3820d 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2643,9 +2643,12 @@ def corr( if len(this) == 0: return np.nan + this_values = np.asarray(this._values) + other_values = np.asarray(other._values) + if method in ["pearson", "spearman", "kendall"] or callable(method): return nanops.nancorr( - this.values, other.values, method=method, min_periods=min_periods + this_values, other_values, method=method, min_periods=min_periods ) raise ValueError( @@ -2698,8 +2701,10 @@ def cov( this, other = self.align(other, join="inner", copy=False) if len(this) == 0: return np.nan + this_values = np.asarray(this._values) + other_values = np.asarray(other._values) return nanops.nancov( - this.values, other.values, min_periods=min_periods, ddof=ddof + this_values, other_values, min_periods=min_periods, ddof=ddof ) @doc( diff --git a/pandas/tests/frame/methods/test_cov_corr.py b/pandas/tests/frame/methods/test_cov_corr.py index c4f5b60918e84..250b07f8248ef 100644 --- a/pandas/tests/frame/methods/test_cov_corr.py +++ b/pandas/tests/frame/methods/test_cov_corr.py @@ -356,7 +356,7 @@ def test_corrwith_mixed_dtypes(self, numeric_only): else: with pytest.raises( TypeError, - match=r"unsupported operand type\(s\) for /: 'str' and 'int'", + match=r"Could not convert \['a' 'b' 'c' 'd'\] to numeric", ): df.corrwith(s, numeric_only=numeric_only) diff --git a/pandas/tests/series/methods/test_cov_corr.py b/pandas/tests/series/methods/test_cov_corr.py index be3483c773143..6ab255cfa3d25 100644 --- a/pandas/tests/series/methods/test_cov_corr.py +++ b/pandas/tests/series/methods/test_cov_corr.py @@ -42,13 +42,14 @@ def test_cov(self, datetime_series): assert isna(ts1.cov(ts2, min_periods=12)) @pytest.mark.parametrize("test_ddof", [None, 0, 1, 2, 3]) - def test_cov_ddof(self, test_ddof): + @pytest.mark.parametrize("dtype", ["float64", "Float64"]) + def test_cov_ddof(self, test_ddof, dtype): # GH#34611 np_array1 = np.random.rand(10) np_array2 = np.random.rand(10) - s1 = Series(np_array1) - s2 = Series(np_array2) + s1 = Series(np_array1, dtype=dtype) + s2 = Series(np_array2, dtype=dtype) result = s1.cov(s2, ddof=test_ddof) expected = np.cov(np_array1, np_array2, ddof=test_ddof)[0][1] @@ -57,9 +58,12 @@ def test_cov_ddof(self, test_ddof): class TestSeriesCorr: @td.skip_if_no_scipy - def test_corr(self, datetime_series): + @pytest.mark.parametrize("dtype", ["float64", "Float64"]) + def test_corr(self, datetime_series, dtype): from scipy import stats + datetime_series = datetime_series.astype(dtype) + # full overlap tm.assert_almost_equal(datetime_series.corr(datetime_series), 1)