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

时间:2018-08-22 06:51:32

标签: 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

我可以通过一系列参数的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

但是我觉得应该有一种更容易或更“整齐”的方式一次完成所有任务,尤其是当有5个以上的项目时。假设我们知道我们不会覆盖相同的单元格或任何东西(索引组合不会重复),因此被修改的单元格不会根据您所在的周期而改变。您可以将此问题称为“向量化分配”。

2 个答案:

答案 0 :(得分:5)

使用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

您可以详细了解<<-运算符here

数据

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

答案 1 :(得分:4)

这完全可以没有循环,无论是for还是*apply循环。
诀窍是使用索引矩阵。但是,由于这仅适用于类matrix的目标对象,因此将tibbledata.frame强制为matrix,然后强制返回。

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

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

identical(df, df1)
#[1] TRUE

数据创建代码。

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)