मुझे अपने कोड में कभी भी पांडा के उपयोग () का उपयोग कब करना चाहिए?


111

मैंने पंडों की विधि के उपयोग से जुड़े स्टैक ओवरफ्लो पर प्रश्नों के कई उत्तर देखे हैं apply। मैंने उपयोगकर्ताओं को यह कहते हुए टिप्पणी करते हुए भी देखा है कि " applyधीमा है, और इससे बचना चाहिए"।

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

  1. यदि applyइतना बुरा है, तो यह एपीआई में क्यों है?
  2. मुझे अपना कोड कैसे और कब बनाना चाहिए apply?
  3. वहाँ कभी कर रहे हैं किसी भी स्थितियों में, जहां applyहै अच्छा (बेहतर अन्य संभावित समाधानों की तुलना में)?

1
returns.add(1).apply(np.log)बनाम np.log(returns.add(1)एक ऐसा मामला है जहां applyआम तौर पर थोड़ी तेजी होगी, जो कि नीचे jpp के आरेख में नीचे दाईं ओर हरे रंग का बॉक्स है।
अलेक्जेंडर

@Alexander धन्यवाद। इन स्थितियों को पूरी तरह से इंगित नहीं किया, लेकिन वे जानना उपयोगी हैं!
सीएस 95 95

जवाबों:


108

applyआप कभी जरूरत नहीं है, सुविधा समारोह

हम ओपी में प्रश्नों को एक-एक करके संबोधित करते हुए शुरू करते हैं।

" अगर आवेदन इतना बुरा है, तो यह एपीआई में क्यों है? "

DataFrame.applyऔर Series.applyकर रहे हैं सुविधा कार्यों DataFrame और सीरीज पर परिभाषित क्रमश आपत्ति है। applyकिसी भी उपयोगकर्ता परिभाषित फ़ंक्शन को स्वीकार करता है जो किसी DataFrame पर परिवर्तन / एकत्रीकरण लागू करता है। applyप्रभावी रूप से एक चांदी की गोली है जो किसी भी मौजूदा पांडा कार्य नहीं कर सकती है।

कुछ चीजें applyकर सकते हैं:

  • DataFrame या Series पर कोई भी उपयोगकर्ता-परिभाषित फ़ंक्शन चलाएँ
  • डेटाफ़्रेम पर किसी फ़ंक्शन को पंक्ति-वार ( axis=1) या स्तंभ-वार ( axis=0) लागू करें
  • फ़ंक्शन को लागू करते समय सूचकांक संरेखण करें
  • उपयोगकर्ता-परिभाषित कार्यों के साथ एकत्रीकरण करें (हालांकि, हम आमतौर पर पसंद करते हैं aggया transformइन मामलों में)
  • तत्व-वार रूपांतरण करें
  • मूल पंक्तियों को समेकित परिणाम प्रसारित करें ( result_typeतर्क देखें )।
  • उपयोगकर्ता द्वारा परिभाषित कार्यों को पारित करने के लिए स्थितीय / खोजशब्द तर्क स्वीकार करें।

...दूसरों के बीच में। अधिक जानकारी के लिए, दस्तावेज़ में पंक्ति या स्तंभ-वार फ़ंक्शन अनुप्रयोग देखें

तो, इन सभी विशेषताओं के साथ, applyबुरा क्यों है ? यह है क्योंकि applyहै धीमी गति से । पंडों आपके फ़ंक्शन की प्रकृति के बारे में कोई धारणा नहीं बनाते हैं, और इसलिए पुनरावृत्ति आवश्यक रूप से प्रत्येक पंक्ति / स्तंभ पर आपके फ़ंक्शन को लागू करती है। इसके अतिरिक्त, उपरोक्त सभी स्थितियों को संभालने का अर्थ है कि applyप्रत्येक पुनरावृत्ति में कुछ प्रमुख ओवरहेड हो। इसके अलावा, applyबहुत अधिक मेमोरी का उपभोग करता है, जो मेमोरी बाउंड एप्लिकेशन के लिए एक चुनौती है।

ऐसी बहुत कम परिस्थितियाँ हैं, जिनका applyउपयोग करना उचित है (उस नीचे अधिक)। यदि आप सुनिश्चित नहीं हैं कि आप का उपयोग किया जाना चाहिए apply, तो आपको शायद नहीं करना चाहिए।


आइए अगले प्रश्न पर ध्यान दें।

" मुझे अपना कोड कब और कैसे लागू करना चाहिए ? "

रीफ़्रेज़ करने के लिए, यहाँ कुछ सामान्य स्थितियाँ हैं जहाँ आप किसी भी कॉल से छुटकारा पाना चाहेंगे apply

संख्यात्मक डेटा

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

applyएक साधारण जोड़ ऑपरेशन के लिए प्रदर्शन का विरोध करें ।

df = pd.DataFrame({"A": [9, 4, 2, 1], "B": [12, 7, 5, 4]})
df

   A   B
0  9  12
1  4   7
2  2   5
3  1   4

df.apply(np.sum)

A    16
B    28
dtype: int64

df.sum()

A    16
B    28
dtype: int64

प्रदर्शन के लिहाज से, इसकी कोई तुलना नहीं है, साइंटोनाइज्ड समकक्ष बहुत तेज है। एक ग्राफ की जरूरत नहीं है, क्योंकि अंतर खिलौना डेटा के लिए भी स्पष्ट है।

%timeit df.apply(np.sum)
%timeit df.sum()
2.22 ms ± 41.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
471 µs ± 8.16 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

यहां तक ​​कि अगर आप rawतर्क के साथ कच्चे सरणियों को पारित करने में सक्षम करते हैं, तो यह अभी भी दो बार धीमा है।

%timeit df.apply(np.sum, raw=True)
840 µs ± 691 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

एक और उदाहरण:

df.apply(lambda x: x.max() - x.min())

A    8
B    8
dtype: int64

df.max() - df.min()

A    8
B    8
dtype: int64

%timeit df.apply(lambda x: x.max() - x.min())
%timeit df.max() - df.min()

2.43 ms ± 450 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.23 ms ± 14.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

सामान्य तौर पर, यदि संभव हो तो सदिश विकल्पों की तलाश करें।

स्ट्रिंग / Regex

पंडों ज्यादातर स्थितियों में "वेक्टराइज्ड" स्ट्रिंग कार्य प्रदान करते हैं, लेकिन ऐसे दुर्लभ मामले हैं जहां उन कार्यों को नहीं ... "लागू करें", इसलिए बोलने के लिए।

एक सामान्य समस्या यह जांचना है कि क्या स्तंभ में एक मान उसी पंक्ति के किसी अन्य स्तंभ में मौजूद है।

df = pd.DataFrame({
    'Name': ['mickey', 'donald', 'minnie'],
    'Title': ['wonderland', "welcome to donald's castle", 'Minnie mouse clubhouse'],
    'Value': [20, 10, 86]})
df

     Name  Value                       Title
0  mickey     20                  wonderland
1  donald     10  welcome to donald's castle
2  minnie     86      Minnie mouse clubhouse

यह पंक्ति दूसरी और तीसरी पंक्ति को वापस कर देना चाहिए, क्योंकि "डोनाल्ड" और "मिन्नी" अपने संबंधित "शीर्षक" कॉलम में मौजूद हैं।

प्रयोग का उपयोग करते हुए, यह प्रयोग किया जाएगा

df.apply(lambda x: x['Name'].lower() in x['Title'].lower(), axis=1)

0    False
1     True
2     True
dtype: bool

df[df.apply(lambda x: x['Name'].lower() in x['Title'].lower(), axis=1)]

     Name                       Title  Value
1  donald  welcome to donald's castle     10
2  minnie      Minnie mouse clubhouse     86

हालाँकि, सूची बोध का उपयोग करके एक बेहतर समाधान मौजूद है।

df[[y.lower() in x.lower() for x, y in zip(df['Title'], df['Name'])]]

     Name                       Title  Value
1  donald  welcome to donald's castle     10
2  minnie      Minnie mouse clubhouse     86

%timeit df[df.apply(lambda x: x['Name'].lower() in x['Title'].lower(), axis=1)]
%timeit df[[y.lower() in x.lower() for x, y in zip(df['Title'], df['Name'])]]

2.85 ms ± 38.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
788 µs ± 16.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

यहां ध्यान देने वाली बात यह है कि applyओवरहेड रूटीन लोअरहेड की वजह से तेज गति से होता है । यदि आपको NaNs और अमान्य dtypes को संभालने की आवश्यकता है, तो आप इस पर एक कस्टम फ़ंक्शन का उपयोग करके बना सकते हैं जिसे आप सूची समझ के अंदर तर्कों के साथ कॉल कर सकते हैं।

सूची बोध को कब एक अच्छा विकल्प माना जाना चाहिए, इस बारे में अधिक जानकारी के लिए, मेरा राइटअप देखें: फॉर लूप्स विद पांडा - आपको कब ध्यान देना चाहिए?

नोट
दिनांक और डेटाटाइम संचालन के भी सदिश संस्करण हैं। इसलिए, उदाहरण के लिए, आपको पसंद करना चाहिए pd.to_datetime(df['date']), ओवर, कहना df['date'].apply(pd.to_datetime),।

डॉक्स पर अधिक पढ़ें ।

एक आम ख़तरा: सूचियों के स्तंभों का विस्फोट

s = pd.Series([[1, 2]] * 3)
s

0    [1, 2]
1    [1, 2]
2    [1, 2]
dtype: object

लोग उपयोग करने के लिए ललचाते हैं apply(pd.Series)। प्रदर्शन के मामले में यह भयानक है।

s.apply(pd.Series)

   0  1
0  1  2
1  1  2
2  1  2

एक बेहतर विकल्प कॉलम को सूचीबद्ध करना और इसे pd.DataFrame को पास करना है।

pd.DataFrame(s.tolist())

   0  1
0  1  2
1  1  2
2  1  2

%timeit s.apply(pd.Series)
%timeit pd.DataFrame(s.tolist())

2.65 ms ± 294 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
816 µs ± 40.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

अंततः,

" क्या कोई ऐसी स्थिति है जहाँ apply अच्छा है? "

लागू करना एक सुविधा कार्य है, इसलिए ऐसी परिस्थितियां हैं जहां ओवरहेड को माफ करने के लिए नगण्य है। यह वास्तव में इस बात पर निर्भर करता है कि फ़ंक्शन को कितनी बार कहा जाता है।

श्रृंखला के लिए वेक्टर किए गए फ़ंक्शंस, लेकिन डेटाफ़्रेम नहीं
यदि आप कई स्तंभों पर एक स्ट्रिंग ऑपरेशन लागू करना चाहते हैं तो क्या होगा? यदि आप कई कॉलमों को डेटाइम में बदलना चाहते हैं तो क्या होगा? इन कार्यों को केवल श्रृंखला के लिए सदिश किया गया है, इसलिए उन्हें प्रत्येक कॉलम पर लागू किया जाना चाहिए जिसे आप कनवर्ट / संचालित करना चाहते हैं।

df = pd.DataFrame(
         pd.date_range('2018-12-31','2019-01-31', freq='2D').date.astype(str).reshape(-1, 2), 
         columns=['date1', 'date2'])
df

       date1      date2
0 2018-12-31 2019-01-02
1 2019-01-04 2019-01-06
2 2019-01-08 2019-01-10
3 2019-01-12 2019-01-14
4 2019-01-16 2019-01-18
5 2019-01-20 2019-01-22
6 2019-01-24 2019-01-26
7 2019-01-28 2019-01-30

df.dtypes

date1    object
date2    object
dtype: object

यह एक स्वीकार्य मामला है apply:

df.apply(pd.to_datetime, errors='coerce').dtypes

date1    datetime64[ns]
date2    datetime64[ns]
dtype: object

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

%timeit df.apply(pd.to_datetime, errors='coerce')
%timeit pd.to_datetime(df.stack(), errors='coerce').unstack()
%timeit pd.concat([pd.to_datetime(df[c], errors='coerce') for c in df], axis=1)
%timeit for c in df.columns: df[c] = pd.to_datetime(df[c], errors='coerce')

5.49 ms ± 247 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.94 ms ± 48.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.16 ms ± 216 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.41 ms ± 1.71 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

आप अन्य ऑपरेशन जैसे स्ट्रिंग ऑपरेशन या श्रेणी में रूपांतरण के लिए एक समान मामला बना सकते हैं।

u = df.apply(lambda x: x.str.contains(...))
v = df.apply(lambda x: x.astype(category))

v / s

u = pd.concat([df[c].str.contains(...) for c in df], axis=1)
v = df.copy()
for c in df:
    v[c] = df[c].astype(category)

और इसी तरह...

सीरीज़िंग को str: astypeबनामapply

ऐसा लगता है जैसे एपीआई का एक idiosyncrasy। applyकिसी श्रृंखला में पूर्णांकों को स्ट्रिंग में बदलने के लिए उपयोग करने की तुलना में (और कभी-कभी तेज) तुलनीय है astype

यहां छवि विवरण दर्ज करेंperfplotपुस्तकालय का उपयोग करके ग्राफ तैयार किया गया था ।

import perfplot

perfplot.show(
    setup=lambda n: pd.Series(np.random.randint(0, n, n)),
    kernels=[
        lambda s: s.astype(str),
        lambda s: s.apply(str)
    ],
    labels=['astype', 'apply'],
    n_range=[2**k for k in range(1, 20)],
    xlabel='N',
    logx=True,
    logy=True,
    equality_check=lambda x, y: (x == y).all())

झांकियों के साथ, मैं देख रहा हूं कि astypeलगातार उतना ही तेज है, या इससे थोड़ा तेज है apply। तो यह इस तथ्य के साथ करना है कि परीक्षण में डेटा पूर्णांक प्रकार है।

GroupBy जंजीर परिवर्तन के साथ संचालन

GroupBy.applyअब तक चर्चा नहीं की गई है, लेकिन GroupBy.applyमौजूदा GroupByकार्यों के लिए कुछ भी संभालने के लिए एक पुनरावृत्ति सुविधा समारोह भी है ।

एक सामान्य आवश्यकता है एक GroupBy और फिर दो प्रमुख ऑपरेशन जैसे "लैग्ड कम्सम" करना:

df = pd.DataFrame({"A": list('aabcccddee'), "B": [12, 7, 5, 4, 5, 4, 3, 2, 1, 10]})
df

   A   B
0  a  12
1  a   7
2  b   5
3  c   4
4  c   5
5  c   4
6  d   3
7  d   2
8  e   1
9  e  10

आपको यहाँ दो क्रमिक कॉल की आवश्यकता होगी:

df.groupby('A').B.cumsum().groupby(df.A).shift()

0     NaN
1    12.0
2     NaN
3     NaN
4     4.0
5     9.0
6     NaN
7     3.0
8     NaN
9     1.0
Name: B, dtype: float64

का उपयोग करते हुए apply, आप इसे एक एकल कॉल के लिए छोटा कर सकते हैं।

df.groupby('A').B.apply(lambda x: x.cumsum().shift())

0     NaN
1    12.0
2     NaN
3     NaN
4     4.0
5     9.0
6     NaN
7     3.0
8     NaN
9     1.0
Name: B, dtype: float64

प्रदर्शन को निर्धारित करना बहुत कठिन है क्योंकि यह डेटा पर निर्भर करता है। लेकिन सामान्य तौर पर, applyएक स्वीकार्य समाधान है यदि लक्ष्य एक groupbyकॉल को कम करना है (क्योंकि groupbyयह काफी महंगा भी है)।


अन्य कैवियट

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

df = pd.DataFrame({
    'A': [1, 2],
    'B': ['x', 'y']
})

def func(x):
    print(x['A'])
    return x

df.apply(func, axis=1)

# 1
# 1
# 2
   A  B
0  1  x
1  2  y

यह व्यवहार GroupBy.applyपांडा के संस्करणों पर भी देखा जाता है <0.25 (यह 0.25 के लिए तय किया गया था, अधिक जानकारी के लिए यहां देखें )


मुझे लगता है कि हमें सावधान रहने की जरूरत है .. %timeit for c in df.columns: df[c] = pd.to_datetime(df[c], errors='coerce')निश्चित रूप से पहली यात्रा के बाद यह बहुत तेज हो जाएगा क्योंकि आप datetime... को परिवर्तित कर रहे हैं datetime?
जाप

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

" to_datetimeतार पर कॉलिंग उतनी ही तेजी से ... datetimeवस्तुओं पर" है .. वास्तव में? मैंने डेटाफ्रेम निर्माण (निश्चित लागत) applyबनाम forलूप टाइमिंग में शामिल किया और अंतर बहुत छोटा है।
जेपी

@jpp खैर, यह वही है जो मुझे (संयुक्त रूप से सीमित) परीक्षण से मिला है। मुझे यकीन है कि यह डेटा पर निर्भर करता है, लेकिन सामान्य विचार यह है कि चित्रण के उद्देश्य के लिए, अंतर "गंभीरता से, इसके बारे में चिंता न करें" है।
cs95

1
@ cs95, नया साल मुबारक हो!
जेपी

49

सभी applyएक जैसे नहीं होते हैं

नीचे दिए गए चार्ट से पता चलता है कि कब apply1 पर विचार करना है । हरे रंग का मतलब संभवतः कुशल है; लाल बचना।

यहां छवि विवरण दर्ज करें

इस में से कुछ सहज है: pd.Series.applyपायथन-स्तरीय पंक्ति-वार लूप, डिट्टो pd.DataFrame.applyपंक्ति-वार ( axis=1) है। इनके दुरुपयोग कई और व्यापक हैं। दूसरी पोस्ट उनके साथ और अधिक गहराई से पेश आती है। लोकप्रिय समाधान वेक्टर किए गए तरीकों, सूची समझ (स्वच्छ डेटा को ग्रहण करता है), या कुशल उपकरण जैसे कि pd.DataFrameकंस्ट्रक्टर (जैसे बचने के लिए apply(pd.Series)) का उपयोग करना है।

यदि आप pd.DataFrame.applyपंक्ति-वार का उपयोग कर रहे हैं , तो निर्दिष्ट करना raw=True(जहाँ संभव हो) अक्सर फायदेमंद होता है। इस स्तर पर, numbaआमतौर पर एक बेहतर विकल्प है।

GroupBy.apply: आम तौर पर इष्ट

प्रदर्शन groupbyसे बचने के लिए दोहराए जाने वाले ऑपरेशन applyप्रदर्शन को प्रभावित करेंगे। GroupBy.applyआमतौर पर यहां ठीक है, बशर्ते आप अपने कस्टम फ़ंक्शन में जिन विधियों का उपयोग करते हैं, वे स्वयं वेक्टरकृत हैं। कभी-कभी आपके द्वारा लागू किए जाने वाले समूहबद्ध एकत्रीकरण के लिए कोई देशी पांडास विधि नहीं होती है। इस मामले में, applyकस्टम फ़ंक्शन वाले कुछ समूहों के लिए अभी भी उचित प्रदर्शन की पेशकश की जा सकती है।

pd.DataFrame.apply कॉलम-वार: एक मिश्रित बैग

pd.DataFrame.applyकॉलम-वार ( axis=0) एक दिलचस्प मामला है। बड़ी संख्या में स्तंभों की एक छोटी संख्या के लिए, यह लगभग हमेशा महंगा होता है। स्तंभों के सापेक्ष बड़ी संख्या में, अधिक सामान्य मामला, आप कभी-कभी महत्वपूर्ण प्रदर्शन सुधार का उपयोग कर सकते हैं apply:

# Python 3.7, Pandas 0.23.4
np.random.seed(0)
df = pd.DataFrame(np.random.random((10**7, 3)))     # Scenario_1, many rows
df = pd.DataFrame(np.random.random((10**4, 10**3))) # Scenario_2, many columns

                                               # Scenario_1  | Scenario_2
%timeit df.sum()                               # 800 ms      | 109 ms
%timeit df.apply(pd.Series.sum)                # 568 ms      | 325 ms

%timeit df.max() - df.min()                    # 1.63 s      | 314 ms
%timeit df.apply(lambda x: x.max() - x.min())  # 838 ms      | 473 ms

%timeit df.mean()                              # 108 ms      | 94.4 ms
%timeit df.apply(pd.Series.mean)               # 276 ms      | 233 ms

1 अपवाद हैं, लेकिन ये आमतौर पर सीमांत या असामान्य हैं। कुछ उदाहरण:

  1. df['col'].apply(str)थोड़ा बाहर हो सकता है df['col'].astype(str)
  2. df.apply(pd.to_datetime)स्ट्रिंग्स पर काम करना पंक्तियों के साथ एक नियमित forलूप के साथ अच्छी तरह से पैमाने पर नहीं होता है ।

2
में पिचिंग के लिए धन्यवाद, कई दृष्टिकोण की सराहना करते हैं :) +1
cs95

