আমার কোডে কখন (না) প্যান্ডাস প্রয়োগ করতে হবে ()?


124

পান্ডস পদ্ধতির ব্যবহারের সাথে জড়িত স্ট্যাক ওভারফ্লোতে প্রশ্নগুলিতে পোস্ট করা অনেক উত্তর আমি দেখেছি apply। আমি ব্যবহারকারীরা তাদের নীচে মন্তব্য করতে দেখেছি যে " applyধীর, এবং এড়ানো উচিত " saying

আমি পারফরম্যান্সের বিষয়ে অনেক নিবন্ধ পড়েছি যা ব্যাখ্যা applyধীর। আমি applyইউডিএফ পাস করার জন্য কীভাবে কোনও সুবিধামত ফাংশন (এটি এখন খুঁজে পেতে পারে না) সে সম্পর্কে ডক্সে একটি অস্বীকৃতিও দেখেছি । সুতরাং, সাধারণ sens applyকমত্য যদি সম্ভব হয় তবে এড়ানো উচিত। তবে এটি নিম্নলিখিত প্রশ্নগুলি উত্থাপন করে:

  1. যদি applyখুব খারাপ হয়, তবে এটি কেন এপিআইতে আছে?
  2. কখন এবং কখন আমার কোড- applyফ্রি করা উচিত ?
  3. সেখানে কখনও কোনো পরিস্থিতিতে যেখানে applyহয় ভাল (ভাল অন্যান্য সম্ভাব্য সমাধান চেয়ে)?

4
returns.add(1).apply(np.log)বনাম np.log(returns.add(1)একটি ক্ষেত্রে applyএটি সাধারণত প্রান্তিক দ্রুততর হবে, যা নীচে নীচে জেপিপির চিত্রের ডানদিকে সবুজ বাক্স।
আলেকজান্ডার

@ আলেকজান্ডার ধন্যবাদ এই পরিস্থিতিগুলি নিঃসন্দেহে নির্দেশ করে না, তবে তারা জানার জন্য দরকারী!
সিএস 95

উত্তর:


119

apply, আপনার সুবিধার্থী সুবিধার প্রয়োজন নেই

আমরা ওপি-তে প্রশ্নগুলি একের পর এক সম্বোধন করে শুরু করি।

"যদি applyখুব খারাপ হয় তবে এটি কেন এপিআইতে আছে?"

DataFrame.applyএবং Series.applyহয় সুবিধার ফাংশন DataFrame এবং সিরিজ সংজ্ঞাসমূহ যথাক্রমে অবজেক্ট। applyকোনও ডেটাফ্রেমে ট্রান্সফর্মেশন / সমষ্টি প্রযোজ্য যে কোনও ব্যবহারকারীর সংজ্ঞায়িত ফাংশন গ্রহণ করে। applyকার্যকরভাবে একটি সিলভার বুলেট যা কোনও বিদ্যমান পান্ডাস ফাংশন যা করতে পারে না তা করে।

কিছু কাজ applyকরতে পারে:

  • কোনও ডেটাফ্রেম বা সিরিজে কোনও ব্যবহারকারী-সংজ্ঞায়িত ফাংশন চালান
  • কোনও ডেটা ফ্রেমে সারি-ভিত্তিক ( axis=1) বা কলাম-ভিত্তিক ( axis=0) ফাংশন প্রয়োগ করুন
  • ফাংশন প্রয়োগ করার সময় সূচী প্রান্তিককরণ সম্পাদন করুন
  • ব্যবহারকারী-সংজ্ঞায়িত ফাংশনগুলির সাথে সমষ্টি সম্পাদন করুন (তবে আমরা সাধারণত পছন্দ করি aggবা transformএই ক্ষেত্রে)
  • উপাদান অনুসারে রূপান্তরগুলি সম্পাদন করুন
  • মূল সারিগুলিতে একত্রিত ফলাফলগুলি সম্প্রচার করুন ( result_typeযুক্তিটি দেখুন)।
  • ব্যবহারকারী-সংজ্ঞায়িত ফাংশনগুলিতে স্থান দিতে অবস্থানগত / কীওয়ার্ড আর্গুমেন্ট গ্রহণ করুন।

...অন্যদের মধ্যে. আরও তথ্যের জন্য ডকুমেন্টেশনে সারি বা কলাম ভিত্তিক ফাংশন অ্যাপ্লিকেশনটি দেখুন

সুতরাং, এই সমস্ত বৈশিষ্ট্য সহ, applyখারাপ কেন ? এটা কারণ 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)

সাধারণভাবে, সম্ভব হলে ভেক্টরাইজড বিকল্পগুলি সন্ধান করুন।


