Подтвердить что ты не робот

Чтение огромного файла .csv

В настоящее время я пытаюсь прочитать данные из файлов .csv в Python 2.7 с количеством строк до 1 миллиона и 200 столбцами (размер файлов от 100 МБ до 1,6 ГБ). Я могу сделать это (очень медленно) для файлов с количеством строк менее 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

Причина предложения else в функции getstuff состоит в том, что все элементы, которые соответствуют критерию, будут перечислены вместе в файле csv, поэтому я покидаю цикл, когда прохожу их, чтобы сэкономить время.

Мои вопросы:

  1. Как я могу заставить это работать с большими файлами?

  2. Есть ли способ сделать это быстрее?

Мой компьютер имеет 8 ГБ оперативной памяти, работает под управлением 64-разрядной Windows 7, а процессор имеет частоту 3,40 ГГц (не знаю, какая информация вам нужна).

4b9b3361

Ответ 1

Вы читаете все строки в список, а затем обрабатываете этот список. Не делай этого.

Обработайте ваши строки, как вы производите их. Если вам нужно сначала отфильтровать данные, используйте функцию генератора:

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 делает функцию функцией- генератором, что означает, что она не будет выполнять никакой работы, пока вы не начнете циклически выполнять ее.

Ответ 2

Хотя ответ Мартиджин является наиболее вероятным. Вот более интуитивный способ обработки больших файлов csv для новичков. Это позволяет обрабатывать группы строк или кусков за раз.

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

Ответ 3

Я довольно много анализирую вибрации и смотрю на большие наборы данных (десятки и сотни миллионов точек). Мой тест показал, что функция pandas.read_csv() работает в 20 раз быстрее, чем numpy.genfromtxt(). А функция genfromtxt() работает в 3 раза быстрее, чем numpy.loadtxt(). Кажется, вам нужны панды для больших наборов данных.

Я разместил код и наборы данных, которые я использовал в этом тестировании, в блоге, в котором обсуждались MATLAB и Python для анализа вибрации.

Ответ 4

то, что работало для меня, было и является сверхбыстрым

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

Ответ 5

вот еще одно решение для Python3:

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 является функцией генератора.

Ответ 6

Для тех, кто приземляется на этот вопрос. Использование панд с "chunksize and" usecols помогло мне прочитать огромный 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)

Ответ 7

Недавно я пытался решить ту же проблему, но нашел, что пакет python pandas является достаточно эффективным.

Здесь вы можете проверить здесь http://pandas.pydata.org/

Pandas - это библиотека анализа данных высокой производительности для больших данных.

Ответ 8

Используйте pytables. "PyTables - это пакет для управления иерархическими наборами данных и предназначен для эффективного и легкого управления чрезвычайно большими объемами данных".