【问题标题】:denormalize/coerce list (with nested vectors) to data.frame in R非规范化/强制列表(带有嵌套向量)到 R 中的 data.frame
【发布时间】:2017-11-11 20:56:24
【问题描述】:

我正在读取一个 yaml 文件,例如

- person_id: 111
  person_name: Russell
  time:
  - 1
  - 2
  - 3
  value:
  - a
  - b
  - c
- person_id: 222
  person_name: Steven
  time:
  - 1
  - 2
  value:
  - d
  - e

我想非规范化为:

  person_id person_name time value
1       111     Russell    1     a
2       111     Russell    2     b
3       111     Russell    3     c
4       222      Steven    1     d
5       222      Steven    2     e

我有一个解决方案,但我希望有更简洁的方法。这是嵌套列表:

l <- list(
  list( 
    person_id   = 111L,
    person_name = "Russell", 
    time        = 1:3, 
    value       = letters[1:3]
  ),
  list( 
    person_id   = 222L,
    person_name = "Steven", 
    time        = 1:2, 
    value       = letters[4:5]
  )
)   

关于可能的重复,这个问题类似于(1)How to denormalize nested list in R?,但结构不同(round/diff/saldo结构与time/value相比在这里被转置) 和 (2) Split comma-separated column into separate rows,但 time 是向量,而不是像 director 这样的逗号分隔元素。我希望这种不同的结构有所帮助。

【问题讨论】:

  • 这是一个简单的基础 R one 班轮:do.call(rbind, lapply(l, data.frame))
  • @lmo,太棒了。我喜欢lapply() 如何复制person_idperson_name 的父变量。如果您将此作为回复发布,我很乐意投票。

标签: r purrr


【解决方案1】:

一个简单的基本 R 方法是使用 lapplydata.frame 返回 data.frames 列表,然后使用 do.callrbind 将 data.frames 组合成单个 data.frame 对象。

do.call(rbind, lapply(l, data.frame))

返回

  person_id person_name time value
1       111     Russell    1     a
2       111     Russell    2     b
3       111     Russell    3     c
4       222      Steven    1     d
5       222      Steven    2     e

请注意,person_name 和 value 将是因子向量,使用起来可能很烦人。如果需要,您可以使用 stringsAsFactors 参数将这些转换为字符向量。

do.call(rbind, lapply(l, data.frame, stringsAsFactors=FALSE))

打印的输出看起来一样,但是这两个变量的底层数据类型发生了变化。

【讨论】:

    【解决方案2】:
    Reduce(rbind,lapply(l,data.frame))
    

    【讨论】:

      【解决方案3】:

      为了补充 @lmo 和 @submartingale 的想法/方法,这里有一个 purrr/tidyverse 版本,它将列出的每个嵌套转换为 data.frame/tibble(通过复制 name 和 id 的父元素),然后将它们堆叠到一个小标题。

      l %>% 
        purrr::map_df(tibble::as_tibble)
      

      感谢大家提出如此简洁和概括的东西。

      【讨论】:

        【解决方案4】:

        这可行,但不太理想,因为 (a) 新 data.frame 中的每个向量都需要处理,并且 (b) 每个向量的类型是明确的(例如,@987654321 @vspurrr:map_int)

        # Step 1: Determine how many time the 'parent' rows need to be replicated.
        values_per_person <- l %>% 
          purrr::modify_depth(2, length) %>% 
          purrr::map_int("value")
        
        # Step 2: Pull out the parent rows and replicate the elements to match `time`.
        id_replicated <- l %>% 
          purrr::map_int("person_id") %>% 
          rep(times=values_per_person)    
        name_replicated <- l %>%
          purrr::map_chr("person_name") %>% 
          rep(times=values_per_person)
        
        # Step 3: Pull out the nested/child rows.
        time <- l %>%
          purrr::modify_depth(1, "time") %>% 
          purrr::flatten_int()
        value <- l %>%
          purrr::modify_depth(1, "value") %>% 
          purrr::flatten_chr()
        
        # Step 4: Combine the vectors in a data frame.
        data.frame(
          person_id   = id_replicated,
          person_name = name_replicated,
          time        = time,
          value       = value
        )
        

        【讨论】:

          【解决方案5】:

          四年后,我仍然每月使用一次或两次。)yaml 包提供了map handler。在这种情况下,每个地图/人都被转换为tibble。然后dplyr::bind_rows() 堆叠所有小标题以创建更长的单个小标题。

          path_yaml |> # Replace this line with code below to see a working example.
            yaml::read_yaml(
              handlers = list(map = \(x) tibble::as_tibble(x))
            ) |> 
            dplyr::bind_rows()
          

          额外细节:使用这个简单的数据集,甚至不需要处理程序——bind_rows() 会自动转换每个部分。但我怀疑它总是知道如何在堆叠之前强制每张地图。此外,这个显式处理程序可以更好地传达意图。

          如果您想使用可重现的示例,请将文件路径(,第一行)替换为

          string <- 
          "- person_id: 111
            person_name: Russell
            time:
            - 1
            - 2
            - 3
            value:
            - a
            - b
            - c
          - person_id: 222
            person_name: Steven
            time:
            - 1
            - 2
            value:
            - d
            - e
          "
          
          textConnection(string) |> 
            yaml::read_yaml(...
          

          【讨论】:

            猜你喜欢
            • 2022-01-20
            • 2017-01-12
            • 2017-12-06
            • 2015-01-10
            • 2011-06-08
            • 2020-10-29
            • 2020-05-06
            • 1970-01-01
            • 2016-11-20
            相关资源
            最近更新 更多