স্ট্রিং / রেজেক্স

পান্ডাস বেশিরভাগ পরিস্থিতিতে "ভেক্টরাইজড" স্ট্রিং ফাংশন সরবরাহ করে তবে এমন বিরল ঘটনা রয়েছে যেখানে এই ফাংশনগুলি ... "প্রয়োগ" হয় না, তাই কথা বলতে হবে।

একটি সাধারণ সমস্যা হ'ল একই সারির অন্য কলামে কলামের মান উপস্থিত আছে কিনা তা পরীক্ষা করা।

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

তবে তালিকা বোঝার সাহায্যে আরও ভাল সমাধান বিদ্যমান a

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 এবং অবৈধ ডাইপগুলি হ্যান্ডেল করার প্রয়োজন হয় তবে আপনি কাস্টম ফাংশনটি ব্যবহার করে এটি তৈরি করতে পারেন তারপরে তালিকার বোধগমের ভিতরে যুক্তি দিয়ে কল করতে পারেন।

তালিকা বোধগম্যকরণকে কখন একটি ভাল বিকল্প হিসাবে বিবেচনা করা উচিত সে সম্পর্কে আরও তথ্যের জন্য, আমার লেখার ব্যবস্থাটি দেখুন: পান্ডাসে লুপগুলি কি আসলেই খারাপ? আমার কখন যত্ন করা উচিত?

দ্রষ্টব্য
তারিখ এবং তারিখের সময় অপারেশনগুলিতেও ভেক্টরাইজড সংস্করণ রয়েছে। সুতরাং, উদাহরণস্বরূপ, আপনার পছন্দ করা উচিত 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(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

এটি API এর একটি 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ফাংশনগুলি না করে এমন কোনও কিছু পরিচালনা করার জন্য এটি একটি পুনরাবৃত্তিমূলক সুবিধার ফাংশন ।

একটি সাধারণ প্রয়োজনীয়তা হ'ল একটি গ্রুপবাই এবং তারপরে দুটি প্রধান ক্রিয়াকলাপ যেমন "লেগড সিউসাম":

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এটি বেশ ব্যয়বহুলও)।



অন্যান্য Caveats

উপরে বর্ণিত সাবধানবাণীগুলি বাদে, এটিও উল্লেখযোগ্য যে 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?
জেপিপি

@jpp আমারও একই উদ্বেগ ছিল। তবে আপনার এখনও যে কোনও উপায়ে লিনিয়ার স্ক্যান করতে হবে, স্ট্রিংগুলিতে_ডেটটাইম কল করা তত দ্রুত না হলে ডেটটাইম অবজেক্টগুলিতে কল করার মতো দ্রুত। বলপার্কের সময় একই are বিকল্পটি হ'ল প্রতিটি সময়সীমার সমাধানের জন্য কিছু প্রাক-অনুলিপি পদক্ষেপ বাস্তবায়ন করা যা মূল বিষয়টি থেকে দূরে নেয়। তবে এটি একটি বৈধ উদ্বেগ।
cs95

" to_datetimeস্ট্রিংগুলিতে কল করা যেমন তত দ্রুত ... datetimeঅবজেক্টস" .. সত্যই? applyবনাম forলুপের সময়কালে আমি ডেটাফ্রেম তৈরি (নির্ধারিত ব্যয়) অন্তর্ভুক্ত করেছি এবং পার্থক্যটি অনেক কম।
jpp

@ জেপিপি ওয়েল, আমার (স্বীকৃতভাবে সীমাবদ্ধ) পরীক্ষা থেকে আমি এটি পেয়েছি। আমি নিশ্চিত যে এটি ডেটার উপর নির্ভর করে, তবে সাধারণ ধারণাটি হ'ল চিত্রের উদ্দেশ্যে, পার্থক্যটি "গুরুত্ব সহকারে, এটি নিয়ে চিন্তা করবেন না"।
CS95

4
@ সিএস 95, শুভ নববর্ষ!
জেপিপি

53

সমস্ত 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লুপের তুলনায় সারিগুলির সাথে ভাল স্কেল করে না ।

4
পিচিং করার জন্য ধন্যবাদ, একাধিক দৃষ্টিভঙ্গি প্রশংসা করুন :) +1
cs95

4
@ কোল্ডস্পিড, ধন্যবাদ, আপনার পোস্টের সাথে খুব বেশি কিছু ভুল নেই (কিছু বনাম খনি বিপরীতমুখী মানদণ্ড বাদে, তবে ইনপুট বা সেটআপ ভিত্তিক হতে পারে)। সমস্যাটি দেখার আলাদা উপায় রয়েছে বলে কেবল অনুভব করেছি।
jpp

