为 Excel 替换 for-loops-like 公式填充 dataframe/matrix
Replacing for-loops for Excel-like formula filling in dataframe/matrix
我正在尝试在 R 中执行类似 excel 的基本公式填充。我想根据同一矩阵中其他单元格的值填充 "cell" 的值,或者data.frame。该函数对于单个单元格非常简单,但似乎更难以跨行和列缩放。
假设我有一个简单的矩阵:
simple <- matrix(c(0,1,2,3,0,4,5,6,7,NA,NA,NA,8,NA,NA,NA), nrow = 4, ncol = 4)
[,1] [,2] [,3] [,4]
[1,] 0 0 7 8
[2,] 1 4 NA NA
[3,] 2 5 NA NA
[4,] 3 6 NA NA
我想用同一行中第 1 列和第 2 列以及同一列中第 1 行的总和填充 NA。在 Excel 中,对于单元格 C2,它将是
=$A2 + $B2 + C
在 R
simple[2,3] <- simple[2,1] + simple[2,2] + simple[1,3]
在 Excel 中,您只需将公式拖到剩余的单元格上,瞧。在 R 中,没那么容易。
由于 r 是矢量化的,我可以通过给出范围而不是单个单元格来很容易地填充整列,如下所示:
simple[2:4,3] <- simple[2:4,1] + simple[2:4,2] + simple[1,3]
[,1] [,2] [,3] [,4]
[1,] 0 0 7 8
[2,] 1 4 12 NA
[3,] 2 5 14 NA
[4,] 3 6 16 NA
但是当我尝试对行和列进行矢量化时,它不起作用,因为它将最后一个值解释为矢量 c(7,8),并尝试以行方式添加它,而不是逐列添加。
simple[2:4,3:4] <- simple[2:4,1] + simple[2:4,2] + simple[1,3:4]
Warning message:
In simple[2:4, 1] + simple[2:4, 2] + simple[1, 3:4] :
longer object length is not a multiple of shorter object length
[,1] [,2] [,3] [,4]
[1,] 0 0 7 8
[2,] 1 4 12 12
[3,] 2 5 15 15
[4,] 3 6 16 16
作为另一种解决方案,可以使用嵌套 for 循环,如下所示:
for (i in 2:4){
for (j in 3:4){
simple[i,j] <- simple[i,1] + simple[i,2] + simple[1,j]
}
}
[,1] [,2] [,3] [,4]
[1,] 0 0 7 8
[2,] 1 4 12 13
[3,] 2 5 14 15
[4,] 3 6 16 17
这确实有效并且非常简单,但是它涉及嵌套的 for 循环,所以,说得够多了。
我觉得 "right" 解决方案应该是使用正确的向量化、apply() 或 dplyr 的解决方案,但我似乎无法弄清楚如何使它们工作,除非重新排列来自将交叉表格式转换为平面格式,但这会使您的文件大小迅速增加。
关于如何以更 R-ish 的方式使这项工作有任何想法吗?
这里有一个更像 R 的方法,让我们先将 simple
转换为 data.frame。
library(tidyverse)
df1 <- as.data.frame(simple)
df1 %>% mutate(V3 = V1 + V2 + first(V3), V4 = V1 + V2 + first(V4))
V1 V2 V3 V4
1 0 0 7 8
2 1 4 12 13
3 2 5 14 15
4 3 6 16 17
first
from dplyr
很方便,因为它可以让您锁定列中的第一个值,就像在 Excel 中使用 C
我可能来晚了,但这里有一个 data.table 和基础 R 解决方案,对于大型数据集,它比 tidyverse 快得多。语法一开始可能看起来更混乱,但是一旦你很好地掌握了 lapply
.
,就可以将其分解为非常合乎逻辑且直接的语法
为了使单元格和您添加的向量兼容,您应该将单元格转换为向量,方法是简单地复制该值的次数与数据帧的观察数或行数一样多。因此,在您的示例中,V3 = rep(7,4) 将生成一个全为 7 的向量。然后 R 会让你做 V3=V1+V2+V3
,其中右侧的 V3 是 rep(7,4)。
data.table
有一些方便的内置特殊只读符号,它们还使您能够将解决方案扩展到示例中提供的两列之外。我最常使用的两个是 .SD
和 .N
。在此示例中,您可以将 .SD
视为一种引用除前两列之外的所有列的方式,而 .N
始终是等于 data.table 中行数的常数。这些符号可以用在 data.table 的 j 槽中,相当于矩阵的列或 data.frame 对象。所以你的代码看起来像这样:
simple <- data.table(simple)
NAcols <- colnames(simple)[-c(1,2)] ##Can modify this to get names of columns you wish to change if its not the first two using match or grep. I can add that if you want?
simple[,NAcols:=lapply(.SD,function(i) V1+V2+rep(i[1],.N)),.SDcols=NAcols]
请注意,lapply 循环中的每次迭代只是第 i 列,i[1] 仅选择该列的第一个元素并将其复制与行数 (.N) 一样多的次数在将三个向量加在一起之前。 .SDcols 用于防止将此功能应用于前两列。虽然在这个问题中不需要分组,但如果你想在应用功能。最后请注意,我不需要将最后一行代码分配给另一个 R 对象,因为 data.table 使用指针更新 'simple' 的旧列,这就是为什么它比基本 R 和 tidyverse 数据快得多框架对象。但是,如果您出于某种原因希望保存原始 data.table,则可以像这样使用 data.table 的复制功能:
final_result <- copy(simple)[,NAcols:=lapply(.SD,function(i) V1+V2+rep(i[1],.N)),.SDcols=NAcols]
无论如何,我希望解释对您有所帮助,如果您需要我澄清任何事情,请告诉我!祝你好运!
在矩阵运算中,每个分量必须是相同的维度或者任何一个都是单项向量。因此,考虑通过为每个需要的行 2-4(即 3 次)复制 7 和 8 来对齐。然后转置2 X 3
维:
simple[2:4,3:4] <- simple[2:4,1] + simple[2:4,2] + t(replicate(length(2:4), simple[1,3:4]))
或者,考虑 sapply
分别迭代 7 和 8 值:
simple[2:4,3:4] <- sapply(3:4, function(i) simple[2:4,1] + simple[2:4,2] + simple[1,i])
使用 rowSums
并省略行索引稍微更简洁:
simple[,3:4] <- sapply(3:4, function(i) rowSums(simple[,1:2]) + simple[1,i])
我正在尝试在 R 中执行类似 excel 的基本公式填充。我想根据同一矩阵中其他单元格的值填充 "cell" 的值,或者data.frame。该函数对于单个单元格非常简单,但似乎更难以跨行和列缩放。
假设我有一个简单的矩阵:
simple <- matrix(c(0,1,2,3,0,4,5,6,7,NA,NA,NA,8,NA,NA,NA), nrow = 4, ncol = 4)
[,1] [,2] [,3] [,4]
[1,] 0 0 7 8
[2,] 1 4 NA NA
[3,] 2 5 NA NA
[4,] 3 6 NA NA
我想用同一行中第 1 列和第 2 列以及同一列中第 1 行的总和填充 NA。在 Excel 中,对于单元格 C2,它将是
=$A2 + $B2 + C
在 R
simple[2,3] <- simple[2,1] + simple[2,2] + simple[1,3]
在 Excel 中,您只需将公式拖到剩余的单元格上,瞧。在 R 中,没那么容易。
由于 r 是矢量化的,我可以通过给出范围而不是单个单元格来很容易地填充整列,如下所示:
simple[2:4,3] <- simple[2:4,1] + simple[2:4,2] + simple[1,3]
[,1] [,2] [,3] [,4]
[1,] 0 0 7 8
[2,] 1 4 12 NA
[3,] 2 5 14 NA
[4,] 3 6 16 NA
但是当我尝试对行和列进行矢量化时,它不起作用,因为它将最后一个值解释为矢量 c(7,8),并尝试以行方式添加它,而不是逐列添加。
simple[2:4,3:4] <- simple[2:4,1] + simple[2:4,2] + simple[1,3:4]
Warning message:
In simple[2:4, 1] + simple[2:4, 2] + simple[1, 3:4] :
longer object length is not a multiple of shorter object length
[,1] [,2] [,3] [,4]
[1,] 0 0 7 8
[2,] 1 4 12 12
[3,] 2 5 15 15
[4,] 3 6 16 16
作为另一种解决方案,可以使用嵌套 for 循环,如下所示:
for (i in 2:4){
for (j in 3:4){
simple[i,j] <- simple[i,1] + simple[i,2] + simple[1,j]
}
}
[,1] [,2] [,3] [,4]
[1,] 0 0 7 8
[2,] 1 4 12 13
[3,] 2 5 14 15
[4,] 3 6 16 17
这确实有效并且非常简单,但是它涉及嵌套的 for 循环,所以,说得够多了。
我觉得 "right" 解决方案应该是使用正确的向量化、apply() 或 dplyr 的解决方案,但我似乎无法弄清楚如何使它们工作,除非重新排列来自将交叉表格式转换为平面格式,但这会使您的文件大小迅速增加。
关于如何以更 R-ish 的方式使这项工作有任何想法吗?
这里有一个更像 R 的方法,让我们先将 simple
转换为 data.frame。
library(tidyverse)
df1 <- as.data.frame(simple)
df1 %>% mutate(V3 = V1 + V2 + first(V3), V4 = V1 + V2 + first(V4))
V1 V2 V3 V4
1 0 0 7 8
2 1 4 12 13
3 2 5 14 15
4 3 6 16 17
first
from dplyr
很方便,因为它可以让您锁定列中的第一个值,就像在 Excel 中使用 C
我可能来晚了,但这里有一个 data.table 和基础 R 解决方案,对于大型数据集,它比 tidyverse 快得多。语法一开始可能看起来更混乱,但是一旦你很好地掌握了 lapply
.
为了使单元格和您添加的向量兼容,您应该将单元格转换为向量,方法是简单地复制该值的次数与数据帧的观察数或行数一样多。因此,在您的示例中,V3 = rep(7,4) 将生成一个全为 7 的向量。然后 R 会让你做 V3=V1+V2+V3
,其中右侧的 V3 是 rep(7,4)。
data.table
有一些方便的内置特殊只读符号,它们还使您能够将解决方案扩展到示例中提供的两列之外。我最常使用的两个是 .SD
和 .N
。在此示例中,您可以将 .SD
视为一种引用除前两列之外的所有列的方式,而 .N
始终是等于 data.table 中行数的常数。这些符号可以用在 data.table 的 j 槽中,相当于矩阵的列或 data.frame 对象。所以你的代码看起来像这样:
simple <- data.table(simple)
NAcols <- colnames(simple)[-c(1,2)] ##Can modify this to get names of columns you wish to change if its not the first two using match or grep. I can add that if you want?
simple[,NAcols:=lapply(.SD,function(i) V1+V2+rep(i[1],.N)),.SDcols=NAcols]
请注意,lapply 循环中的每次迭代只是第 i 列,i[1] 仅选择该列的第一个元素并将其复制与行数 (.N) 一样多的次数在将三个向量加在一起之前。 .SDcols 用于防止将此功能应用于前两列。虽然在这个问题中不需要分组,但如果你想在应用功能。最后请注意,我不需要将最后一行代码分配给另一个 R 对象,因为 data.table 使用指针更新 'simple' 的旧列,这就是为什么它比基本 R 和 tidyverse 数据快得多框架对象。但是,如果您出于某种原因希望保存原始 data.table,则可以像这样使用 data.table 的复制功能:
final_result <- copy(simple)[,NAcols:=lapply(.SD,function(i) V1+V2+rep(i[1],.N)),.SDcols=NAcols]
无论如何,我希望解释对您有所帮助,如果您需要我澄清任何事情,请告诉我!祝你好运!
在矩阵运算中,每个分量必须是相同的维度或者任何一个都是单项向量。因此,考虑通过为每个需要的行 2-4(即 3 次)复制 7 和 8 来对齐。然后转置2 X 3
维:
simple[2:4,3:4] <- simple[2:4,1] + simple[2:4,2] + t(replicate(length(2:4), simple[1,3:4]))
或者,考虑 sapply
分别迭代 7 和 8 值:
simple[2:4,3:4] <- sapply(3:4, function(i) simple[2:4,1] + simple[2:4,2] + simple[1,i])
使用 rowSums
并省略行索引稍微更简洁:
simple[,3:4] <- sapply(3:4, function(i) rowSums(simple[,1:2]) + simple[1,i])