通过行列索引替换数据框中的值时,如何避免循环?

Cal*_*You 4 r

我希望能够通过按行和列索引来替换数据框中的值,给定行索引,列名和值的列表.

library(tidyverse)
cols <- sample(letters[1:10], 5)
vals <- sample.int(5)
rows <- sample.int(5)
df <- matrix(rep(0L, times = 50), ncol = 10) %>%
  `colnames<-`(letters[1:10]) %>%
  as_tibble
Run Code Online (Sandbox Code Playgroud)

我可以通过参数列表上的for循环执行此操作:

items <- list(cols, vals, rows) %>%
  pmap(~ list(..3, ..1, ..2))

for (i in items){
  df[i[[1]], i[[2]]] <- i[[3]]
}
df
#> # A tibble: 5 x 10
#>       a     b     c     d     e     f     g     h     i     j
#>   <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
#> 1     0     0     0     0     0     0     0     1     0     0
#> 2     0     0     5     0     0     0     0     0     0     0
#> 3     0     0     0     0     0     0     4     0     0     0
#> 4     0     0     0     0     0     0     0     0     3     0
#> 5     0     0     0     0     0     0     0     0     0     2
Run Code Online (Sandbox Code Playgroud)

但我觉得应该有一个更简单或"更整洁"的方式来同时完成所有作业,特别是如果有超过5个项目.假设我们知道我们不会覆盖相同的单元格或任何东西(索引组合不会重复),因此被修改的单元格不会根据您所处的循环而改变.您可以将此问题称为"矢量化分配".

Ron*_*hah 6

一种方法mapply是:

mapply(function(x, y, z) df[x, y] <<- z, rows, cols, vals)
df

#      a     b     c     d     e     f     g     h     i     j
#  <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1     0     0     0     0     5     0     0     0     0     0
#2     0     0     0     0     0     0     0     2     0     0
#3     0     0     0     0     0     1     0     0     0     0
#4     0     4     0     0     0     0     0     0     0     0
#5     0     0     0     0     0     0     0     0     3     0
Run Code Online (Sandbox Code Playgroud)

你可以阅读更多关于<<-运营商这里.

数据

set.seed(1234)
cols <- sample(letters[1:10], 5)
vals <- sample.int(5)
rows <- sample.int(5)
Run Code Online (Sandbox Code Playgroud)


Rui*_*das 6

可以完全不存在任何循环,无论是循环for还是*apply循环。
诀窍是使用索引矩阵。但是,因为这仅适用于class的目标对象matrix,所以将tibbleor 强制data.framematrix,然后强制返回。

我将重复使用@Ronak的解决方案添加数据创建代码,以使代码自成一体。

inx <- cbind(rows, match(cols, names(df1)))
df1 <- as.matrix(df1)
df1[inx] <- vals
df1 <- as.tibble(df1)

identical(df, df1)
#[1] TRUE
Run Code Online (Sandbox Code Playgroud)

数据创建代码。

set.seed(1234)
cols <- sample(letters[1:10], 5)
vals <- sample.int(5)
rows <- sample.int(5)
df <- matrix(rep(0L, times = 50), ncol = 10) %>%
  `colnames<-`(letters[1:10]) %>%
  as_tibble

df1 <- df
mapply(function(x, y, z) df[x, y] <<- z, rows, cols, vals)
Run Code Online (Sandbox Code Playgroud)