在R中使用for循环遍历数据框

6

我正在尝试使用R中的for循环函数进行解累计操作,因为公司提供的财务信息是针对不同概念进行累加的(这意味着1月份的信息仅属于1月份,2月份的信息是1月份和2月份的总和,3月份的信息是1月份、2月份和3月份的总和等)。

例如,假设我有下面这个数据框:

Concepts <- c("Concept1", "Concept2", "Concept3")
January <- c(5,10,16)
February <- c(9,14,20)
March <- c(16,20,23)

df <- data.frame(Concepts, January, February, March)

这将给我下一个数据框:
Concepts  January  February  March
Concept1    5         9        16 
Concept2    10        14       20
Concept3    16        20       23 

我需要实现的是下一个数据框(请注意,二月份是二月份和一月份之间的差异,而三月份是二月份和三月份之间的差异):
Concepts  January  February  March
Concept1    5         4        7 
Concept2    10        4        6
Concept3    16        4        3

为了得到第二个数据框,我首先创建了一个与df相同行数的空数据框,然后通过for循环将数据框的前两行(因为它们不需要任何处理)cbind在一起,并使用索引添加下一个经过计算的差异值的行。以下是代码示例:
df <- data.frame(Concepts, January, February, March)
df2 <- data.frame(matrix(nrow=nrow(df),ncol=ncol(df))) #Empty Dataframe with the same number  of rows

for(i in 1:ncol(df)) {
  if(i == 1){
    df2 <- cbind(df2, df[ , i])
  } else if (i == 2){
    df2 <- cbind(df2, df[, i])
  } else {
    diference <- df[,i] - df[,i-1]
    df2 <- cbind(df2,diference)
  }

我遇到了以下错误:

[.data.table(df, , i) 中出现错误: j (即[...]中的第二个参数) 是一个单一的符号,但是列名“i”不存在。也许你想使用 DT[, ..i]。这种与 data.frame 的差异是有意的,并在常见问题解答1.1中有解释。

我希望得到我的代码纠正或者其他替代方案,以便对多年的数据框进行计算。
4个回答

2
请注意,如果您对月份列应用基本函数diff,则会少一个列并且转置。
apply(df[-1], 1, diff)
#         [,1] [,2] [,3]
#February    4    4    4
#March       7    6    4

所以需要转置它以获得正确的方向。
t(apply(df[-1], 1, diff))
#     February March
#[1,]        4     7
#[2,]        4     6
#[3,]        4     4

将其与前两列进行cbind。由于第一个参数是数据框的子集,因此调用的方法是cbind.data.frame,结果也是数据框。

cbind(df[1:2], t(apply(df[-1], 1, diff)))
#  Concepts January February March
#1 Concept1       5        4     7
#2 Concept2      10        4     6
#3 Concept3      15        4     4

1

我的问题的解决方案比我想象的更简单:

for(i in 1:ncol(df)) {
  if(i == 1){
   df2 <- cbind(df2, df[ ,..i])
} else if (i == 2){
   df2 <- cbind(df2, df[,..i])
} else {
   diference <- df[,i] - df[,..i-1]
   df2 <- cbind(df2,diference)
}

感谢提供的所有替代方案!

1
如下评论所述,for循环速度较慢。此外,您发布的代码无法正常工作。将其应用于我在下面答案中创建的df时,会出现Error in [.data.frame(df, , ..i) : object '..i' not found错误。请至少更新您的答案,使其自包含、完整和正确,以便下一个遇到此问题的人可以受益。一旦您发布了一个可行的示例,我们还可以进行速度测试。 - Avraham

0
一个 tidyverse 的解决方案:
library(tidyverse)

df %>%
  pivot_longer(!Concepts) %>%
  group_by(Concepts) %>%
  mutate(value2 = value - lag(value, default = first(value))) %>%
  rowwise %>%
  mutate(value2 = ifelse(value2 == 0, value, value2)) %>%
  select(-value) %>%
  pivot_wider(names_from = "name", values_from = "value2")

输出

# A tibble: 3 × 4
  Concepts January February March
  <chr>      <dbl>    <dbl> <dbl>
1 Concept1       5        4     7
2 Concept2      10        4     6
3 Concept3      16        4     3

虽然我欣赏tidyverse在R语言领域的普及和一致性动词集的价值,但根据我的基准测试,与基础版本相比,tidyverse方法慢了65倍以上,并且需要至少两个额外的包(dplyr和tidyr)。因此,如果OP有一个巨大的数据集,使用基础版本可能更有效率。 - Avraham

0

这可能不是最优雅的方法,但它应该能够工作。诀窍在于提取数据框的数字部分,并按行应用diff函数,然后转置它,并将其粘贴回初始值。

df <- data.frame(Concepts = c("Concept1", "Concept2", "Concept3"),
                 January = c(5,10,16),
                 February = c(9,14,20),
                 March = c(16,20,23),
                 April = c(20, 27, 33))

dfdiff <- apply(df[, -1L], 1L, diff)
df2 <- data.frame(Concepts = c("Concept1", "Concept2", "Concept3"),
                  January = c(5,10,16))
df2 <- cbind(df2, t(dfdiff))                  
df2
  Concepts January February March April
1 Concept1       5        4     7     4
2 Concept2      10        4     6     7
3 Concept3      16        4     3    10

现在你知道它是如何工作的,为了更有效的调用,你可以这样做:

df2 <- cbind(df[, 1:2], t(apply(df[, -1L], 1L, diff)))

这应该适用于任何具有上述结构的数据框:一个标题列和其余为累积数据列。

与tidyverse方法的速度比较

microbenchmark(TV = df2 <- df %>% pivot_longer(!Concepts) %>% group_by(Concepts) %>%
                 dplyr::mutate(value2 = value - lag(value, default = first(value))) %>%
                 rowwise %>%mutate(value2 = ifelse(value2 == 0, value, value2)) %>%
                 select(-value) %>%pivot_wider(names_from = "name", values_from = "value2"),
               BASE = df2 <- cbind(df[, 1:2], t(apply(df[, -1L], 1L, diff))),
               times = 1000L, control = list(order = 'block'))

Unit: microseconds
 expr     min       lq       mean   median       uq     max neval cld
   TV 11141.7 11554.05 12245.1253 11803.75 12300.25 22903.5  1000   b
 BASE   160.4   164.35   176.8356   165.70   168.70  3833.2  1000  a 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接