पंडों डेटाफ्रेम के दो स्तंभों के लिए एक फ़ंक्शन कैसे लागू करें


368

मान लीजिए कि मेरे पास एक dfकॉलम है 'ID', 'col_1', 'col_2'। और मैं एक फ़ंक्शन को परिभाषित करता हूं:

f = lambda x, y : my_function_expression

अब मैं लागू करना चाहते fकरने के लिए dfकी दो कॉलम 'col_1', 'col_2'तत्व के लिहाज से गणना करने के लिए एक नया स्तंभ 'col_3'कुछ हद तक की तरह,:

df['col_3'] = df[['col_1','col_2']].apply(f)  
# Pandas gives : TypeError: ('<lambda>() takes exactly 2 arguments (1 given)'

कैसे करें ?

** नीचे के रूप में विस्तार नमूना जोड़ें ***

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

  ID  col_1  col_2            col_3
0  1      0      1       ['a', 'b']
1  2      2      4  ['c', 'd', 'e']
2  3      3      5  ['d', 'e', 'f']

4
क्या आप सीधे कॉलमों पर f लागू कर सकते हैं: df ['col_3'] = f (df ['col_1'], df ['col_2'])
btel

1
पता है कि उपयोगी होगा fक्या कर रही है
tehmisvh

2
नहीं, df ['col_3'] = f (df ['col_1'], df ['col_2']) काम नहीं करते। एफ केवल स्केलर इनपुट को स्वीकार करता है, न कि वेक्टर इनपुट को। ठीक है, आप f = lambda x, y: x + y मान सकते हैं। (बेशक, मेरा असली च नहीं है कि सरल, नहीं तो मैं कर सकता हूँ सीधे df [ 'col_3'] = df [ 'col_1'] df [ 'col_2'])
bigbug

1
मुझे url के नीचे एक संबंधित Q & A मिला, लेकिन मेरा मुद्दा दो मौजूदा कॉलमों की गणना कर रहा है, न कि 1 से 2। stackoverflow.com/questions/12356501/…
bigbug

मुझे लगता है कि मेरी प्रतिक्रिया stackoverflow.com/a/52854800/5447172 सबसे पाइथोनिक / पांडनिक तरीके से इसका जवाब देती है, जिसमें कोई वर्कअराउंड या संख्यात्मक सूचकांक नहीं है। यह आपके उदाहरण में आपके द्वारा आवश्यक आउटपुट का उत्पादन करता है।
अजरवित

जवाबों:


291

यहां applyडेटाफ्रेम पर उपयोग करने वाला एक उदाहरण है , जिसे मैं कॉल कर रहा हूं axis = 1

अंतर पर ध्यान दें कि फ़ंक्शन में दो मान पास करने की कोशिश करने के बजाय f, एक पांडा श्रृंखला ऑब्जेक्ट को स्वीकार करने के लिए फ़ंक्शन को फिर से लिखें, और फिर आवश्यक मान प्राप्त करने के लिए श्रृंखला को अनुक्रमित करें।

In [49]: df
Out[49]: 
          0         1
0  1.000000  0.000000
1 -0.494375  0.570994
2  1.000000  0.000000
3  1.876360 -0.229738
4  1.000000  0.000000

In [50]: def f(x):    
   ....:  return x[0] + x[1]  
   ....:  

In [51]: df.apply(f, axis=1) #passes a Series object, row-wise
Out[51]: 
0    1.000000
1    0.076619
2    1.000000
3    1.646622
4    1.000000

आपके उपयोग के मामले के आधार पर, यह कभी-कभी एक पांडा groupवस्तु बनाने में सहायक होता है , और फिर applyसमूह पर उपयोग होता है।


हां, मैंने आवेदन का उपयोग करने की कोशिश की, लेकिन मान्य वाक्यविन्यास अभिव्यक्ति नहीं मिल रही है। और अगर df की प्रत्येक पंक्ति अद्वितीय है, तब भी समूह का उपयोग करें?
Bigbug

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

