关于python:读取一个巨大的.csv文件

Reading a huge .csv file

我目前正在尝试从python 2.7中的.csv文件中读取数据,最多100万行,200列(文件大小从100MB到1.6GB)。对于30万行以下的文件,我可以这样做(非常慢),但一旦我超过了这一点,就会出现内存错误。我的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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文件中,所以当我超过这些元素时,就离开循环以节省时间。

我的问题是:

  • 我如何才能让它与更大的文件一起工作?

  • 有什么方法可以让我更快吗?

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

    非常感谢您的帮助!


    您正在将所有行读取到一个列表中,然后处理该列表。不要那样做。

    在生成行时处理它们。如果需要先筛选数据,请使用生成器函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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()中也可以这样做:

    1
    2
    3
    4
    def getdata(filename, criteria):
        for criterion in criteria:
            for row in getstuff(filename, criterion):
                yield row

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

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

    现在,您在内存中只保存一行,而不是按照标准保存数千行。

    yield将一个函数作为一个生成器函数,这意味着在开始循环之前它不会做任何工作。


    尽管马提金的回答是最好的。这是一种更直观的方法来处理初学者的大型csv文件。这允许您一次处理一组行或块。

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


    我做了大量的振动分析,并查看了大型数据集(数千万和数亿个点)。我的测试显示pandas.read_csv()函数比numpy.genfromtxt()快20倍。genfromtxt()函数的速度是numpy.loadtxt()的3倍。对于大型数据集,您似乎需要熊猫。

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


    对我有用的东西过去和现在都是超快的

    1
    2
    3
    4
    5
    6
    7
    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)

    另一个有效的解决方案是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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

    这是另一种治疗Python3的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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是一个生成器函数。