क्या पंडों के पुनरावृत्तियों में प्रदर्शन के मुद्दे हैं?


96

पंडों से पुनरावृत्तियों का उपयोग करते समय मैंने बहुत खराब प्रदर्शन देखा है।

क्या यह कुछ ऐसा है जो दूसरों द्वारा अनुभव किया जाता है? क्या यह पुनरावृत्तियों के लिए विशिष्ट है और क्या इस फ़ंक्शन को एक निश्चित आकार के डेटा से बचा जाना चाहिए (मैं 2-3 मिलियन पंक्तियों के साथ काम कर रहा हूं)?

GitHub पर इस चर्चा ने मुझे विश्वास दिलाया कि यह डेटाफ़्रेम में dtypes को मिलाते समय होता है, हालाँकि नीचे दिए गए सरल उदाहरण से पता चलता है कि यह एक dtype (float64) का उपयोग करते समय भी होता है। मेरी मशीन पर 36 सेकंड लगते हैं:

import pandas as pd
import numpy as np
import time

s1 = np.random.randn(2000000)
s2 = np.random.randn(2000000)
dfa = pd.DataFrame({'s1': s1, 's2': s2})

start = time.time()
i=0
for rowindex, row in dfa.iterrows():
    i+=1
end = time.time()
print end - start

क्यों वेक्टराइज्ड ऑपरेशंस इतनी जल्दी लागू होते हैं? मुझे लगता है कि वहाँ भी कुछ पंक्ति पंक्ति द्वारा चलना चाहिए।

मैं यह पता नहीं लगा सकता कि मेरे मामले में पुनरावृत्तियों का उपयोग कैसे न करें (यह मैं भविष्य के प्रश्न के लिए बचाऊंगा)। इसलिए मैं सुनवाई की सराहना करता हूं यदि आप लगातार इस चलना से बचने में सक्षम हैं। मैं अलग-अलग डेटाफ़्रेम में डेटा के आधार पर गणना कर रहा हूं। धन्यवाद!

--- संपादित करें: जो मैं चलाना चाहता हूं उसका सरलीकृत संस्करण नीचे जोड़ा गया है ---

import pandas as pd
import numpy as np

#%% Create the original tables
t1 = {'letter':['a','b'],
      'number1':[50,-10]}

t2 = {'letter':['a','a','b','b'],
      'number2':[0.2,0.5,0.1,0.4]}

table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)

#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=[0])

#%% Iterate through filtering relevant data, optimizing, returning info
for row_index, row in table1.iterrows():   
    t2info = table2[table2.letter == row['letter']].reset_index()
    table3.ix[row_index,] = optimize(t2info,row['number1'])

#%% Define optimization
def optimize(t2info, t1info):
    calculation = []
    for index, r in t2info.iterrows():
        calculation.append(r['number2']*t1info)
    maxrow = calculation.index(max(calculation))
    return t2info.ix[maxrow]

7
applyसदिश नहीं है। iterrowsयह और भी बुरा है क्योंकि यह सब कुछ बॉक्स (कि 'के साथ पूर्ण अंतर apply)। आपको केवल iterrowsबहुत ही कम स्थितियों में उपयोग करना चाहिए । IMHO कभी नहीं। दिखाएँ कि आप वास्तव में क्या कर रहे हैं iterrows
जेफ

2
मुद्दा आप के बजाय से जुड़ा हुआ एक की मुक्केबाजी के साथ क्या करना है DatetimeIndexमें Timestamps(अजगर अंतरिक्ष में लागू किया गया था), और इतना मास्टर में सुधार किया गया है।
जेफ

1
एक और अधिक पूरी चर्चा के लिए इस मुद्दे को देखें: github.com/pydata/pandas/issues/7194
जेफ

विशिष्ट प्रश्न से लिंक करें (यह सामान्य रहेगा): stackoverflow.com/questions/24875096/…
KieranPC

कृपया पुनरावृत्तियों () के उपयोग की अनुशंसा न करें। यह पंडों के इतिहास में सबसे खराब विरोधी प्रतिमानों का एक प्रखर प्रलाप है।
cs95

जवाबों:


186

आम तौर पर, iterrowsकेवल बहुत, बहुत विशिष्ट मामलों में उपयोग किया जाना चाहिए। यह विभिन्न ऑपरेशनों के प्रदर्शन के लिए पूर्वता का सामान्य क्रम है:

1) vectorization
2) using a custom cython routine
3) apply
    a) reductions that can be performed in cython
    b) iteration in python space
4) itertuples
5) iterrows
6) updating an empty frame (e.g. using loc one-row-at-a-time)

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

