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 万个元素数组上调用unique
和 include?
。这是第二个 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将结果合并为一个