首页 > 解决方案 > l/sapply 忽略条件替换

问题描述

我有一个有点复杂的数据结构,有一个列表列表——每个列表都有不同长度的数字向量。我需要根据与主列表长度相同的单独向量,用 NA 替换向量中的一些值。但是,R 不是仅替换特定值,而是替换整个向量。贝娄,一个例子:

> ## Minimal example with a list of vectors
> # Creating list
> mylist = rep(list(1:3), 3)
> 
> # Vector on which I will base the replacement
> myvec = 1:3
> 
> # Replacing 1s by NA
> for(i in 1:length(mylist)){
+   mylist[[i]] = sapply(mylist[[i]], function(x) x[x ==myvec[i]] = NA)
+ }
> 
> # But I get NAs for every observation
> mylist
[[1]]
[1] NA NA NA

[[2]]
[1] NA NA NA

[[3]]
[1] NA NA NA

> 
> # This is what I wanted:
> list(c(NA, 2, 3), c(1, NA, 3), c(1, 2, NA))
[[1]]
[1] NA  2  3

[[2]]
[1]  1 NA  3

[[3]]
[1]  1  2 NA

> 
> 
> ## More complex example, with a list of lists of vectors that more closely approximates my data structure
> # Creating list of lists
> mynewlist = rep(list(rep(list(1:3), 3)), 3)
> 
> # Replacing 1s by NAs
> for(i in 1:length(mynewlist)){
+   mynewlist[[i]] = lapply(mynewlist[[i]], function(x) x[x ==myvec[i]] = NA)
+ }
> 
> # But now each vector becomes a single NA
> mynewlist
[[1]]
[[1]][[1]]
[1] NA

[[1]][[2]]
[1] NA

[[1]][[3]]
[1] NA


[[2]]
[[2]][[1]]
[1] NA

[[2]][[2]]
[1] NA

[[2]][[3]]
[1] NA


[[3]]
[[3]][[1]]
[1] NA

[[3]][[2]]
[1] NA

[[3]][[3]]
[1] NA


> 
> # What I wanted:
> list(rep(list(c(NA, 2, 3)), 3), rep(list(c(1, NA, 3)), 3), rep(list(c(1, 2, NA)), 3))
[[1]]
[[1]][[1]]
[1] NA  2  3

[[1]][[2]]
[1] NA  2  3

[[1]][[3]]
[1] NA  2  3


[[2]]
[[2]][[1]]
[1]  1 NA  3

[[2]][[2]]
[1]  1 NA  3

[[2]][[3]]
[1]  1 NA  3


[[3]]
[[3]][[1]]
[1]  1  2 NA

[[3]][[2]]
[1]  1  2 NA

[[3]][[3]]
[1]  1  2 NA

请注意,无论替换的具体值如何,都会发生同样的情况(例如,如果我将替换更改为 0,而不是 NA,那么无论条件如何,0 都会替换所有值)。

这是怎么回事?为什么 lapply/sapply 忽略条件?

顺便说一句,我欢迎在没有任何 for 循环的情况下执行此操作的建议。

编辑:除了编辑上面的代码以明确我将替换基于一个单独的向量(因此是 for 循环)之外,我还能够使用 ifelse 获得我想要的东西。不过,我仍然不明白,为什么 l/sapply 不能识别括号的选择和替换。我很感激对此的任何解释,以及如何在没有循环的情况下做到这一点。

带有 ifelse 的代码:

> # Creating list of lists
> mynewestlist = rep(list(rep(list(1:3), 3)), 3)
> 
> # Replacing 1s by NAs
> for(i in 1:length(mynewestlist)){
+   mynewestlist[[i]] = lapply(mynewestlist[[i]], function(x) ifelse(x ==myvec[i], NA, x))
+ }
> 
> # That's better:
> mynewestlist
[[1]]
[[1]][[1]]
[1] NA  2  3

[[1]][[2]]
[1] NA  2  3

[[1]][[3]]
[1] NA  2  3


[[2]]
[[2]][[1]]
[1]  1 NA  3

[[2]][[2]]
[1]  1 NA  3

[[2]][[3]]
[1]  1 NA  3


[[3]]
[[3]][[1]]
[1]  1  2 NA

[[3]][[2]]
[1]  1  2 NA

[[3]][[3]]
[1]  1  2 NA


> list(rep(list(c(NA, 2, 3), 3), rep(list(1, NA, 3), 3), rep(list(1, 2, NA), 3))

标签: rlistloopsreplacelapply

解决方案


无需sapply在元素的每个元素上运行,list因为我们可以以矢量化形式执行此操作。

lapply(mylist, function(x) replace(x, x <=1, NA))

或带有for循环

for(i in seq_along(mylist)) mylist[[i]] <- replace(mylist[[i]], 
                          mylist[[i]] <=1, NA)

请注意,OP 更改了条件,即创建一个与“mylist”长度相同的向量(“myvec”),并希望将那些与“mylist”的相应元素匹配的值替换为 NA。它可以通过很多方式完成。一种选择是Map遍历 和 的每个元素list以及vectorreplaceNA 匹配的值。

Map(function(x, y) replace(x, x == y, NA), mylist, myvec)
#[[1]]
#[1] NA  2  3

#[[2]]
#[1]  1 NA  3

#[[3]]
#[1]  1  2 NA

或使用for循环

for(i in seq_along(mylist)) mylist[[i]] <- replace(mylist[[i]], 
                      mylist[[i]] == myvec[i], NA)

或者lapply通过遍历序列来使用

lapply(seq_along(mylist) function(i) 
          replace(mylist[[i]], mylist[[i]] == myvec[i], NA))

关于为什么 OP 只获得 NA,在 中sapply,返回值是对对象的赋值,NA而不是对象本身。检查lapply/sapply这里的输出

lapply(mylist[[1]], function(x) x[x==myvec[1]] <- NA)
#[[1]]
#[1] NA

#[[2]]
#[1] NA

#[[3]]
#[1] NA

它是赋值,而不是“x”。返回 '​​x' 以获得 'x' 的输出(正如@user20650 评论的那样)

lapply(mylist[[1]], function(x) {x[x==myvec[1]] <- NA; x})
#[[1]]
#[1] NA

#[[2]]
#[1] 2

#[[3]]
#[1] 3

推荐阅读