1) वैश्वीकरण हमेशा, सबसे पहले और सबसे अच्छा विकल्प है। हालांकि, मामलों का एक छोटा सा सेट है (आमतौर पर एक पुनरावृत्ति शामिल है) जिसे स्पष्ट तरीकों से वेक्टर नहीं किया जा सकता है। इसके अलावा, एक छोटे से पर DataFrame, यह अन्य विधियों का उपयोग करने के लिए तेज़ हो सकता है।

3) apply आमतौर पर साइथन अंतरिक्ष में एक पुनरावृत्तिकर्ता द्वारा नियंत्रित किया जा सकता है। यह आंतरिक रूप से पांडा द्वारा नियंत्रित किया जाता है, हालांकि यह इस बात पर निर्भर करता है कि applyअभिव्यक्ति के अंदर क्या चल रहा है । उदाहरण के लिए, df.apply(lambda x: np.sum(x))बहुत तेजी से निष्पादित किया जाएगा, हालांकि, df.sum(1)यह और भी बेहतर है। हालाँकि df.apply(lambda x: x['b'] + 1)पायथन स्पेस में कुछ निष्पादित किया जाएगा, और परिणामस्वरूप बहुत धीमी है।

4) itertuplesडेटा को बॉक्स में नहीं रखता है Series। यह सिर्फ टुपल्स के रूप में डेटा लौटाता है।

5) iterrowsडेटा को बॉक्स में रखें Series। जब तक आपको वास्तव में इसकी आवश्यकता न हो, तब तक दूसरी विधि का उपयोग करें।

6) एक खाली फ्रेम को सिंगल-रो-ए-ए-टाइम अपडेट करना। मैंने देखा है कि इस विधि का उपयोग बहुत अधिक किया जाता है। यह अब तक का सबसे धीमा है। यह शायद आम जगह है (और कुछ अजगर संरचनाओं के लिए यथोचित रूप से तेज़), लेकिन DataFrameअनुक्रमण पर उचित संख्या में जांच करता है, इसलिए एक समय में एक पंक्ति को अपडेट करने के लिए यह हमेशा बहुत धीमा होगा। नई संरचनाओं को बनाने के लिए बहुत बेहतर है और concat


1
हां, मैंने नंबर 6 (और 5) का इस्तेमाल किया। मुझे कुछ सीखने को मिला है। यह एक रिश्तेदार शुरुआत करने के लिए स्पष्ट पसंद की तरह लगता है।
किरानपीसी

3
मेरे अनुभव में, उपयोग के मामले के आधार पर 3, 4 और 5 के बीच का अंतर सीमित है।
आईएएनएस

