首页 文章

基于谓词函数(dplyr :: mutate_if)变换数据框的列

提问于
浏览
5

我想使用dplyr的 mutate_if() 函数将列表列转换为数据帧列,但是当我尝试这样做时遇到一个令人费解的错误 . 我使用的是dplyr 0.5.0,purrr 0.2.2,R 3.3.0 .

基本设置如下所示:我有一个数据框 d ,其中一些列是列表:

d <- dplyr::data_frame(
  A = list(
    list(list(x = "a", y = 1), list(x = "b", y = 2)),
    list(list(x = "c", y = 3), list(x = "d", y = 4))
  ),
  B = LETTERS[1:2]
)

我想使用以下函数将列的列(在本例中为 d$A )转换为数据帧列:

tblfy <- function(x) {
  x %>%
    purrr::transpose() %>%
    purrr::simplify_all() %>%
    dplyr::as_data_frame()
}

也就是说,我希望列表列 d$A 被列表 lapply(d$A, tblfy) 替换,这是

[[1]]
#  A tibble: 2 x 2
      x     y
  <chr> <dbl>
1     a     1
2     b     2

[[2]]
# A tibble: 2 x 2
      x     y
  <chr> <dbl>
1     c     3
2     d     4

当然,在这个简单的例子中,我可以做一个简单的重新分配 . 然而,重点是我想以编程方式,理想情况下使用dplyr,以一种通用的方式处理任意数量的列表列 .

这是我绊倒的地方:当我尝试使用以下应用程序将list-columns转换为data-frame-columns时

d %>% dplyr::mutate_if(is.list, funs(tblfy))

我收到一条错误消息,我不知道如何解释:

Error: Each variable must be named.
Problem variables: 1, 2

为什么 mutate_if() 会失败?如何正确应用它以获得所需的结果?

Remark

一位意见提供者指出函数 tblfy() 应该被矢量化 . 这是一个合理的建议 . 但是 - 除非我的矢量化不正确 - 这似乎并没有找到问题的根源 . 插入 tblfy() 的矢量化版本,

tblfy_vec <- Vectorize(tblfy)

进入 mutate_if() 失败并显示错误

Error: wrong result size (4), expected 2 or 1

Update

在获得purrr的一些经验之后,我现在发现以下方法是自然的,如果有点啰嗦:

d %>%
  map_if(is.list, ~ map(., ~ map_df(., identity))) %>%
  as_data_frame()

这与@ alistaire的解决方案或多或少完全相同,但是使用了 map_if() ,resp . map() ,代替 mutate_if() ,resp . Vectorize() .

2 回答

  • 6

    最初的 tblfy 函数错误(即使它的元素是直接链接的),所以让我们重新构建一下,添加矢量化,这样我们就可以避免以前必要的事先 rowwise() 调用:

    tblfy <- Vectorize(function(x){x %>% purrr::map_df(identity) %>% list()})
    

    现在我们可以很好地使用 mutate_if

    d %>% mutate_if(purrr::is_list, tblfy)
    ## Source: local data frame [2 x 2]
    ## 
    ##                A     B
    ##           <list> <chr>
    ## 1 <tbl_df [2,2]>     A
    ## 2 <tbl_df [2,2]>     B
    

    ......如果我们不知道那里有什么,

    d %>% mutate_if(purrr::is_list, tblfy) %>% tidyr::unnest()
    ## Source: local data frame [4 x 3]
    ## 
    ##       B     x     y
    ##   <chr> <chr> <dbl>
    ## 1     A     a     1
    ## 2     A     b     2
    ## 3     B     c     3
    ## 4     B     d     4
    

    几个笔记:

    • map_df(identity) 似乎比任何替代配方更有效地 Build 一个组合 . 我知道 identity 电话似乎没必要,但大多数其他一切都打破了 .

    • 我不确定 tblfy 会有多广泛,因为它有用's somewhat dependent on the structure of the lists in the list column, which can vary enormously. If you have a lot with a similar structure, I suppose it' .

    • 可能有一种方法可以用 pmap 而不是 Vectorize 来做到这一点,但我不能让它与一些粗略的尝试一起工作 .

  • 7

    没有任何复制的就地转换:

    library(data.table)
    
    for (col in d) if (is.list(col)) lapply(col, setDF)
    
    d
    #Source: local data frame [2 x 2]
    #
    #                A B
    #1 <S3:data.frame> A
    #2 <S3:data.frame> B
    

相关问题