पंडों: पहले से मौजूद कॉलम से गणना के मान के साथ डेटाफ्रेम में दो नए कॉलम बनाएं


100

मैं पांडा लाइब्रेरी के साथ काम कर रहा हूं और मैं dfn कॉलम (n> 0) के साथ दो नए कॉलम एक डेटाफ्रेम में जोड़ना चाहता हूं ।
ये नए कॉलम डेटाफ्रेम में कॉलम के एक फ़ंक्शन के अनुप्रयोग के परिणामस्वरूप होते हैं।

लागू करने के लिए समारोह की तरह है:

def calculate(x):
    ...operate...
    return z, y

केवल मान लौटाने वाले फ़ंक्शन के लिए एक नया स्तंभ बनाने के लिए एक विधि है:

df['new_col']) = df['column_A'].map(a_function)

इसलिए, मैं जो चाहता हूं, और असफल रूप से (*) कोशिश की, वह कुछ इस तरह है:

(df['new_col_zetas'], df['new_col_ys']) = df['column_A'].map(calculate)

इसे पूरा करने का सबसे अच्छा तरीका क्या हो सकता है? मैंने दस्तावेज़ को बिना किसी सुराग के स्कैन किया ।

** df['column_A'].map(calculate)एक पंडों की श्रृंखला देता है जिसमें प्रत्येक आइटम एक tuple z, y से मिलकर बनता है। और इसे दो डेटाफ़्रेम कॉलम में निर्दिष्ट करने का प्रयास करना एक ValueError पैदा करता है। *

जवाबों:


119

मैं अभी उपयोग करूँगा zip:

In [1]: from pandas import *

In [2]: def calculate(x):
   ...:     return x*2, x*3
   ...: 

In [3]: df = DataFrame({'a': [1,2,3], 'b': [2,3,4]})

In [4]: df
Out[4]: 
   a  b
0  1  2
1  2  3
2  3  4

In [5]: df["A1"], df["A2"] = zip(*df["a"].map(calculate))

In [6]: df
Out[6]: 
   a  b  A1  A2
0  1  2   2   3
1  2  3   4   6
2  3  4   6   9

धन्यवाद, महान, यह काम करता है। मुझे डॉक्स में 0.8.1 के लिए ऐसा कुछ नहीं मिला ... मुझे लगता है कि मुझे हमेशा सीरीज़ पर ट्यूपल्स की सूची के बारे में सोचना चाहिए ...
जॉकेक्विन

क्या इसके बजाय ऐसा करने पर कोई अंतर wrt प्रदर्शन है? ज़िप (* नक्शा (गणना, डीएफ ["ए]]) के बजाय ज़िप (* डीएफ [" ए] नक्शा (गणना)), जो भी देता है (ऊपर के रूप में) [(२, ४, ६), () 3, 6, 9)]?
१४'१४

1
इस तरह के नए कॉलम निर्माण करते समय मुझे चेतावनी मिलती है: "SettingWithCopyWarning: एक मान एक DataFrame से एक स्लाइस की कॉपी पर सेट करने की कोशिश कर रहा है। इसके बजाय .loc [row_indexer, colind.indexer] = value का उपयोग करने का प्रयास करें।" क्या मुझे उस बारे में चिंतित होना चाहिए? पांडा v.0.15
तारास

46

मेरी राय में शीर्ष उत्तर त्रुटिपूर्ण है। उम्मीद है, कोई भी बड़े पैमाने पर अपने नाम स्थान के साथ सभी पांडा आयात नहीं कर रहा है from pandas import *। इसके अलावा, यह mapविधि उन समयों के लिए आरक्षित होनी चाहिए जब इसे एक शब्दकोश या श्रृंखला पास करते हैं। यह एक फ़ंक्शन ले सकता है लेकिन यह वह applyहै जिसका उपयोग किया जाता है।

इसलिए, यदि आप उपरोक्त दृष्टिकोण का उपयोग करना चाहते हैं, तो मैं इसे इस तरह से लिखूंगा

df["A1"], df["A2"] = zip(*df["a"].apply(calculate))

वास्तव में यहाँ ज़िप का उपयोग करने का कोई कारण नहीं है। आप बस यह कर सकते हैं:

