我希望能够通过按行和列索引来替换数据框中的值,给定行索引,列名和值的列表.
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个项目.假设我们知道我们不会覆盖相同的单元格或任何东西(索引组合不会重复),因此被修改的单元格不会根据您所处的循环而改变.您可以将此问题称为"矢量化分配".
一种方法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)
可以完全不存在任何循环,无论是循环for还是*apply循环。
诀窍是使用索引矩阵。但是,因为这仅适用于class的目标对象matrix,所以将tibbleor 强制data.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
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)