【问题标题】:Completely flatten highly nested lists of lists with rapply, purrr, tidyr使用 rapply、purrr、tidyr 完全展平高度嵌套的列表列表
【发布时间】:2021-12-18 14:51:25
【问题描述】:

下载的 Facebook 数据让我头疼。它是高度嵌套的(列表列表),并非所有列表都一样长。数据应该成为一个平面矩阵,其中一个列表及其子列表在一行中,即一个列表包括每行的子列表。到目前为止,我已经探索了三个选项。

选项 1:从 purrr 展平

扁平化数据结构但打乱它。所以没有办法知道什么时候用什么样的图片发布了什么文字。根据 purrr reference manual,我无法指定对象,例如时间戳,列表应该被展平?我正在考虑reshape2 package,它允许定义一个 ID 变量,通过该变量对数据进行整形/操作。

library(RJSONIO)
#read in data with utf-8 encoding else the German Umlaute won't display
dataRAW <- RJSONIO::fromJSON("C:/***file path***/FB rot 2.json",
                    encoding = 'utf-8', stringAsFactors = F)

dataRAWflat <- purrr:::flatten(dataRAW) #scrambles data

--> 我知道jsonlite 在读取 JSON 文件时有一个 flatten 功能。但是来自 jsonlite 的 fromJSON 不允许定义编码。需要定义编码,否则无法正确显示德语变音符号。还尝试了rjson,但没有成功。帖子的文本是该项目的关键。我花了很多时间弄清楚如何显示元音变音符号,很高兴能提供帮助 :-)

选项 2:unnest_wider 来自 tidyr
给出一条错误消息,指出它应该是数字或字符,但 dataRAW 中的列表“数据”是一个字符。 tibbles 作为一种特殊的数据框是新手。像数据框一样,小标题是否需要同样长的列?我错过了什么?

library(tibble)
tib <- tibble(dataRAW)
tib %>% tidyr:::unnest_wider(data)
Error: Must extract column with a single valid subscript.
x Subscript `var` has the wrong type `function`.
i It must be numeric or character.
Run `rlang::last_error()` to see where the error occurred.
> rlang::last_error()
<error/vctrs_error_subscript_type>
Must extract column with a single valid subscript.
x Subscript `var` has the wrong type `function`.
i It must be numeric or character.
Backtrace:
  1. tib %>% tidyr:::unnest_wider(data)
  2. tidyr:::unnest_wider(., data)
  3. tidyselect::vars_pull(tbl_vars(data), !!enquo(col))
  4. tidyselect:::pull_as_location2(loc, n, vars)
 12. vctrs::vec_as_subscript2(i, arg = "var", logical = "error")
 13. vctrs:::result_get(...)
Run `rlang::last_trace()` to see the full context.
> rlang:::last_trace()
<error/vctrs_error_subscript_type>
Must extract column with a single valid subscript.
x Subscript `var` has the wrong type `function`.
i It must be numeric or character.
Backtrace:
     x
  1. +-tib %>% tidyr:::unnest_wider(data)
  2. \-tidyr:::unnest_wider(., data)
  3.   \-tidyselect::vars_pull(tbl_vars(data), !!enquo(col))
  4.     \-tidyselect:::pull_as_location2(loc, n, vars)
  5.       +-tidyselect:::with_subscript_errors(...)
  6.       | +-base::tryCatch(...)
  7.       | | \-base:::tryCatchList(expr, classes, parentenv, handlers)
  8.       | |   \-base:::tryCatchOne(expr, names, parentenv, handlers[[1L]])
  9.       | |     \-base:::doTryCatch(return(expr), name, parentenv, handler)
 10.       | \-tidyselect:::instrument_base_errors(expr)
 11.       |   \-base::withCallingHandlers(...)
 12.       \-vctrs::vec_as_subscript2(i, arg = "var", logical = "error")
 13.         \-vctrs:::result_get(...)



选项 3:rapply and lapply
两种代码 sn-ps 都可以工作并保留数据结构。当我想将数据转换为矩阵以进行进一步处理时,数据结构就会混乱。我怀疑是因为数据仍然嵌套了一层。