df["A1"], df["A2"] = calculate(df['a'])

बड़े डेटाफ़्रेम पर यह दूसरी विधि भी बहुत तेज़ है

df = pd.DataFrame({'a': [1,2,3] * 100000, 'b': [2,3,4] * 100000})

DataFrame 300,000 पंक्तियों के साथ बनाया गया

%timeit df["A1"], df["A2"] = calculate(df['a'])
2.65 ms ± 92.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
159 ms ± 5.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

ज़िप से 60x तेज


सामान्य तौर पर, लागू होने से बचें

लागू करें आमतौर पर पायथन सूची पर पुनरावृत्ति की तुलना में बहुत तेजी से नहीं होता है। चलो ऊपर के रूप में एक ही काम करने के लिए फॉर-लूप के प्रदर्शन का परीक्षण करें

%%timeit
A1, A2 = [], []
for val in df['a']:
    A1.append(val**2)
    A2.append(val**3)

df['A1'] = A1
df['A2'] = A2

298 ms ± 7.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

तो यह दोगुना धीमा है जो एक भयानक प्रदर्शन प्रतिगमन नहीं है, लेकिन यदि हम ऊपर का पता लगाते हैं, तो हमें बेहतर प्रदर्शन मिलता है। मान लें, आप ipython का उपयोग कर रहे हैं:

%load_ext cython

%%cython
cpdef power(vals):
    A1, A2 = [], []
    cdef double val
    for val in vals:
        A1.append(val**2)
        A2.append(val**3)

    return A1, A2

%timeit df['A1'], df['A2'] = power(df['a'])
72.7 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

बिना आवेदन के सीधे कार्य करना

यदि आप प्रत्यक्ष सदिश संचालन का उपयोग करते हैं तो आप और भी अधिक गति सुधार प्राप्त कर सकते हैं।

%timeit df['A1'], df['A2'] = df['a'] ** 2, df['a'] ** 3
5.13 ms ± 320 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

यह हमारे छोरों के बजाय NumPy के बेहद तेज वेक्टर किए गए कार्यों का लाभ उठाता है। अब हमारे पास मूल पर 30x स्पीडअप है।


के साथ सबसे सरल गति परीक्षण apply

उपरोक्त उदाहरण को स्पष्ट रूप से दिखाना चाहिए कि कितना धीमा applyहो सकता है, लेकिन बस इसके अतिरिक्त स्पष्ट चलो सबसे बुनियादी उदाहरण देखें। चलो लागू होते हैं और बिना लागू के 10 मिलियन संख्याओं की एक श्रृंखला को स्क्वायर करते हैं

s = pd.Series(np.random.rand(10000000))

%timeit s.apply(calc)
3.3 s ± 57.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

बिना आवेदन 50x तेज है

%timeit s ** 2
66 ms ± 2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

1
यह एक बहुत अच्छा जवाब है। मैं पूछना चाहता था: applymapजब आप डेटाफ़्रेम के प्रत्येक तत्व के लिए एक विशिष्ट फ़ंक्शन को लागू करना चाहते हैं तो आप उस मामले के लिए क्या सोचते हैं ?
डेविड

3
जबकि इस उत्तर में कुछ अच्छी सलाह है, मेरा मानना ​​है कि func(series)इसके बजाय उपयोग करने के लिए मुख्य सलाह series.apply(func)केवल तब लागू होती है जब फंक पूरी तरह से उन ऑपरेशनों का उपयोग करके परिभाषित किया जाता है जो एक व्यक्तिगत मूल्य और एक श्रृंखला पर दोनों के समान व्यवहार करते हैं। उदाहरण के मामले में यह पहला उत्तर है, लेकिन यह ओपी के सवाल में ऐसा नहीं है, जो आमतौर पर स्तंभों पर कार्य करने के बारे में अधिक पूछ रहा है। 1/2
ग्राहम ली

1
एक उदाहरण के रूप में, यदि df है: DataFrame({'a': ['Aaron', 'Bert', 'Christopher'], 'b': ['Bold', 'Courageous', 'Distrusted']})और calcहै: def calc(x): return x[0], len(x)तो tdf.a.apply(calc))और calc(tdf.a)बहुत अलग चीजें लौटाएं।
ग्राहम ली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.