【问题标题】:Is there a way to pass an open file to pandas read_csv有没有办法将打开的文件传递给 pandas read_csv
【发布时间】:2020-09-20 07:08:51
【问题描述】:

我有一个 csv 文件太大而无法一次全部读入内存(约 46 GB),我想要一个可以放入内存的数据子集。我可以读取文件的一部分,通过使用先前创建的过滤索引进行内部连接来过滤掉我不想要的内容,然后继续到文件的下一部分,如下所示:

    with open('Filtered_By_Month/all_data.csv','r') as f:
        cols=np.array(f.readline().strip('\n').split(','))#column names
        data=[]
        df=None
        for i in f:
            data.append(i.strip('\n').split(',')
            if len(data)==1000000:
                df_sub=pd.DataFrame(data,columns=cols).set_index('KEY_')
                df_sub=df_sub.join(filtered_keys,how='inner')
                data=[]
                if df is None:
                    df=df_sub
                else:
                    df=pd.concat([df,df_sub])

这似乎可以解决问题,只是速度非常慢。另一种方法是使用 pandas read_csv 函数以及 nrows 和 skip_rows 参数,但这也变得非常慢,因为每次我调用 read_csv 方法时,文件都会重新打开并且光标位于第一行,我必须遍历这些行,直到我经过skip_rows。有没有办法将这两种方法结合起来,以便我将一个文件传递给已经打开的 read_csv,因此我不需要重新开始光标。

【问题讨论】:

  • pd.read_csv 有一个 nrows 选项。我相信你的案子可以用pd.read_csv('file.csv', nrows=1000000)来完成。
  • 根据文档,read_csv 确实接受类似文件的对象。

标签: python pandas


【解决方案1】:

狭义的回答:是的

您可以提供engine='python'nrows=N 参数来获取pandas 阅读器在文本文件中离开的位置,或者对单个打开的文件对象多次使用pd.read_csv

Pandas 在文件中搜索分隔符,例如逗号换行符。默认情况下(基于您提供的参数),pandas 使用经过优化的 C 编译阅读器,速度更快,但功能更少。使用此阅读器,光标对象并不总是留在最后读取行的末尾,有时会耗尽文件阅读器,有时它会留在下一行的中间。

使用具有两列和 1,000,000 行的简单约 200MB 文本文件:

In [10]: # create some data
    ...: pd.DataFrame(
    ...:     {'A': np.arange(0, 1e6), 'B': np.arange(1e6, 0, -1)}
    ...: ).to_csv('sample.csv', index=False)

我们可以看到,使用engine='C' 解析器读取除最后两行之外的所有行会将光标留在文件末尾(什么都不会打印):

In [11]: %%time
    ...: with open('sample.csv', 'r') as f:
    ...:     top = pd.read_csv(f, nrows=999998, header=0, engine='c')
    ...:     print(f.read())

CPU times: user 130 ms, sys: 2.99 ms, total: 133 ms
Wall time: 132 ms

使用engine='python',文件对象留在行尾,我们可以读取文件的其余部分,或者再次调用pd.read_csv

In [12]: %%time
    ...: with open('sample.csv', 'r') as f:
    ...:     top = pd.read_csv(f, nrows=999998, header=0, engine='python')
    ...:     print(f.read())
999998,2
999999,1

CPU times: user 2.86 s, sys: 150 ms, total: 3.01 s
Wall time: 2.96 s

但是,python 阅读器的速度要慢得多。

更好的答案:不要

提供chunksize 作为pd.read_csv 的参数将返回一个迭代器。

来自IO tools: Iterating through files chunk by chunk 上的 pandas 文档:

通过将chunksize 指定为read_csv,返回值将是TextFileReader 类型的可迭代对象

使用这种分块读取模式可以让我们重新使用 C 引擎。上面的例子:

In [13]: %%time
    ...: reader = pd.read_csv('sample.csv', chunksize=100000, header=0, engine='c')
    ...: for chunk in reader:
    ...:     tail = chunk.iloc[-2:]
    ...: print(tail)
             A  B
999998  999998  2
999999  999999  1
CPU times: user 136 ms, sys: 9.86 ms, total: 146 ms
Wall time: 146 ms

我们现在几乎不比没有分块的 C 解析器慢,即使对一个非常小的文件进行 10 次迭代也是如此。由于这个文件变得比内存大,使用分块读取将大大优于任何纯 python 文件处理。

您可以使用这种分块读取方法来过滤您的数据:

reader = pd.read_csv('Filtered_By_Month/all_data.csv', chunksize=1000000)

chunks = []

for chunk in reader:
    # filter data
    filtered = do_something(chunk)
    chunks.append(filtered)

df = pd.concat(chunks)

替代方案:并行框架

如果您处理的文件远大于内存,您可能需要查看 Dask 等并行计算库。

Dask 有tools for reading in CSV data 使用多个线程、处理器甚至服务器。跨处理器或通过网络传播任务会带来性能开销,但对于足够大的任务,这些收益将是值得的。还有一个学习曲线,但是在将 dask.Dataframe API 与 pandas 对齐方面的一些出色工作已经降低了学习曲线,因此遵循 dask 网站上的教程应该可以让您很快上手并运行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-24
    • 1970-01-01
    相关资源
    最近更新 更多