@jpp আমি সবসময় আপনার অবশেষে যখন আমি দেখতাম আজ সেই যে একটি নির্দেশনা হিসাবে করছি চমৎকার ফ্লোচার্ট ব্যবহৃত সারি ভিত্তিকapply দ্রুততর তুলনায় উল্লেখযোগ্যভাবে হয় আমার সমাধান সঙ্গে any। এই সম্পর্কে কোন চিন্তা?
স্টিফ

4
@ জেপ্পি: আপনি ঠিক বলেছেন: 1 মিও সারিগুলির জন্য x 100 কলস এর anyচেয়ে প্রায় 100 গুণ বেশি গতিযুক্ত apply। এটি 2000 সারি x 1000 কলস দিয়ে আমার প্রথম পরীক্ষা করেছিল এবং এখানে applyদ্বিগুণ দ্রুত গতি ছিলany
স্টেফ

4
@jppi আমি আপনার চিত্রটি উপস্থাপনা / নিবন্ধে ব্যবহার করতে চাই। আপনি কি ঠিক আছে? আমি অবশ্যই উত্স উল্লেখ করব। ধন্যবাদ
এরফান

4

জন্য 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)

এটি আমাকে বেশ অবাক করে দিয়েছিল যে এটি আমাকে কিছু ক্ষেত্রে আরও ভাল পারফরম্যান্স দিয়েছে। এটি বিশেষত যখন আমার একাধিক জিনিস করার প্রয়োজন ছিল তখন কলাম মানগুলির একটি পৃথক উপসেট রয়েছে especially "সমস্ত প্রয়োগ একরকম নয়" উত্তরটি যখন সাহায্য করার সম্ভাবনা রয়েছে তখন এটি নির্ধারণ করতে সহায়তা করতে পারে তবে আপনার ডেটার নমুনায় পরীক্ষা করা খুব কঠিন নয়।
ডেনসন

4
কয়েকটি পয়েন্টার: পারফরম্যান্সের জন্য একটি তালিকা বোধগম্য লুপের চেয়ে বেশি হয়ে উঠবে; zip(df, row[1:])এখানে যথেষ্ট; সত্যই, এই পর্যায়ে, বিবেচনা করুন numbaযদি ফানক একটি সংখ্যার গণনা হয়। ব্যাখ্যার জন্য এই উত্তরটি দেখুন ।
জেপিপি

@ জেপিপি - আপনার আরও ভাল ফাংশন থাকলে শেয়ার করুন। আমি মনে করি এটি আমার বিশ্লেষণ থেকে সর্বোত্তম কাছাকাছি। হ্যাঁ numbaদ্রুত, faster_df_applyএটি এমন লোকদের জন্য বোঝানো হয় যা কেবলমাত্র সমান কিছু চায় তবে এর চেয়ে দ্রুত DataFrame.apply(যা বিস্ময়করভাবে ধীর)।
পিট ক্যাসিওপিপি

এটি বাস্তবে কীভাবে .applyপ্রয়োগ করা হয় তার খুব কাছাকাছি , তবে এটি এমন একটি কাজ করে যা তা উল্লেখযোগ্যভাবে ধীর করে দেয়, এটি মূলত করে: row = pd.Series({f:v for f,v in zip(cols, row[1:])})যা প্রচুর টানা যোগ করে। আমি একটি উত্তর লিখেছিলাম যা বাস্তবায়নের বর্ণনা দিয়েছিল, যদিও আমি মনে করি এটি পুরানো, সাম্প্রতিক সংস্করণগুলি .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। তবে আমি @ জে পি পি দ্বারা অনুস্মারকটির জন্য প্লেইন ওল তালিকার উপলব্ধিটি ভুলে যাচ্ছিলাম।


ভাল, না। এর চেয়ে [unidecode.unidecode(x) for x in s]বা কীভাবে ভালো list(map(unidecode.unidecode, s))?
জেপিপি

4
যেহেতু এটি ইতিমধ্যে একটি পান্ডাস সিরিজ ছিল, তাই আমি প্রয়োগটি ব্যবহার করার লোভ দেখিয়েছিলাম, হ্যাঁ আপনি ঠিকই বলেছেন, প্রয়োগের চেয়ে তালিকা-কম্প ব্যবহার করা ভাল, তবে ডাউনভোটটি কিছুটা কঠোর ছিল, আমি এর পক্ষে পরামর্শ দিচ্ছিলাম না apply, ভেবেছিলাম এটি ভাল হতে পারত ব্যবহারের ক্ষেত্রে.
অ্যাস্ট্রো 123
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.