繁体   English   中英

如何读取带有 pandas 的大型 csv 文件?

[英]How do I read a large csv file with pandas?

我正在尝试读取 pandas 中的大型 csv 文件(大约 6 GB),我收到 memory 错误:

MemoryError                               Traceback (most recent call last)
<ipython-input-58-67a72687871b> in <module>()
----> 1 data=pd.read_csv('aphro.csv',sep=';')

...

MemoryError: 

对此有什么帮助吗?

该错误表明机器没有足够的内存一次将整个 CSV 读取到 DataFrame 中。 假设您一次不需要内存中的整个数据集,避免该问题的一种方法是分块处理 CSV (通过指定chunksize参数):

chunksize = 10 ** 6
for chunk in pd.read_csv(filename, chunksize=chunksize):
    process(chunk)

chunksize参数指定每个块的行数。 (当然,最后一个块可能包含少于chunksize大小的行。)


熊猫 >= 1.2

带有chunksizeread_csv返回一个上下文管理器,可以像这样使用:

chunksize = 10 ** 6
with pd.read_csv(filename, chunksize=chunksize) as reader:
    for chunk in reader:
        process(chunk)

GH38225

分块不应该总是解决这个问题的第一站。

  1. 由于重复的非数字数据或不需要的列,文件是否很大?

    如果是这样,您有时可以通过将列作为类别读取并通过pd.read_csv usecols参数选择所需的列来节省大量内存。

  2. 您的工作流程是否需要切片、操作、导出?

    如果是这样,您可以使用dask.dataframe进行切片、执行计算并迭代导出。 分块由 dask 静默执行,它还支持 pandas API 的子集。

  3. 如果一切都失败了,请通过块逐行读取。

    作为最后的手段,通过 pandas或通过csv 库进行分块。

对于大数据,我建议您使用库“dask”
例如:

# Dataframes implement the Pandas API
import dask.dataframe as dd
df = dd.read_csv('s3://.../2018-*-*.csv')

您可以从此处的文档中阅读更多信息。

另一个不错的选择是使用modin ,因为所有功能都与 pandas 相同,但它利用了分布式数据帧库,例如 dask。

在我的项目中,另一个高级库是datatables

# Datatable python library
import datatable as dt
df = dt.fread("s3://.../2018-*-*.csv")

我是这样进行的:

chunks=pd.read_table('aphro.csv',chunksize=1000000,sep=';',\
       names=['lat','long','rf','date','slno'],index_col='slno',\
       header=None,parse_dates=['date'])

df=pd.DataFrame()
%time df=pd.concat(chunk.groupby(['lat','long',chunk['date'].map(lambda x: x.year)])['rf'].agg(['sum']) for chunk in chunks)

上面的回答已经很满意题目了。 无论如何,如果您需要内存中的所有数据 - 看看bcolz 它压缩内存中的数据。 我有非常好的经验。 但它缺少很多熊猫功能

编辑:我认为我的压缩率约为 1/10 或原始大小,当然这取决于数据的类型。 缺少的重要特征是聚合。

您可以将数据作为块读取并将每个块保存为泡菜。

import pandas as pd 
import pickle

in_path = "" #Path where the large file is
out_path = "" #Path to save the pickle files to
chunk_size = 400000 #size of chunks relies on your available memory
separator = "~"

reader = pd.read_csv(in_path,sep=separator,chunksize=chunk_size, 
                    low_memory=False)    


for i, chunk in enumerate(reader):
    out_file = out_path + "/data_{}.pkl".format(i+1)
    with open(out_file, "wb") as f:
        pickle.dump(chunk,f,pickle.HIGHEST_PROTOCOL)

在下一步中,您将阅读泡菜并将每个泡菜附加到所需的数据帧中。

import glob
pickle_path = "" #Same Path as out_path i.e. where the pickle files are

data_p_files=[]
for name in glob.glob(pickle_path + "/data_*.pkl"):
   data_p_files.append(name)


