在R中从数据帧创建相关矩阵

6

我有一个相关性数据框,看起来像这样(尽管我的真实数据中有大约15,000行)

phen1<-c("A","B","C")
phen2<-c("B","C","A")
cors<-c(0.3,0.7,0.8)

data<-as.data.frame(cbind(phen1, phen2, cors))

    phen1  phen2   cors
1     A      B      0.3
2     B      C      0.7
3     C      A      0.8

这个数据框架是由外部创建并读入到R中的,我想将这个数据框架转换成一个相关矩阵,其中phen1和phen2作为该矩阵的行和列标签。我只计算了下三角或上三角的值,对角线的值为1。因此,我希望最终结果是一个完整的相关矩阵,但第一步可能是创建下/上三角矩阵,然后将其转换为完整矩阵。我不确定如何执行这两个步骤。

另外,结果可能不是直观的顺序,但我不确定这是否重要。理想情况下,我希望有一种方法可以使用phen1和phen2中的标签来确保矩阵中有正确的值放在正确的位置。

本质上,我希望得到类似于以下的结果:

  A    B    C
A 1    0.3  0.8
B 0.3  1    0.7
C 0.8  0.7  1

1
不要使用 cbind,它会将数字强制转换为字符字符串。只需使用 data <- data.frame(phen1, phen2, cors) - Roland
7个回答

4

这里是另一个基于R语言的方法,我们创建了一个与data相同的对称数据框,但是将phen1phen2列进行了倒置。然后我们使用xtabs函数获取相关系数矩阵,并将对角线设为1。

data1 <- data.frame(phen1 = data$phen2, phen2 = data$phen1, cors = data$cors)  
df <- rbind(data, data1)
df1 <- as.data.frame.matrix(xtabs(cors ~ ., df))
diag(df1) <- 1
df1

#    A   B   C
#A 1.0 0.3 0.8
#B 0.3 1.0 0.7
#C 0.8 0.7 1.0

数据

phen1<-c("A","B","C")
phen2<-c("B","C","A")
cors<-c(0.3,0.7,0.8)
data<- data.frame(phen1, phen2, cors)

1
啊,这就是我在寻找的:使用xtabs的解决方案。干得好+1。有趣的是,在OP的data定义中(其中data$cors是一个factor),xtabs无法工作并抛出错误。那就是我卡住的地方... - Maurits Evers

3

我认为有一种优雅的方式可以实现它,然而这里是一个 dplyrtidyr 的可能性:

data %>%
 spread(phen1, cors) %>%
 rename(phen = "phen2") %>%
 bind_rows(data %>%
            spread(phen2, cors) %>%
            rename(phen = "phen1")) %>%
 group_by(phen) %>%
 summarise_all(~ ifelse(all(is.na(.)), 1, first(na.omit(.))))

  phen      A     B     C
  <chr> <dbl> <dbl> <dbl>
1 A       1     0.3   0.8
2 B       0.3   1     0.7
3 C       0.8   0.7   1  

3
您可以使用Matrix包来实现这一功能。您拥有的是数据的稀疏表示,您希望将其转换为密集(冗余)矩阵。
data <- data.frame(phen1, phen2, cors)

inds <- cbind(as.integer(data$phen1), as.integer(data$phen2))
inds <- t(apply(inds, 1, sort))

library(Matrix)
res <- sparseMatrix(i = inds[,1], 
             j = inds[,2], 
             x = data$cors,
             symmetric = TRUE)
#3 x 3 sparse Matrix of class "dsCMatrix"
#
#[1,] .   0.3 0.8
#[2,] 0.3 .   0.7
#[3,] 0.8 0.7 . 

res <- as.matrix(res)
diag(res) <- 1
dimnames(res) <- list(sort(data$phen1), sort(data$phen2))
res
#    A   B   C
#A 1.0 0.3 0.8
#B 0.3 1.0 0.7
#C 0.8 0.7 1.0

