在R语言中使用asplit()按行和列拆分矩阵的问题
从矩阵中按名称提取非NA比较值的优化方法
问题场景
我们需要从名为matr的矩阵中,提取与指定名称(如A、B、C)对应的所有非NA比较值,初始实现如下:
### 构建示例矩阵 ### matr <- matrix(c(2,0,3,0,5,0.7,1,0,0.9,6,11,9,0,1,0.5,2,0,1,0.3,3,6,1,0.31,0,0), nrow = 5, ncol = 5) dimnames(matr) = list(c("A", "B", "A", "C", "A"), c("A", "B", "A", "C", "A")) matr # 模拟对称矩阵(实际场景中矩阵是对称的) matr[upper.tri(matr, diag = TRUE)] <- NA # 保留下三角 matr # 将行名与列名相同的位置设为NA for (rowLoopCounter in 1:nrow(matr)){ matr_work <- matr[rowLoopCounter,,drop=FALSE] for (colLoopCounter in 1:nrow(matr)) { if (row.names(matr)[rowLoopCounter] == colnames(matr)[colLoopCounter]){ matr[rowLoopCounter, colLoopCounter] <- NA } } } # 提取A对应的所有非NA值 A_row <- c(matr[grepl("A", row.names(matr)), ]) # 行中含A的元素 sA_col <- c(matr[, grepl("A", colnames(matr))]) # 列中含A的元素 total <- as.numeric(na.omit(unlist(c(A_row, sA_col)))) # 合并并去除NA total #[1] 0 6 3 0 0 1
现有方法的不足
上述实现不仅繁琐,且只能单独处理单个名称(如A),若要处理B和C还需重复编写代码。尝试用split()函数时结果异常,比如A对应的结果遗漏了值1(该值被错误归入C):
splt <- split(matr, colnames(matr)) # 用rownames(matr)结果一致 #$A #[1] NA NA NA NA 0 6 NA NA NA NA NA 3 NA NA NA #$B #[1] 0 NA NA NA NA #$C #[1] 0.0 0.9 1.0 NA NA
尝试使用asplit()时直接报错:
asplit(matr, c(1, 2)) #Error in array(newx[, i], d.call, dn.call) : 'dims' cannot be of length 0
我们期望得到按名称分组的命名列表,每个列表元素对应该名称的所有非NA比较值。
优化解决方案
方法一:Base R 实现
通过构建行名和列名的分组,结合lapply和na.omit批量处理所有名称:
# 获取所有唯一名称 names <- unique(c(rownames(matr), colnames(matr))) # 按名称分组提取非NA值 result <- lapply(names, function(x) { # 提取行名为x或列名为x的所有元素 elements <- matr[(rownames(matr) == x) | (colnames(matr) == x)] # 去除NA并转为数值型 as.numeric(na.omit(elements)) }) # 为列表命名 names(result) <- names # 查看结果 result #$A #[1] 0 6 3 0 0 1 #$B #[1] 0 0.7 1 #$C #[1] 0.9 0.5 0.3 0.31
方法二:tidyverse 实现
将矩阵转为长格式数据框后处理,逻辑更直观:
library(tidyverse) # 矩阵转为长格式数据框 matr_df <- as.data.frame.table(matr, responseName = "value") %>% rename(row = Var1, col = Var2) %>% filter(!is.na(value)) # 按名称分组,提取所有涉及该名称的value result_tidy <- matr_df %>% group_by(name = case_when(row %in% names ~ row, col %in% names ~ col)) %>% summarise(values = list(na.omit(value))) %>% deframe() # 查看结果 result_tidy #$A #[1] 0 6 3 0 0 1 #$B #[1] 0 0.7 1 #$C #[1] 0.9 0.5 0.3 0.31
原方法出错原因
split(matr, colnames(matr))是按列名对矩阵的列进行分组,仅会将列名为目标名称的列归入对应组,行名为目标名称但列名不符的元素不会被包含,因此遗漏部分值。asplit()的作用是将数组按指定维度拆分为列表,asplit(matr, c(1,2))尝试同时按行和列拆分,相当于把每个元素拆成单独数组,不符合需求所以报错。
内容的提问来源于stack exchange,提问作者compbiostats
相关产品推荐
相关产品推荐

