2016-07-07 75 views
5

我想使用dplyr的mutate_if()函数将list-columns转换为data-frame-columns,但是当我试图将数据框列转换为令人费解的错误时这样做。我使用dplyr 0.5.0,purrr 0.2.2,R 3.3.0。基于谓词函数的数据框的变化列(dplyr :: mutate_if)

基本设置是这样的:我有一个数据帧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,可以处理任意数量的列表列。

这里就是我绊倒:当我尝试使用下面的应用

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

我得到一个错误信息,我不知道如何解释到转换列表列数据帧列:

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

为什么mutate_if()失败?我如何正确地应用它来获得期望的结果?

备注

一个评论者指出,功能tblfy()应该被量化。这是一个合理的建议。但是 - 除非我错误地进行了矢量化 - 这似乎并没有解决问题的根源。堵在tblfy()向量化版本,

tblfy_vec <- Vectorize(tblfy) 

mutate_if()失败,出现错误

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

更新

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

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

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

+2

那么究竟是什么样的预期产出呢?你想将列表中的A从列表中更改为一个骰子列表? – MrFlick

+1

你的功能不是矢量化的,它只接受一个列表。看看'tblfy(d $ A)'。有一个错误,因为'd $ A'中有两个列表。你不是在比较苹果和苹果。在你的'lapply(d $ A,tblfy)'中,你一次给你的函数列出一个列表,这就是它的原因。 'tblfy(d $ A [[1]])''和'tblfy(d $ A [[2]])''。在你的dplyr函数中,你提供了两个列表。将'tblfy'更改为接受多个列表,或更改dplyr调用。或者如弗里克先生所问,更广泛地思考你正在建设什么。 –

+0

@MrFlick我编辑了这个问题,以使所需的输出清晰。现在清楚吗? – egnha

回答

5

原来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 

...如果我们UNNEST,看看那里有什么,

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)似乎是在构建一个tibble比任何替代配方更有效。我知道identity调用似乎没有必要,但大部分其他事情都会中断。
  • 我不确定tblfy会有多大的用处,因为它在某种程度上取决于列表列中的列表结构,而列表结构可能有很大的差异。如果你有很多类似的结构,我认为它很有用。
  • 可能有办法做到这一点与pmap而不是Vectorize,但我不能让它与粗略尝试工作。
+1

谢谢,就是这样!你使用'map_df()'的'tblfy()'版本比我的更简洁。没有想过这样做。实际上,查看'map_df()'的源代码可以解释为什么你的解决方案能够工作,特别是为什么'list()'是必须的(最初让我感到困惑):因为'map_df'实际上是'map',其次是'bind_rows',省略'list()'会导致尺寸为4 x 2的数据帧。 – egnha

6

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

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 
+0

不回答有关'mutate_if'的原始问题,但是它是一个很好的解决基本问题的方法。 data.table对我来说是新的。谢谢! – egnha