2014-05-22 12 views
5

मेरे पास पांडा में एक पैनल है और प्रत्येक चरण में एक व्यक्ति खर्च करने की अवधि की गणना करने की कोशिश कर रहा हूं। देने के लिए यह मेरा डाटासेट की बेहतर समझ इस प्रकार है:पांडों में रोलिंग समय अंतर की गणना कुशलतापूर्वक

group  date stage 
A  2014-01-01 one 
A  2014-01-03 one  
A  2014-01-04 one  
A  2014-01-05 two  
B  2014-01-02 four  
B  2014-01-06 five  
B  2014-01-10 five  
C  2014-01-03 two  
C  2014-01-05 two  

मैं मंच अवधि की गणना करने के लिए देने के लिए देख रहा हूँ:

group  date stage dur 
    A  2014-01-01 one 0 
    A  2014-01-03 one 2 
    A  2014-01-04 one 3 
    A  2014-01-05 two 0 
    B  2014-01-02 four 0 
    B  2014-01-06 five 0 
    B  2014-01-10 five 4 
    C  2014-01-03 two 0 
    C  2014-01-05 two 2 

विधि है कि मैं नीचे का उपयोग कर रहा बेहद धीमी गति से होता है। एक त्वरित विधि पर कोई विचार?

df['stage_duration'] = df.groupby(['group', 'stage']).date.apply(lambda y: (y - y.iloc[0])).apply(lambda y:y/np.timedelta64(1, 'D'))) 
+0

आप अंतिम लागू की जरूरत नहीं है यहाँ देखने दे सकता है लागू होते हैं: http://pandas-docs.github.io/pandas-docs -travis/timeseries.html # समय-डेल्टा-रूपांतरण, आप बस '' टाइप टाइप कर सकते हैं ('timedelta64 [D]') '' या np.timedelta64 (1, 'डी') द्वारा विभाजित करें '(वे sligthly हैं वे कैसे दौर में अलग हैं। – Jeff

उत्तर

4

अपने कोड (आपके groupby/apply) के आधार पर, ऐसा लगता है (आपके उदाहरण के बावजूद ... लेकिन हो सकता है कि मैं आपको क्या समझना चाहूंगा और फिर एंडी क्या सबसे अच्छा विचार होगा) कि आप 'डेट' के साथ काम कर रहे हैं कॉलम जो datetime64 dtype है और आपके वास्तविक डेटा में integer dtype नहीं है। ऐसा लगता है कि आप दिए गए group/stage के पहले अवलोकन से मापा गया दिनों में परिवर्तन की गणना करना चाहते हैं। मुझे लगता है कि इस उदाहरण डेटा का एक बेहतर सेट है (अगर मैं सही ढंग से अपने लक्ष्य को समझने):

>>> df 

    group  date stage dur 
0  A 2014-01-01 one 0 
1  A 2014-01-03 one 2 
2  A 2014-01-04 one 3 
3  A 2014-01-05 two 0 
4  B 2014-01-02 four 0 
5  B 2014-01-06 five 0 
6  B 2014-01-10 five 4 
7  C 2014-01-03 two 0 
8  C 2014-01-05 two 2 

यह देखते हुए कि तुम सिर्फ भाग देकर अपना लागू (के रूप में जेफ उसकी टिप्पणी में पता चलता है) को संशोधित करने से कुछ गति-अप मिलना चाहिए के माध्यम से द्वारा एक vectorized तरह से timedelta64 के बाद लागू (या आप लागू में यह कर सकता है):

>>> df['dur'] = df.groupby(['group','stage']).date.apply(lambda x: x - x.iloc[0]) 
>>> df['dur'] /= np.timedelta64(1,'D') 
>>> df 

    group  date stage dur 
0  A 2014-01-01 one 0 
1  A 2014-01-03 one 2 
2  A 2014-01-04 one 3 
3  A 2014-01-05 two 0 
4  B 2014-01-02 four 0 
5  B 2014-01-06 five 0 
6  B 2014-01-10 five 4 
7  C 2014-01-03 two 0 
8  C 2014-01-05 two 2 

