基于谓词函数改变数据框的列 (dplyr::mutate_if)
Mutating columns of a data frame based on a predicate function (dplyr::mutate_if)
我想使用 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,以一种普遍适用的方式来处理任意数量的列表列。
这是我遇到的问题:当我尝试使用以下应用程序将列表列转换为数据框列时
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()
.
没有任何复制的就地转换:
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
原来的 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)
在构建 tibble 方面似乎比任何其他公式都更有效。我知道 identity
调用似乎没有必要,但大多数其他内容都会中断。
- 我不确定
tblfy
的用处有多大,因为它在某种程度上取决于列表列中列表的结构,这些结构可能会有很大差异。如果你有很多类似的结构,我想它很有用。
- 可能有一种方法可以用
pmap
而不是 Vectorize
来做到这一点,但我无法通过一些粗略的尝试来实现它。
我想使用 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,以一种普遍适用的方式来处理任意数量的列表列。
这是我遇到的问题:当我尝试使用以下应用程序将列表列转换为数据框列时
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()
.
没有任何复制的就地转换:
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
原来的 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)
在构建 tibble 方面似乎比任何其他公式都更有效。我知道identity
调用似乎没有必要,但大多数其他内容都会中断。- 我不确定
tblfy
的用处有多大,因为它在某种程度上取决于列表列中列表的结构,这些结构可能会有很大差异。如果你有很多类似的结构,我想它很有用。 - 可能有一种方法可以用
pmap
而不是Vectorize
来做到这一点,但我无法通过一些粗略的尝试来实现它。