读取一个巨大的.csv 文件

我正在尝试读取。Python 2.7中的 csv 文件,最多有100万行,200列(文件范围从100mb 到1.6 gb)。我可以对低于300,000行的文件执行这个操作(非常慢) ,但是一旦超过这个值,就会出现内存错误。我的代码是这样的:

def getdata(filename, criteria):
data=[]
for criterion in criteria:
data.append(getstuff(filename, criteron))
return data


def getstuff(filename, criterion):
import csv
data=[]
with open(filename, "rb") as csvfile:
datareader=csv.reader(csvfile)
for row in datareader:
if row[3]=="column header":
data.append(row)
elif len(data)<2 and row[3]!=criterion:
pass
elif row[3]==criterion:
data.append(row)
else:
return data

Getstuff 函数中 else 子句的原因是,符合条件的所有元素都将在 csv 文件中一起列出,所以当我越过这些元素时,为了节省时间,我留下了循环。

我的问题是:

  1. 我如何设法使这个工作与更大的文件?

  2. 有什么办法能让它快点吗?

我的电脑有8gb 内存,运行64位 Windows7,处理器是3.40 GHz (不确定你需要什么信息)。

245626 次浏览

将所有行读入一个列表,然后处理该列表。

在生成数据行时对其进行处理。如果需要首先过滤数据,请使用生成器函数:

import csv


def getstuff(filename, criterion):
with open(filename, "rb") as csvfile:
datareader = csv.reader(csvfile)
yield next(datareader)  # yield the header row
count = 0
for row in datareader:
if row[3] == criterion:
yield row
count += 1
elif count:
# done when having read a consecutive series of rows
return

我还简化了您的过滤器测试; 逻辑是相同的,但更简洁。

因为您只匹配符合条件的单个行序列,所以您还可以使用:

import csv
from itertools import dropwhile, takewhile


def getstuff(filename, criterion):
with open(filename, "rb") as csvfile:
datareader = csv.reader(csvfile)
yield next(datareader)  # yield the header row
# first row, plus any subsequent rows that match, then stop
# reading altogether
# Python 2: use `for row in takewhile(...): yield row` instead
# instead of `yield from takewhile(...)`.
yield from takewhile(
lambda r: r[3] == criterion,
dropwhile(lambda r: r[3] != criterion, datareader))
return

你现在可以直接在 getstuff()上循环。在 getdata()中也这样做:

def getdata(filename, criteria):
for criterion in criteria:
for row in getstuff(filename, criterion):
yield row

现在直接在代码中的 getdata()上循环:

for row in getdata(somefilename, sequence_of_criteria):
# process row

您现在只在内存中保存 一排,而不是每个条件保存数千行。

yield使一个函数成为 发电机功能发电机功能,这意味着在开始循环之前它不会做任何工作。

我做了相当数量的振动分析和查看大型数据集(千万和亿万点)。我的测试显示,Read _ csv ()函数的速度是 numpy.genfrom txt ()的 < em > 20 倍。函数的运行速度是 numpy.loadtxt ()的3倍。看来你 需要熊猫为大数据集。

我在一个讨论 MATLAB 与 Python 在振动分析中的比较的博客上发布了我在这个测试中使用的代码和数据集。

尽管 Martijin 的回答可能是最好的。这里有一个更直观的方法来处理大型的 csv 文件的初学者。这允许您一次处理一组行或块。

import pandas as pd
chunksize = 10 ** 8
for chunk in pd.read_csv(filename, chunksize=chunksize):
process(chunk)

无论过去还是现在,对我有效的是

import pandas as pd
import dask.dataframe as dd
import time
t=time.clock()
df_train = dd.read_csv('../data/train.csv', usecols=[col1, col2])
df_train=df_train.compute()
print("load train: " , time.clock()-t)

另一个可行的解决方案是:

import pandas as pd
from tqdm import tqdm


PATH = '../data/train.csv'
chunksize = 500000
traintypes = {
'col1':'category',
'col2':'str'}


cols = list(traintypes.keys())


df_list = [] # list to hold the batch dataframe


for df_chunk in tqdm(pd.read_csv(PATH, usecols=cols, dtype=traintypes, chunksize=chunksize)):
# Can process each chunk of dataframe here
# clean_data(), feature_engineer(),fit()


# Alternatively, append the chunk to list and merge all
df_list.append(df_chunk)


# Merge all dataframes into one dataframe
X = pd.concat(df_list)


# Delete the dataframe list to release memory
del df_list
del df_chunk

下面是 Python 3的另一个解决方案:

import csv
with open(filename, "r") as csvfile:
datareader = csv.reader(csvfile)
count = 0
for row in datareader:
if row[3] in ("column header", criterion):
doSomething(row)
count += 1
elif count > 2:
break

这里 datareader是一个生成器函数。

对于一个被问到这个问题的人来说。使用带有“ 大块的”和“ 用卡”的 熊猫帮助我比其他提议的选项更快地读取一个巨大的 zip 文件。

import pandas as pd


sample_cols_to_keep =['col_1', 'col_2', 'col_3', 'col_4','col_5']


# First setup dataframe iterator, ‘usecols’ parameter filters the columns, and 'chunksize' sets the number of rows per chunk in the csv. (you can change these parameters as you wish)
df_iter = pd.read_csv('../data/huge_csv_file.csv.gz', compression='gzip', chunksize=20000, usecols=sample_cols_to_keep)


# this list will store the filtered dataframes for later concatenation
df_lst = []


# Iterate over the file based on the criteria and append to the list
for df_ in df_iter:
tmp_df = (df_.rename(columns={col: col.lower() for col in df_.columns}) # filter eg. rows where 'col_1' value grater than one
.pipe(lambda x:  x[x.col_1 > 0] ))
df_lst += [tmp_df.copy()]


# And finally combine filtered df_lst into the final lareger output say 'df_final' dataframe
df_final = pd.concat(df_lst)

如果你正在使用大熊猫并且有大量的内存(足够将整个文件读入内存) ,尝试使用 pd.read_csvlow_memory=False,例如:

import pandas as pd
data = pd.read_csv('file.csv', low_memory=False)