1
क्या आप अपना कोड पेस्ट करेंगे? मैं फ़ंक्शन को फिर से लिखता हूं: def get_sublist (x): रिटर्न mylist [x [1]: x [2] + 1] और df ['col_3'] = df.apply (get_sublist, axis = 1) 'ValueError: ऑपरेंड्स दे सकता है आकृतियों (2) (3) '
bigbug

3
@Aman: पंडों के संस्करण ०.१४.१ (और संभवतः पहले) के साथ, लैंबडा अभिव्यक्ति का उपयोग कर सकते हैं। दे दो dfवस्तु आप परिभाषित, एक और दृष्टिकोण (बराबर परिणामों के साथ) है df.apply(lambda x: x[0] + x[1], axis = 1)
बुलबुले

2
@CanCeylan आप केवल इंडेक्स के बजाय फ़ंक्शन में कॉलम नामों का उपयोग कर सकते हैं, फिर आपको ऑर्डर बदलने के बारे में चिंता करने की आवश्यकता नहीं है, या नाम से इंडेक्स प्राप्त करें जैसे कि stackoverflow.com/questions/13021654/…
डेविस

165

पंडों में ऐसा करने का एक साफ, एक-लाइन तरीका है:

df['col_3'] = df.apply(lambda x: f(x.col_1, x.col_2), axis=1)

यह fकई इनपुट मूल्यों के साथ एक उपयोगकर्ता-परिभाषित फ़ंक्शन होने की अनुमति देता है, और स्तंभों तक पहुंचने के लिए (असुरक्षित) संख्यात्मक सूचक के बजाय (सुरक्षित) स्तंभ नामों का उपयोग करता है।

डेटा के साथ उदाहरण (मूल प्रश्न पर आधारित):

import pandas as pd

df = pd.DataFrame({'ID':['1', '2', '3'], 'col_1': [0, 2, 3], 'col_2':[1, 4, 5]})
mylist = ['a', 'b', 'c', 'd', 'e', 'f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

df['col_3'] = df.apply(lambda x: get_sublist(x.col_1, x.col_2), axis=1)

का आउटपुट print(df):

  ID  col_1  col_2      col_3
0  1      0      1     [a, b]
1  2      2      4  [c, d, e]
2  3      3      5  [d, e, f]

यदि आपके स्तंभ नामों में स्थान हैं या किसी मौजूदा डेटाफ़्रेम विशेषता के साथ नाम साझा करते हैं, तो आप वर्ग कोष्ठक के साथ अनुक्रमण कर सकते हैं:

df['col_3'] = df.apply(lambda x: f(x['col 1'], x['col 2']), axis=1)

2
ध्यान दें, यदि का उपयोग करते हुए axis=1और आप स्तंभ कहा जाता है nameकि यह वास्तव में अपने स्तंभ डेटा लेकिन नहीं लौटेगा indexnameएक में प्राप्त करने के समान groupby()। मैंने अपने कॉलम का नाम बदलकर इसे हल किया।
टॉम हेम्स

2
यह बात है! मुझे नहीं पता था कि आप लैम्बदास में कई इनपुट मापदंडों के साथ उपयोगकर्ता-परिभाषित फ़ंक्शन सम्मिलित कर सकते हैं। यह नोट करना महत्वपूर्ण है (मुझे लगता है) कि आप DF.apply () के बजाय Series.apply () का उपयोग कर रहे हैं। इससे आप अपने इच्छित दो कॉलमों का उपयोग करते हुए df को अनुक्रमित कर सकते हैं और पूरे कॉलम को फ़ंक्शन में पास कर सकते हैं, लेकिन क्योंकि आप लागू () का उपयोग कर रहे हैं, यह फ़ंक्शन को पूरे कॉलम के नीचे तत्व-वार फैशन में लागू करता है। प्रतिभाशाली! प्रविष्टि के लिए धन्यवाद!
डेटा-फील

1
आखिरकार! आपने मेरा दिन बचाया!
मिस्टीरियो

मेरा मानना ​​है कि ऐसा करने का सुझाया तरीका df.loc है: ['नई बस्ती'] = df.apply .....
valearner

@valearner मुझे नहीं लगता .locकि उदाहरण में पसंद करने का कोई कारण है । यदि आप इसे किसी अन्य समस्या सेटिंग (जैसे स्लाइस के साथ काम करना) के लिए अनुकूलित करते हैं, तो इसकी आवश्यकता हो सकती है।
ajrwhite

86

एक सरल उपाय है:

df['col_3'] = df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)

