使用 purrr::map 简化 xml。添加简化列表的索引号

Using purrr::map to simplify xml. Add index number of the simplified list

坚持了几个小时。

我正在简化一个超过 15000 行的 xml 文件,其中包含有关肺功能测试的数据。每个 xml 文件包含多个测试。使用 xml2map 我可以将数据放入长度为 n-of-tests.

的列表中

这是文件中两个测试列表的摘录:

[[1]]
[[1]][[1]]
    Name       UM    Value 
"MEF75%"    "L/s"   "6.82" 

[[1]][[2]]
     Name        UM     Value Predicted  PercPred    ZScore       LLN       ULN 
   "FEV1"       "L"    "3.83"    "4.16"      "92"   "-0.62"    "3.27"    "5.01" 


...

[[2]]
[[2]][[1]]
    Name       UM    Value 
"MEF75%"    "L/s"   "6.65" 

[[2]][[2]]
     Name        UM     Value Predicted  PercPred    ZScore       LLN       ULN 
   "FEV1"       "L"    "3.79"    "4.16"      "91"   "-0.69"    "3.27"    "5.01" 
....

我可以使用 map_dfr 或 bind_rows 轻松地将其转换为 tibble 但是 我似乎无法弄清楚的是如何添加列表索引[[1]] 或 [[2]] 作为小标题中的一列。如果我使用 .id 参数,它只是按顺序对行进行编号,不引用列表:

map(trials, ~xml_find_all(., "AdditionalData/Parameters/Parameter")) %>%
map (., ~xml_attrs(.)) %>% bind_rows(. , .id = "test")
A tibble: 104 x 9
   test    Name      UM    Value Predicted PercPred ZScore LLN   ULN  
   <chr> <chr>     <chr> <chr> <chr>     <chr>    <chr>  <chr> <chr>
 1 1     MEF75%    L/s   6.82  NA        NA       NA     NA    NA   
 2 2     FEV1      L     3.83  4.16      92       -0.62  3.27  5.01 
 ...
 53 53    MEF75% L/s   6.65  NA        NA       NA     NA    NA 
 54 54    FEV1  L     3.79  4.16      91       -0.69  3.27  5.01 

我想要得到的是(第一列中的差异 - “测试”):

map(trials, ~xml_find_all(., "AdditionalData/Parameters/Parameter")) %>%
map (., ~xml_attrs(.)) %>% bind_rows(. , .id = "test")
A tibble: 104 x 9
   test    Name      UM    Value Predicted PercPred ZScore LLN   ULN  
   <chr> <chr>     <chr> <chr> <chr>     <chr>    <chr>  <chr> <chr>
 1 1     MEF75%    L/s   6.82  NA        NA       NA     NA    NA   
 2 1     FEV1      L     3.83  4.16      92       -0.62  3.27  5.01 
 ...
 53 2    MEF75% L/s   6.65  NA        NA       NA     NA    NA 
 54 2    FEV1  L     3.79  4.16      91       -0.69  3.27  5.01 

这是 do-able 和 tidyverse 吗?我应该尝试使用 base-R 循环来解决吗?

感谢任何帮助,谢谢。 -BF

要根据可变长度列表元素创建 ID 列,我们可以重复列表中元素的索引(参见 this),元素数量 次。

x <- list(
  list(
    c(Name = "a", UM = "L/s", Value = "1"),
    c(Name = "a", UM = "L", Value = "3.1", Predicted = "1")
  ),
  list(
    c(Name = "b", UM = "L", Value = "2"),
    c(Name = "b", UM = "L/s", Value = "4", Predicted = "1.1"),
    c(Name = "b", UM = "L/s", Value = "4", Predicted = "1.1", ZScore = "-.50")
  ),
  list(1)
)
y <- sapply(x, length)
unlist(Map(function(n, i) rep(i, n), y, seq_along(y)), use.names = F)
#> [1] 1 1 2 2 2 3

或使用tidyverse函数

imap(map_int(x, length), ~rep(.y, .x)) %>% flatten_int()
#> [1] 1 1 2 2 2 3

并将其添加为 ID 列。 如果测试次数相等(原始 post 中为 2),只需 rep(1:length(x), each = 2) 其中 each 参数是测试次数。

我不太清楚你在 post 中显示的列表是命名向量还是 data.frames 有 1 行。在任何情况下 - 一个替代方案,使用 set_names 因为 bind_rows 可以采用命名列表:

list(
  data.frame(x = 1, y = 2),
  data.frame(x = 10, y = 15)
) %>%
  set_names(1:2) %>%
  bind_rows(.id = "test") # %>% a character column
  # mutate(test = as.numeric(test))

#>   test  x  y
#> 1    1  1  2
#> 2    2 10 15