tidyr::unite 跨列模式

tidyr::unite across column patterns

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

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 好像不存在。

这是一个具有基本功能的解决方案。首先,我在列中查找 ***_1 的索引。我还使用 gsub()unique() 为最终过程的列创建了名称。应用部分使用 / 粘贴两列。如果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

两个选项,其实是同一件事重新排列。


选项 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 方法来:

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