Ruby 在处理大文件时搜索和合并 CSV 文件

Posted

技术标签:

【中文标题】Ruby 在处理大文件时搜索和合并 CSV 文件【英文标题】:Ruby to Search and Combine CSV files when dealing with large files 【发布时间】:2020-04-28 16:03:22 【问题描述】:

总结

查看与此有些相符的其他问题并没有帮助,因为我仍在逐行打开文件,因此我没有用完大文件的内存。事实上,我的内存使用率很低,但创建较小的文件需要很长时间,以便我可以搜索其他 CSV 并将其连接到文件中。

问题

已经5天了,我不知道我还有多远,但它还没有退出主文件的foreach行,csv文件中有1780万条记录。有没有更快的方法来处理 ruby​​ 中的这种处理?我可以对 MacOSX 做些什么来优化它?任何建议都会很棒。

# # -------------------------------------------------------------------------------------
# # USED TO GET ID NUMBERS OF THE SPECIFIC ITEMS THAT ARE NEEDED
# # -------------------------------------------------------------------------------------
etas_title_file = './HathiTrust ETAS Titles.csv'
oclc_id_array = []
angies_csv = []
CSV.foreach(etas_title_file ,'r', :headers => true, :header_converters => :symbol) do |row| 
  oclc_id_array << row[:oclc]
  angies_csv << row.to_h
end 
oclc_id_array.uniq!


# -------------------------------------------------------------------------------------
# RUN ONCE IF DATABASE IS NOT POPULATED
# -------------------------------------------------------------------------------------

headers = %i[htid   access  rights  ht_bib_key  description source  source_bib_num  oclc_num    isbn    issn    lccn    title   imprint rights_reason_code  rights_timestamp    us_gov_doc_flag rights_date_used    pub_place   lang    bib_fmt collection_code content_provider_code   responsible_entity_code digitization_agent_code access_profile_code author]

remove_keys = %i[access rights description  source  source_bib_num isbn issn    lccn    title   imprint rights_reason_code  rights_timestamp    us_gov_doc_flag rights_date_used    pub_place   lang    bib_fmt collection_code content_provider_code   responsible_entity_code digitization_agent_code access_profile_code author]

new_hathi_csv = []
processed_keys = []
CSV.foreach('./hathi_full_20200401.txt' ,'r', :headers => headers, :col_sep => "\t", quote_char: "\0" ) do |row| 
  next unless oclc_id_array.include? row[:oclc_num]
  next if processed_keys.include? row[:oclc_num]
  puts "#row[:oclc_num] included? #oclc_id_array.include? row[:oclc_num]"
  new_hathi_csv << row.to_h.except(*remove_keys)
  processed_keys << row[:oclc_num]
end 

【问题讨论】:

这里有很多***优化。您在 1800 万个元素数组上调用 uniqueinclude?。这是第二个 CSV 中行数的 1800 万倍的最坏情况比较。因此,每行将添加 1800 万次比较最坏的情况,但除此之外,您还要为每个循环执行两次。它会完成吗……很难说。您应该改用哈希(或二进制搜索),它的运行速度会快数百万倍.. @Casper 你能用一个快速的示例或链接到一些关于在 ruby​​ 中使用哈希或二进制搜索的代码吗?我会这样做并发布结果或问题。 【参考方案1】:

据我所知,OCLC ID 是字母数字的。这意味着我们要使用 Hash 来存储这些 ID。哈希的一般查找复杂度为 O(1),而未排序数组的查找复杂度为 O(n)。

如果您使用数组,最坏情况下的查找是 1800 万次比较(要查找单个元素,Ruby 必须遍历所有 1800 万个 ID),而使用 Hash 将是一次比较。简而言之:使用 Hash 将比您当前的实现快数百万倍。

下面的伪代码将告诉您如何继续。我们将使用 Set,它类似于 Hash,但当您需要做的只是检查是否包含时会很方便:

oclc_ids = Set.new

CSV.foreach(...) 
  oclc_ids.add(row[:oclc])  # Add ID to Set
  ...


# No need to call unique on a Set. 
# The elements in a Set are always unique.

processed_keys = Set.new

CSV.foreach(...) 
   next unless oclc_ids.include?(row[:oclc_num])   # Extremely fast lookup
   next if processed_keys.include?(row[:oclc_num]) # Extremely fast lookup
   ...
   processed_keys.add(row[:oclc_num])

【讨论】:

哦,很好。感谢您解释您的意思,我熟悉这些集合,但由于某种原因总是忘记在现实生活场景中使用它们。我了解哈希表并经常使用它们。 CSV 转换为哈希。因此,您的意思是忘记使用 CSV 解析器解析 CSV,而是逐行打开文件并将该数据转换为哈希以进行搜索。 好的,所以使用集合和更智能的哈希使用方式,我花了大约 10 分钟来处理所有 1700 万条记录。非常感谢!

以上是关于Ruby 在处理大文件时搜索和合并 CSV 文件的主要内容,如果未能解决你的问题,请参考以下文章

如何打破大型csv文件,在多个核心上处理它并使用nodeJs将结果合并为一个

Ruby如何合并两个标题略有不同的CSV文件

PHP快速按行读取CSV大文件的封装类分享(也适用于其它超大文本文件)

将 CSV 文件与批处理文件合并,包括每行中的文件名

使用 Ruby 逐行读取、编辑和写入文本文件

将 csv 文件与 pandas 连接时内存不足