#code line returns list nested one level deep
FBraw <- lapply(dataRAW, rapply, f = c)
str(FBraw)
List of 40
 $ : Named chr [1:7] "1611853326" "posts/media/ChronikFotos_QNGAWvS8aw/144245114_3813727445333297_3682316138130576479_n_3813727441999964.jpg" "1611853319" "1613542113" ...
  ..- attr(*, "names")= chr [1:7] "timestamp" "attachments.data.media.uri" "attachments.data.media.creation_timestamp" "attachments.data.media.media_metadata.photo_metadata.exif_data.taken_timestamp" ...
 $ : Named chr [1:7] "1611860575" "posts/media/ChronikFotos_QNGAWvS8aw/143276316_3813978641974844_3663341405860849380_n_3813978635308178.png" "1611860403" "1612935033" ...
  ..- attr(*, "names")= chr [1:7] "timestamp" "attachments.data.media.uri" "attachments.data.media.creation_timestamp" "attachments.data.media.media_metadata.photo_metadata.exif_data.taken_timestamp" ...
 $ : Named chr [1:7] "1612948020" "posts/media/ChronikFotos_QNGAWvS8aw/143732770_3813831571989551_5247994518213519901_n_3813831568656218.png" "1611856188" "1617631305" ...

#code snippet 2 
FBraw <- lapply(dataRAW, function(x) data.frame(t(rapply(x, function(x) x[1]))))
str(FBraw, head = 1)
List of 40
 $ :'data.frame':   1 obs. of  7 variables:
 $ :'data.frame':   1 obs. of  7 variables:
 $ :'data.frame':   1 obs. of  7 variables:



样本数据

dataRAW <- list(
  list(
    timestamp = 1611853326, attachments = list(list(data = list(
      list(media = list(
        uri = "posts/media/ChronikFotos_QNGAWvS8aw/144245114_3813727445333297_3682316138130576479_n_3813727441999964.jpg",
        creation_timestamp = 1611853319, media_metadata = list(
          photo_metadata = list(exif_data = list(c(taken_timestamp = 1613542113)))
        ),
        title = "Chronik-Fotos", description = "Da haben wir den Salat! <U+0001F957> \nGemischt oder grün: Verfeinert mit Frieda’s Traum Salatsauce wird der einfachste Salat zum Gaumenschmaus.\n\nProbieren Sie auch unsere Gewürze, Bouillons und verschiedene Käse! \nHier finden Sie alle unsere würzigen Produkte:  www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>'<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E> – Saucen Bouillons Gewürze\nwww.friedas-traum.ch | shop@friedas.ch | Tel. 055 0"
      ))
    ))),
    data = list(post = 1)
  ),
  list(
    timestamp = 1611860575, attachments = list(list(data = list(
      list(media = list(
        uri = "posts/media/ChronikFotos_QNGAWvS8aw/143276316_3813978641974844_3663341405860849380_n_3813978635308178.png",
        creation_timestamp = 1611860403, media_metadata = list(
          photo_metadata = list(exif_data = list(c(taken_timestamp = 1612935033)))
        ),
        title = "Chronik-Fotos", description = "Früher über die Gasse – heute im Online- Shop: <U+0001D5D9><U+0001D5FF><U+0001D5F6><U+0001D5F2><U+0001D5F1><U+0001D5EE>’<U+0001D600> <U+0001D5E7><U+0001D5FF><U+0001D5EE><U+0001D602><U+0001D5FA> Produkte. \n\nWas im Restaurant Löwen in Spreitenbach begann, geht heute online weiter: Sie erhalten 100% Geschmack!\n\nEinfach bestellen im Shop: www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>’<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E> – Saucen, Bouillons, Gewürze\nshop@friedas.ch  | Tel. +41 (0) 55 0"
      ))
    ))),
    data = list(c(post = "Früher über die Gasse – heute im Online- Shop: <U+0001D5D9><U+0001D5FF><U+0001D5F6><U+0001D5F2><U+0001D5F1><U+0001D5EE>’<U+0001D600> <U+0001D5E7><U+0001D5FF><U+0001D5EE><U+0001D602><U+0001D5FA> Produkte. \n\nWas im Restaurant Löwen in Spreitenbach begann, geht heute online weiter: Sie erhalten 100% Geschmack!\n\nEinfach bestellen im Shop: www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>’<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E> – Saucen, Bouillons, Gewürze\nshop@friedas.ch  | Tel. +41 (0) 55 0"))
  ),
  list(
    timestamp = 1612948020, attachments = list(list(data = list(
      list(media = list(
        uri = "posts/media/ChronikFotos_QNGAWvS8aw/143732770_3813831571989551_5247994518213519901_n_3813831568656218.png",
        creation_timestamp = 1611856188, media_metadata = list(
          photo_metadata = list(exif_data = list(c(taken_timestamp = 1617631305)))
        ),
        title = "Chronik-Fotos", description = "<U+0001D5E1><U+0001D5EE><U+0001D5F0><U+0001D5F5> <U+0001D5EE><U+0001D5F9><U+0001D601><U+0001D5F2><U+0001D5FA> <U+0001D5E5><U+0001D5F2><U+0001D607><U+0001D5F2><U+0001D5FD><U+0001D601> von Hand gemischt und abgefüllt: Frieda’s Salatsaucen sind beliebt wie eh und je. <U+0001F44C>\n\nFrüher der Renner im Restaurant Löwen in Spreitenbach, heute: DER Hit zum Bestellen für Sie zu Hause.\n\nProbieren Sie auch unsere Bouillons, Gewürze und unseren Käse! \n\nHier geht’s zum Shop:  www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>’<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E>® – Saucen Bouillons Gewürze\nshop@friedas.ch | Tel. 055 0"
      ))
    ))),
    data = list(c(post = "<U+0001D5E1><U+0001D5EE><U+0001D5F0><U+0001D5F5> <U+0001D5EE><U+0001D5F9><U+0001D601><U+0001D5F2><U+0001D5FA> <U+0001D5E5><U+0001D5F2><U+0001D607><U+0001D5F2><U+0001D5FD><U+0001D601> von Hand gemischt und abgefüllt: Frieda’s Salatsaucen sind beliebt wie eh und je. <U+0001F44C>\n\nFrüher der Renner im Restaurant Löwen in Spreitenbach, heute: DER Hit zum Bestellen für Sie zu Hause.\n\nProbieren Sie auch unsere Bouillons, Gewürze und unseren Käse! \n\nHier geht’s zum Shop:  www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>’<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E>® – Saucen Bouillons Gewürze\nshop@friedas.ch | Tel. 055 0"))
  )
)
  