1
@coldspeed, धन्यवाद, आपकी पोस्ट में कुछ भी गलत नहीं है (कुछ विरोधाभासी बेंचमार्किंग के अलावा मेरा, लेकिन इनपुट या सेटअप आधारित हो सकता है)। लगा कि समस्या को देखने का एक अलग तरीका है।
जेपी

@jpp मैं हमेशा आप जब तक जब मैं आज देखा कि एक मार्गदर्शन के रूप में कर रहे हैं उत्कृष्ट प्रवाह संचित्र इस्तेमाल किया पंक्ति-वारapply तेजी की तुलना में काफी है मेरे समाधान के साथ any। इस पर कोई विचार?
स्टेफ

1
@jpp: आप सही कह रहे हैं: 1mio पंक्तियों के लिए x 100 cols anyसे लगभग 100 गुना तेज है apply। इसने मेरा पहला परीक्षण 2000 पंक्तियों x 1000 कॉल के साथ किया था और यहाँ applyदो बार उपवास किया थाany
Stef

1
@jpp मैं एक प्रस्तुति / लेख में आपकी छवि का उपयोग करना चाहूंगा। क्या आपको वह ठीक लगता है? मैं स्पष्ट रूप से स्रोत का उल्लेख करूंगा। धन्यवाद
इरफान

