使用 R 中的 tidyverse 按组完成整数序列

问题描述 投票:0回答:2

给定一个数据集,其中包含分组变量和一列不完整的整数(包含 NA),并且开始和结束整数因组而异,并且每组的长度也不同(并且可能是 NA)。如何通过完成序列来填写 NA 整数值。

可以使用以下数据集作为示例:

library(dplyr)
set.seed(5112021)
dat1 <- bind_rows(data.frame(Group=1,Seq=(3:20)),
                  data.frame(Group=2,Seq=(-1:25))) %>%
  mutate(rn = rnorm(45,mean=0.5,sd=1),
         Seq = ifelse(rn < 0.4,NA,Seq)) %>% 
  select(-rn) %>% 
  group_by(Group) %>% 
  mutate(Seq = ifelse(Seq==-1,NA,Seq))

dat1
   Group Seq
1      1  NA
2      1  NA
3      1  NA
4      1   6
5      1   7
6      1   8
7      1  NA
8      1  10
9      1  11
10     1  NA
11     1  13
12     1  NA
13     1  15
14     1  NA
15     1  NA
16     1  NA
17     1  NA
18     1  20
19     2  NA
20     2   0
21     2  NA
22     2   2
23     2   3
24     2  NA
25     2   5
26     2   6
27     2   7
28     2   8
29     2  NA
30     2  10
31     2  NA
32     2  12
33     2  NA
34     2  NA
35     2  NA
36     2  16
37     2  17
38     2  NA
39     2  NA
40     2  NA
41     2  NA
42     2  22
43     2  NA
44     2  NA
45     2  NA

实现此目的的一种方法是按组使用 row_numbers (因为它们是整数序列)并计算非缺失值和行号(这是唯一值)之间的差,然后添加该值返回行号。

例如

dat2 <- dat1 %>% 
  group_by(Group) %>% 
  mutate(rn = row_number(),
         diff = mean(Seq-rn,na.rm=T)) %>% 
  mutate(New_Seq = rn+diff) %>% 
  select(-rn,-diff)

dat2
   Group Seq New_Seq
1      1  NA       3
2      1  NA       4
3      1  NA       5
4      1   6       6
5      1   7       7
6      1   8       8
7      1  NA       9
8      1  10      10
9      1  11      11
10     1  NA      12
11     1  13      13
12     1  NA      14
13     1  15      15
14     1  NA      16
15     1  NA      17
16     1  NA      18
17     1  NA      19
18     1  20      20
19     2  NA      -1
20     2   0       0
21     2  NA       1
22     2   2       2
23     2   3       3
24     2  NA       4
25     2   5       5
26     2   6       6
27     2   7       7
28     2   8       8
29     2  NA       9
30     2  10      10
31     2  NA      11
32     2  12      12
33     2  NA      13
34     2  NA      14
35     2  NA      15
36     2  16      16
37     2  17      17
38     2  NA      18
39     2  NA      19
40     2  NA      20
41     2  NA      21
42     2  22      22
43     2  NA      23
44     2  NA      24
45     2  NA      25

虽然这有效,但它看起来不太优雅,并且对于具有许多分组变量的非常大的数据集可能会很慢。我很好奇是否有更“Tidyverse”的方式来做到这一点。

r dplyr tidyverse tidyr
2个回答
4
投票

你可以这样做:

df %>% 
  group_by(Group) %>%
  mutate(newseq  = seq(n()) - 1 + first(na.omit(Seq)) - sum(cumall(is.na(Seq)))) %>% 
  ungroup()

# OR

df %>% 
  group_by(Group) %>%
  mutate(newseq  = seq_along(Group) + (first(na.omit(Seq)) - sum(cumall(is.na(Seq)))) - 1) %>% 
  ungroup()

# OR 

df %>% 
  group_by(Group) %>%
  mutate(newseq  = seq(first(na.omit(Seq)) - sum(cumall(is.na(Seq))), length.out = n())) %>%
  ungroup()

所有这些都做同样的事情:将序列的开头移动第一个非 NA 值与其之前的 NA 数量的差值。

输出

   Group   Seq newseq
   <int> <int>  <dbl>
 1     1    NA      3
 2     1    NA      4
 3     1    NA      5
 4     1     6      6
 5     1     7      7
 6     1     8      8
 7     1    NA      9
 8     1    10     10
 9     1    11     11
10     1    NA     12
# ... with 35 more rows

2
投票

首先创建行号,然后取

max
Seq
row_number
差并添加到行号:

  dat1 %>%
    group_by(Group) %>%
    mutate(rn = row_number(),
           Seq = rn + max(Seq - rn, na.rm = TRUE)) %>% 
    ungroup() %>%
    select(-rn)

输出:

  Group   Seq
   <dbl> <int>
 1     1     3
 2     1     4
 3     1     5
 4     1     6
 5     1     7
 6     1     8
 7     1     9
 8     1    10
 9     1    11
10     1    12
11     1    13
12     1    14
13     1    15
14     1    16
15     1    17
16     1    18
17     1    19
18     1    20
19     2    -1
20     2     0
21     2     1
22     2     2
23     2     3
24     2     4
25     2     5
26     2     6
27     2     7
28     2     8
29     2     9
30     2    10
31     2    11
32     2    12
33     2    13
34     2    14
35     2    15
36     2    16
37     2    17
38     2    18
39     2    19
40     2    20
# … with 5 more rows

数据:

set.seed(5112021)
dat1 <- bind_rows(data.frame(Group=1,Seq=(3:20)),
                  data.frame(Group=2,Seq=(-1:25))) %>%
  mutate(rn = rnorm(45,mean=0.5,sd=1),
         Seq = ifelse(rn < 0.4,NA,Seq)) %>% 
  select(-rn) %>% 
  group_by(Group) %>% 
  mutate(Seq = ifelse(Seq==-1,NA,Seq))
© www.soinside.com 2019 - 2024. All rights reserved.