1
यह उत्तर किस तरह से पूछताछ के दृष्टिकोण के लिए अलग है: df ['col_3'] = df [['col_1', 'col_2']]। (f) सिर्फ पुष्टि करने के लिए, प्रश्न में दृष्टिकोण काम नहीं किया क्योंकि। पोस्टर ने इस अक्ष = 1 को निर्दिष्ट नहीं किया, डिफ़ॉल्ट अक्ष = 0 है?
लॉस्ट

1
यह उत्तर @ अनमन के उत्तर के समान है, लेकिन थोड़ा अस्थिर है। वह एक अनाम फ़ंक्शन का निर्माण कर रहा है, जो एक पुनरावृत्ति लेता है, और इसे फ़ंक्शन f में पास करने से पहले इसे अनपैक कर देता है।
टिआओ

39

एक दिलचस्प सवाल! मेरा जवाब नीचे के रूप में:

import pandas as pd

def sublst(row):
    return lst[row['J1']:row['J2']]

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

df['J3'] = df.apply(sublst,axis=1)
print df

आउटपुट:

  ID  J1  J2
0  1   0   1
1  2   2   4
2  3   3   5
  ID  J1  J2      J3
0  1   0   1     [a]
1  2   2   4  [c, d]
2  3   3   5  [d, e]

मैंने ID <J1 <J2 <J3 सुनिश्चित करने के लिए कॉलम नाम को ID, J1, J2, J3 में बदल दिया, इसलिए कॉलम सही क्रम में प्रदर्शित हो।

एक और संक्षिप्त संस्करण:

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

df['J3'] = df.apply(lambda row:lst[row['J1']:row['J2']],axis=1)
print df

23

जिस विधि की आप तलाश कर रहे हैं, वह Series.combine है। हालाँकि, ऐसा लगता है कि डेटाटाइप्स के आसपास कुछ देखभाल की जानी चाहिए। आपके उदाहरण में, आप (जैसा कि मैंने उत्तर का परीक्षण करते समय किया था) भोलेपन से कहते हैं

df['col_3'] = df.col_1.combine(df.col_2, func=get_sublist)

हालाँकि, यह त्रुटि फेंकता है:

ValueError: setting an array element with a sequence.

मेरा सबसे अच्छा अनुमान यह है कि यह उसी प्रकार के परिणाम की अपेक्षा करता है जैसे श्रृंखला कॉलिंग विधि (df.col_1 यहाँ)। हालांकि, निम्नलिखित काम करता है:

df['col_3'] = df.col_1.astype(object).combine(df.col_2, func=get_sublist)

df

   ID   col_1   col_2   col_3
0   1   0   1   [a, b]
1   2   2   4   [c, d, e]
2   3   3   5   [d, e, f]

12

जिस तरह से आपने f लिखा है, उसे दो इनपुट की जरूरत है। यदि आप त्रुटि संदेश को देखते हैं तो यह कहता है कि आप f को दो इनपुट प्रदान नहीं कर रहे हैं, सिर्फ एक। त्रुटि संदेश सही है।
बेमेल इसलिए है क्योंकि df [['col1', 'col2']] दो कॉलम के साथ एक एकल डेटाफ्रेम लौटाता है, दो अलग कॉलम नहीं।

आपको अपने f को बदलने की आवश्यकता है ताकि यह एक एकल इनपुट ले, उपरोक्त डेटा फ्रेम को इनपुट के रूप में रखें, फिर इसे फंक्शन बॉडी के अंदर x, y तक तोड़ दें । फिर आपको जो भी आवश्यक हो और एक भी मूल्य वापस करें।

आपको इस फ़ंक्शन हस्ताक्षर की आवश्यकता है क्योंकि सिंटैक्स .apply (f) है तो f को एकल चीज़ = डेटाफ़्रेम और दो चीज़ों को लेने की आवश्यकता है जो कि आपकी वर्तमान f अपेक्षा है।