1
为什么在创建inds部分中有as.integer()?这些列中的值都是字符串,因此该部分会引发关于强制转换引入NA的警告。 - user5481267
我假设它们是因子(如果你创建了一个标准的数据框并且没有设置" stringsAsFactors = FALSE",它们应该是因子)。 - Roland

1
这里有另一个选项。
首先,将数据从长格式转换为宽格式,并转换为矩阵。你有不同的选项可以实现(如:`reshape2`、`tidyr`等),这里我使用了 `tidyr::spread`。
library(tidyverse)
mat <- data %>% spread(phen2, cors) %>% column_to_rownames("phen1") %>% as.matrix()

我们接着从上三角矩阵和下三角矩阵中填充缺失的 NA 值,并将对角线填充为 1
mat[lower.tri(mat)] <- mapply(sum, mat[lower.tri(mat)], mat[upper.tri(mat)], na.rm = T)
mat[upper.tri(mat)] <- mat[lower.tri(mat)]
diag(mat) <- 1
mat
#    A   B   C
#A 1.0 0.3 0.8
#B 0.3 1.0 0.7
#C 0.8 0.7 1.0

0
这是我写的一个函数:
long2cormat <- function(xlong, x = "x", y = "y", r = "r") {
    # Takes some inspiration from https://dev59.com/67bna4cB1Zd3GeqPYEXN#57904948
    xlong <- xlong[,c(x, y, r)]
    names(xlong) <- c("x", "y", "r")
    
    data1 <- data.frame(x = xlong$x, y = xlong$y, r = xlong$r)    
    data2 <- data.frame(x = xlong$y, y = xlong$x, r = xlong$r)  
    df <- rbind(data1, data2)
    
    uv <- unique(c(df$x, df$y))
    df1 <- matrix(NA, nrow = length(uv), ncol = length(uv), dimnames = list(uv, uv))
    for (i in seq(nrow(df))) df1[df$x[i], df$y[i]] <- df$r[i]
    diag(df1) <- 1
    df1
}

要运行它,请执行以下操作:

xlong <- data.frame(phen1 = c("A","B","C"),
    phen2 = c("B","C","A"),
    cors = c(0.3,0.7,0.8))
long2cormat(xlong, "phen1", "phen2", "cors")

重要的是,对于我的使用情况,它将缺失的相关性作为NA。


通过省略循环并使用双列字符矩阵在LHS上进行索引,您可以获得更高的效率,以便将整个相关向量分配给:df1 [data.matrix(df [,-3])] <- df [,3] - IRTFM

0
你可以使用reshape库。
library(reshape)
data <- melt(data)
your_mat <- cast(data, phen1 ~ phen2 )

输出:

  phen1    A    B    C
1     A <NA>  0.3 <NA>
2     B <NA> <NA>  0.7
3     C  0.8 <NA> <NA>

你会出现NAs的原因是因为你的输入表格中有很多缺失的组合。为了避免这种情况,你需要像这样的输入表格:
  phen1 phen2 cors
1     A     B  0.3
2     B     C  0.7
3     C     A  0.8
4     A     C  0.8
5     B     A  0.3
6     C     B  0.7
7     A     A  1.0
8     B     B  1.0
9     C     C  1.0

0
已经有很多解决方案了,但我会提供另一种方法。注意:我正在设置数据,以便cors是数字,而不是你原始数据框中的因子。
data <- data.frame(phen1, phen2, cors)

然后我们可以使用缺失组合扩展数据框,然后使用reshape2::acast()将数据转换为宽格式。

library(tidyverse)
library(reshape2)

data %>% 
  select(phen1 = phen2, phen2 = phen1, cors) %>%
  bind_rows(data) %>%
  acast(phen1 ~ phen2, fill = 1)

acast 可以方便地使用其他指定的值来填充缺失值,本例中为 1。

此外,请查看 corrr 包,它可能能够更整洁地完成此操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接