8
मैंने इस नोटबुक में रनटाइम की जाँच करने का प्रयास किया है । किसी तरह itertuplesसे तेज है apply:(
डिमगोल्ड

1
pd.DataFrame.applyअक्सर धीमी होती है itertuples। इसके अलावा, यह गैर-सदिश गणनाओं के लिए सूची समझ, mapखराब नाम np.vectorizeऔर numba(किसी विशेष क्रम में) पर विचार करने के लायक है , उदाहरण के लिए यह उत्तर देखें ।
JPP

2
@ जिफ़, जिज्ञासा से बाहर, आपने सूची संकलन को यहाँ क्यों नहीं जोड़ा है? हालांकि यह सच है कि वे इंडेक्स अलाइनमेंट या मिसिंग डेटा (जब तक कि आप किसी फंक्शन को ट्राइ-कैच के साथ इस्तेमाल नहीं करते हैं) को हैंडल नहीं करते हैं, वे बहुत सारे उपयोग के मामलों (स्ट्रिंग / रेगेक्स स्टफ) के लिए अच्छे हैं, जहां पांडा के तरीकों का सदिशीकरण नहीं है ( शब्द के ट्रूस्ट अर्थ में) कार्यान्वयन। क्या आपको लगता है कि यह ध्यान देने योग्य है कि LCs पांडा के लागू होने के लिए एक तेज़, निचला ओवरहेड विकल्प हैं और कई पांडा स्ट्रिंग फ़ंक्शन हैं?
cs95

17

Numpy और पांडा में वेक्टर ऑपरेशन कई कारणों से वेनिला पायथन में स्केलर ऑपरेशन की तुलना में बहुत तेज़ हैं :

  • प्रवर्धित प्रकार का लुकअप : पायथन एक गतिशील रूप से टाइप की जाने वाली भाषा है, इसलिए एक एरे में प्रत्येक तत्व के लिए रनटाइम ओवरहेड है। हालाँकि, Numpy (और इस प्रकार पांडा) C (अक्सर साइथन के माध्यम से) में गणना करते हैं। सरणी का प्रकार केवल पुनरावृत्ति की शुरुआत में निर्धारित किया जाता है; यह बचत अकेले सबसे बड़ी जीत में से एक है।

  • बेहतर कैशिंग : एक सी ऐरे से अधिक कैचिंग कैश-फ्रेंडली है और इस प्रकार बहुत तेज़ है। एक पांडा डेटाफ़्रेम एक "कॉलम-ओरिएंटेड टेबल" है, जिसका अर्थ है कि प्रत्येक कॉलम वास्तव में सिर्फ एक सरणी है। इसलिए आप जिन नेटवर्क्स पर एक DataFrame पर काम कर सकते हैं (जैसे किसी कॉलम में सभी तत्वों को समेटना) कुछ कैश मिस होने वाले हैं।

  • समानता के लिए अधिक अवसर : एक सरल सी सरणी को SIMD निर्देशों के माध्यम से संचालित किया जा सकता है। Numpy के कुछ हिस्से आपके CPU और स्थापना प्रक्रिया के आधार पर SIMD को सक्षम करते हैं। समानता के लाभ स्थैतिक टाइपिंग और बेहतर कैशिंग के रूप में नाटकीय नहीं होंगे, लेकिन वे अभी भी एक ठोस जीत हैं।

मोरल ऑफ़ द स्टोरी: नम्पी और पांडा में वेक्टर ऑपरेशन का उपयोग करें। वे सरल कारणों से पायथन में अदिश परिचालनों की तुलना में अधिक तेज़ हैं कि ये संक्रियाएँ वास्तव में वही हैं जो एक सी प्रोग्रामर ने वैसे भी हाथ से लिखी होंगी। (सिवाय इसके कि एंबेडियन सिम्बियन निर्देश के साथ स्पष्ट लूप की तुलना में ऐरे नोटेशन को पढ़ना बहुत आसान है।)


11

यहाँ अपनी समस्या करने का तरीका है। यह सब सदिश है।

In [58]: df = table1.merge(table2,on='letter')

In [59]: df['calc'] = df['number1']*df['number2']

In [60]: df
Out[60]: 
  letter  number1  number2  calc
0      a       50      0.2    10
1      a       50      0.5    25
2      b      -10      0.1    -1
3      b      -10      0.4    -4

In [61]: df.groupby('letter')['calc'].max()
Out[61]: 
letter
a         25
b         -1
Name: calc, dtype: float64

In [62]: df.groupby('letter')['calc'].idxmax()
Out[62]: 
letter
a         1
b         2
Name: calc, dtype: int64

In [63]: df.loc[df.groupby('letter')['calc'].idxmax()]
Out[63]: 
  letter  number1  number2  calc
1      a       50      0.5    25
2      b      -10      0.1    -1

बहुत स्पष्ट उत्तर धन्यवाद। मैं विलय की कोशिश करूंगा लेकिन मुझे संदेह है क्योंकि मेरे पास 5 बिलियन पंक्तियां (2.5million * 2000) होंगी। इस क्यू सामान्य को बनाए रखने के लिए मैंने एक विशिष्ट क्यू बनाया है। मुझे इस विशालकाय टेबल से बचने के लिए एक विकल्प देखकर खुशी होगी, यदि आप एक के बारे में जानते हैं: यहां: stackoverflow.com/questions/24875096/…
KieranPC

1
यह कार्टेशियन उत्पाद नहीं बनाता है - यह एक संपीड़ित स्थान है और यह बहुत ही कुशल मेमोरी है। आप जो कर रहे हैं वह बहुत मानक समस्या है। कोशिश करो। (आपके लिंक किए गए प्रश्न में बहुत समान है)
जेफ

7

एक अन्य विकल्प का उपयोग करना है to_records(), जो दोनों की तुलना में तेज है itertuplesऔर iterrows

लेकिन आपके मामले के लिए, अन्य प्रकार के सुधारों के लिए बहुत जगह है।

यहाँ मेरा अंतिम अनुकूलित संस्करण है

def iterthrough():
    ret = []
    grouped = table2.groupby('letter', sort=False)
    t2info = table2.to_records()
    for index, letter, n1 in table1.to_records():
        t2 = t2info[grouped.groups[letter].values]
        # np.multiply is in general faster than "x * y"
        maxrow = np.multiply(t2.number2, n1).argmax()
        # `[1:]`  removes the index column
        ret.append(t2[maxrow].tolist()[1:])
    global table3
    table3 = pd.DataFrame(ret, columns=('letter', 'number2'))

बेंचमार्क परीक्षण:

-- iterrows() --
100 loops, best of 3: 12.7 ms per loop
  letter  number2
0      a      0.5
1      b      0.1
2      c      5.0
3      d      4.0

-- itertuple() --
100 loops, best of 3: 12.3 ms per loop

-- to_records() --
100 loops, best of 3: 7.29 ms per loop

-- Use group by --
100 loops, best of 3: 4.07 ms per loop
  letter  number2
1      a      0.5
2      b      0.1
4      c      5.0
5      d      4.0

-- Avoid multiplication --
1000 loops, best of 3: 1.39 ms per loop
  letter  number2
0      a      0.5
1      b      0.1
2      c      5.0
3      d      4.0

पूर्ण कोड:

import pandas as pd
import numpy as np

#%% Create the original tables
t1 = {'letter':['a','b','c','d'],
      'number1':[50,-10,.5,3]}

t2 = {'letter':['a','a','b','b','c','d','c'],
      'number2':[0.2,0.5,0.1,0.4,5,4,1]}

table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)

