एक बड़ी .csv फ़ाइल पढ़ना


107

मैं वर्तमान में पायथन फ़ाइलों से पायथन 2.7 में 1 मिलियन पंक्तियों और 200 कॉलम (100mb से 1.6gb तक की फाइलें) के डेटा को पढ़ने की कोशिश कर रहा हूं। मैं 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

गेटस्टफ फ़ंक्शन में अन्य क्लॉज़ का कारण यह है कि कसौटी पर खरा उतरने वाले सभी तत्वों को सीएसवी फ़ाइल में एक साथ सूचीबद्ध किया जाएगा, इसलिए मैं पाश को छोड़ देता हूं जब मुझे समय बचाने के लिए उन्हें मिलता है।

मेरे प्रश्न हैं:

  1. बड़ी फ़ाइलों के साथ काम करने के लिए मैं इसे कैसे प्रबंधित कर सकता हूं?

  2. क्या कोई तरीका है जिससे मैं इसे तेज कर सकूं?

मेरे कंप्यूटर में 8 जीबी रैम है, 64 बिट विंडोज 7 चल रहा है, और प्रोसेसर 3.40 गीगाहर्ट्ज है (निश्चित नहीं कि आपको कौन सी जानकारी चाहिए)।


1
मुझे पता है कि कई ऐसे ही प्रतीत होते प्रश्न हैं, लेकिन उनमें से कोई भी मेरी समस्या के लिए पर्याप्त नहीं लग रहा था। क्षमा करें यदि कोई ऐसा है जो मैं चूक गया।
चार्ल्स डिलन

2
आपको रीड डेटा को मेमोरी में रखने के बजाय एक डेटाबेस (जैसे Sqlite) में स्टोर करना चाहिए। फिर आप आगे की प्रक्रिया को चला सकते हैं जैसे कि db पर फ़िल्टर करना
माइकल कसाईचर

जवाबों:


158

आप सभी पंक्तियों को एक सूची में पढ़ रहे हैं, फिर उस सूची को संसाधित कर रहे हैं। ऐसा मत करो

अपनी पंक्तियों को संसाधित करें क्योंकि आप उन्हें बनाते हैं। यदि आपको पहले डेटा को फ़िल्टर करने की आवश्यकता है, तो एक जनरेटर फ़ंक्शन का उपयोग करें:

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एक फ़ंक्शन को एक जनरेटर फ़ंक्शन बनाता है , जिसका अर्थ है कि यह तब तक कोई काम नहीं करेगा जब तक आप इस पर लूपिंग शुरू नहीं करते।


क्या आपको इस तकनीक का उपयोग करते समय समान मेमोरी दक्षता मिलती है csv.DictReader? क्योंकि 2.5GB .csv फ़ाइल पर मेरे परीक्षण दिखाते हैं कि इस तरह से पंक्ति द्वारा पंक्ति को पुनरावृत्त करने का प्रयास करते समय, इसका उपयोग करते समय इसके बजाय csv.readerपायथन प्रक्रिया को पूर्ण 2.5GB मेमोरी उपयोग के लिए बढ़ने का कारण बनता है।
14:35 पर user5359531

@ user5359531 जो आपको संकेत करता है कि आप कहीं न कहीं शब्दकोश की वस्तुओं का संदर्भ रखेंगे। DictReader अपने आप में संदर्भ नहीं रखता है, इसलिए समस्या कहीं और है।
मार्टिज़न पीटरर्स

39

यद्यपि मार्टिजिन का उत्तर सर्वश्रेष्ठ है। यहां शुरुआती लोगों के लिए बड़ी सीएसवी फ़ाइलों को संसाधित करने का एक अधिक सहज तरीका है। यह आपको एक समय में पंक्तियों, या विखंडू के समूहों को संसाधित करने की अनुमति देता है।

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

9
पांडा का उपयोग करना इसे अधिक सहज क्यों बनाता है?
wwii

25
कोड की 4 पंक्तियाँ हमेशा खुद की तरह newbies के लिए बेहतर होती हैं।
mmann1123

3
नियमित पायथन कोड बस के रूप में छोटा है, और आपको प्रति पंक्ति प्रक्रिया करने देता है। जनरेटर फ़ंक्शन केवल सामान को फ़िल्टर करने के लिए है; आप पंडों में उसी फ़िल्टरिंग को कैसे करेंगे?
मार्टिन पीटर्स

1
यह कमाल का है! पंडों का उपयोग करके बड़ी सीएसवी फ़ाइलों को लोड करने और संसाधित करने की मेरी समस्या का समाधान किया। धन्यवाद!
एल्सा ली

1
यह बहुत अच्छी तरह से तब भी काम करता है जब कुछ पंक्तियों की सामग्री कई पंक्तियों में फैली हो!
डायल्सन सेल्स

19

मैं कंपन विश्लेषण की एक उचित मात्रा में करता हूं और बड़े डेटा सेट (दसियों और लाखों बिंदुओं के सैकड़ों) को देखता हूं। मेरे परीक्षण ने pandas.read_csv () फ़ंक्शन को numpy.genfromtxt () से 20 गुना तेज दिखाया। और genfromtxt () फ़ंक्शन numpy.loadtxt () से 3 गुना तेज है। ऐसा लगता है कि आपको बड़े डेटा सेट के लिए पांडा की आवश्यकता है

मैंने कंपन विश्लेषण के लिए MATLAB बनाम पायथन की चर्चा करते हुए इस परीक्षण में उपयोग किए गए कोड और डेटा सेट पोस्ट किए ।


3
ओपी का प्राथमिक मुद्दा गति में से एक नहीं था, यह मेमोरी थकावट में से एक था। फ़ाइल को संसाधित करने के लिए एक अलग फ़ंक्शन का उपयोग करना एक स्ट्रीम प्रोसेसर का उपयोग करने के बजाय इसे एक सूची में पढ़ने के डाउनसाइड्स को दूर नहीं करता है।
19

6

मेरे लिए जो काम किया था और वह सुपरफास्ट है

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

नहीं करता है df_train=df_train.compute()अपने पहले समाधान में लाइन स्मृति में पूरे डाटासेट ... है जो कि वह क्या करने के लिए नहीं कोशिश कर रहा है लोड?
सैम डिलार्ड

3

जो इस प्रश्न के लिए भूमि। का उपयोग करते हुए पांडा 'के साथ chunksize ' और ' usecols ' मुझे मदद की तेजी से अन्य प्रस्तावित विकल्पों की तुलना में एक विशाल ज़िप फ़ाइल को पढ़ने के लिए।

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)

1

यहाँ 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एक जनरेटर फ़ंक्शन है।


तो, यह समाधान के रूप में कुशलता से काम करता है जो उपज ऑपरेटर का उपयोग करता है। : क्षमा करें, यह नहीं है। कॉलबैक फ़ंक्शन कॉल अधिक ओवरहेड जोड़ता है, खासकर जब से आपको स्पष्ट रूप से और अलग से राज्य को संभालना पड़ता है।
मार्टिन पीटर्स

@MartijnPieters धन्यवाद। उत्तर अपडेट किया गया।
ऋषभ अग्रहरी

0

आप पांडा उपयोग कर रहे हैं और राम की बहुत सारी (स्मृति में पूरे फ़ाइल को पढ़ने के लिए पर्याप्त है) का उपयोग कर प्रयास pd.read_csvके साथ low_memory=False, जैसे:

import pandas as pd
data = pd.read_csv('file.csv', low_memory=False)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.