3

के लिए axis=1(यानी पंक्ति-वार फ़ंक्शंस) तो आप इसके बदले में निम्न फ़ंक्शन का उपयोग कर सकते हैं apply। मुझे आश्चर्य है कि यह pandasव्यवहार क्यों नहीं है। (यौगिक अनुक्रमित के साथ अप्रकाशित, लेकिन यह बहुत तेजी से प्रकट होता है apply)

def faster_df_apply(df, func):
    cols = list(df.columns)
    data, index = [], []
    for row in df.itertuples(index=True):
        row_dict = {f:v for f,v in zip(cols, row[1:])}
        data.append(func(row_dict))
        index.append(row[0])
    return pd.Series(data, index=index)

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

कुछ संकेत: प्रदर्शन के लिए एक सूची समझ पाश के लिए बेहतर प्रदर्शन करेगा; zip(df, row[1:])यहाँ पर्याप्त है; वास्तव में, इस स्तर पर, विचार करें numbaकि क्या फंक एक संख्यात्मक गणना है। स्पष्टीकरण के लिए यह उत्तर देखें ।
जेपी

@jpp - यदि आपके पास एक बेहतर कार्य है तो कृपया साझा करें। मुझे लगता है कि यह मेरे विश्लेषण से इष्टतम के बहुत करीब है। हां numbaतेज़ है, ऐसे faster_df_applyलोगों के लिए है जो सिर्फ उसी के बराबर कुछ चाहते हैं, लेकिन उससे भी तेज़ DataFrame.apply(जो कि अजीब तरह से धीमा है)।
पीट कैकियोपी

