【问题标题】:tidyr::unite across column patternstidyr::unite 跨列模式
【发布时间】:2016-06-30 08:36:46
【问题描述】:

我有一个看起来像这样的数据集

site <- c("A", "B", "C", "D", "E")
D01_1 <- c(1, 0, 0, 0, 1)
D01_2 <- c(1, 1, 0, 1, 1)
D02_1 <- c(1, 0, 1, 0, 1)
D02_2 <- c(0, 1, 0, 0, 1)
D03_1 <- c(1, 1, 0, 0, 0)
D03_2 <- c(0, 1, 0, 0, 1)
df <- data.frame(site, D01_1, D01_2, D02_1, D02_2, D03_1, D03_2)

我正在尝试合并 D0x_1D0x_2 列,以便列中的值用斜杠分隔。我可以用下面的代码做到这一点,它工作得很好:

library(dplyr)
library(tidyr)

df.unite <- df %>%
  unite(D01, D01_1, D01_2, sep = "/", remove = TRUE) %>%
  unite(D02, D02_1, D02_2, sep = "/", remove = TRUE) %>%
  unite(D03, D03_1, D03_2, sep = "/", remove = TRUE)

...但问题是它需要我多次输入每个unite 对,而且它在我的数据集中的大量列中很笨拙。 dplyr 有没有办法在类似模式的列名之间联合起来,然后在列之间循环? unite_each 似乎不存在。

【问题讨论】:

    标签: r dplyr tidyr


    【解决方案1】:

    两个选项,实际上是相同的东西重新排列。


    选项 1. 嵌套调用

    首先,您可以使用lapply 以编程方式跨列应用unite_(您可以向其传递字符串的标准评估版本)。为此,您需要构建一个名称列表供其使用,然后将lapply 包装在do.call(cbind 中以捕获列,并将cbind site 包装回它。总而言之:

    cols <- unique(substr(names(df)[-1], 1, 3))
    cbind(site = df$site, do.call(cbind,
            lapply(cols, function(x){unite_(df, x, grep(x, names(df), value = TRUE), 
                                            sep = '/', remove = TRUE) %>% select_(x)})
            ))
    
    #   site D01 D02 D03
    # 1    A 1/1 1/0 1/0
    # 2    B 0/1 0/1 1/1
    # 3    C 0/0 1/0 0/0
    # 4    D 0/1 0/0 0/0
    # 5    E 1/1 1/1 0/1
    

    选项 2:链式

    或者,如果您真的喜欢管道,您实际上可以将整个东西破解成一个链(包括lapply!),将一些基本函数换成dplyr 的:

    df %>% select(-site) %>% names() %>% substr(1,3) %>% unique() %>%
      lapply(function(x){unite_(df, x, grep(x, names(df), value = TRUE), 
                                sep = '/', remove = TRUE) %>% select_(x)}) %>%
      bind_cols() %>% mutate(site = as.character(df$site)) %>% select(site, starts_with('D'))
    
    # Source: local data frame [5 x 4]
    # 
    #    site   D01   D02   D03
    #   (chr) (chr) (chr) (chr)
    # 1     A   1/1   1/0   1/0
    # 2     B   0/1   0/1   1/1
    # 3     C   0/0   1/0   0/0
    # 4     D   0/1   0/0   0/0
    # 5     E   1/1   1/1   0/1
    

    查看中间产品以了解它们如何组合在一起,但它与基本方法的逻辑几乎相同。

    【讨论】:

    • 太棒了,非常感谢您的指导和指导 - 学习如何更有效地在 R 中做事真是太好了!
    【解决方案2】:

    这是一个具有基本功能的解决方案。首先,我在列中查找 ***_1 的索引。我还使用gsub()unique() 为最终流程的列创建了名称。 sapply 部分用/ 粘贴两列。如果 x = 1,则 x +1 = 2。因此,您始终选择相邻的两列并处理粘贴作业。然后,我添加了sitecbind() 并创建了一个数据框。最后一项工作是分配列名。

    library(magrittr)
    
    ind <- grep(pattern = "1$", x = names(df))
    
    names <- unique(gsub(pattern = "_\\d+$",
                    replacement = "", x = names(df)))
    
    sapply(ind, function(x){
            foo <- paste(df[,x], df[, x+1], sep = "/")
            foo
           }) %>%
    cbind(as.character(df$site), .) %>%
    data.frame -> out
    
    names(out) <- names
    
    #  site D01 D02 D03
    #1    A 1/1 1/0 1/0
    #2    B 0/1 0/1 1/1
    #3    C 0/0 1/0 0/0
    #4    D 0/1 0/0 0/0
    #5    E 1/1 1/1 0/1
    

    【讨论】:

      【解决方案3】:

      您也可以使用简单的基本 R 方法:

      cols <- split(names(df)[-1], sub("_\\d+", "", names(df)[-1]))
      
      cbind(df[1], sapply(names(cols), function(col) {
        do.call(paste, c(df[cols[[col]]], sep = "/"))
      }))
      #  site D01 D02 D03
      #1    A 1/1 1/0 1/0
      #2    B 0/1 0/1 1/1
      #3    C 0/0 1/0 0/0
      #4    D 0/1 0/0 0/0
      #5    E 1/1 1/1 0/1
      

      【讨论】:

        猜你喜欢
        • 2016-09-29
        • 1970-01-01
        • 1970-01-01
        • 2019-03-13
        • 2017-07-28
        • 1970-01-01
        • 2018-11-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多