【问题标题】:Replacement of plyr::cbind.fill in dplyr?在 dplyr 中替换 plyr::cbind.fill?
【发布时间】:2020-05-21 18:58:39
【问题描述】:

如果这个问题很简单,我很抱歉,但我一直在网上搜索,似乎找不到简单的解决方案。

我目前有一个 R 对象列表(命名向量或 1 个变量的数据框,我可以使用其中任何一个),我想将它们加入 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: Is there a good replacement for plyr::rbind.fill in dplyr? 但正如我所提到的,这个解决方案实际上似乎不起作用。 dplyr::full_join 也没有,甚至包裹在 do.call 中。是否有直接的解决方案,或者是编写自定义函数的唯一解决方案?

【问题讨论】:

  • 不确定您是否注意到,但由于您在 data.frame 中放置了一个没有名称的向量,因此您的名称很乱,例如 c.1..2..3.
  • @camille 我确实注意到,在我的实际数据中并非如此。列表元素一开始只是命名向量,但我可以轻松地将它们强制转换为 1 个变量的数据框,并且列名最终与相应的列表元素名称相同。

标签: r dplyr plyr cbind


【解决方案1】:

另一个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

【讨论】:

    【解决方案2】:

    我们可以使用rownames_to_column 将行名转换为一列,然后将rename 转换为第二列,将list 元素与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 请检查您的tidyr 版本。它从 1.0.0 开始提供
    • 我真的很喜欢这种方法!一件小事:使用purrr::map_dfr() 你可以跳过bind_rows() 该行看起来像这样:map_dfr(l1, ~ rownames_to_column(.x, 'rn') %&gt;% 2, ~'v1'), .id = 'grp')
    • @Till 谢谢。我忘记了。
    【解决方案3】:

    这里有一些purrrdplyr 函数的方法。创建列名来表示每个数据框——因为每个只有一列,使用setNames 很容易,但如果使用更多列,您可以使用dplyr::rename。根据原始行名对整个列表进行全连接,并用 0 填充 NAs。

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-08
      • 1970-01-01
      • 1970-01-01
      • 2014-03-10
      • 1970-01-01
      • 1970-01-01
      • 2017-11-11
      相关资源
      最近更新 更多