चूँकि आपने f का शरीर प्रदान नहीं किया है, इसलिए मैं अब और विस्तार से मदद नहीं कर सकता - लेकिन यह आपके कोड को बदलने या लागू करने के बजाय कुछ अन्य तरीकों का उपयोग किए बिना रास्ता प्रदान करना चाहिए


12

मैं np.vectorize के लिए एक वोट डालने जा रहा हूं। यह आपको केवल स्तंभों की x संख्या से अधिक शूट करने और फ़ंक्शन में डेटाफ़्रेम से निपटने की अनुमति नहीं देता है, इसलिए यह उन फ़ंक्शन के लिए बहुत अच्छा है जिन्हें आप नियंत्रित नहीं करते हैं या 2 कॉलम भेजने और फ़ंक्शन में एक निरंतरता (जैसे col_1, col_2) 'foo')।

import numpy as np
import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

df.loc[:,'col_3'] = np.vectorize(get_sublist, otypes=["O"]) (df['col_1'], df['col_2'])


df

ID  col_1   col_2   col_3
0   1   0   1   [a, b]
1   2   2   4   [c, d, e]
2   3   3   5   [d, e, f]

1
यह वास्तव में पांडा के उपयोग से प्रश्न का उत्तर नहीं देता है।
mnky9800n

18
सवाल यह है कि "पंडों के दो स्तंभों के लिए एक फ़ंक्शन कैसे लागू किया जाए" नहीं "केवल पंडों के तरीकों का उपयोग करके पांडस डेटाफ़्रेम के दो स्तंभों के लिए एक फ़ंक्शन कैसे लागू करें" और खस्ता पंडों की एक निर्भरता है, इसलिए आपको इसे वैसे भी स्थापित करना होगा। तो यह एक अजीब आपत्ति की तरह लगता है।
ट्राई वालेस

12

से एक सूची वापस applyकरना एक खतरनाक ऑपरेशन है क्योंकि परिणामी वस्तु को श्रृंखला या डेटाफ़्रेम होने की गारंटी नहीं है। और अपवाद कुछ मामलों में उठाए जा सकते हैं। चलो एक सरल उदाहरण के माध्यम से चलते हैं:

df = pd.DataFrame(data=np.random.randint(0, 5, (5,3)),
                  columns=['a', 'b', 'c'])
df
   a  b  c
0  4  0  0
1  2  0  1
2  2  2  2
3  1  2  2
4  3  0  0

सूची से लौटने के साथ तीन संभावित परिणाम हैं apply

1) यदि लौटी हुई सूची की लंबाई स्तंभों की संख्या के बराबर नहीं है, तो सूचियों की एक श्रृंखला वापस आ जाती है।

df.apply(lambda x: list(range(2)), axis=1)  # returns a Series
0    [0, 1]
1    [0, 1]
2    [0, 1]
3    [0, 1]
4    [0, 1]
dtype: object

2) जब लौटी सूची की लंबाई स्तंभों की संख्या के बराबर होती है तो एक DataFrame वापस कर दिया जाता है और प्रत्येक कॉलम को सूची में संबंधित मान मिलता है।

df.apply(lambda x: list(range(3)), axis=1) # returns a DataFrame
   a  b  c
0  0  1  2
1  0  1  2
2  0  1  2
3  0  1  2
4  0  1  2

3) यदि दी गई सूची की लंबाई पहली पंक्ति के लिए स्तंभों की संख्या के बराबर है, लेकिन कम से कम एक पंक्ति है जहां सूची में स्तंभों की संख्या की तुलना में तत्वों की एक अलग संख्या है, तो एक ValueError उठाया जाता है।

i = 0
def f(x):
    global i
    if i == 0:
        i += 1
        return list(range(3))
    return list(range(4))

df.apply(f, axis=1) 
ValueError: Shape of passed values is (5, 4), indices imply (5, 3)

बिना आवेदन के समस्या का जवाब देना