df = pd.DataFrame([])
for i in range(len(data_p_files)):
    df = df.append(pd.read_pickle(data_p_files[i]),ignore_index=True)

我想根据已经提供的大多数潜在解决方案做出更全面的答案。 我还想指出另一种可能有助于阅读过程的潜在帮助。

选项 1:数据类型

“dtypes”是一个非常强大的参数,您可以使用它来减少read方法的内存压力。 看到这个这个答案。 默认情况下,Pandas 会尝试推断数据的 dtypes。

参考数据结构,存储的每个数据都会发生内存分配。 在基本级别上,请参考以下值(下表说明了 C 编程语言的值):

The maximum value of UNSIGNED CHAR = 255                                    
The minimum value of SHORT INT = -32768                                     
The maximum value of SHORT INT = 32767                                      
The minimum value of INT = -2147483648                                      
The maximum value of INT = 2147483647                                       
The minimum value of CHAR = -128                                            
The maximum value of CHAR = 127                                             
The minimum value of LONG = -9223372036854775808                            
The maximum value of LONG = 9223372036854775807

请参阅页面以查看 NumPy 和 C 类型之间的匹配。

假设您有一个数字整数数组。 您可以在理论上和实践上分配,例如 16 位整数类型的数组,但是您将分配比实际需要存储该数组更多的内存。 为了防止这种情况,您可以在read_csv上设置dtype选项。 您不想将数组项存储为长整数,实际上您可以将它们与 8 位整数( np.int8np.uint8 )匹配。

观察以下 dtype 映射。

来源: https ://pbpython.com/pandas_dtypes.html

您可以将dtype参数作为 pandas 方法的参数传递为像 {column: type} 这样的 dict read

import numpy as np
import pandas as pd

df_dtype = {
        "column_1": int,
        "column_2": str,
        "column_3": np.int16,
        "column_4": np.uint8,
        ...
        "column_n": np.float32
}

df = pd.read_csv('path/to/file', dtype=df_dtype)

选项 2:按块读取

分块读取数据允许您访问内存中的部分数据,并且您可以对数据进行预处理并保留处理后的数据而不是原始数据。 如果将此选项与第一个选项dtypes结合使用会更好。

我想指出该过程的 pandas 食谱部分,您可以在此处找到它。 请注意那里的这两部分;

选项 3:达斯克

Dask 是一个框架,在Dask 的网站上定义为:

Dask 为分析提供高级并行性,为您喜爱的工具实现大规模性能

它的诞生是为了覆盖熊猫无法到达的必要部分。 Dask 是一个强大的框架,通过以分布式方式处理数据,您可以访问更多数据。

您可以使用 dask 对整个数据进行预处理,Dask 负责分块部分,因此与 pandas 不同,您只需定义处理步骤并让 Dask 完成工作。 Dask 在compute和/或persist化明确推送之前不会应用计算(请参阅此处的答案以了解差异)。

其他辅助(想法)

  • 为数据设计的 ETL 流。 只保留原始数据中需要的内容。
    • 首先,使用 Dask 或 PySpark 等框架将 ETL 应用于整个数据,并导出处理后的数据。
    • 然后看看处理后的数据是否可以作为一个整体放入内存中。
  • 考虑增加你的内存。
  • 考虑在云平台上使用该数据。

函数 read_csv 和 read_table 几乎相同。 但是在程序中使用函数 read_table 时必须指定分隔符“,”。

def get_from_action_data(fname, chunk_size=100000):
    reader = pd.read_csv(fname, header=0, iterator=True)
    chunks = []
    loop = True
    while loop:
        try:
            chunk = reader.get_chunk(chunk_size)[["user_id", "type"]]
            chunks.append(chunk)
        except StopIteration:
            loop = False
            print("Iteration is stopped")

    df_ac = pd.concat(chunks, ignore_index=True)

解决方案1:

使用处理大数据的 pandas

解决方案2:

TextFileReader = pd.read_csv(path, chunksize=1000)  # the number of rows per chunk

