R语言如何用purrr实现嵌套列表中所有矩阵元素1减原值替换
问题描述
我有一个嵌套存储的列表结构,外层列表的每个子列表中包含8个矩阵,同一子列表内的矩阵维度一致,不同子列表的矩阵维度存在差异,以下是示例:
a <- matrix(c(1, 0.2, 0.3, 0.4, 1, 0.6, 0.7, 0.8, 1), 3, 3) b <- matrix(c(1, 0.2, 0.3, 0.4, 1, 0.6, 0.7, 0.8, 1), 3, 3, byrow = T) c <- matrix(c(1, 0.1, 0.15, 0.2, 0.25, 1, 0.35, 0.4, 0.45, 0.5, 1, 0.6, 0.65, 0.7, 0.75, 1), 4, 4) d <- matrix(c(1, 0.1, 0.15, 0.2, 0.25, 1, 0.35, 0.4, 0.45, 0.5, 1, 0.6, 0.65, 0.7, 0.75, 1), 4, 4, byrow = T) lst1 <- list(a = a, b = b) lst2 <- list(c = c, d = d) lst <- list(lst1 = lst1, lst2 = lst2) lst $lst1 $lst1$a [,1] [,2] [,3] [1,] 1.0 0.4 0.7 [2,] 0.2 1.0 0.8 [3,] 0.3 0.6 1.0 $lst1$b [,1] [,2] [,3] [1,] 1.0 0.2 0.3 [2,] 0.4 1.0 0.6 [3,] 0.7 0.8 1.0 $lst2 $lst2$c [,1] [,2] [,3] [,4] [1,] 1.00 0.25 0.45 0.65 [2,] 0.10 1.00 0.50 0.70 [3,] 0.15 0.35 1.00 0.75 [4,] 0.20 0.40 0.60 1.00 $lst2$d [,1] [,2] [,3] [,4] [1,] 1.00 0.1 0.15 0.2 [2,] 0.25 1.0 0.35 0.4 [3,] 0.45 0.5 1.00 0.6 [4,] 0.65 0.7 0.75 1.0
我需要将所有矩阵中的每一个元素都替换为1减去该元素的计算结果,预期输出如下:
$lst1 $lst1$a [,1] [,2] [,3] [1,] 0.0 0.6 0.3 [2,] 0.8 0.0 0.2 [3,] 0.7 0.4 0.0 $lst1$b [,1] [,2] [,3] [1,] 0.0 0.8 0.7 [2,] 0.6 0.0 0.4 [3,] 0.3 0.2 0.0 $lst2 $lst2$c [,1] [,2] [,3] [,4] [1,] 0.00 0.75 0.55 0.35 [2,] 0.90 0.00 0.50 0.30 [3,] 0.85 0.65 0.00 0.25 [4,] 0.80 0.60 0.40 0.00 $lst2$d [,1] [,2] [,3] [,4] [1,] 0.00 0.9 0.85 0.8 [2,] 0.75 0.0 0.65 0.6 [3,] 0.55 0.5 0.00 0.4 [4,] 0.35 0.3 0.25 0.0
我原本尝试用类似lst %>% modify(~. - 1)的反向写法lst %>% modify(~1 - .)实现,但没有成功。我发现直接给矩阵减某个值很容易,但反过来用固定值减矩阵所有元素很难实现。之前我手动创建对应维度的全1矩阵,用全1矩阵减去原矩阵再回填到列表中,示例代码如下:
e <- matrix(1, 3, 3) f <- matrix(1, 4, 4) a <- e-a b <- e-b c <- f-c d <- f-d lst1 <- list(a = a, b = b) lst2 <- list(c = c, d = d) lst <- list(lst1 = lst1, lst2 = lst2)
后续我想通过自动将矩阵所有值替换为1再减去原矩阵的方式实现,但一直没成功,希望能得到基于purrr的通用解决方案。
解决方案
你之前的写法报错是因为modify默认只操作列表的第一层元素,你的第一层是lst1、lst2两个子列表,直接对列表做1 - .运算自然不成立。R本身支持标量和矩阵的直接广播运算,不需要手动生成同维度全1矩阵,只要调整purrr的遍历深度即可。
方案1:指定遍历深度(最适合当前两层嵌套结构)
library(purrr) lst_new <- lst %>% # .depth=2 代表遍历到第二层元素(即所有矩阵) modify_depth(.depth = 2, .f = ~ 1 - .x)
方案2:嵌套modify写法(逻辑更直观)
lst_new <- lst %>% # 先遍历外层的每个子列表 modify( # 再遍历子列表内的每个矩阵做运算 ~ modify(.x, function(mat) 1 - mat) )
方案3:适配任意深度嵌套结构
如果你的嵌套层级不固定,用purrr 1.0.0及以上版本提供的modify_tree可以自动识别列表节点和矩阵叶子节点,不需要手动指定深度:
lst_new <- lst %>% modify_tree( .is_node = ~ is.list(.x), .leaf = ~ if(is.matrix(.x)) 1 - .x else .x )
以上三种方案输出的结果都和预期结果完全一致。
内容的提问来源于stack exchange,提问作者Tobias Richter
相关产品推荐
相关产品推荐