任何想法和建议表示赞赏。谢谢。

【问题讨论】:

  • @Grothendick 认为可以轻松复制和粘贴数据样本以创建可重现的示例。我知道 dput(x) - 它打印出所有数据,使其 a) 令人困惑并且 b) 造成隐私问题。我正在处理个人数据。将以文件的形式添加数据样本。还没有找到仅dput the first 3 lists 的方法?
  • library(rrapply) rrapply(X, f = head, n = 6, dfaslist = FALSE) 保留列表结构,请参阅here
  • @G.Grothendieck 感谢您的建议。它打印出一半的数据... ;-) 希望示例数据下载有效吗?
  • @G.Grothendieck 手动选择了 dput() 输出。
  • @user63230 感谢分享。代码输出所有数据点。还给出警告消息In rrapply(dataRAW, f = head, n = 3, dfaslist = FALSE) : 'dfaslist' is deprecated, use classes = 'data.frame' instead - 现在以任何方式修复数据示例问题。

标签: r lapply tidyr purrr nested-lists


【解决方案1】:

您有一个具有相同属性的元素列表(例如timestampsattachments)。由于这些是不同的类型,您可以通过 enframing 列表来使用数据框而不是矩阵。 请注意元素数据可以是(第一个条目)数字或字符(否则)。 我们需要使用as.character 强制执行将这些元素放入一列所需的相同类。 这样做,数字将被转换为字符。

library(tidyverse)