#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=table1.index)


print('\n-- iterrows() --')

def optimize(t2info, t1info):
    calculation = []
    for index, r in t2info.iterrows():
        calculation.append(r['number2'] * t1info)
    maxrow_in_t2 = calculation.index(max(calculation))
    return t2info.loc[maxrow_in_t2]

#%% Iterate through filtering relevant data, optimizing, returning info
def iterthrough():
    for row_index, row in table1.iterrows():   
        t2info = table2[table2.letter == row['letter']].reset_index()
        table3.iloc[row_index,:] = optimize(t2info, row['number1'])

%timeit iterthrough()
print(table3)

print('\n-- itertuple() --')
def optimize(t2info, n1):
    calculation = []
    for index, letter, n2 in t2info.itertuples():
        calculation.append(n2 * n1)
    maxrow = calculation.index(max(calculation))
    return t2info.iloc[maxrow]

def iterthrough():
    for row_index, letter, n1 in table1.itertuples():   
        t2info = table2[table2.letter == letter]
        table3.iloc[row_index,:] = optimize(t2info, n1)

%timeit iterthrough()


print('\n-- to_records() --')
def optimize(t2info, n1):
    calculation = []
    for index, letter, n2 in t2info.to_records():
        calculation.append(n2 * n1)
    maxrow = calculation.index(max(calculation))
    return t2info.iloc[maxrow]

def iterthrough():
    for row_index, letter, n1 in table1.to_records():   
        t2info = table2[table2.letter == letter]
        table3.iloc[row_index,:] = optimize(t2info, n1)

%timeit iterthrough()

print('\n-- Use group by --')

def iterthrough():
    ret = []
    grouped = table2.groupby('letter', sort=False)
    for index, letter, n1 in table1.to_records():
        t2 = table2.iloc[grouped.groups[letter]]
        calculation = t2.number2 * n1
        maxrow = calculation.argsort().iloc[-1]
        ret.append(t2.iloc[maxrow])
    global table3
    table3 = pd.DataFrame(ret)

%timeit iterthrough()
print(table3)

print('\n-- Even Faster --')
def iterthrough():
    ret = []
    grouped = table2.groupby('letter', sort=False)
    t2info = table2.to_records()
    for index, letter, n1 in table1.to_records():
        t2 = t2info[grouped.groups[letter].values]
        maxrow = np.multiply(t2.number2, n1).argmax()
        # `[1:]`  removes the index column
        ret.append(t2[maxrow].tolist()[1:])
    global table3
    table3 = pd.DataFrame(ret, columns=('letter', 'number2'))

%timeit iterthrough()
print(table3)

मूल संस्करण की तुलना में अंतिम संस्करण लगभग 10x तेज है। रणनीति है:

  1. groupbyमूल्यों की तुलना करने से बचने के लिए उपयोग करें ।
  2. to_recordsकच्चे numpy.records ऑब्जेक्ट तक पहुंचने के लिए उपयोग करें ।
  3. जब तक आप सभी डेटा संकलित नहीं करते हैं तब तक DataFrame पर काम न करें।

0

हां, पंडास इटर्टुपल्स () इट्रोज () से तेज है। आप प्रलेखन का उल्लेख कर सकते हैं: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iterrows.html

"पंक्तियों पर पुनरावृति करते हुए dtypes को संरक्षित करने के लिए, itertuples () का उपयोग करना बेहतर होता है, जो मानों के नामितों को लौटाता है और जो आमतौर पर iterrows की तुलना में तेज़ होता है।"


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.