【问题标题】:Ruby CSV.foreach start at specific rowRuby CSV.foreach 从特定行开始
【发布时间】:2017-07-14 14:28:16
【问题描述】:

我看过一些关于此的帖子,没有真正的答案或过时的答案,所以我想知道是否有任何新的解决方案。我有一个巨大的 CSV 我需要读入。我不能在它上面调用 open() ,因为它会杀死我的服务器。我别无选择,只能使用 .foreach()。

这样做,我的脚本需要 6 天才能运行。我想看看我是否可以通过使用线程并将任务分成两个或四个来减少它。因此,一个线程读取第 1-n 行,而一个线程同时读取第 n+1-end 行。

所以我需要能够在一个线程中只读取文件的后半部分(稍后如果我将其拆分为更多线程,则只需通过特定行的特定行)。

Ruby 中有没有办法做到这一点?这可以从某一行开始吗?

CSV.foreach(FULL_FACT_SHEET_CSV_PATH) do |trial|

编辑: 只是想知道我的一个线程是什么样子的:

threads << Thread.new { 
CSV.open('matches_thread3.csv', 'wb') do |output_csv|

  output_csv << HEADER
  count = 1
  index = 0

    CSV.foreach(CSV_PATH) do |trial|
        index += 1
        if index > 120000 
            break if index > 180000
            #do stuff
        end
    end
end
}

但正如您所见,它必须迭代文件,直到它开始记录 120,000。因此,目标是通过从第 120,000 行开始读取来消除读取第 120,000 行之前的所有行。

【问题讨论】:

  • 6 天真的很长。即使您将任务并行化为 6 个进程,这也至少需要一天时间。我认为您真的应该找到提高速度的方法,也许可以提供更多关于我们可以提供帮助的每一行的过程的详细信息。
  • CSV.forEach 是一种符合 ruby​​ 标准的快速方法。因此,仅通过优化它而不丢掉大量 CPU,您可能不会获得很大的改进。我建议你尝试用 Python 或其他语言来解析它。
  • @Nerve 除非 CSV 存在缺陷,否则将语言从 Ruby 切换到 Python 不会有太多好处。
  • @MarkThomas 整个过程现在需要 3 天(因为我删除了一些搜索词)。看起来它以每行 2.5 秒的速度读取每一行。但是我在那里有遍历另一个 CSV 的逻辑,并且其中有更多循环。这是一种匹配算法,可以进行转储以最终上传到数据库。通常我会为此使用 db 和类似 solr 的东西,但这不是我的项目。我只有这一项任务。
  • 听起来你有一个 N+1 风格的问题。第二个 CSV 是否适合内存?否则,也许您应该生成后台作业。

标签: ruby-on-rails ruby multithreading csv foreach


【解决方案1】:

但正如您所见,它必须迭代文件,直到它开始记录 120,000。因此,目标是通过从第 120,000 行开始读取来消除读取第 120,000 行之前的所有行。

不可能。 CSV 文件的内容只是一个文本块,带有一些逗号和换行符。如果不知道行 N-1 的结束位置,您将无法知道文件行 N 中的哪个偏移量开始。要知道这一点,您必须知道第 N-1 行从哪里开始(参见递归?)并读取文件直到看到它在哪里结束(遇到不属于字段值的换行符)。

如果您的所有行都是固定大小的,则例外。在这种情况下,您可以直接寻求偏移 120_000 * row_size。不过,我还没有看到这样的文件。

【讨论】:

    【解决方案2】:

    如果仍然相关,您可以在之后使用.with_index 执行类似操作:

    rows_array = []
    
    CSV.foreach(path).with_index do |row, i|
      next if i == 0 #skip first row
      rows_array << columns.map { |n| row[n] }
    end
    

    【讨论】:

      【解决方案3】:

      根据我对您的 Ruby 问题的理解,它可能会对您有所帮助。

      require 'csv'
      csv_file = "matches_thread3.csv"
      # define one Constant Chunk Size for Jobs
      CHUNK_SIZE = 120000
      # split - by splitting (\n) will generate an array of CSV records
      # each_slice - will create array of records of CHUNK_SIZE defined
      
      File.read(csv_file).split("\n").drop(1).each_slice(CHUNK_SIZE).with_index 
      do |chunk, index|   
      
        data = []
         # chunk will be work as multiple Jobs of 120000 records 
      
        chunk.each do |row|
         data << r
         ##do stuff
        end
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-07
        • 2023-03-19
        • 2017-04-11
        • 2020-11-05
        • 2015-08-27
        • 2012-10-30
        • 2013-05-11
        相关资源
        最近更新 更多