如何将列表嵌套转换为tibble(数据框)

10
我有以下列表。它包含两个变量:pair和genes。变量pair的内容始终为包含两个字符串的向量。变量genes是一个向量,可以包含多个值。
lol <- list(structure(list(pair = c("BoneMarrow", "Pulmonary"), genes = "PRR11"), .Names = c("pair", 
"genes")), structure(list(pair = c("BoneMarrow", "Umbilical"), 
    genes = "GNB2L1"), .Names = c("pair", "genes")), structure(list(
    pair = c("Pulmonary", "Umbilical"), genes = "ATP1B1"), .Names = c("pair", 
"genes")))


lol
#> [[1]]
#> [[1]]$pair
#> [1] "BoneMarrow" "Pulmonary" 
#> 
#> [[1]]$genes
#> [1] "PRR11"
#> 
#> 
#> [[2]]
#> [[2]]$pair
#> [1] "BoneMarrow" "Umbilical" 
#> 
#> [[2]]$genes
#> [1] "GNB2L1"
#> 
#> 
#> [[3]]
#> [[3]]$pair
#> [1] "Pulmonary" "Umbilical"
#> 
#> [[3]]$genes
#> [1] "ATP1B1"

我该如何将它转换成这个数据框:
pair1         pair2        genes_vec
BoneMarrow    Pulmonary    PRR11
BoneMarrow    Umbilical    GNB2L1
Pulmonary     Umbilical    ATP1B1

请注意,genes变量是一个向量而不是单个字符串。
我最好的尝试是下面这个,但它并没有给出我想要的结果:
> do.call(rbind, lapply(lol, data.frame, stringsAsFactors=FALSE))
        pair  genes
1 BoneMarrow  PRR11
2  Pulmonary  PRR11
3 BoneMarrow GNB2L1
4  Umbilical GNB2L1
5  Pulmonary ATP1B1
6  Umbilical ATP1B1

更新: 新增示例,展示genes的向量内容。
lol2 <- list(structure(list(pair = c("BoneMarrow", "Pulmonary"), genes = c("GNB2L1", 
"PRR11")), .Names = c("pair", "genes")), structure(list(pair = c("BoneMarrow", 
"Umbilical"), genes = "GNB2L1"), .Names = c("pair", "genes")), 
    structure(list(pair = c("Pulmonary", "Umbilical"), genes = "ATP1B1"), .Names = c("pair", 
    "genes")))

lol2
#> [[1]]
#> [[1]]$pair
#> [1] "BoneMarrow" "Pulmonary" 
#> 
#> [[1]]$genes
#> [1] "GNB2L1" "PRR11" 
#> 
#> 
#> [[2]]
#> [[2]]$pair
#> [1] "BoneMarrow" "Umbilical" 
#> 
#> [[2]]$genes
#> [1] "GNB2L1"
#> 
#> 
#> [[3]]
#> [[3]]$pair
#> [1] "Pulmonary" "Umbilical"
#> 
#> [[3]]$genes
#> [1] "ATP1B1"

期望的输出结果是:
pair1         pair2        genes_vec
BoneMarrow    Pulmonary    PRR11,GNB2L1
BoneMarrow    Umbilical    GNB2L1
Pulmonary     Umbilical    ATP1B1
6个回答

16
使用“tidyverse”,您可以使用“purrr”来帮助您。
library(dplyr)
library(purrr)

tibble(
  pair = map(lol, "pair"),
  genes_vec = map_chr(lol, "genes")
) %>% 
  mutate(
    pair1 = map_chr(pair, 1),
    pair2 = map_chr(pair, 2) 
  ) %>%
  select(pair1, pair2, genes_vec)
#> # A tibble: 3 x 3
#>        pair1     pair2 genes_vec
#>        <chr>     <chr>     <chr>
#> 1 BoneMarrow Pulmonary     PRR11
#> 2 BoneMarrow Umbilical    GNB2L1
#> 3  Pulmonary Umbilical    ATP1B1

使用第二个示例,只需将map_chr(lol,“genes”)替换为map(lol2,“genes”),因为您想保留一个带有列表列的嵌套数据框。
tibble(
  pair = map(lol2, "pair"),
  genes_vec = map(lol2, "genes")
) %>% 
  mutate(
    pair1 = map_chr(pair, 1),
    pair2 = map_chr(pair, 2) 
  ) %>%
  select(pair1, pair2, genes_vec)
#> # A tibble: 3 x 3
#>        pair1     pair2 genes_vec
#>        <chr>     <chr>    <list>
#> 1 BoneMarrow Pulmonary <chr [2]>
#> 2 BoneMarrow Umbilical <chr [1]>
#> 3  Pulmonary Umbilical <chr [1]>

一种更通用的方法是使用嵌套的tibbles,并根据需要对它们进行展开

library(dplyr)
library(purrr)
library(tidyr)

tab1 <-lol %>%
  transpose() %>%
  as_tibble() %>%
  mutate(pair = map(pair, ~as_tibble(t(.x)))) %>%
  mutate(pair = map(pair, ~set_names(.x, c("pair1", "pair2"))))
tab1
#> # A tibble: 3 x 2
#>               pair     genes
#>             <list>    <list>
#> 1 <tibble [1 x 2]> <chr [1]>
#> 2 <tibble [1 x 2]> <chr [1]>
#> 3 <tibble [1 x 2]> <chr [1]>

对于lol2,除非列表变为lol2,否则不会发生任何变化。

