在dplyr中替换plyr :: cbind.fill函数的方法是什么?

5

很抱歉如果这个问题太基础了,但我已经搜遍了互联网,似乎找不到一个简单的解决方法。

目前我有一系列的R对象(命名向量或仅包含1个变量的数据框,两者都可以),我想将它们合并成一个大型数据框,每个唯一名称/行名为1行,原始列表中的每个元素为1列。

我的起始列表看起来像:

l1 <- list(df1 = data.frame(c(1,2,3), row.names = c("A", "B", "C")), 
       df2 = data.frame(c(2,6), row.names = c("B", "D")),
       df3 = data.frame(c(3,6,9), row.names = c("C", "D", "A")),
       df4 = data.frame(c(4,12), row.names = c("A", "E")))

我希望你能够将输出结果变成这个样子:

data.frame("df1" = c(1,2,3,NA,NA),
+            "df2" = c(NA,2,NA,6,NA),
+            "df3" = c(9,NA,3,6,NA),
+            "df4" = c(4,NA,NA,NA,12), row.names = c("A", "B", "C", "D", "E"))
  df1 df2 df3 df4
A   1  NA   9   4
B   2   2  NA  NA
C   3  NA   3  NA
D  NA   6   6  NA
E  NA  NA  NA  12

我不介意填充值为NA或0(最终我想要0,但这很容易解决)。

我几乎可以确定plyr :: cbind.fill正好做到了这一点,但是我在脚本的其余部分中使用了dplyr,而且我认为同时使用两者不是一个好主意。 dplyr :: bind_cols 似乎不能处理不同长度的向量。我知道这里已经提出了一个非常类似的问题:R:是否有一个很好的替代方法可以在dplyr中替换plyr :: rbind.fill?但是正如我所提到的,这个解决方案似乎并不起作用。即使包装在do.call中,dplyr :: full_join 也不起作用。是否有简单的解决方案,还是唯一的解决方案是编写自定义函数?


不知道你是否注意到了,由于你在data.frame中放置了一个没有名称的向量,所以你得到了像c.1..2..3.这样混乱的名称。 - camille
@camille 我也注意到了,但我实际的数据没有这种情况。列表元素最初只是具有名称的向量,但我可以轻松地将它们强制转换为一个变量的数据框,并且列名最终与相应的列表元素名称相同。 - Tom
3个回答

7
我们可以使用 rownames_to_column 将行名转换为列,然后通过 rename 重命名第二列,并使用 bind_rows 绑定列表元素,最后使用 pivot_wider 转换为 "宽" 格式。
library(dplyr)
library(tidyr)
library(purrr)
library(tibble)
map_dfr(l1, ~ rownames_to_column(.x, 'rn') %>% 
              rename_at(2, ~'v1'), .id = 'grp') %>%        
   pivot_wider(names_from = grp, values_from = v1) %>% 
   column_to_rownames('rn')

我不确定我做错了什么,或者这只是我的机构使用的RStudio版本过时了,但我得到了“Error in pivot_wider(., names_from = grp, values_from = v1) : could not find function "pivot_wider"” 的错误提示。 - Tom
1
@Tom,请问你能否检查一下你的 tidyr 版本。它应该是 1.0.0 及以上版本。 - akrun
1
我真的很喜欢这种方法!有一个小问题:使用 purrr::map_dfr(),你可以跳过 bind_rows()。这行代码看起来像这样:map_dfr(l1, ~ rownames_to_column(.x, 'rn') %>% 2, ~'v1'), .id = 'grp') - Till
@Till 谢谢。我忘了那件事。 - akrun

6

以下是使用 purrrdplyr 函数的一种方法。为每个数据框创建列名 - 由于每个数据框仅有一列,因此可以使用 setNames 轻松实现,但如果有更多列,则可以使用 dplyr :: rename 。基于原始行名称在整个列表中执行 full-join,并用 0 填充 NA

library(dplyr)
library(purrr)

l1 %>%
  imap(~setNames(.x, .y)) %>%
  map(tibble::rownames_to_column) %>%
  reduce(full_join, by = "rowname") %>%
  mutate_all(tidyr::replace_na, 0)
#>   rowname df1 df2 df3 df4
#> 1       A   1   0   9   4
#> 2       B   2   2   0   0
#> 3       C   3   0   3   0
#> 4       D   0   6   6   0
#> 5       E   0   0   0  12

2

另一个 purrrdplyr 的选项可能是:

l1 %>%
 map2_dfr(.x = ., .y = names(.), ~ setNames(.x, .y) %>%
           rownames_to_column()) %>%
 group_by(rowname) %>%
 summarise_all(~ ifelse(all(is.na(.)), NA, first(na.omit(.))))

  rowname   df1   df2   df3   df4
  <chr>   <dbl> <dbl> <dbl> <dbl>
1 A           1    NA     9     4
2 B           2     2    NA    NA
3 C           3    NA     3    NA
4 D          NA     6     6    NA
5 E          NA    NA    NA    12

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接