【问题标题】:Rowwise name of column where first non-zero value appears出现第一个非零值的列的行名
【发布时间】:2020-07-22 23:23:56
【问题描述】:

我有一堆以前缀 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),它返回列的名称 第一次其中一个 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 解决方案,那将是首选。

【问题讨论】:

    标签: r dplyr names


    【解决方案1】:

    我们可以使用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
    

    【讨论】:

      【解决方案2】:

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

      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))])
      

      【讨论】:

      • @thiagoveloso 您确实已经有几个答案和一个接受的答案。他们不工作吗?
      【解决方案3】:

      如果它永远不会从 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"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        • 2017-12-28
        • 1970-01-01
        • 1970-01-01
        • 2021-01-04
        • 2015-03-26
        • 1970-01-01
        相关资源
        最近更新 更多