【问题标题】:why `stack` cannot work on the result of `tapply`?为什么`stack`不能对`tapply`的结果起作用?
【发布时间】:2022-01-17 01:32:54
【问题描述】:

假设我有一个数据框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 &lt;- 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 成功?有没有其他方法可以在不进行任何后处理的情况下使用tapply 而不是u1(如Map(c, u1))?

【问题讨论】:

    标签: r stack tapply


    【解决方案1】:

    或者也可以使用as.vector/c去除属性,将1d向量转换为没有dim属性的向量

    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

    请注意,堆栈适用于向量(由 is.vector 确定):非向量列(例如因子)将被忽略并发出警告。

    is.vector 为 'u1' 的所有成员元素返回 FALSE

    > sapply(u1, is.vector)
        X     Y     Z 
    FALSE FALSE FALSE 
    

    正如@GregorThomas 提到的tapply 中的simplify 参数,combn 中还有一个simplify 选项,默认情况下为 TRUE。如果我们指定 FALSE,它会返回一个 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 
    

    【讨论】:

    • 非常感谢。点赞!在运行stack 之前,似乎对u1 进行一些后处理是不可避免的。使用enframe + unnest 是一个有趣的解决方法
    • @ThomasIsCoding 谢谢。否则正如 GregorThomas 提到的,tapply 输出更改为 simplify
    • @ThomasIsCoding 我认为这里的基本问题也与combn 有关。即u1 &lt;- with( df, tapply(y, x, FUN = function(u) combn(u, 2, FUN = toString, simplify = FALSE)) )现在,你可以使用stack(u1)
    • combn 的发现真的很棒,超级棒!
    【解决方案2】:

    tapply 返回一个array(或者如果您设置了simplify = FALSE,则返回一个list),并且stack 不喜欢数组输入。 tapply 文档听起来不像有其他输出选项。来自?tapply(强调我的):

    simplify:

    logical;如果FALSEtapply 总是返回一个模式数组“list”;换句话说,一个带有dim 属性的list。如果TRUE(默认),那么如果FUN总是返回一个标量,tapply返回一个数组与标量的模式。

    所以我建议转换为角色:

    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
    

    【讨论】:

      猜你喜欢
      • 2013-03-16
      • 1970-01-01
      • 2020-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多