tab2 <- lol2 %>%
  transpose() %>%
  as_tibble() %>%
  mutate(pair = map(pair, ~as_tibble(t(.x)))) %>%
  mutate(pair = map(pair, ~set_names(.x, c("pair1", "pair2"))))
tab2
#> # A tibble: 3 x 2
#>               pair     genes
#>             <list>    <list>
#> 1 <tibble [1 x 2]> <chr [2]>
#> 2 <tibble [1 x 2]> <chr [1]>
#> 3 <tibble [1 x 2]> <chr [1]>

您可以取消嵌套所需的列。
tab1 %>%
  unnest()
#> # A tibble: 3 x 3
#>    genes      pair1     pair2
#>    <chr>      <chr>     <chr>
#> 1  PRR11 BoneMarrow Pulmonary
#> 2 GNB2L1 BoneMarrow Umbilical
#> 3 ATP1B1  Pulmonary Umbilical

tab2 %>% 
  unnest(pair)
#> # A tibble: 3 x 3
#>       genes      pair1     pair2
#>      <list>      <chr>     <chr>
#> 1 <chr [2]> BoneMarrow Pulmonary
#> 2 <chr [1]> BoneMarrow Umbilical
#> 3 <chr [1]>  Pulmonary Umbilical

3

对于第一个问题,与其他答案基本相同,稍微更短/更紧凑:

library(tidyverse)
lol <- list(structure(list(pair = c("BoneMarrow", "Pulmonary"), genes = "PRR11"),
                      .Names = c("pair", "genes")),
            structure(list(pair = c("BoneMarrow", "Umbilical"), genes = "GNB2L1"),
                      .Names = c("pair", "genes")),
            structure(list(pair = c("Pulmonary", "Umbilical"), genes = "ATP1B1"), .Names = c("pair","genes")))


map_dfr(lol, ~as_tibble(.) %>% 
          mutate(row=paste0("pair", row_number()))%>% 
          spread(row, pair) %>% 
          select(pair1, pair2, genes))
#> # A tibble: 3 x 3
#>   pair1      pair2     genes 
#>   <chr>      <chr>     <chr> 
#> 1 BoneMarrow Pulmonary PRR11 
#> 2 BoneMarrow Umbilical GNB2L1
#> 3 Pulmonary  Umbilical ATP1B1

这篇文章是使用reprex软件包 (v0.3.0)在2020-12-04创建的。


2

编辑:已更新以适用于向量lol2。

也许是这样:

as.data.frame(do.call(rbind,lapply(lol2, function(x) {c(unlist(x[1]),gene=paste(unlist(x[2]),collapse=","))})),stringsAsFactors = F)




       pair1     pair2         genes
1 BoneMarrow Pulmonary GNB2L1, PRR11
2 BoneMarrow Umbilical        GNB2L1
3  Pulmonary Umbilical        ATP1B1

谢谢。您的方法在genes是向量时似乎会失败。请查看我的更新。 - littleworth
谢谢。有没有办法将您的最后输出简化为只有3个变量(列)的简单数据框架?现在,str() 显示数据框架包含嵌套列表。要进一步检查,您可以尝试 as.tibble(your_output) - littleworth
是的,您可以粘贴基因列,再次更新,希望这更好地反映了您的预期输出。 - Florian

2
这应该可以工作:
data.frame(do.call(rbind,lol2))
data.frame(do.call(rbind,lol2))
                   pair         genes
1 BoneMarrow, Pulmonary GNB2L1, PRR11
2 BoneMarrow, Umbilical        GNB2L1
3  Pulmonary, Umbilical        ATP1B1

您可以将基因视为向量,同样地,您也可以将一对数据视为向量:只需使用这两个数据,而不是分别使用第一个和第二个。


1
> lol1 <- data.frame(t(sapply(lol,c)))
> as.data.frame(t(apply(lol1, 1, unlist)))
       pair1     pair2  genes
1 BoneMarrow Pulmonary  PRR11
2 BoneMarrow Umbilical GNB2L1
3  Pulmonary Umbilical ATP1B1

谢谢。不太符合我的要求,我该如何进一步将这对分成两列? - littleworth
@yaffle 更新了解决方案 - Prasanna Nandakumar
谢谢,但是你最新的方法在genes是向量时似乎无法成功。请看我的更新。 - littleworth

0
另一个tidyverse解决方案:版本lol的结果与OP中相同。lol2将基因向量分成适当数量的列:
lol2 <- list(structure(list(pair = c("BoneMarrow", "Pulmonary"), genes = c("GNB2L1", 
"PRR11")), .Names = c("pair", "genes")), structure(list(pair = c("BoneMarrow", 
"Umbilical"), genes = "GNB2L1"), .Names = c("pair", "genes")), 
    structure(list(pair = c("Pulmonary", "Umbilical"), genes = "ATP1B1"), .Names = c("pair", 
    "genes")))

lol2_result <- lol2 |> 
    purrr::transpose() |> 
    tibble::as_tibble() |> 
    tidyr::unnest_wider(col = c(pair, genes), names_sep = "_")
lol2_result
#> # A tibble: 3 × 4
#>   pair_1     pair_2    genes_1 genes_2
#>   <chr>      <chr>     <chr>   <chr>  
#> 1 BoneMarrow Pulmonary GNB2L1  PRR11  
#> 2 BoneMarrow Umbilical GNB2L1  <NA>   
#> 3 Pulmonary  Umbilical ATP1B1  <NA>

使用 reprex v2.0.2 于2023年4月13日创建


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接