2

क्या कभी कोई स्थिति है जहाँ applyअच्छा है? हाँ कभी कभी।

कार्य: डिकोड यूनिकोड तार।

import numpy as np
import pandas as pd
import unidecode

s = pd.Series(['mañana','Ceñía'])
s.head()
0    mañana
1     Ceñía


s.apply(unidecode.unidecode)
0    manana
1     Cenia

अद्यतन
मैं किसी भी तरह से उपयोग के लिए वकालत नहीं कर रहा था apply, बस सोच रहा था NumPyकि उपरोक्त स्थिति से निपट नहीं सकता है, यह एक अच्छा उम्मीदवार हो सकता है pandas apply। लेकिन मैं @jpp द्वारा रिमाइंडर की बदौलत सादा ओल सूची समझने की भूल कर रहा था।


नहीं। यह कैसे [unidecode.unidecode(x) for x in s]या से बेहतर है list(map(unidecode.unidecode, s))?
JPP

1
चूंकि यह पहले से ही एक पांडा श्रृंखला थी, मुझे आवेदन का उपयोग करने का प्रलोभन दिया गया था, हाँ आप सही हैं, इसका बेहतर उपयोग करने के लिए सूची-COMP का उपयोग करना बेहतर है, लेकिन नीचे की ओर थोड़ा कठोर था, मैं इसकी वकालत नहीं कर रहा था apply, बस सोचा कि यह एक अच्छा हो सकता है उदाहरण।
astro123
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.