लेकिन क्या आप भी बच सकते हैं groupby/apply अपने डेटा को देखते हुए समूह, मंच, तिथि के क्रम में है। प्रत्येक ['group','stage'] समूहिंग के लिए पहली तारीख तब होती है जब समूह बदलता है या चरण बदलता है। तो मुझे लगता है आप निम्नलिखित की तरह कुछ कर सकते हैं:

>>> beg = (df.group != df.group.shift(1)) | (df.stage != df.stage.shift(1)) 
>>> df['dur'] = (df['date'] - df['date'].where(beg).ffill())/np.timedelta64(1,'D') 
>>> df 

    group  date stage dur 
0  A 2014-01-01 one 0 
1  A 2014-01-03 one 2 
2  A 2014-01-04 one 3 
3  A 2014-01-05 two 0 
4  B 2014-01-02 four 0 
5  B 2014-01-06 five 0 
6  B 2014-01-10 five 4 
7  C 2014-01-03 two 0 
8  C 2014-01-05 two 2 

स्पष्टीकरण: नोट क्या df['date'].where(beg) बनाता है:

>>> beg = (df.group != df.group.shift(1)) | (df.stage != df.stage.shift(1)) 
>>> df['date'].where(beg) 

0 2014-01-01 
1   NaT 
2   NaT 
3 2014-01-05 
4 2014-01-02 
5 2014-01-06 
6   NaT 
7 2014-01-03 
8   NaT 

और फिर मैं मान ffill और 'तारीख' कॉलम के साथ अंतर ले।

संपादित करें: एंडी बताते के रूप में आप भी transform इस्तेमाल कर सकते हैं:

>>> df['dur'] = df.date - df.groupby(['group','stage']).date.transform(lambda x: x.iloc[0]) 
>>> df['dur'] /= np.timedelta64(1,'D') 

    group  date stage dur 
0  A 2014-01-01 one 0 
1  A 2014-01-03 one 2 
2  A 2014-01-04 one 3 
3  A 2014-01-05 two 0 
4  B 2014-01-02 four 0 
5  B 2014-01-06 five 0 
6  B 2014-01-10 five 4 
7  C 2014-01-03 two 0 
8  C 2014-01-05 two 2 

स्पीड: मैं 400,000 टिप्पणियों के साथ एक समान dataframe का उपयोग कर दो विधि का समय समाप्त हो:

विधि लागू करें:

1 loops, best of 3: 18.3 s per loop 

गैर-लागू विधि:

1 loops, best of 3: 1.64 s per loop 

तो मैं परहेज लगता है कि कुछ महत्वपूर्ण गति-अप

+0

+1 यह शायद ओपी चाहता है के लिए अधिक समझ में आता है ... मुझे लगता है कि आप एक ट्रांसफॉर्म का उपयोग करके इसे और अधिक कुशलतापूर्वक करने में सक्षम हो सकते हैं। –

+0

हाँ @ एंडी, मैंने 'ट्रांसफॉर्म' के बारे में सोचा लेकिन कम से कम 0.13.1 के लिए मैं आम तौर पर सामान्य 'लागू' से अधिक तेज़ नहीं बदलता, इसलिए मैंने इसे शामिल नहीं किया। लेकिन मैं इसके साथ एक विकल्प के रूप में जवाब अद्यतन कर दूंगा। –

+0

यह देखने में दिलचस्पी है कि यह तेज़ है या नहीं, मेरा अनुमान है कि यह होगा (हालांकि समूह के आकार पर निर्भर होगा - बड़े समूहों के मामले में संदिग्ध तेज होगा)। –

4

मुझे लगता है कि मैं यहाँ diff का उपयोग करेंगे:

In [11]: df.groupby('stage')['date'].diff().fillna(0) 
Out[11]: 
0 0 
1 2 
2 0 
3 0 
4 0 
5 4 
dtype: float64 

(यह मानते हुए कि चरणों सन्निहित हैं।)

तुम सिर्फ प्रत्येक समूह में पहले घटाकर रहे हैं, तो एक transform का उपयोग :

In [21]: df['date'] - df.groupby('stage')['date'].transform(lambda x: x.iloc[0]) 
Out[21]: 
0 0 
1 2 
2 0 
3 0 
4 0 
5 4 
Name: date, dtype: int64 

नोट: यह शायद काफी तेज़ है ...

संबंधित मुद्दे