代码之家  ›  专栏  ›  技术社区  ›  iago

如何有效地从其他索引变量的条件中创建变量?

  •  0
  • iago  · 技术社区  · 6 年前

    让我们看一个例子。为此,我有两个观察重复了4次:

    > data(anscombe)
    > anscombe
       x1 x2 x3 x4    y1   y2    y3    y4
    1  10 10 10  8  8.04 9.14  7.46  6.58
    2   8  8  8  8  6.95 8.14  6.77  5.76
    3  13 13 13  8  7.58 8.74 12.74  7.71
    4   9  9  9  8  8.81 8.77  7.11  8.84
    5  11 11 11  8  8.33 9.26  7.81  8.47
    6  14 14 14  8  9.96 8.10  8.84  7.04
    7   6  6  6  8  7.24 6.13  6.08  5.25
    8   4  4  4 19  4.26 3.10  5.39 12.50
    9  12 12 12  8 10.84 9.13  8.15  5.56
    10  7  7  7  8  4.82 7.26  6.42  7.91
    11  5  5  5  8  5.68 4.74  5.73  6.89
    

    如果我想知道第一次观测值大于10,第二次观测值大于9的四次中有多少次,我至少有两个选择可以继续:

    其次,我可以做下一步:

    library(dplyr)
    library(purrr)
    anscombe %>%
      mutate(new_var = rowSums(map_dfc(
        1:4,
        ~ anscombe[[paste0("x",.)]] > 10 & anscombe[[paste0("y",.)]] > 9
      ), na.rm = T)) 
    
       x1 x2 x3 x4    y1   y2    y3    y4 new_var
    1  10 10 10  8  8.04 9.14  7.46  6.58       0
    2   8  8  8  8  6.95 8.14  6.77  5.76       0
    3  13 13 13  8  7.58 8.74 12.74  7.71       1
    4   9  9  9  8  8.81 8.77  7.11  8.84       0
    5  11 11 11  8  8.33 9.26  7.81  8.47       1
    6  14 14 14  8  9.96 8.10  8.84  7.04       1
    7   6  6  6  8  7.24 6.13  6.08  5.25       0
    8   4  4  4 19  4.26 3.10  5.39 12.50       1
    9  12 12 12  8 10.84 9.13  8.15  5.56       2
    10  7  7  7  8  4.82 7.26  6.42  7.91       0
    11  5  5  5  8  5.68 4.74  5.73  6.89       0
    

    太好了!它起作用了。但是,因为在我的实际数据中,每次我都有更多的观察和条件,所以我想做一行 anscombe[[paste0("x",.)]] > 10 & anscombe[[paste0("y",.)]] > 9

    例如,使用 dplyr 函数中,数据帧名称往往可以避免。也许我得用 rlang 功能 sym 具体如下:

    !!sym(paste0("x",.)) > 10 & !!sym(paste0("y",.)) > 9
    

    我试过了,但没用。也许还有别的功能 map_dfc 在里面 dplyr公司 purrr 或者是其他一些能让你以更简单、更有效的方式完成任务的软件包。你有什么想法吗?

    非常感谢你。

    2 回复  |  直到 6 年前
        1
  •  4
  •   akrun    6 年前

    1) 拆分/映射2: 这里有一个选项 split 基于 names 数据集的。这里,我们把名字末尾的数字部分去掉, 分裂 将数据集转换为 list 属于 data.frames ,使用 map2 reduce 然后得到 rowSums

    library(dplyr)
    library(purrr)
    library(stringr)
    anscombe %>%
        split.default(str_remove(names(.), "\\d+$")) %>%
        map2(.,  c(10, 9),  `>`) %>% 
        reduce(`&`) %>% 
        rowSums %>%
        bind_cols(anscombe, new_var = .)
    #   x1 x2 x3 x4    y1   y2    y3    y4 new_var
    #1  10 10 10  8  8.04 9.14  7.46  6.58       0
    #2   8  8  8  8  6.95 8.14  6.77  5.76       0
    #3  13 13 13  8  7.58 8.74 12.74  7.71       1
    #4   9  9  9  8  8.81 8.77  7.11  8.84       0
    #5  11 11 11  8  8.33 9.26  7.81  8.47       1
    #6  14 14 14  8  9.96 8.10  8.84  7.04       1
    #7   6  6  6  8  7.24 6.13  6.08  5.25       0
    #8   4  4  4 19  4.26 3.10  5.39 12.50       1
    #9  12 12 12  8 10.84 9.13  8.15  5.56       2
    #10  7  7  7  8  4.82 7.26  6.42  7.91       0
    #11  5  5  5  8  5.68 4.74  5.73  6.89       0
    

    2) 轴长: 另一个选择是 pivot_longer tidyr 它可以获取多组列并将其重塑为“long”格式

    library(dplyr)
    library(tidyr) #1.0.0
    library(tibble)
    anscombe  %>% 
       rownames_to_column('rn') %>% 
       pivot_longer( -rn, names_to = c(".value", "repl"), 
            values_to = c('x', 'y'), names_pattern = '(\\D+)(\\d+)') %>% 
       group_by(rn) %>% 
       summarise(new_var =  sum(x > 10 & y > 9, na.rm = TRUE)) %>%
       arrange(as.integer(rn)) %>%
       select(-rn) %>%
       bind_cols(anscombe, .)
    #   x1 x2 x3 x4    y1   y2    y3    y4 new_var
    #1  10 10 10  8  8.04 9.14  7.46  6.58       0
    #2   8  8  8  8  6.95 8.14  6.77  5.76       0
    #3  13 13 13  8  7.58 8.74 12.74  7.71       1
    #4   9  9  9  8  8.81 8.77  7.11  8.84       0
    #5  11 11 11  8  8.33 9.26  7.81  8.47       1
    #6  14 14 14  8  9.96 8.10  8.84  7.04       1
    #7   6  6  6  8  7.24 6.13  6.08  5.25       0
    #8   4  4  4 19  4.26 3.10  5.39 12.50       1
    #9  12 12 12  8 10.84 9.13  8.15  5.56       2
    #10  7  7  7  8  4.82 7.26  6.42  7.91       0
    #11  5  5  5  8  5.68 4.74  5.73  6.89       0
    

    3) 底部R: (类似于第一种方法的逻辑)。这样我们就可以自动完成了 分裂 基于前缀相似度的数据分块

    anscombe$new_var <- rowSums(Reduce(`&`, Map(`>`, 
      split.default(anscombe, sub("\\d+$", "", names(anscombe))), c(10, 9))))
    

    或者使用前缀匹配的另一个选项是循环通过唯一的子字符串前缀(比 )然后申请

    rowSums(Reduce(`&`, Map(`>`, lapply(unique(sub("\\d+$", "", 
            names(anscombe))), function(nm)
               anscombe[grep(nm, names(anscombe))]), c(10, 9))))
     #[1] 0 0 1 0 1 1 0 1 2 0 0
    
        2
  •  2
  •   Yifu Yan    6 年前

    你可以试试 pmap 在里面 purrr 明智地迭代数据帧行

    library(dplyr)
    library(purrr)
    library(stringr)
    
    
    new_var <- pmap_dbl(anscombe, function(...){
        row <- unlist(list(...))
        x <- row[str_subset(names(row),"^x")]
        y <- row[str_subset(names(row),"^y")]
        sum((x > 10) & (y > 9))
    })
    
    
    anscombe[,"new_var"] <- new_var
    
    > anscombe
       x1 x2 x3 x4    y1   y2    y3    y4 new_var
    1  10 10 10  8  8.04 9.14  7.46  6.58       0
    2   8  8  8  8  6.95 8.14  6.77  5.76       0
    3  13 13 13  8  7.58 8.74 12.74  7.71       1
    4   9  9  9  8  8.81 8.77  7.11  8.84       0
    5  11 11 11  8  8.33 9.26  7.81  8.47       1
    6  14 14 14  8  9.96 8.10  8.84  7.04       1
    7   6  6  6  8  7.24 6.13  6.08  5.25       0
    8   4  4  4 19  4.26 3.10  5.39 12.50       1
    9  12 12 12  8 10.84 9.13  8.15  5.56       2
    10  7  7  7  8  4.82 7.26  6.42  7.91       0
    11  5  5  5  8  5.68 4.74  5.73  6.89       0
    
        3
  •  2
  •   jay.sf    6 年前

    rowSums(anscombe[1:4] > 10 & anscombe[5:8] > 9)
    # [1] 0 0 1 0 1 1 0 1 2 0 0
    

    rowSums(anscombe[grep("^x", names(anscombe))] > 10 & 
              anscombe[grep("^y", names(anscombe))] > 9)
    # [1] 0 0 1 0 1 1 0 1 2 0 0
    
        4
  •  0
  •   iago    6 年前

    在github中,另一个与我的尝试类似的选择告诉我:

    library(dplyr)
    library(purrr)
    anscombe %>%
      mutate(new_var = rowSums(map_dfc(
        1:4,
        ~ get(paste0("x",.)) > 10 & get(paste0("y",.)) > 9
      ), na.rm = T)) 
    

    Date 或者别的什么),允许灵活地编写条件,缩短脚本几个,而且很直观。