【问题标题】:Iterate through csv by column按列遍历 csv
【发布时间】:2015-08-18 14:36:52
【问题描述】:

我有一堆大的(约 400 万个值)csv 文件,我需要获取每一列并创建一个文件,以一种可以由不同程序解释的方式组织值。列的长度完全不同(200 万到 1000 个值之间),每个 csv 可能有 4 到 100 列。

我可以将整个内容加载到 pandas.DataFrame 中,然后遍历该系列,但这很慢:

import pandas as pd
import re
import os
for f in os.listdir(folder):
    gc = pd.read_csv('{}/{}'.format(folder, f))
    strain = f[:-7] # files have regular name structure, this just gets the name

    with open('{}.txt'.format(strain), 'w+') as out_handle:
        for column in gc:
            series = gc[column]
            for i in range(len(series))[::10]:
                pos = i + 1
                gc_cont = s[i]
                if pd.isnull(gc_cont):
                    continue
                out_handle.write('{} {}'.format(pos, gc_cont) 
                # I'm writing other info, but it's not important here 

也许用一百万 + NaN 值填充较小的列并将整个内容加载到内存中会产生很大的性能成本?无论如何,我认为逐列阅读会更有效率,但我找不到这样做的方法。

Pandas 可以做到chunk size (docs),但这是对行进行分块。如果我逐行写入,我要么必须一次打开 4-100 个文件,要么多次遍历原始文件以写入每个单独的列。这些方法中的任何一种是否合适,或者我缺少什么?

【问题讨论】:

    标签: python csv pandas dataframe


    【解决方案1】:

    usecols 选项到 read_csv 怎么样?此外,您可以考虑使用squeeze 选项来返回pandas.Series,如果您只使用单列,这可能会更快。类似的东西

    cols = ['col0', 'col1', 'col2'] # the columns you want to load
    for col in cols:
        data = pandas.read_csv(..., usecols=[col], squeeze=True)
        # format column data etc.
    

    这里是文档

    usecols:类数组

    返回列的子集。结果是更快的解析时间和更低的内存使用量。

    挤压:布尔值,默认为 False

    如果解析后的数据只包含一列,则返回一个系列

    【讨论】:

    • 这里唯一的问题是获取列名,因为它们的长度是可变的。但我想我只需要阅读 csv 的第一行就可以做到这一点。
    【解决方案2】:

    最简单的方法可能是将整个文件读入 pandas df 并将每一列写入自己的文件。

    import pandas as pd
    import os
    
    for f in os.listdir(folder):
        gc = pd.read_csv('{}/{}'.format(folder, f))
        strain = f[:-7]
    
        for col in gc.columns:
            temp = gc.col
            temp.to_csv('new_path'+strain+col)
    

    这样,即使您有一个消耗内存的操作,您也只是将较大的帧拆分为列并创建自己的文件,这样会更容易处理。

    【讨论】:

      【解决方案3】:

      如何将整个文件读入字符串并将其包装在 StringIO(或 BytesIO,取决于 Python 2/3)中?然后将其用作 csv 文件并每列迭代一次。

      类似这样的:

      with open('{}/{}'.format(folder, f)) as in_file:
          data = in_file.read()
      
      for index in number_of_columns: # not sure how to derive this
          csv_file = csv.reader(StringIO.StringIO(data))
          for data_row in csv_file:
              # do what you want with data_row[index]
      

      编辑:

      这似乎并没有解决性能问题。根据您的 cmets,我认为提高性能的最佳方法是一次打开所有 4 到 100 个文件,并在您阅读时写入它们。我认为现代操作系统不会有任何问题。这在算法上是最简单的,而且还可以最大限度地减少内存使用。任何版本都需要它在读取和解析和写入方面所做的工作。我认为可能存在磁盘磁头争用的风险,但我猜我认为这不会成为问题。

      我认为只有测试才能显示它是否工作得更快 - 这并不明显。

      那就是这样的

      with open('{}/{}'.format(folder, f)) as in_file:
          csv_file = csv.reader(in_file)
      
          # open up the files needed and put them into file_list 
      
          for data_row in csv_file:
              for index,datum in data_row:
                  if datum != "":
                      file_list[index].write(datum)
      

      我没有完全模仿你的写作方案,但我相信你明白我的意思。显然你需要一种机制来找到正确数量的文件(也许看看第一行?),然后关闭它们等等。

      【讨论】:

      • 我的想法是解决所有NaNs 被加载到内存中的问题,但我们仍在将整个文件加载到内存中,对吗?
      • 是的,就是这个主意。我们将其全部加载为字符串,但我们不构建一个大表。但我不相信它会解决性能问题,因为我真的不明白为什么它很慢。您可以尝试分析以查看慢速位在哪里,而不是进行试验。
      • 这需要学习分析,而我根本没有做过(但......我知道我应该这样做)。事实证明,它在数百万行中运行了数百次,这就是问题所在,而不是将其保存在内存中。你的方法有同样的问题,因为即使我到达没有价值的列,那里仍然有一条线。如果我将整个内容加载到 DataFrame 中,但在迭代之前过滤每个系列以删除 Nans,它会加快很多速度。
      • 不确定 - 你是说你认为你对自己的进步感到满意还是想要更多的想法?
      • 好吧,我的解决方案有点工作,但并没有真正回答问题(按列遍历 csv)。我想我可以添加自己的答案...
      猜你喜欢
      • 2019-02-26
      • 2021-03-04
      • 2018-03-15
      • 2020-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多