applyअक्ष = 1 के साथ उपयोग करना बहुत धीमा है। बुनियादी पुनरावृत्ति विधियों के साथ बहुत बेहतर प्रदर्शन (विशेष रूप से बड़े डेटासेट पर) प्राप्त करना संभव है।

बड़ा डेटाफ़्रेम बनाएँ

df1 = df.sample(100000, replace=True).reset_index(drop=True)

समय

# apply is slow with axis=1
%timeit df1.apply(lambda x: mylist[x['col_1']: x['col_2']+1], axis=1)
2.59 s ± 76.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# zip - similar to @Thomas
%timeit [mylist[v1:v2+1] for v1, v2 in zip(df1.col_1, df1.col_2)]  
29.5 ms ± 534 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

@ थोमस का जवाब

%timeit list(map(get_sublist, df1['col_1'],df1['col_2']))
34 ms ± 459 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

1
जहां से सीखना संभव है, वहां से इतने विस्तृत उत्तर देखकर अच्छा लगा।
एंड्रिया मोरो

7

मुझे यकीन है कि यह पंडों या Numpy संचालन का उपयोग करने वाले समाधान के रूप में तेज़ नहीं है, लेकिन यदि आप अपने फ़ंक्शन को फिर से लिखना नहीं चाहते हैं तो आप मानचित्र का उपयोग कर सकते हैं। मूल उदाहरण डेटा का उपयोग करना -

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

df['col_3'] = list(map(get_sublist,df['col_1'],df['col_2']))
#In Python 2 don't convert above to list

हम उतने ही तर्कों को पारित कर सकते हैं जितना हम इस तरह से कार्य करना चाहते हैं। आउटपुट वही है जो हम चाहते थे

ID  col_1  col_2      col_3
0  1      0      1     [a, b]
1  2      2      4  [c, d, e]
2  3      3      5  [d, e, f]

1
यह वास्तव में बहुत तेजी से उन जवाब है कि इस्तेमाल होता है applyके साथaxis=1
टेड Petrou

2

आपके प्रश्नों के लिए मेरा उदाहरण:

def get_sublist(row, col1, col2):
    return mylist[row[col1]:row[col2]+1]
df.apply(get_sublist, axis=1, col1='col_1', col2='col_2')

2

यदि आपके पास एक विशाल डेटा-सेट है, तो आप स्विफ्ट का उपयोग करके ऐसा करने का एक आसान लेकिन तेज़ (निष्पादन समय) तरीका उपयोग कर सकते हैं:

import pandas as pd
import swifter

def fnc(m,x,c):
    return m*x+c

df = pd.DataFrame({"m": [1,2,3,4,5,6], "c": [1,1,1,1,1,1], "x":[5,3,6,2,6,1]})
df["y"] = df.swifter.apply(lambda x: fnc(x.m, x.x, x.c), axis=1)

1

मुझे लगता है कि आप get_sublistफ़ंक्शन को बदलना नहीं चाहते हैं , और बस काम करने के लिए DataFrame की applyविधि का उपयोग करना चाहते हैं । मनचाहा परिणाम पाने के लिए, मैंने दो सहायता कार्य लिखे हैं: get_sublist_listऔर unlist। जैसा कि फ़ंक्शन नाम का सुझाव है, पहले सब लिस्ट की सूची प्राप्त करें, दूसरा उस सूची से उस सबलिस्ट को निकालें। अंत में, हमें बाद applyमें df[['col_1','col_2']]DataFrame को उन दो कार्यों को लागू करने के लिए फ़ंक्शन को कॉल करना होगा ।

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

def get_sublist_list(cols):
    return [get_sublist(cols[0],cols[1])]

def unlist(list_of_lists):
    return list_of_lists[0]

df['col_3'] = df[['col_1','col_2']].apply(get_sublist_list,axis=1).apply(unlist)

df

यदि आप फ़ंक्शन []को संलग्न करने के लिए उपयोग नहीं करते हैं get_sublist, तो get_sublist_listफ़ंक्शन एक सादे सूची वापस करेगा, यह बढ़ा देगा ValueError: could not broadcast input array from shape (3) into shape (2), जैसा कि @Ted Petrou ने उल्लेख किया था।

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