为什么 `stack` 不能处理 `tapply` 的结果?
why `stack` cannot work on the result of `tapply`?
假设我有一个数据框df
> dput(df)
structure(list(x = c("X", "X", "X", "Y", "Y", "Z", "Z", "Z"),
y = c("A", "B", "C", "B", "C", "A", "C", "D")), class = "data.frame", row.names = c(NA,
-8L))
> df
x y
1 X A
2 X B
3 X C
4 Y B
5 Y C
6 Z A
7 Z C
8 Z D
并生成如下列表u1
u1 <- with(
df,
tapply(y, x, combn, 2, toString)
)
哪里
> u1
$X
[1] "A, B" "A, C" "B, C"
$Y
[1] "B, C"
$Z
[1] "A, C" "A, D" "C, D"
> str(u1)
List of 3
$ X: chr [1:3(1d)] "A, B" "A, C" "B, C"
$ Y: chr [1(1d)] "B, C"
$ Z: chr [1:3(1d)] "A, C" "A, D" "C, D"
- attr(*, "dim")= int 3
- attr(*, "dimnames")=List of 1
..$ : chr [1:3] "X" "Y" "Z"
当我运行stack(u1)
时,会出现如下错误
> stack(u1)
Error in stack.default(u1) : at least one vector element is required
似乎我不能直接在 tapply
的输出上使用 stack
,即使它是一个命名列表。
但是,当我使用 u2 <- Map(c,u1)
进行后处理时,一切又恢复正常了
> u2 <- Map(c, u1)
> u2
$X
[1] "A, B" "A, C" "B, C"
$Y
[1] "B, C"
$Z
[1] "A, C" "A, D" "C, D"
> str(u2)
List of 3
$ X: chr [1:3] "A, B" "A, C" "B, C"
$ Y: chr "B, C"
$ Z: chr [1:3] "A, C" "A, D" "C, D"
> stack(u2)
values ind
1 A, B X
2 A, C X
3 B, C X
4 B, C Y
5 A, C Z
6 A, D Z
7 C, D Z
正如我们所见,在 str(u2)
中,属性被过滤掉了,这似乎解决了问题。
我的问题是:
为什么u1
失败了,u2
却成功了?有没有其他方法可以在 u1
上使用 tapply
而无需任何后处理(如 Map(c, u1)
)?
tapply
returns 一个 array
(或者 list
如果你设置 simplify = FALSE
),stack
不喜欢一个数组输入。 tapply
文档听起来没有其他输出选项。来自 ?tapply
(强调我的):
simplify
:
logical
; if FALSE
, tapply
always returns an array of mode "list"; in other words, a list
with a dim
attribute. If TRUE
(the default), then if FUN
always returns a scalar, tapply
returns an array with the mode of the scalar.
所以我建议转换为角色:
stack(lapply(u1, as.character))
# values ind
# 1 A, B X
# 2 A, C X
# 3 B, C X
# 4 B, C Y
# 5 A, C Z
# 6 A, D Z
# 7 C, D Z
如果您关心速度,可以 运行 基准测试来查看,删除 dim
属性可能比 as.character()
、
更快
stack(lapply(u1, "dim<-", NULL))
# same result
或者也可以使用 as.vector/c
删除属性并将 1d
向量转换为没有模糊属性的向量
stack(lapply(u1, c))
values ind
1 A, B X
2 A, C X
3 B, C X
4 B, C Y
5 A, C Z
6 A, D Z
7 C, D Z
根据?stack
Note that stack applies to vectors (as determined by is.vector): non-vector columns (e.g., factors) will be ignored with a warning.
is.vector
returnsFALSE
为'u1'
的所有成员元素
> sapply(u1, is.vector)
X Y Z
FALSE FALSE FALSE
正如@GregorThomas 提到的 tapply
中的 simplify
参数,combn
中还有一个 simplify
选项,默认情况下为 TRUE。如果我们指定 FALSE
,它 returns 一个 list
并且应该工作
u1 <- with(
df,
tapply(y, x, FUN = function(u) combn(u, 2, FUN = toString, simplify = FALSE))
)
> stack(u1)
values ind
1 A, B X
2 A, C X
3 B, C X
4 B, C Y
5 A, C Z
6 A, D Z
7 C, D Z
但是,这也适用于 1d
向量上的 enframe
library(tibble)
library(tidyr)
enframe(u1) %>%
unnest(value)
# A tibble: 7 × 2
name value
<chr> <chr>
1 X A, B
2 X A, C
3 X B, C
4 Y B, C
5 Z A, C
6 Z A, D
7 Z C, D
假设我有一个数据框df
> dput(df)
structure(list(x = c("X", "X", "X", "Y", "Y", "Z", "Z", "Z"),
y = c("A", "B", "C", "B", "C", "A", "C", "D")), class = "data.frame", row.names = c(NA,
-8L))
> df
x y
1 X A
2 X B
3 X C
4 Y B
5 Y C
6 Z A
7 Z C
8 Z D
并生成如下列表u1
u1 <- with(
df,
tapply(y, x, combn, 2, toString)
)
哪里
> u1
$X
[1] "A, B" "A, C" "B, C"
$Y
[1] "B, C"
$Z
[1] "A, C" "A, D" "C, D"
> str(u1)
List of 3
$ X: chr [1:3(1d)] "A, B" "A, C" "B, C"
$ Y: chr [1(1d)] "B, C"
$ Z: chr [1:3(1d)] "A, C" "A, D" "C, D"
- attr(*, "dim")= int 3
- attr(*, "dimnames")=List of 1
..$ : chr [1:3] "X" "Y" "Z"
当我运行stack(u1)
时,会出现如下错误
> stack(u1)
Error in stack.default(u1) : at least one vector element is required
似乎我不能直接在 tapply
的输出上使用 stack
,即使它是一个命名列表。
但是,当我使用 u2 <- Map(c,u1)
进行后处理时,一切又恢复正常了
> u2 <- Map(c, u1)
> u2
$X
[1] "A, B" "A, C" "B, C"
$Y
[1] "B, C"
$Z
[1] "A, C" "A, D" "C, D"
> str(u2)
List of 3
$ X: chr [1:3] "A, B" "A, C" "B, C"
$ Y: chr "B, C"
$ Z: chr [1:3] "A, C" "A, D" "C, D"
> stack(u2)
values ind
1 A, B X
2 A, C X
3 B, C X
4 B, C Y
5 A, C Z
6 A, D Z
7 C, D Z
正如我们所见,在 str(u2)
中,属性被过滤掉了,这似乎解决了问题。
我的问题是:
为什么u1
失败了,u2
却成功了?有没有其他方法可以在 u1
上使用 tapply
而无需任何后处理(如 Map(c, u1)
)?
tapply
returns 一个 array
(或者 list
如果你设置 simplify = FALSE
),stack
不喜欢一个数组输入。 tapply
文档听起来没有其他输出选项。来自 ?tapply
(强调我的):
simplify
:
logical
; ifFALSE
,tapply
always returns an array of mode "list"; in other words, alist
with adim
attribute. IfTRUE
(the default), then ifFUN
always returns a scalar,tapply
returns an array with the mode of the scalar.
所以我建议转换为角色:
stack(lapply(u1, as.character))
# values ind
# 1 A, B X
# 2 A, C X
# 3 B, C X
# 4 B, C Y
# 5 A, C Z
# 6 A, D Z
# 7 C, D Z
如果您关心速度,可以 运行 基准测试来查看,删除 dim
属性可能比 as.character()
、
stack(lapply(u1, "dim<-", NULL))
# same result
或者也可以使用 as.vector/c
删除属性并将 1d
向量转换为没有模糊属性的向量
stack(lapply(u1, c))
values ind
1 A, B X
2 A, C X
3 B, C X
4 B, C Y
5 A, C Z
6 A, D Z
7 C, D Z
根据?stack
Note that stack applies to vectors (as determined by is.vector): non-vector columns (e.g., factors) will be ignored with a warning.
is.vector
returnsFALSE
为'u1'
> sapply(u1, is.vector)
X Y Z
FALSE FALSE FALSE
正如@GregorThomas 提到的 tapply
中的 simplify
参数,combn
中还有一个 simplify
选项,默认情况下为 TRUE。如果我们指定 FALSE
,它 returns 一个 list
并且应该工作
u1 <- with(
df,
tapply(y, x, FUN = function(u) combn(u, 2, FUN = toString, simplify = FALSE))
)
> stack(u1)
values ind
1 A, B X
2 A, C X
3 B, C X
4 B, C Y
5 A, C Z
6 A, D Z
7 C, D Z
但是,这也适用于 1d
向量上的 enframe
library(tibble)
library(tidyr)
enframe(u1) %>%
unnest(value)
# A tibble: 7 × 2
name value
<chr> <chr>
1 X A, B
2 X A, C
3 X B, C
4 Y B, C
5 Z A, C
6 Z A, D
7 Z C, D