簡體   English   中英

將列表(帶有嵌套向量)非規范化/強制轉換為 R 中的 data.frame

[英]denormalize/coerce list (with nested vectors) to data.frame in R

我正在閱讀一個 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)如何在 R 中非規范化嵌套列表? ,但結構不同( round / diff / saldo結構在這里與time / value相比轉置),以及(2)將逗號分隔的列拆分為單獨的行,但time是向量,而不是逗號分隔的元素喜歡director 我希望這種不同的結構有所幫助。

Reduce(rbind,lapply(l,data.frame))

為了補充@lmo和@submartingale的想法/方法,這是一個purrr / tidyverse版本,該版本將列出的每個嵌套轉換為data.frame / tibble(通過復制name和id的父元素),然后將它們堆疊為一個tibble 。

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

謝謝你們提出的如此簡潔和可概括的建議。

一個簡單的基本R方法是使用lapplydata.frame返回data.frame的列表,然后將其與rbind一起使用do.call將data.frame組合為單個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))

打印的輸出看起來相同,但是這兩個變量的基礎數據類型已更改。

這可行,但不理想,因為(a)需要處理新data.frame中的每個向量,並且(b)每個向量的類型都是顯式的( 例如 purrr:map_chrpurrr: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
)

四年后,我仍然每個月使用一兩次。 )yaml 包提供了一個地圖處理程序 在這種情況下,每個 map/person 都被轉換為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(...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM