根据多个条件选择单行

Choosing a single row based on multiple criteria

谁能想到如何在 R 中做到这一点?

简单数据:

    seq<-c("A","A","A","B","B","B","B")
    rank<-c(1,2,3,1,2,3,4)
    match<-c("y","n","y","n","n","y","y")
    df<- as.data.frame(cbind(seq,rank,match))

      seq rank match
    1   A    1     y
    2   A    2     n
    3   A    3     y
    4   B    1     n
    5   B    2     n
    6   B    3     y
    7   B    4     y

我想创建一个 ‘choose’ 列,其中,对于每个唯一的序列,matchy 的第一个实例被赋予 T,所有其余的被赋予 F

所需的输出为:

      seq rank match choose
    1   A    1     y      T
    2   A    2     n      F
    3   A    3     y      F
    4   B    1     n      F
    5   B    2     n      F
    6   B    3     y      T
    7   B    4     y      F

通过将 ifelse 语句与滞后相结合,很容易 return 排名 1 和 2 的正确值,但是一旦排名 >2,我就被难住了。

真正的 dataset 包含 +100k rows,排名可能会上升到数百,所以我不想只扩展 ifelse 语句来使用滞后检查以上值.

我的最终目标是从 "choose" 下的所有 T 中创建一个新的 dataset,所以如果有人知道如何直接将它们拉出而不创建新列,那就更好了!

我猜 ifelse 声明是一种愚蠢的做法,但我被卡住了:/

任何帮助将不胜感激:)

一个 dplyr 可能性是:

df %>%
 group_by(seq) %>%
 mutate(choose = +(match == "y") * (cumsum(match == "y") == 1))

  seq   rank  match choose
  <fct> <fct> <fct>  <int>
1 A     1     y          1
2 A     2     n          0
3 A     3     y          0
4 B     1     n          0
5 B     2     n          0
6 B     3     y          1
7 B     4     y          0

如果你想要 TRUE/FALSE 个值:

df %>%
 group_by(seq) %>%
 mutate(choose = as.logical(+(match == "y") * (cumsum(match == "y") == 1)))

  seq   rank  match choose
  <fct> <fct> <fct> <lgl> 
1 A     1     y     TRUE  
2 A     2     n     FALSE 
3 A     3     y     FALSE 
4 B     1     n     FALSE 
5 B     2     n     FALSE 
6 B     3     y     TRUE  
7 B     4     y     FALSE

base R相同:

with(df, ave(match, seq, FUN = function(x) +(x == "y") * (cumsum(x == "y") == 1)))

或:

with(df, ave(match, seq, FUN = function(x) as.logical(+(x == "y") * (cumsum(x == "y") == 1))))

您可以尝试类似的方法:

library(dplyr)

df %>% group_by(seq) %>% filter(choose =="T") %>% top_n(1) %>% mutate(choose = "T")

df[is.na(df)] <- "F

一个选项:

df %>%
  group_by(seq) %>%
  mutate(choose = row_number() %in% which(match == 'y')[1])

输出:

# A tibble: 7 x 4
# Groups:   seq [2]
  seq   rank  match choose
  <fct> <fct> <fct> <lgl> 
1 A     1     y     TRUE  
2 A     2     n     FALSE 
3 A     3     y     FALSE 
4 B     1     n     FALSE 
5 B     2     n     FALSE 
6 B     3     y     TRUE  
7 B     4     y     FALSE 

您可以将新列创建为逻辑向量,当 match == 'y' 并且该行是 (match, seq) 对的第一次出现(即 rowid(match, seq) == 1 时,它是 TRUE )

library(data.table)
setDT(df)

df[, choose := match == 'y' & rowid(match, seq) == 1]

df
#    seq rank match choose
# 1:   A    1     y   TRUE
# 2:   A    2     n  FALSE
# 3:   A    3     y  FALSE
# 4:   B    1     n  FALSE
# 5:   B    2     n  FALSE
# 6:   B    3     y   TRUE
# 7:   B    4     y  FALSE

或者直接对数据进行子集化而不创建新列

df[match == 'y' & rowid(match, seq) == 1]

#    seq rank match
# 1:   A    1     y
# 2:   B    3     y

一个选项是

library(dplyr)
df %>% 
   group_by(seq) %>% 
   mutate(choose = row_number() == match("y", match))
# A tibble: 7 x 4
# Groups:   seq [2]
#  seq   rank  match choose
#  <fct> <fct> <fct> <lgl>                              
#1 A     1     y     TRUE                               
#2 A     2     n     FALSE                              
#3 A     3     y     FALSE                              
#4 B     1     n     FALSE                              
#5 B     2     n     FALSE                              
#6 B     3     y     TRUE                               
#7 B     4     y     FALSE