R中的子子列表元素的向量化替换

ikfrs5lh  于 2023-03-10  发布在  其他
关注(0)|答案(3)|浏览(124)

我有一个有点复杂的数据结构,看起来像这样:

x <- list(
  list(1, "a", 2, "b", 0.1),
  list(3, "c", 4, "d", 0.2),
  list(5, "e", 6, "f", 0.3)
)

y <- rep(list(x), 10)

我有一个数据框,

df <- data.frame(
  x1 = c(0.33,1.67,-0.62,-0.56,0.17,0.73,0.59,0.56,-0.22,1.49),
  x2 = c(-0.82,1.22,0.65,0.54,-2.26,1.21,-0.44,-0.92,-0.56,0.50),
  x3 = c(-0.16,0.49,-0.82,-0.71,0.13,1.22,1.23,-0.01,-1.11,0.97)
)

(the列名不重要),并且基本上 Dataframe 的每一行(即,0.33、-0.82和-0.16)表示需要在y内的x的每个副本的每个子列表的最后位置中的值。
我的python/julia大脑在循环中工作得最好,所以我可以通过循环y,然后循环每个sub-sub列表来得到我想要的结果,如下所示:

for(i in seq_len(length(y))) {
  for(j in 1:3) {
    y[[i]][[j]][[5]] <- df[[i, j]]
  }
}

这是可行的。但是对于我拥有的大得多的数据来说,它真的很慢。所以我试着找出最快的矢量化版本。
我一直在尝试使用Map(),但肯定是搞砸了某处,错过了一个步骤,因为这:

y_new <- y
for (j in 1:3) {
  y_new <- Map(function(sublist, value) {sublist[[5]] <- value; sublist},
               y_new, df[, j])
}

不起作用(identical(y, y_new)返回FALSE,我想我在这里遗漏了一个子集级别。
我根本没有嫁给Map(),从字面上看,我正在寻找的只是嵌套for循环的 * 最快 * 替代品。
任何/所有的帮助都非常感谢!!

kiz8lqtg

kiz8lqtg1#

使用Map,我们可以

y_new <- Map(\(u, v) Map(\(uu, vv) {uu[5] <- vv; uu}, u, v), y, asplit(df, 1))
  • 使用OP代码进行测试
> for(i in seq_len(length(y))) {
+   for(j in 1:3) {
+     y[[i]][[j]][[5]] <- df[[i, j]]
+   }
+ }
> 
> all.equal(y_new, y)
[1] TRUE

作为一种矢量化方法,可以选择unlist并指定

v1 <- unlist(y)
v1[seq(5, length(v1), by = 5)] <- c(t(df))
y_new <- type.convert(relist(v1, skeleton = y), as.is = TRUE)
  • 检查
> for(i in seq_len(length(y))) {
+   for(j in 1:3) {
+     y[[i]][[j]][[5]] <- df[[i, j]]
+   }
+ }
> 
> all.equal(y, y_new)
[1] TRUE
suzh9iv8

suzh9iv82#

我们可以使用map2两次:
首先,我们使用asplit(df, 1)将 Dataframe df转换为一个向量列表,第一个map2()迭代y和行向量列表,第二个map2()y中每个嵌套列表的第5个元素替换为df中的相应值。
我们使用大括号{}和分号;将多个表达式组合为单个表达式。

library(purrr)

map2(y, asplit(df, 1), ~ map2(.x, .y, ~ { .x[[5]] <- .y; .x }))
9jyewag0

9jyewag03#

@akrun关于取消列表和重新列表的建议很优雅,也很符合习惯用法(“R式”)。
但我希望这样做时不要强制,特别是从数字到字符再到字符,这可能会很慢,并导致精度损失。像这样的操作会更快更安全:

unlist0 <- function(x) unlist(x, recursive = FALSE, use.names = FALSE)
split0 <- function(x, f) unname(split(x, f))

n <- length(y) # 10
n1 <- length(y[[1L]]) # 3
n11 <- length(y[[c(1L, 1L)]]) # 5

uy <- unlist0(unlist0(y))
uy[seq.int(n11, n * n1 * n11, n11)] <- as.list(t(df))
suy <- split0(split0(uy, gl(n * n1, n11)), gl(n, n1))

这里有一个基准:
x一个一个一个一个x一个一个二个x
请注意,只有第三和第四个答案与第一个答案相同(在identical的意义上)。第二个答案由于精度损失而不同。

相关问题