使用包含启动和结束索引的向量分配数据帧中的组

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

df <- data.frame(X = LETTERS[1:20], Y = paste0("abc_", 1:20)) # X Y #1 A abc_1 #2 B abc_2 #3 C abc_3 #4 D abc_4 #5 E abc_5 #6 F abc_6 # ...

我需要基于两个整数向量分配组ID。一个指示组从哪里开始,另一个指示组在哪里结束:

start_ix <- c(2, 5, 8, 10, 15, 18) end_ix <- c(4, 7, 9, 13, 17, 19)

I.E。第一组是第2至4行,第二组是第5至7行,依此类推。这些索引中未包含的任何行(或开始和停止值之间的跨度)应为
NA

期望的结果是:

df_want <- structure(list(X = c("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T"), Y = c("abc_1", "abc_2", "abc_3", "abc_4", "abc_5", "abc_6", "abc_7", "abc_8", "abc_9", "abc_10", "abc_11", "abc_12", "abc_13", "abc_14", "abc_15", "abc_16", "abc_17", "abc_18", "abc_19", "abc_20"), grp = c(NA, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 4, NA, 5, 5, 5, 6, 6, NA)), row.names = c(NA, -20L ), class = "data.frame") # X Y grp # 1 A abc_1 NA # 2 B abc_2 1 # 3 C abc_3 1 # 4 D abc_4 1 # 5 E abc_5 2 # 6 F abc_6 2 # 7 G abc_7 2 # 8 H abc_8 3 # 9 I abc_9 3 # 10 J abc_10 4 # 11 K abc_11 4 # 12 L abc_12 4 # 13 M abc_13 4 # 14 N abc_14 NA # 15 O abc_15 5 # 16 P abc_16 5 # 17 Q abc_17 5 # 18 R abc_18 6 # 19 S abc_19 6 # 20 T abc_20 NA

在我的具体情况下,解决方案需要在基本r
中完成,但是为了其他可能存在相同问题的人,可以随意从外部包装发布解决方案。

我尝试了索引,分类和seq的组合,但似乎无法提出解决方案。

a
base

使用
r dataframe
5个回答
3
投票
.

. <- 1 + end_ix - start_ix df[sequence(., start_ix), "grp"] <- rep(seq_along(.), .)

用另一个使用
Map

base
meth。
. <- Map(seq, start_ix, end_ix) df[unlist(.), "grp"] <- rep(seq_along(.), lengths(.))

基准测试

bench::mark(
jpsmith = local({seqV <- Vectorize(seq.default, vectorize.args = c("to", "from"))
  ix <- unlist(seqV(start_ix, end_ix))
  df[ix, "grp"] <- rep(1:length(start_ix), (end_ix - start_ix) + 1)
  df}),
zx8754 = local({for(i in seq_along(start_ix)){
  df[ start_ix[ i ]:end_ix[ i ], "grp"] <- i}
  df}),
GKiSeq = local({. <- 1 + end_ix - start_ix
  df[sequence(., start_ix), "grp"] <- rep(seq_along(.), .)
  df}),
GKiMap = local({. <- Map(seq, start_ix, end_ix)
  df[unlist(.), "grp"] <- rep(seq_along(.), lengths(.))
  df})
)
#  expression      min  median itr/s…¹ mem_a…² gc/se…³ n_itr  n_gc total…⁴ result
#  <bch:expr> <bch:tm> <bch:t>   <dbl> <bch:b>   <dbl> <int> <dbl> <bch:t> <list>
#1 jpsmith     186.1µs 198.4µs   4944.    280B    12.3  2408     6   487ms <df>  
#2 zx8754      212.4µs 226.6µs   4339.    280B    12.4  2095     6   483ms <df>  
#3 GKiSeq       60.6µs  67.1µs  14679.    280B    12.3  7160     6   488ms <df>  
#4 GKiMap        103µs 111.6µs   8748.    280B    12.3  4265     6   488ms <df>  

虽然可能有更好的解决方案,这是一个潜在的解决方案,该解决方案是
Vectorize

函数索引行,然后使用

seq
中的启动和终端位置差的向量来识别组:

1
投票

使用
for loop:
#Index
seqV <- Vectorize(seq.default, vectorize.args = c("to", "from"))
ix <- unlist(seqV(start_ix, end_ix))

#Assign groups
df[ix, "grp"] <- rep(1:length(start_ix), (end_ix - start_ix) + 1)

# Validate
all.equal(df_want, df)
# [1] TRUE
其他选项,使用

data.table::foverlaps

1
投票
for(i in seq_along(start_ix)){ df[ start_ix[ i ]:end_ix[ i ], "grp"] <- i }

a
library(data.table)

df1 <- cbind(data.table(start = seq(nrow(df)), end = seq(nrow(df))), df)
df2 <- data.table(start = start_ix, end = end_ix, grp = seq_along(start_ix))

setkey(df1, start, end)
setkey(df2, start, end)

foverlaps(df1, df2)[, .(X, Y, group)]
#     X      Y   grp
#  1: A  abc_1    NA
#  2: B  abc_2     1
#  3: C  abc_3     1
#  4: D  abc_4     1
#  5: E  abc_5     2
#  etc...
选项可能是:

tidyverse

使用索引,
ids_lst <- map2(start_ix, end_ix, seq)

df %>%
 mutate(grp = map_int(row_number(), 
                      function(rowid) match(TRUE, map2_lgl(rowid, ids_lst, function(rowid, ids) rowid %in% ids), nomatch = NA)))

   X      Y grp
1  A  abc_1  NA
2  B  abc_2   1
3  C  abc_3   1
4  D  abc_4   1
5  E  abc_5   2
6  F  abc_6   2
7  G  abc_7   2
8  H  abc_8   3
9  I  abc_9   3
10 J abc_10   4
11 K abc_11   4
12 L abc_12   4
13 M abc_13   4
14 N abc_14  NA
15 O abc_15   5
16 P abc_16   5
17 Q abc_17   5
18 R abc_18   6
19 S abc_19   6
20 T abc_20  NA

1
投票

rbind
从@gki添加基准:

df$grp <- rbind(NA, 1:nrow(df))[
  findInterval(
    1:nrow(df),
    c(
      rbind(
        start_ix,
        end_ix + 0.1
      )
    )
  ) + 1L
]

0
投票

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.