dfList = []
for df in TextFileReader:
    dfList.append(df)

df = pd.concat(dfList,sort=False)

在使用 chunksize 选项之前,如果您想确定要在 @unutbu 提到的分块 for 循环中编写的过程函数,您可以简单地使用 nrows 选项。

small_df = pd.read_csv(filename, nrows=100)

一旦确定进程块已准备就绪,就可以将其放入整个数据帧的分块 for 循环中。

下面是一个例子:

chunkTemp = []
queryTemp = []
query = pd.DataFrame()

for chunk in pd.read_csv(file, header=0, chunksize=<your_chunksize>, iterator=True, low_memory=False):

    #REPLACING BLANK SPACES AT COLUMNS' NAMES FOR SQL OPTIMIZATION
    chunk = chunk.rename(columns = {c: c.replace(' ', '') for c in chunk.columns})

    #YOU CAN EITHER: 
    #1)BUFFER THE CHUNKS IN ORDER TO LOAD YOUR WHOLE DATASET 
    chunkTemp.append(chunk)

    #2)DO YOUR PROCESSING OVER A CHUNK AND STORE THE RESULT OF IT
    query = chunk[chunk[<column_name>].str.startswith(<some_pattern>)]   
    #BUFFERING PROCESSED DATA
    queryTemp.append(query)

#!  NEVER DO pd.concat OR pd.DataFrame() INSIDE A LOOP
print("Database: CONCATENATING CHUNKS INTO A SINGLE DATAFRAME")
chunk = pd.concat(chunkTemp)
print("Database: LOADED")

#CONCATENATING PROCESSED DATA
query = pd.concat(queryTemp)
print(query)

您可以尝试 sframe,它与 pandas 具有相同的语法,但允许您操作大于 RAM 的文件。

如果您使用 pandas 将大文件读入块然后逐行产生,这就是我所做的

import pandas as pd

def chunck_generator(filename, header=False,chunk_size = 10 ** 5):
   for chunk in pd.read_csv(filename,delimiter=',', iterator=True, chunksize=chunk_size, parse_dates=[1] ): 
        yield (chunk)

def _generator( filename, header=False,chunk_size = 10 ** 5):
    chunk = chunck_generator(filename, header=False,chunk_size = 10 ** 5)
    for row in chunk:
        yield row

if __name__ == "__main__":
filename = r'file.csv'
        generator = generator(filename=filename)
        while True:
           print(next(generator))

如果有人仍在寻找类似的东西,我发现这个名为modin的新库可以提供帮助。 它使用可以帮助读取的分布式计算。 这是一篇很好的文章,将其功能与 pandas 进行了比较。 它本质上使用与 pandas 相同的功能。

import modin.pandas as pd
pd.read_csv(CSV_FILE_NAME)

如果您有包含millions个数据条目的csv文件并且您想加载完整的数据集,您应该使用dask_cudf

import dask_cudf as dc

df = dc.read_csv("large_data.csv")

除了上面的答案,对于那些想要处理 CSV 然后导出到 csv、parquet 或 SQL 的人来说, d6tstack是另一个不错的选择。 您可以加载多个文件,它处理数据架构更改(添加/删除的列)。 已经内置了核心支持的分块。

def apply(dfg):
    # do stuff
    return dfg

c = d6tstack.combine_csv.CombinerCSV([bigfile.csv], apply_after_read=apply, sep=',', chunksize=1e6)

# or
c = d6tstack.combine_csv.CombinerCSV(glob.glob('*.csv'), apply_after_read=apply, chunksize=1e6)

# output to various formats, automatically chunked to reduce memory consumption
c.to_csv_combine(filename='out.csv')
c.to_parquet_combine(filename='out.pq')
c.to_psql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # fast for postgres
c.to_mysql_combine('mysql+mysqlconnector://usr:pwd@localhost/db', 'tablename') # fast for mysql
c.to_sql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # slow but flexible

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM