R语言中如何逐行获取仅含0和1的矩阵中多个1的列索引或列名?
嗨,我来帮你搞定这个需求!你需要从0-1矩阵里提取每行中1对应的列索引或列名,而且每行的1数量还不一样,对吧?这里有几种实用的R实现方案,都能完美适配你的场景:
方法1:用apply()逐行处理(直观易读)
这是最直接的方式,对矩阵的每一行应用自定义函数,筛选出值为1的列位置:
set.seed(444) m3 <- matrix(round(runif(8*8)), 8,8) # 提取每行1对应的列索引 row_ones_indices <- apply(m3, 1, function(x) which(x == 1)) # 如果需要列名,先给矩阵添加列名再提取 colnames(m3) <- paste0("col", 1:ncol(m3)) # 自定义列名示例 row_ones_names <- apply(m3, 1, function(x) colnames(m3)[which(x == 1)])
解释:apply(m3, 1, ...)表示按行遍历矩阵(第二个参数1代表行),which(x == 1)会返回当前行中值为1的元素的位置索引;如果矩阵有列名,直接用colnames(m3)[which(x == 1)]就能拿到对应的列名。最终返回的是一个列表,每个元素对应一行的结果,完全适配每行1数量不同的情况。
方法2:用tidyverse工具(适合数据框工作流)
如果你习惯用tidyverse的风格处理数据,可以把矩阵转成长格式数据框后再筛选分组:
library(tidyverse) set.seed(444) m3 <- matrix(round(runif(8*8)), 8,8) %>% as.data.frame() %>% mutate(row_id = row_number()) # 添加行号用于分组 # 提取列索引 row_ones_indices_tidy <- m3 %>% pivot_longer(-row_id, names_to = "col_name", values_to = "value") %>% filter(value == 1) %>% group_by(row_id) %>% summarise(col_indices = list(as.integer(str_remove(col_name, "V")))) # 提取默认列名中的数字 # 提取自定义列名的情况 colnames(m3) <- paste0("col", 1:ncol(m3)) row_ones_names_tidy <- m3 %>% pivot_longer(-row_id, names_to = "col_name", values_to = "value") %>% filter(value == 1) %>% group_by(row_id) %>% summarise(col_names = list(col_name))
解释:先把矩阵转成数据框并添加行号,再用pivot_longer把宽格式转成长格式,筛选出值为1的行后按行号分组汇总,把每行的列索引/列名存成列表,结果清晰易读。
方法3:向量化操作(适合大型矩阵,效率更高)
如果你的矩阵规模很大,这种向量化方法比逐行循环更高效:
set.seed(444) m3 <- matrix(round(runif(8*8)), 8,8) # 获取所有1的行列位置矩阵 ones_pos <- which(m3 == 1, arr.ind = TRUE) # 按行号分组,提取列索引 row_ones_indices_vec <- split(ones_pos[, "col"], ones_pos[, "row"]) # 提取列名的情况 colnames(m3) <- paste0("col", 1:ncol(m3)) row_ones_names_vec <- split(colnames(m3)[ones_pos[, "col"]], ones_pos[, "row"])
解释:which(m3 == 1, arr.ind = TRUE)会直接返回所有值为1的元素的行列位置矩阵,接着用split按行号分组,把对应的列索引/列名拆分到每个行的列表元素中,这种方法避免了逐行循环,处理大型矩阵时速度更快。
你可以根据自己的习惯和矩阵规模选择合适的方法,比如你提到的示例中第一行的2、3、8列是1,运行代码后row_ones_indices[[1]]会返回c(2, 3, 8),完全符合预期~
内容的提问来源于stack exchange,提问作者drexel star