dataRAW <- list(
  list(
    timestamp = 1611853326, attachments = list(list(data = list(
      list(media = list(
        uri = "posts/media/ChronikFotos_QNGAWvS8aw/144245114_3813727445333297_3682316138130576479_n_3813727441999964.jpg",
        creation_timestamp = 1611853319, media_metadata = list(
          photo_metadata = list(exif_data = list(c(taken_timestamp = 1613542113)))
        ),
        title = "Chronik-Fotos", description = "Da haben wir den Salat! <U+0001F957> \nGemischt oder grün: Verfeinert mit Frieda’s Traum Salatsauce wird der einfachste Salat zum Gaumenschmaus.\n\nProbieren Sie auch unsere Gewürze, Bouillons und verschiedene Käse! \nHier finden Sie alle unsere würzigen Produkte:  www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>'<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E> – Saucen Bouillons Gewürze\nwww.friedas-traum.ch | shop@friedas.ch | Tel. 055 0"
      ))
    ))),
    data = list(post = 1)
  ),
  list(
    timestamp = 1611860575, attachments = list(list(data = list(
      list(media = list(
        uri = "posts/media/ChronikFotos_QNGAWvS8aw/143276316_3813978641974844_3663341405860849380_n_3813978635308178.png",
        creation_timestamp = 1611860403, media_metadata = list(
          photo_metadata = list(exif_data = list(c(taken_timestamp = 1612935033)))
        ),
        title = "Chronik-Fotos", description = "Früher über die Gasse – heute im Online- Shop: <U+0001D5D9><U+0001D5FF><U+0001D5F6><U+0001D5F2><U+0001D5F1><U+0001D5EE>’<U+0001D600> <U+0001D5E7><U+0001D5FF><U+0001D5EE><U+0001D602><U+0001D5FA> Produkte. \n\nWas im Restaurant Löwen in Spreitenbach begann, geht heute online weiter: Sie erhalten 100% Geschmack!\n\nEinfach bestellen im Shop: www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>’<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E> – Saucen, Bouillons, Gewürze\nshop@friedas.ch  | Tel. +41 (0) 55 0"
      ))
    ))),
    data = list(c(post = "Früher über die Gasse – heute im Online- Shop: <U+0001D5D9><U+0001D5FF><U+0001D5F6><U+0001D5F2><U+0001D5F1><U+0001D5EE>’<U+0001D600> <U+0001D5E7><U+0001D5FF><U+0001D5EE><U+0001D602><U+0001D5FA> Produkte. \n\nWas im Restaurant Löwen in Spreitenbach begann, geht heute online weiter: Sie erhalten 100% Geschmack!\n\nEinfach bestellen im Shop: www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>’<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E> – Saucen, Bouillons, Gewürze\nshop@friedas.ch  | Tel. +41 (0) 55 0"))
  ),
  list(
    timestamp = 1612948020, attachments = list(list(data = list(
      list(media = list(
        uri = "posts/media/ChronikFotos_QNGAWvS8aw/143732770_3813831571989551_5247994518213519901_n_3813831568656218.png",
        creation_timestamp = 1611856188, media_metadata = list(
          photo_metadata = list(exif_data = list(c(taken_timestamp = 1617631305)))
        ),
        title = "Chronik-Fotos", description = "<U+0001D5E1><U+0001D5EE><U+0001D5F0><U+0001D5F5> <U+0001D5EE><U+0001D5F9><U+0001D601><U+0001D5F2><U+0001D5FA> <U+0001D5E5><U+0001D5F2><U+0001D607><U+0001D5F2><U+0001D5FD><U+0001D601> von Hand gemischt und abgefüllt: Frieda’s Salatsaucen sind beliebt wie eh und je. <U+0001F44C>\n\nFrüher der Renner im Restaurant Löwen in Spreitenbach, heute: DER Hit zum Bestellen für Sie zu Hause.\n\nProbieren Sie auch unsere Bouillons, Gewürze und unseren Käse! \n\nHier geht’s zum Shop:  www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>’<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E>® – Saucen Bouillons Gewürze\nshop@friedas.ch | Tel. 055 0"
      ))
    ))),
    data = list(c(post = "<U+0001D5E1><U+0001D5EE><U+0001D5F0><U+0001D5F5> <U+0001D5EE><U+0001D5F9><U+0001D601><U+0001D5F2><U+0001D5FA> <U+0001D5E5><U+0001D5F2><U+0001D607><U+0001D5F2><U+0001D5FD><U+0001D601> von Hand gemischt und abgefüllt: Frieda’s Salatsaucen sind beliebt wie eh und je. <U+0001F44C>\n\nFrüher der Renner im Restaurant Löwen in Spreitenbach, heute: DER Hit zum Bestellen für Sie zu Hause.\n\nProbieren Sie auch unsere Bouillons, Gewürze und unseren Käse! \n\nHier geht’s zum Shop:  www.friedas-traum.ch/\n\n<U+0001D46D><U+0001D493><U+0001D48A><U+0001D486><U+0001D485><U+0001D482>’<U+0001D494> <U+0001D47B><U+0001D493><U+0001D482><U+0001D496><U+0001D48E>® – Saucen Bouillons Gewürze\nshop@friedas.ch | Tel. 055 0"))
  )
)

