对任意数量的列采用 R tibble 中列的滚动差异

Taking rolling differences of columns in R tibble for arbitrary number of columns

我想对每对连续的列求差,但对任意数量的列求差。例如...

df <- as.tibble(data.frame(group = rep(c("a", "b", "c"), each = 4),
                       subgroup = rep(c("adam", "boy", "charles", "david"), times = 3),
                       iter1 = 1:12,
                       iter2 = c(13:22, NA, 24),
                       iter3 = c(25:35, NA)))

我想按列计算差异。我通常会使用...

df %>%
  mutate(diff_iter2 = iter2 - iter1,
         diff_iter3 = iter3 - iter2)

但是...我想:

  1. 容纳任意数量的列并且
  2. 这样对待 NA:
    如果我们减去的数字是 NA,那么结果应该是 NA。例如。 NA - 11 = NA
    如果我们要减去的数字是 NA,那么 NA 将被有效地视为 0。例如35 - 不适用 = 35

结果应该是这样的...

   group subgroup iter1 iter2 iter3 diff_iter2 diff_iter3
   <chr> <chr>    <int> <dbl> <int>      <dbl>      <dbl>
 1 a     adam         1    13    25         12         12
 2 a     boy          2    14    26         12         12
 3 a     charles      3    15    27         12         12
 4 a     david        4    16    28         12         12
 5 b     adam         5    17    29         12         12
 6 b     boy          6    18    30         12         12
 7 b     charles      7    19    31         12         12
 8 b     david        8    20    32         12         12
 9 c     adam         9    21    33         12         12
10 c     boy         10    22    34         12         12
11 c     charles     11    NA    35         NA         35
12 c     david       12    24    NA         12         NA

最初,这个 df 是长格式的,但问题是我相信 lag() 函数在组内的位置上运行,并且所有组都不相同,因为有些组缺少记录(因此 NA 在更宽 table 如上所示)。

从长格式开始就可以了,但请假设上面显示的带有 NA 值的记录不会存在于那个较长的数据帧中。

感谢任何帮助。

tidyverse 中的一个选项是 - 循环 across 'iter' 的列而不是 iter1,然后 get 通过替换列值列名 (cur_column()) 子串通过用 str_replace 减去 1 (as.numeric(x) -1),然后根据 OP 将 NA 元素替换为 0 (replace_na)逻辑,从循环列中减去并通过在 .names 中添加前缀来创建新列("diff_{.col}" - {.col} 将是原始列名称)

library(dplyr)
library(stringr)
library(tidyr)
df <- df %>% 
    mutate(across(iter2:iter3, ~
     . - replace_na(get(str_replace(cur_column(), '\d+', 
     function(x) as.numeric(x) - 1)), 0), .names = 'diff_{.col}'))

-输出

df
# A tibble: 12 × 7
   group subgroup iter1 iter2 iter3 diff_iter2 diff_iter3
   <chr> <chr>    <int> <dbl> <int>      <dbl>      <dbl>
 1 a     adam         1    13    25         12         12
 2 a     boy          2    14    26         12         12
 3 a     charles      3    15    27         12         12
 4 a     david        4    16    28         12         12
 5 b     adam         5    17    29         12         12
 6 b     boy          6    18    30         12         12
 7 b     charles      7    19    31         12         12
 8 b     david        8    20    32         12         12
 9 c     adam         9    21    33         12         12
10 c     boy         10    22    34         12         12
11 c     charles     11    NA    35         NA         35
12 c     david       12    24    NA         12         NA

找到名称以iter、ix开头的列,然后将除第一个以外的所有列作为df1,除最后一个以外的所有列作为df2,并将df2中的NA替换为0。然后将它们相减并cbind df。没有使用包。

ix <- grep("^iter", names(df))
df1 <- df[tail(ix, -1)]
df2 <- df[head(ix, -1)]
df2[is.na(df2)] <- 0
cbind(df, diff = df1 - df2)

给予:

   group subgroup iter1 iter2 iter3 diff.iter2 diff.iter3
1      a     adam     1    13    25         12         12
2      a      boy     2    14    26         12         12
3      a  charles     3    15    27         12         12
4      a    david     4    16    28         12         12
5      b     adam     5    17    29         12         12
6      b      boy     6    18    30         12         12
7      b  charles     7    19    31         12         12
8      b    david     8    20    32         12         12
9      c     adam     9    21    33         12         12
10     c      boy    10    22    34         12         12
11     c  charles    11    NA    35         NA         35
12     c    david    12    24    NA         12         NA