【问题标题】:subsetting a list column of integer matrices对整数矩阵的列表列进行子集化
【发布时间】:2020-04-19 11:56:36
【问题描述】:

背景

我陷入了这样一种情况,即 tibble/dataframe 中的一列由一个整数矩阵列表组成,这些矩阵有零行或多行,正好有 2 列。此列恰好是 stringr::str_locate_all() 调用的输出,所以我认为这是一种常见的情况。

我想做的是只选择整数矩阵的一列,然后取消嵌套数据框,但我对如何正确执行此操作感到困惑。

示例

这是一个示例(我必须手动创建它,因为dpasta() 似乎不适用于列表列标题)。无论如何,我的出发点是 mydf:

library(tidyverse)

m1 <- matrix( c(761,784),             nrow=1,ncol=2, dimnames = list(c(),c("start","end")) )
m2 <- matrix( integer(0),             nrow=0,ncol=2, dimnames = list(c(),c("start","end")) )
m3 <- matrix( c(1001,2300,1010,2310), nrow=2,ncol=2, dimnames = list(c(),c("start","end")) )

mydf <- tibble( item = c("a","b","c"), pos = list(m1,m2,m3))

下面是 rstudio 查看器中的样子。 这有点误导,因为它表明 pos 行只是整数向量。它们实际上是 nx2 矩阵,没有任何提示表明它更复杂。这让我有些困惑,但现在已经不重要了。

我想要做的是最终得到一个未嵌套的小标题,其中选择了第一列“开始”。所需的输出将如下所示(取消嵌套后):

mydf_desired <- tibble( item = c("a","c","c"), start_pos = c(761,1001,2300))

请注意,mydf 中的第一行在其 pos 矩阵中只有一行,因此它在所需结果中有一行。 item="b" 的行有一个 0x2 矩阵,所以它不会出现(但如果它也显示为 NA 就可以了)。 item="c" 的行在 pos 矩阵中有两行,所以它在期望的结果中有两行。

我尝试了什么

这看起来很简单,我之前没有嵌套过列表列。这里唯一的转折是我必须先选择“开始”列,然后再取消嵌套,对吗?我只是map pos 列表列到 [,1] 来挑选第一列(“开始”列)。然后应该是取消嵌套的问题...

mydf_desired <- mydf %>% 
                mutate(start_pos = map(pos, ~ .[,1])) %>% 
                unnest()
#> Error in vec_rbind(!!!x, .ptype = ptype): Internal error in `vec_assign()`: `value` should have been recycled to fit `x`.
#> Warning: `cols` is now required.
#> Please use `cols = c(pos, start_pos)`

不知道“value should have been recycled to fit x”实际上是什么意思,但它也警告我不要在unnest() 中提供列。现在怀疑是关于我给unnest()的东西。

如果我省略 unnest(),我不会收到该错误...

mydf_desired <- mydf %>% 
                mutate(start_pos = map(pos, ~ .[,1]))

输出看起来像这样......

这种看起来不错,我注意到integer(0) 的 item=b 仍有一个 pos 条目。但即使我省略了该行,当我尝试 unnest() 时也会遇到同样的错误。

这就是我难过的地方。为什么我不能只 unnest() 这个 tibble? value should have been recycled to fit x 错误是什么意思?

【问题讨论】:

    标签: r dplyr purrr unnest


    【解决方案1】:

    出现错误是因为 unnest 试图取消嵌套 pos 列。您可以明确指定要unnest 的列以避免错误。

    library(dplyr)
    library(purrr)
    
    mydf %>% mutate(start_pos = map(pos, ~.[, 1])) %>% unnest(start_pos)
    
    # A tibble: 3 x 3
    #  item  pos               start_pos
    #  <chr> <list>                <dbl>
    #1 a     <dbl[,2] [1 × 2]>       761
    #2 c     <dbl[,2] [2 × 2]>      1001
    #3 c     <dbl[,2] [2 × 2]>      2300
    

    如果你想要NA 用于"b" 项目,你可以使用unnest_longer

    mydf %>% 
       mutate(start_pos = map(pos, ~.[, 1])) %>% 
       unnest_longer(start_pos, indices_include = FALSE)
    
    # A tibble: 4 x 3
    #  item  pos               start_pos
    #  <chr> <list>                <dbl>
    #1 a     <dbl[,2] [1 × 2]>       761
    #2 b     <int[,2] [0 × 2]>        NA
    #3 c     <dbl[,2] [2 × 2]>      1001
    #4 c     <dbl[,2] [2 × 2]>      2300
    

    或者unnestkeep_empty = TRUE

    mydf %>%
      mutate(start_pos = map(pos, ~.[, 1])) %>%
      unnest(start_pos, keep_empty = TRUE)
    

    【讨论】:

    • 谢谢,这是我搞砸的核心问题。 unnest() 有一些新功能,实际上明确指出要取消嵌套的列比仅仅依靠它来选择一个(即使它是唯一可能的)更有意义。
    【解决方案2】:

    一种选择是filter 行,然后map 覆盖list 元素并从matrix 中提取列,然后使用unnest_longer

    library(dplyr)
    library(purrr)
    mydf %>% 
       filter(lengths(pos) > 0) %>%
       transmute(item, start_pos = map(pos, ~ as.vector(.x[,1]))) %>% 
       unnest_longer(c(start_pos))
    # A tibble: 3 x 2
    #  item  start_pos
    #  <chr>     <dbl>
    #1 a           761
    #2 c          1001
    #3 c          2300
    

    另外,如果我们转换为tibble,可以避免filter这一步

    mydf %>%
       transmute(item, pos = map(pos, ~ .x[,1] %>%
                              tibble(start_pos = .))) %>%
       unnest(c(pos))
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-29
    • 2018-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多