【问题标题】:Extract data from large XML using R使用 R 从大型 XML 中提取数据
【发布时间】:2021-12-08 06:49:01
【问题描述】:

我有一个很大的 XML,由于内存不足,我无法在 R 中完全解析。我只想提取一些特定的列。我发现其他人问过类似的问题:

How to read large (~20 GB) xml file in R? Storing specific XML node values with R's xmlEventParse

我无法让它与我的数据一起工作,它运行,但没有返回数据。我确实尝试将建议的解决方案调整为我的 XML,但它仍然不起作用。可能是我缺乏XML知识。下面是我的 XML 数据示例,其中 cl, clssc, clp, clpssc, primclp 是列。如何在不先解析整个文档的情况下仅提取 clclssc

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<abc:abc xmlns:abc="http://abc/abc" xsi:schemaLocation="http://abc/abc lala_20Q2.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <chcp>
    <cl>2000000</cl>
    <clssc>10934</clssc>
    <clp>200000</clp>
    <clpssc>10934</clpssc>
    <primclp>Y</primclp>
  </chcp>
  <chcp>
    <cl>2000000</cl>
    <clssc>10934</clssc>
    <clp>200000</clp>
    <clpssc>10934</clpssc>
    <primclp>Y</primclp>
  </chcp>
  <chcp>
    <cl>2000000</cl>
    <clssc>10934</clssc>
    <clp>2000000</clp>
    <clpssc>10934</clpssc>
    <primclp>Y</primclp>
  </chcp>
</abc:abc>

【问题讨论】:

  • 如果您能提供您的解决方案,但我们可以一起尝试修复它,这将是有帮助的。重要的是,实现必须具有流式传输功能(例如 xslt 3.0) - 但我想,链接问题的答案已经满足了这一点。
  • 顺便说一句,为什么要走这么大的路(XML解析巨大的XML)-通过grep的简单过滤步骤也应该可以解决问题(如果XML打印得很漂亮,这意味着它有换行符每个元素),还是?
  • @AydinK。 “grep”是什么意思?确实它打印得很漂亮,但是如果使用模式匹配,我仍然需要将整个文件加载到 R 中,即 >20gb。还是?
  • grep from bash 放入另一个文件可能更简单,而不是专门将其全部加载到 R 中?
  • @runr bash 对我来说是新事物,所以对我来说,这听起来并不容易。不过我会看看的。

标签: r xml


【解决方案1】:

disk.frame 包用于处理中等大小的数据。它支持将数据批量转换为.fst,并通过fst package 实现快速I/O,并通过data.table 实现快速数据操作。这里,data.table 包的dtplyr 接口用于最后的争吵。

第一步:准备输入文件*

1.1 创建一个文件夹并将您的 .xml 文件放入其中。

1.2 删除前两行和最后一行,留下一个具有以下结构的 .xml 文件:

  <chcp>
    <cl>2000000</cl>
    <clssc>10934</clssc>
    <clp>200000</clp>
    <clpssc>10934</clpssc>
    <primclp>Y</primclp>
  </chcp>
  <chcp>
    <cl>2000000</cl>
    <clssc>10934</clssc>
    <clp>200000</clp>
    <clpssc>10934</clpssc>
    <primclp>Y</primclp>
  </chcp>
  <chcp>
    <cl>2000000</cl>
    <clssc>10934</clssc>
    <clp>2000000</clp>
    <clpssc>10934</clpssc>
    <primclp>Y</primclp>
  </chcp>

第二步:设置disk.frame

library(tidyverse)
library(disk.frame)

path <- file.path(file.choose()) # filepath to your folder containing .fst
setup_disk.frame(workers = 10) # adjust this to your machine
options(future.globals.maxSize = Inf)
old <- getOption("scipen") ; options(scipen = 100) # prevent scientific numbers later

第 3 步:将 .xml 转换为 .fst

l <- csv_to_disk.frame( #works for .xml
  paste0(path, "b.xml"), # replace b.xml with your filename (in folder)
  outdir = paste0(path, "combined.df"),
  in_chunk_size = 7,
  backend = "data.table", header = F)

这会在您的 R 环境中提供类 "disk.frame" "disk.frame.folder" 的对象 l。您现在应该有一个子文件夹“combined.df”,其中包含您指定目录中的一堆 .fst 文件。

第四步:读入所需列并整理数据

cbind(
  get_chunk(l, 1) %>%
    `[`(seq(2, nrow(.), 2)),  
  get_chunk(l, 2) %>%
    `[`(seq(1, nrow(.), 2))
) %>%
  rename("cl" = 1, "clssc" = 2) %>%
  mutate(across(.fns = parse_number)) %>%
  as_tibble() # omit this to keep data.table

# A tibble: 3 x 2
       cl clssc
    <dbl> <dbl>
1 2000000 10934
2 2000000 10934
3 2000000 10934

最后,别忘了恢复科学记数法选项options(scipen = old)

*注意:第 1 步可以通过使用块大小或通过在 R 中对 .xml 进行一些操作来避免。我不知道该怎么做(目前)。

注意2:建议仔细阅读disk.frame 文档以获取有关如何正确设置您的机器的提示。

【讨论】:

    【解决方案2】:

    在 Windows 机器上

    这是一种方法,您可以使用 Windows findstr-command 的输出来使用 data.table::fread() 导入数据。它在将数据加载到 R 之前使用 windows 版本的“grep”过滤数据。这样您就不会很快遇到内存问题。

    xml 的位置:e:/testdata.xml

    进一步的解释在下面的数据的 cmets 中

    library(data.table)
    # Import output from windows findstr-command
    #  assumes location of data is e:/testdata.xml
    #  !! use \\ in path, else findstr does not undeerstand !!
    DT <- data.table::fread(cmd = 'findstr "<clssc> <cl>" e:\\testdata.xml', 
                            sep = "\n", col.names = "line", header = FALSE )
    #                    line
    # 1:     <cl>2000000</cl>
    # 2: <clssc>10934</clssc>
    # 3:     <cl>2000000</cl>
    # 4: <clssc>10934</clssc>
    # 5:     <cl>2000000</cl>
    # 6: <clssc>10934</clssc>
    
    # Extract data from raw line
    DT[, name  := gsub("^<(.+?)>.*$", "\\1", line)]
    DT[, value := gsub("^.*>([0-9]+?)<.*$", "\\1", line)]
    #                    line  name   value
    # 1:     <cl>2000000</cl>    cl 2000000
    # 2: <clssc>10934</clssc> clssc   10934
    # 3:     <cl>2000000</cl>    cl 2000000
    # 4: <clssc>10934</clssc> clssc   10934
    # 5:     <cl>2000000</cl>    cl 2000000
    # 6: <clssc>10934</clssc> clssc   10934
    
    # Build some id's
    DT[, id := rowid(name)]
    
    # Cast to wide format
    dcast(DT, id ~ name, value.var = "value")
    
    #    id      cl clssc
    # 1:  1 2000000 10934
    # 2:  2 2000000 10934
    # 3:  3 2000000 10934
    

    在 unix 机器上
    我无法测试,因为我只在这个位置使用 windows。
    将 findstr-command(和正则表达式)中的 cmd='...' 部分替换为您系统的 grep-command。

    【讨论】:

      猜你喜欢
      • 2015-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-22
      • 1970-01-01
      • 1970-01-01
      • 2015-06-12
      相关资源
      最近更新 更多