dataRAW %>%
  enframe()
#> # A tibble: 3 × 2
#>    name value           
#>   <int> <list>          
#> 1     1 <named list [3]>
#> 2     2 <named list [3]>
#> 3     3 <named list [3]>

dataRAW %>%
  enframe() %>%
  unnest_wider(value)
#> # A tibble: 3 × 4
#>    name  timestamp attachments data            
#>   <int>      <dbl> <list>      <list>          
#> 1     1 1611853326 <list [1]>  <named list [1]>
#> 2     2 1611860575 <list [1]>  <list [1]>      
#> 3     3 1612948020 <list [1]>  <list [1]>

dataRAW %>%
  enframe() %>%
  unnest_wider(value) %>%
  # flatten list with only one element
  unnest(data) %>%
  # Enforce data to have the same type
  mutate(data = data %>% as.character()) %>%
  unnest(data) %>%
  unnest(attachments) %>%
  unnest(attachments) %>%
  unnest(attachments) %>%
  unnest(attachments) %>%
  unnest_wider(attachments) %>%
  select(name, timestamp, creation_timestamp, title, data)
#> # A tibble: 3 × 5
#>    name  timestamp creation_timestamp title         data                        
#>   <int>      <dbl>              <dbl> <chr>         <chr>                       
#> 1     1 1611853326         1611853319 Chronik-Fotos "1"                         
#> 2     2 1611860575         1611860403 Chronik-Fotos "Früher über die Gasse – he…
#> 3     3 1612948020         1611856188 Chronik-Fotos "<U+0001D5E1><U+0001D5EE><U…

reprex package 创建于 2021-11-04 (v2.0.1)

【讨论】:

  • 谢谢让我试试这个
  • 代码一直运行到第 4 行,即第二个 unnest(value)。然后它给我一个错误错误:无法组合..1$data ..20$data 。回溯: 1. %&gt;%(...) 3. tidyr:::unnest.data.frame(., data) 4. tidyr::unhop(data, any_of(cols), keep_empty = keep_empty, ptype = ptype) 5. tidyr:::df_uncop_info(cols, ptype) 6. vctrs::vec_unhop(pieces, ptype = col_ptype) 8. vctrs::vec_default_ptype2(...) 9. vctrs::stop_incompatible_type(...) 10. vctrs :::stop_incompatible(...) 11. vctrs:::stop_vctrs(...)
  • @Simone 我编辑了示例数据和解决问题的答案
  • 它在第 11 行抛出错误 unnest_wider(attachments)saying Error: Can't combine `..1$uri` &lt;list&gt; and `..30$uri` &lt;character&gt;. Backtrace: 1. %&gt;%(...) 2. tidyr::unnest_wider(.,附件,names_repair = "unique") 3. tidyr::unhop(data, any_of(col), keep_empty = TRUE) 4. tidyr:::df_unhop_info(cols, ptype) 5. vctrs::vec_unhop(pieces, ptype = col_ptype ) 7. vctrs::vec_default_ptype2(...) 8. vctrs::stop_incompatible_type(...) 9. vctrs:::stop_incompatible(...) 10. vctrs:::stop_vctrs(...)
  • 你需要统一attachments的类型,就像data一样。您是否总是有一个data 元素,有时每个帖子有多个attachments?,然后使用unnest(attachments) 而不是unnest_wider(attachments) 创建多行而不是多列
猜你喜欢
  • 2023-02-06
  • 2013-07-23
  • 2013-05-11
  • 2018-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-17
相关资源
最近更新 更多