diff --git a/docs/source/cov.rst b/docs/source/cov.rst index c06212f99..888ec9b0d 100644 --- a/docs/source/cov.rst +++ b/docs/source/cov.rst @@ -44,3 +44,11 @@ This package implements functions for computing scatter matrix, as well as weigh A weighting vector ``wv`` can be specified. ``vardim`` that designates whether the variables are columns in the matrix (``1``) or rows (``2``). Finally, bias correction is applied to the covariance calculation if ``corrected=true``. See ``cov`` documentation for more details. + +.. function:: cov2cor(C, s) + + Compute the correlation matrix from the covariance matrix ``C`` and a vector of standard deviations ``s``. Use ``Base.cov2cor!`` for an in-place version. + +.. function:: cor2cov(C, s) + + Compute the covariance matrix from the correlation matrix ``C`` and a vector of standard deviations ``s``. Use ``StatsBase.cor2cov!`` for an in-place version. diff --git a/src/StatsBase.jl b/src/StatsBase.jl index d3f5e739f..8d50aefd5 100644 --- a/src/StatsBase.jl +++ b/src/StatsBase.jl @@ -86,6 +86,8 @@ module StatsBase # cov scattermat, # scatter matrix (i.e. unnormalized covariance) + cov2cor, # converts a covariance matrix to a correlation matrix + cor2cov, # converts a correlation matrix to a covariance matrix ## counts addcounts!, # add counts to an accumulating array or map diff --git a/src/cov.jl b/src/cov.jl index 5df4b9dd5..07170eb1b 100644 --- a/src/cov.jl +++ b/src/cov.jl @@ -120,3 +120,34 @@ function mean_and_cov(x::DenseMatrix, wv::AbstractWeights, vardim::Int=1; m = mean(x, wv, vardim) return m, Base.cov(x, wv, vardim; corrected=depcheck(:mean_and_cov, corrected)) end + +""" + cov2cor(C, s) + +Compute the correlation matrix from the covariance matrix `C` and a vector of standard +deviations `s`. Use `Base.cov2cor!` for an in-place version. +""" +cov2cor(C::AbstractMatrix, s::AbstractArray) = Base.cov2cor!(copy(C), s) + +""" + cor2cov(C, s) + +Compute the covariance matrix from the correlation matrix `C` and a vector of standard +deviations `s`. Use `StatsBase.cor2cov!` for an in-place version. +""" +cor2cov(C::AbstractMatrix, s::AbstractArray) = cor2cov!(copy(C), s) + +""" + cor2cov!(C, s) + +Converts the correlation matrix `C` to a covariance matrix in-place using a vector of +standard deviations `s`. +""" +function cor2cov!(C::AbstractMatrix, s::AbstractArray) + n = length(s) + size(C) == (n, n) || throw(DimensionMismatch("inconsistent dimensions")) + for i in CartesianRange(size(C)) + @inbounds C[i] *= s[i[1]] * s[i[2]] + end + return C +end diff --git a/test/cov.jl b/test/cov.jl index c169ba708..f66e1044e 100644 --- a/test/cov.jl +++ b/test/cov.jl @@ -101,6 +101,29 @@ weight_funcs = (weights, aweights, fweights, pweights) @test m == mean(X, wv2, 2) @test C == cov(X, wv2, 2; corrected=false) end + @testset "Conversions" begin + std1 = std(X, wv1, 1; corrected=false) + std2 = std(X, wv2, 2; corrected=false) + + cov1 = cov(X, wv1, 1; corrected=false) + cov2 = cov(X, wv2, 2; corrected=false) + + cor1 = cor(X, wv1, 1) + cor2 = cor(X, wv2, 2) + + @testset "cov2cor" begin + @test cov2cor(cov(X, 1), std(X, 1)) ≈ cor(X, 1) + @test cov2cor(cov(X, 2), std(X, 2)) ≈ cor(X, 2) + @test cov2cor(cov1, std1) ≈ cor1 + @test cov2cor(cov2, std2) ≈ cor2 + end + @testset "cor2cov" begin + @test cor2cov(cor(X, 1), std(X, 1)) ≈ cov(X, 1) + @test cor2cov(cor(X, 2), std(X, 2)) ≈ cov(X, 2) + @test cor2cov(cor1, std1) ≈ cov1 + @test cor2cov(cor2, std2) ≈ cov2 + end + end end @testset "Corrected" begin @@ -153,6 +176,56 @@ weight_funcs = (weights, aweights, fweights, pweights) @test C == cov(X, wv2, 2; corrected=true) end end + @testset "Conversions" begin + if !isa(wv1, Weights) + std1 = std(X, wv1, 1; corrected=true) + std2 = std(X, wv2, 2; corrected=true) + + cov1 = cov(X, wv1, 1; corrected=true) + cov2 = cov(X, wv2, 2; corrected=true) + + cor1 = cor(X, wv1, 1) + cor2 = cor(X, wv2, 2) + + @testset "cov2cor" begin + @test cov2cor(cov(X, 1), std(X, 1)) ≈ cor(X, 1) + @test cov2cor(cov(X, 2), std(X, 2)) ≈ cor(X, 2) + @test cov2cor(cov1, std1) ≈ cor1 + @test cov2cor(cov2, std2) ≈ cor2 + end + + @testset "cov2cor!" begin + tmp_cov1 = copy(cov1) + @test !(tmp_cov1 ≈ cor1) + Base.cov2cor!(tmp_cov1, std1) + @test tmp_cov1 ≈ cor1 + + tmp_cov2 = copy(cov2) + @test !(tmp_cov2 ≈ cor2) + Base.cov2cor!(tmp_cov2, std2) + @test tmp_cov2 ≈ cor2 + end + + @testset "cor2cov" begin + @test cor2cov(cor(X, 1), std(X, 1)) ≈ cov(X, 1) + @test cor2cov(cor(X, 2), std(X, 2)) ≈ cov(X, 2) + @test cor2cov(cor1, std1) ≈ cov1 + @test cor2cov(cor2, std2) ≈ cov2 + end + + @testset "cor2cov!" begin + tmp_cor1 = copy(cor1) + @test !(tmp_cor1 ≈ cov1) + StatsBase.cor2cov!(tmp_cor1, std1) + @test tmp_cor1 ≈ cov1 + + tmp_cor2 = copy(cor2) + @test !(tmp_cor2 ≈ cov2) + StatsBase.cor2cov!(tmp_cor2, std2) + @test tmp_cor2 ≈ cov2 + end + end + end end @testset "Correlation" begin