R语言 为非NA值的运行创建组编号

nue99wik  于 2023-03-27  发布在  其他
关注(0)|答案(4)|浏览(121)

我有以下 Dataframe df(dput如下):

> df
   id value
1   1     1
2   2     3
3   3     2
4  NA     1
5  NA     3
6   8     4
7   9     2
8  10     1
9  NA     1
10 NA     3
11 15     2
12 16     1
13 NA     3
14 NA     4
15 NA     2
16 20     1
17 21     1
18 22     3
19 NA     2
20 NA     1
21 NA     3
22 66     4
23 67     2
24 68     1

对于非NA“id”的每个连续运行,我想创建一个唯一的组ID(示例中的变量“id 2”)。对于中间的NA,“id 2”应该是NA
在示例中,第一次运行的非NA“id”(1、2和3)应全部属于组1(id 2 = 1)。第4-5行的NA应具有id 2 = NA。第二次运行的非NA“id”(8、9、10)应具有id 2 = 2,依此类推。
所需的输出应如下所示:

id value id2
1   1     1   1
2   2     3   1
3   3     2   1
4  NA     1  NA
5  NA     3  NA
6   8     4   2
7   9     2   2
8  10     1   2
9  NA     1  NA
10 NA     3  NA
11 15     2   3
12 16     1   3
13 NA     3  NA
14 NA     4  NA
15 NA     2  NA
16 20     1   4
17 21     1   4
18 22     3   4
19 NA     2  NA
20 NA     1  NA
21 NA     3  NA
22 66     4   5
23 67     2   5
24 68     1   5

正如你所看到的,id 2显示了我想要的NA之间的id值的唯一id。可能在值之后只有NA,比如id的1,2和3。所以我想知道是否有人知道如何为NA之间的值创建一个唯一的ID?
df的dput

df <- structure(list(id = c(1, 2, 3, NA, NA, 8, 9, 10, NA, NA, 15, 
16, NA, NA, NA, 20, 21, 22, NA, NA, NA, 66, 67, 68), value = c(1, 
3, 2, 1, 3, 4, 2, 1, 1, 3, 2, 1, 3, 4, 2, 1, 1, 3, 2, 1, 3, 4, 
2, 1)), class = "data.frame", row.names = c(NA, -24L))
zlwx9yxi

zlwx9yxi1#

这里有一个可能性,也许不是最有效的:
1.找到有效的idNA之间的转换。每次我们有一个id,它是NA,但前一个是有效的。
1.累计
1.根据需要添加NA

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
df <- structure(list(id = c(1, 2, 3, NA, NA, 8, 9, 10, NA, NA, 15, 
                            16, NA, NA, NA, 20, 21, 22, NA, NA, NA, 66, 67, 68), value = c(1, 
                                                                                           3, 2, 1, 3, 4, 2, 1, 1, 3, 2, 1, 3, 4, 2, 1, 1, 3, 2, 1, 3, 4, 
                                                                                           2, 1)), class = "data.frame", row.names = c(NA, -24L))

df |> 
  mutate(
    id2 = as.numeric(is.na(id) & !is.na(lag(id))),
    id2 = cumsum(id2) + 1,
    id2 = ifelse(is.na(id), NA, id2)
  )
#>    id value id2
#> 1   1     1   1
#> 2   2     3   1
#> 3   3     2   1
#> 4  NA     1  NA
#> 5  NA     3  NA
#> 6   8     4   2
#> 7   9     2   2
#> 8  10     1   2
#> 9  NA     1  NA
#> 10 NA     3  NA
#> 11 15     2   3
#> 12 16     1   3
#> 13 NA     3  NA
#> 14 NA     4  NA
#> 15 NA     2  NA
#> 16 20     1   4
#> 17 21     1   4
#> 18 22     3   4
#> 19 NA     2  NA
#> 20 NA     1  NA
#> 21 NA     3  NA
#> 22 66     4   5
#> 23 67     2   5
#> 24 68     1   5

创建于2023-03-21带有reprex v2.0.2

qjp7pelc

qjp7pelc2#

使用rle + cumsum + replace的基本R选项

transform(
  df,
  id2 = replace(with(
    rle(!is.na(id)),
    rep(cumsum(values), lengths)
  ), is.na(id), NA)
)

或者一个更简洁的with(rle(!is.na(id)), rep(cumsum(values) * NA^!values, lengths))(感谢Henrik的评论),它可以给予

id value id2
1   1     1   1
2   2     3   1
3   3     2   1
4  NA     1  NA
5  NA     3  NA
6   8     4   2
7   9     2   2
8  10     1   2
9  NA     1  NA
10 NA     3  NA
11 15     2   3
12 16     1   3
13 NA     3  NA
14 NA     4  NA
15 NA     2  NA
16 20     1   4
17 21     1   4
18 22     3   4
19 NA     2  NA
20 NA     1  NA
21 NA     3  NA
22 66     4   5
23 67     2   5
24 68     1   5
xuo3flqw

xuo3flqw3#

使用data.table

library(data.table)
setDT(df)[, grp := rleid(!is.na(id))][!is.na(id), id2 := .GRP, grp][, grp := NULL]
  • 输出
> df
    id value id2
 1:  1     1   1
 2:  2     3   1
 3:  3     2   1
 4: NA     1  NA
 5: NA     3  NA
 6:  8     4   2
 7:  9     2   2
 8: 10     1   2
 9: NA     1  NA
10: NA     3  NA
11: 15     2   3
12: 16     1   3
13: NA     3  NA
14: NA     4  NA
15: NA     2  NA
16: 20     1   4
17: 21     1   4
18: 22     3   4
19: NA     2  NA
20: NA     1  NA
21: NA     3  NA
22: 66     4   5
23: 67     2   5
24: 68     1   5

或在base R

inverse.rle(within.list( rle(!is.na(df$id)), {
 values[values] <- seq_along(values[values])
  values[!values] <- NA}))
igetnqfo

igetnqfo4#

在一个不优雅的(主要是)base R方法中,你可以在NA值上使用split,然后将do.call(rbind,...)放在一起,然后清理组赋值。“主要是”base R,因为我使用data.table::rleid()作为最终的组名:

# Assign group values
df$id2 <- NA
ll <- split(df, cumsum(is.na(df$id)))
ll2 <- lapply(seq_along(ll), function(x) {ll[[x]]$id2 <- x; ll[[x]]})

# Combine and clean up
comb <- do.call(rbind, ll2)
comb[is.na(comb$id),"id2"] <- NA
comb$id2[!is.na(comb$id2)] <- data.table::rleid(comb$id2[!is.na(comb$id2)])

输出:

id value id2
1   1     1   1
2   2     3   1
3   3     2   1
4  NA     1  NA
5  NA     3  NA
6   8     4   2
7   9     2   2
8  10     1   2
9  NA     1  NA
10 NA     3  NA
11 15     2   3
12 16     1   3
13 NA     3  NA
14 NA     4  NA
15 NA     2  NA
16 20     1   4
17 21     1   4
18 22     3   4
19 NA     2  NA
20 NA     1  NA
21 NA     3  NA
22 66     4   5
23 67     2   5
24 68     1   5

相关问题