出现第一个非零值的列的按行名称

Rowwise name of column where first non-zero value appears

我有一堆列都以前缀 wtp_ 开头,它们出现在宽数据帧中间(wtp_ 列前后有几列)。迷你示例:

df <- tribble(~id, ~complete, ~wtp_20,~wtp_40,~wtp_60,~wtp_80,~wtp_100, ~sex,
          1, 1,  0,0,1,1,1,  "F",
          2, 0,  0,0,0,1,1,  "F",
          3, 0,  0,0,0,0,1,  "M",
          4, 1,  1,1,1,1,1,  "M",
          5, 1,  0,0,0,0,0,  "M",
          6, 0,  0,1,1,1,1,  "F"); df

我要查找的内容:我需要创建一个新变量 (min_wtp),returns 第一次 列的名称wtp_ 列之一从 0 切换到 1。换句话说,我需要一个解决方案来创建以下内容:

df_needed <- tribble(~id, ~complete, ~wtp_20,~wtp_40,~wtp_60,~wtp_80,~wtp_100, ~sex, ~min_wtp,
          1, 1,  0,0,1,1,1,  "F", "wtp_60",
          2, 0,  0,0,0,1,1,  "F", "wtp_80",
          3, 0,  0,0,0,0,1,  "M", "wtp_100",
          4, 1,  1,1,1,1,1,  "M", "wtp_20",
          5, 1,  0,0,0,0,0,  "M", "NA",
          6, 0,  0,1,1,1,1,  "F", "wtp_40"); df_needed

请注意以下并发症:

-有些人(比如 id==5)永远不会变成 1,而其他人(比如 id==4)一直都是 1。
- 在 wtp_ 列之前出现了一些不相关的列,其中包含 0 和 1,在 min_wtp.
的构造中应忽略这些列 - 列(包括 wtp_ 列)比我上面包含的最小示例多得多。

我试过将 whichcolnames 函数与 select(starts_with("wtp_")) 结合使用,但没有成功。

如果有人有 dplyr 解决方案,那将是首选。

我们可以使用 apply 为每一行获取满足您条件的第一列的编号。然后我们使用该数字作为索引来获取列名。

df$min_wtp = apply(df[ , grepl("wtp", names(df))], 1, function(x) {
  names(x)[min(which(x > 0))]
  })

df
     id complete wtp_20 wtp_40 wtp_60 wtp_80 wtp_100 sex   min_wtp
  <dbl>    <dbl>  <dbl>  <dbl>  <dbl>  <dbl>   <dbl> <chr> <chr>  
1     1        1      0      0      1      1       1 F     wtp_60 
2     2        0      0      0      0      1       1 F     wtp_80 
3     3        0      0      0      0      0       1 M     wtp_100
4     4        1      1      1      1      1       1 M     wtp_20 
5     5        1      0      0      0      0       0 M     NA     
6     6        0      0      1      1      1       1 F     wtp_40

如果你得到长格式的数据会容易得多:

library(dplyr)

df %>%
  tidyr::pivot_longer(cols = starts_with('wtp')) %>%
  group_by(id) %>%
  summarise(min_wtp = name[which(value == 1 & 
                           lag(value, default = 0) == 0)[1]]) %>%
  left_join(df, by = 'id')

# A tibble: 6 x 9
#     id min_wtp complete wtp_20 wtp_40 wtp_60 wtp_80 wtp_100 sex  
#  <dbl> <chr>      <dbl>  <dbl>  <dbl>  <dbl>  <dbl>   <dbl> <chr>
#1     1 wtp_60         1      0      0      1      1       1 F    
#2     2 wtp_80         0      0      0      0      1       1 F    
#3     3 wtp_100        0      0      0      0      0       1 M    
#4     4 wtp_20         1      1      1      1      1       1 M    
#5     5 NA             1      0      0      0      0       0 M    
#6     6 wtp_40         0      0      1      1      1       1 F   

无需重塑数据,您可以将 rowwisec_across 一起使用:

apply_fun <- function(x) {
    which(x == 1 & lag(x, default = 0) == 0)[1]
}

cols <- grep('^wtp', names(df), value = TRUE)

df %>%
  rowwise() %>%
  mutate(min_wtp = cols[apply_fun(c_across(cols))])

如果它永远不会从 1 倒退到 0,那么您可以通过一些基本的求和很快找到变化点:

sw  <- startsWith(names(df), "wtp_")
names(df[sw])[sum(sw) - rowSums(df[sw]) + 1]
#[1] "wtp_60"  "wtp_80"  "wtp_100" "wtp_20"  NA        "wtp_40"