পান্ডায় লুপগুলি কি আসলেই খারাপ? আমার কখন যত্ন করা উচিত?


106

হয় forলুপ সত্যিই "খারাপ"? যদি তা না হয় তবে কোন পরিস্থিতিতে (গুলি) তারা আরও প্রচলিত "ভেক্টরাইজড" পদ্ধতির ব্যবহারের চেয়ে ভাল হবে?

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

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


1 - যদিও এটি সত্য যে প্রশ্নটি কিছুটা বিস্তৃত মনে হচ্ছে, সত্য এটি হ'ল খুব নির্দিষ্ট পরিস্থিতি রয়েছে যখন forলুপ সাধারণত প্রচলিতভাবে ডেটা দ্বারা পুনরাবৃত্তি করার চেয়ে ভাল হয়। এই পোস্টটি উত্তরোত্তর জন্য এটি ক্যাপচার লক্ষ্য।

উত্তর:


143

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

  1. যখন আপনার ডেটা ছোট হয় (... আপনি যা করছেন তার উপর নির্ভর করে),
  2. যখন সঙ্গে তার আচরণ object/ মিশ্র dtypes
  3. str/ রেজেক্স অ্যাকসেসর ফাংশনগুলি ব্যবহার করার সময়

আসুন স্বতন্ত্রভাবে এই পরিস্থিতিগুলি পরীক্ষা করে দেখি।


ছোট ডেটাতে ইন্টেরেশন ভি / এস ভেক্টরাইজেশন

পান্ডাস তার এপিআই নকশায় একটি "কনভেনশন ওভার কনফিগারেশন" পদ্ধতির অনুসরণ করে । এর অর্থ হ'ল একই এপিআইতে বিস্তৃত ডেটা এবং কেস ব্যবহারের ক্ষেত্রে ফিট করা হয়েছে।

যখন কোনও পান্ডাস ফাংশন বলা হয়, কাজ নিশ্চিত করার জন্য নিম্নলিখিত জিনিসগুলি (অন্যদের মধ্যে) অভ্যন্তরীণভাবে ফাংশনটি দ্বারা পরিচালনা করা উচিত

  1. সূচি / অক্ষ সারিবদ্ধতা
  2. মিশ্র ডেটাটাইপগুলি পরিচালনা করছে
  3. নিখোঁজ ডেটা হ্যান্ডলিং

প্রায় প্রতিটি ফাংশনকে এগুলি বিবিধ এক্সটেন্টগুলির সাথে মোকাবেলা করতে হবে এবং এটি একটি উপরি উপস্থাপন করে । ওভারহেড সংখ্যাযুক্ত ফাংশনগুলির জন্য কম (উদাহরণস্বরূপ Series.add), যখন স্ট্রিং ফাংশনগুলির জন্য এটি আরও বেশি উচ্চারণ করা হয় (উদাহরণস্বরূপ Series.str.replace)।

forঅন্যদিকে লুপগুলি তত দ্রুত হয় আপনি ভাবেন। আরও ভাল যা তালিকার বোধগম্যতা (যা forলুপগুলির মাধ্যমে তালিকা তৈরি করে ) আরও তত দ্রুততর কারণ তারা তালিকা তৈরির জন্য পুনরাবৃত্তিমূলক প্রক্রিয়াটি অনুকূলিতকরণ করেছে।

তালিকা বোঝার প্যাটার্ন অনুসরণ

[f(x) for x in seq]

যেখানে seqপান্ডাস সিরিজ বা ডেটাফ্রেম কলাম রয়েছে। বা, একাধিক কলামের উপরে অপারেট করার সময়,

[f(x, y) for x, y in zip(seq1, seq2)]

কোথায় seq1এবং seq2কলাম হয়।

সংখ্যা তুলনা
একটি সাধারণ বুলিয়ান ইনডেক্সিং অপারেশন বিবেচনা করুন। Series.ne( !=)) এবং এর বিরুদ্ধে তালিকার বোধগম্য পদ্ধতিটি সময়সীমার করা হয়েছে query। ফাংশনগুলি এখানে:

# Boolean indexing with Numeric value comparison.
df[df.A != df.B]                            # vectorized !=
df.query('A != B')                          # query (numexpr)
df[[x != y for x, y in zip(df.A, df.B)]]    # list comp

সরলতার জন্য, আমি perfplotএই পোস্টে সমস্ত টাইমিট পরীক্ষা চালানোর জন্য প্যাকেজটি ব্যবহার করেছি । উপরোক্ত অপারেশনের সময় নীচে রয়েছে:

এখানে চিত্র বর্ণনা লিখুন

তালিকা বোধগম্য queryমাঝারি আকারের এন এর জন্য প্রসারিত করে, এবং এমনকি ভেক্টরাইজড ক্ষুদ্র এন এর সাথে তুলনা করার সমান হয় না। দুর্ভাগ্যক্রমে, তালিকা বোধগম্য স্কেরির রৈখিকভাবে স্কেল করে, সুতরাং এটি বৃহত্তর এন এর পক্ষে খুব বেশি পারফরম্যান্স লাভের প্রস্তাব দেয় না N

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

df[df.A.values != df.B.values]

যা পান্ডা এবং তালিকার উপলব্ধি সমতুল্য উভয়কেই ছাড়িয়ে যায়:

NumPy ভেক্টরাইজেশন এই পোস্টের আওতার বাইরে, তবে পারফরম্যান্সের বিষয়টি বিবেচনা করলে তা অবশ্যই বিবেচ্য।

মান গণনা
অন্য একটি উদাহরণ গ্রহণ করছে - এবার অন্য ভ্যানিলা পাইথন নির্মাণের সাথে যা লুপের চেয়ে দ্রুতcollections.Counter । একটি সাধারণ প্রয়োজন হ'ল মান গণনা করা এবং অভিধান হিসাবে ফলাফলটি ফেরত দেওয়া। এই সঙ্গে সম্পন্ন করা হয় value_counts, np.uniqueএবং Counter:

# Value Counts comparison.
ser.value_counts(sort=False).to_dict()           # value_counts
dict(zip(*np.unique(ser, return_counts=True)))   # np.unique
Counter(ser)                                     # Counter

এখানে চিত্র বর্ণনা লিখুন

ফলাফলগুলি আরও Counterস্পষ্টভাবে প্রকাশিত হয়, ছোট এন (~ 3500) এর বৃহত্তর পরিসরের জন্য উভয় ভেক্টরাইজড পদ্ধতিতে জিতেছে।


আরও ট্রিভিয়া নোট করুন (সৌজন্যে @ ব্যবহারকারী 2357112)। Counterএকটি সঙ্গে বাস্তবায়িত হয় সি বেগবর্ধক , এটা এখনও কাজ করতে হবে, যাতে যখন পাইথন অন্তর্নিহিত সি datatypes পরিবর্তে বস্তুর সঙ্গে, এটা এখনও দ্রুত একটি চেয়ে forলুপ। পাইথন শক্তি!

অবশ্যই, এখান থেকে সরিয়ে নেওয়া হ'ল পারফরম্যান্সটি আপনার ডেটা এবং ব্যবহারের ক্ষেত্রে নির্ভর করে। এই উদাহরণগুলির মূল বক্তব্য হ'ল বৈধ বিকল্প হিসাবে এই সমাধানগুলি বাতিল না করার জন্য আপনাকে বোঝানো। এগুলি যদি এখনও আপনাকে আপনার প্রয়োজনীয় পারফরম্যান্স না দেয় তবে সর্বদা সিথন এবং নাম্বা থাকে । আসুন এই পরীক্ষাটি মিশ্রণে যুক্ত করি।

from numba import njit, prange

@njit(parallel=True)
def get_mask(x, y):
    result = [False] * len(x)
    for i in prange(len(x)):
        result[i] = x[i] != y[i]

    return np.array(result)

df[get_mask(df.A.values, df.B.values)] # numba

এখানে চিত্র বর্ণনা লিখুন

নুম্বা খুব শক্তিশালী ভেক্টরাইজড কোডটিতে লুপী পাইথন কোডের জেআইটি সংকলন সরবরাহ করে। কীভাবে নাম্বার কাজ করা যায় তা বোঝার মধ্যে একটি শেখার বক্রতা জড়িত।


মিশ্র / objectdtyype সঙ্গে অপারেশন

স্ট্রিং-ভিত্তিক তুলনা
প্রথম বিভাগ থেকে ফিল্টারিং উদাহরণের পুনর্বিবেচনা, যদি কলামগুলির তুলনা করা হচ্ছে স্ট্রিংগুলি? উপরের একই 3 টি ফাংশনটি বিবেচনা করুন তবে ইনপুট দিয়ে ডেটাফ্রেমকে স্ট্রিংয়ে ফেলেছে।

# Boolean indexing with string value comparison.
df[df.A != df.B]                            # vectorized !=
df.query('A != B')                          # query (numexpr)
df[[x != y for x, y in zip(df.A, df.B)]]    # list comp

এখানে চিত্র বর্ণনা লিখুন

তো, কী বদলে গেল? এখানে লক্ষণীয় বিষয় হ'ল স্ট্রিং অপারেশনগুলি ভেক্টরাইজ করা সহজাতভাবে কঠিন। পান্ডস স্ট্রিংকে অবজেক্ট হিসাবে বিবেচনা করে এবং বস্তুর সমস্ত ক্রিয়াকলাপ ধীর, লুপী প্রয়োগে ফিরে আসে।

এখন, যেহেতু এই লুপী প্রয়োগটি উপরে উল্লিখিত সমস্ত ওভারহেড দ্বারা বেষ্টিত রয়েছে, সেগুলি একই স্কেল করা সত্ত্বেও এই সমাধানগুলির মধ্যে একটি ধ্রুবক মাত্রার পার্থক্য রয়েছে।

পরিবর্তনীয় / জটিল বস্তুগুলির ক্রিয়াকলাপে যখন আসে তখন কোনও তুলনা হয় না। তালিকান বোধগম্যতা ডিক্টস এবং তালিকাগুলি সম্পর্কিত সমস্ত ক্রিয়াকলাপকে ছাপিয়ে যায়।

কী দ্বারা অভিধান মান (গুলি) অ্যাক্সেস করা
এখানে দুটি ক্রিয়াকলাপের সময় রয়েছে যা অভিধানের কলাম থেকে একটি মান বের করে: mapএবং তালিকাটি উপলব্ধি করে। "কোড স্নিপেটস" শিরোনামে সেটআপটি পরিশিষ্টে রয়েছে in

# Dictionary value extraction.
ser.map(operator.itemgetter('value'))     # map
pd.Series([x.get('value') for x in ser])  # list comprehension

এখানে চিত্র বর্ণনা লিখুন


3 টি ক্রিয়াকলাপের অবস্থানগত তালিকা সূচীকরণের সময়গুলি কলামের তালিকা (ব্যতিক্রমগুলি পরিচালনা করে) map, str.getঅ্যাক্সেসর পদ্ধতি এবং তালিকাটি বোঝার থেকে 0 তম উপাদানটি বের করে :

# List positional indexing. 
def get_0th(lst):
    try:
        return lst[0]
    # Handle empty lists and NaNs gracefully.
    except (IndexError, TypeError):
        return np.nan

ser.map(get_0th)                                          # map
ser.str[0]                                                # str accessor
pd.Series([x[0] if len(x) > 0 else np.nan for x in ser])  # list comp
pd.Series([get_0th(x) for x in ser])                      # list comp safe

দ্রষ্টব্য
সূচকটি যদি গুরুত্বপূর্ণ হয় তবে আপনি এটি করতে চান:

pd.Series([...], index=ser.index)

সিরিজটির পুনর্গঠন করার সময়।

এখানে চিত্র বর্ণনা লিখুন

তালিকা সমতলকরণ
একটি চূড়ান্ত উদাহরণ হ'ল সমতলকরণের তালিকা। এটি অন্য একটি সাধারণ সমস্যা এবং এটি নির্ধারণ করে যে খাঁটি অজগরটি এখানে কতটা শক্তিশালী।

# Nested list flattening.
pd.DataFrame(ser.tolist()).stack().reset_index(drop=True)  # stack
pd.Series(list(chain.from_iterable(ser.tolist())))         # itertools.chain
pd.Series([y for x in ser for y in x])                     # nested list comp

এখানে চিত্র বর্ণনা লিখুন

উভয় itertools.chain.from_iterableএবং নেস্টেড তালিকা অনুধাবন খাঁটি অজগর কাঠামো এবং stackসমাধানের চেয়ে অনেক ভাল স্কেল ।

এই সময়গুলি এই সত্যের দৃ strong় ইঙ্গিত দেয় যে পান্ডাস মিশ্রিত টাইপগুলির সাথে কাজ করার জন্য সজ্জিত নয় এবং সম্ভবত এটি করার জন্য আপনার ব্যবহার করা থেকে বিরত থাকতে হবে। যেখানেই সম্ভব, ডেটা পৃথক কলামে স্কেলার মান হিসাবে (ints / floats / স্ট্রিং) উপস্থিত থাকা উচিত।

শেষ অবধি, এই সমাধানগুলির প্রয়োগযোগ্যতা আপনার ডেটার উপর ব্যাপকভাবে নির্ভর করে। সুতরাং, সর্বোত্তম কাজটি হ'ল কী হবে তা ঠিক করার আগে আপনার ডেটাতে এই ক্রিয়াকলাপগুলি পরীক্ষা করা। লক্ষ্য করুন আমি কীভাবে applyএই সমাধানগুলিতে সময় নিই না , কারণ এটি গ্রাফটি স্কু করবে (হ্যাঁ, এটি এত ধীর)।


রেজেক্স অপারেশন এবং .strঅ্যাকসেসর পদ্ধতি

পান্ডাস যেমন Regex অপারেশন প্রয়োগ করতে পারেন str.contains, str.extractএবং str.extractallসেইসাথে অন্যান্য "ভেক্টরকৃত" STRING অপারেশন (যেমন, str.split, str.find ,str.translate`, ইত্যাদি) স্ট্রিং কলাম উপর। এই ফাংশনগুলি তালিকা বোধের চেয়ে ধীর এবং এটিকে অন্য যে কোনও কিছুর চেয়ে বেশি সুবিধাজনক ফাংশন হিসাবে বোঝানো হচ্ছে।

একটি রেইজেক্স প্যাটার্নটি প্রাক-সংকলন করা এবং আপনার ডেটা দিয়ে পুনরাবৃত্তি করা সাধারণত খুব দ্রুত হয় re.compile(এছাড়াও পাইথনের রে ডক্লাইপাইলটি ব্যবহার করা কি উপযুক্ত? )। লিস্টের সমষ্টিটি str.containsদেখতে এরকম কিছু দেখাচ্ছে:

p = re.compile(...)
ser2 = pd.Series([x for x in ser if p.search(x)])

বা,

ser2 = ser[[bool(p.search(x)) for x in ser]]

আপনার যদি NaNs পরিচালনা করতে হয় তবে আপনি এর মতো কিছু করতে পারেন

ser[[bool(p.search(x)) if pd.notnull(x) else False for x in ser]]

str.extract( গোষ্ঠীবিহীন) এর সমপরিমান তালিকাটি দেখতে এরকম কিছু দেখতে পাবে:

df['col2'] = [p.search(x).group(0) for x in df['col']]

আপনার যদি কোনও ম্যাচ এবং NaN হ্যান্ডেল করার প্রয়োজন হয় তবে আপনি একটি কাস্টম ফাংশন (এখনও দ্রুত!) ব্যবহার করতে পারেন:

def matcher(x):
    m = p.search(str(x))
    if m:
        return m.group(0)
    return np.nan

df['col2'] = [matcher(x) for x in df['col']]

matcherফাংশন খুব প্রসার্য হয়। প্রয়োজন অনুসারে প্রতিটি ক্যাপচার গ্রুপের জন্য একটি তালিকা ফিরিয়ে আনতে এটি লাগানো যেতে পারে। ম্যাচারের অবজেক্টের groupবা groupsগুণাবলীটি কেবল অনুসন্ধান করুন ।

জন্য str.extractallপরিবর্তন p.searchকরতে p.findall

স্ট্রিং এক্সট্রাকশন
একটি সাধারণ ফিল্টারিং অপারেশন বিবেচনা করুন। ধারণাটি হ'ল 4 অঙ্কগুলি উত্তোলন করা উচিত যদি এটির আগে কোনও বড় অক্ষরের অক্ষর থাকে।

# Extracting strings.
p = re.compile(r'(?<=[A-Z])(\d{4})')
def matcher(x):
    m = p.search(x)
    if m:
        return m.group(0)
    return np.nan

ser.str.extract(r'(?<=[A-Z])(\d{4})', expand=False)   #  str.extract
pd.Series([matcher(x) for x in ser])                  #  list comprehension

এখানে চিত্র বর্ণনা লিখুন

আরও উদাহরণ
সম্পূর্ণ প্রকাশ - আমি নীচে তালিকাভুক্ত এই পোস্টগুলির লেখক (অংশ বা সম্পূর্ণ) in


উপসংহার

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

আপনি যে স্পিডআপ পান তা আপনার ডেটা এবং আপনার সমস্যার উপর নির্ভর করে, তাই আপনার মাইলেজটি আলাদা হতে পারে। করণীয় হ'ল সর্বোত্তম বিষয় হ'ল সতর্কতার সাথে পরীক্ষা চালানো এবং দেখুন অর্থ প্রদানের চেষ্টাটি উপযুক্ত কিনা।

"ভেক্টরাইজড" ফাংশনগুলি তাদের সরলতা এবং পঠনযোগ্যতার সাথে জ্বলজ্বল করে, সুতরাং যদি পারফরম্যান্স সমালোচনা না করে তবে অবশ্যই আপনার সেগুলি পছন্দ করা উচিত।

অন্য পক্ষের নোট, নির্দিষ্ট স্ট্রিং অপারেশনগুলি সীমাবদ্ধতাগুলি মোকাবেলা করে যা নুমপি ব্যবহারের পক্ষে। এখানে দুটি উদাহরণ রয়েছে যেখানে সাবধানে NumPy ভেক্টরাইজেশন অজগরকে ছাপিয়েছে:

উপরন্তু, কখনও কখনও মাধ্যমে অন্তর্নিহিত অ্যারে উপর অপারেটিং .valuesযেমন সিরিজ অর DataFrames উপর উল্টোদিকে সবচেয়ে স্বাভাবিক পরিস্থিতিতে জন্য একটি সুস্থ যথেষ্ট speedup দিতে পারে (দেখুন নোট মধ্যে সাংখ্যিক তুলনা উপরে অধ্যায়)। সুতরাং, উদাহরণস্বরূপ df[df.A.values != df.B.values]তাত্ক্ষণিক কর্মক্ষমতা বাড়িয়ে তোলে df[df.A != df.B]। ব্যবহার .valuesপ্রতিটি পরিস্থিতিতে উপযুক্ত নাও হতে পারে তবে এটি একটি দরকারী হ্যাক।

উপরে উল্লিখিত হিসাবে, এই সমাধানগুলি বাস্তবায়নের সমস্যার জন্য মূল্যবান কিনা তা সিদ্ধান্ত নেওয়া আপনার পক্ষে নির্ভর করে।


পরিশিষ্ট: কোড স্নিপেটস

import perfplot  
import operator 
import pandas as pd
import numpy as np
import re

from collections import Counter
from itertools import chain

# Boolean indexing with Numeric value comparison.
perfplot.show(
    setup=lambda n: pd.DataFrame(np.random.choice(1000, (n, 2)), columns=['A','B']),
    kernels=[
        lambda df: df[df.A != df.B],
        lambda df: df.query('A != B'),
        lambda df: df[[x != y for x, y in zip(df.A, df.B)]],
        lambda df: df[get_mask(df.A.values, df.B.values)]
    ],
    labels=['vectorized !=', 'query (numexpr)', 'list comp', 'numba'],
    n_range=[2**k for k in range(0, 15)],
    xlabel='N'
)

# Value Counts comparison.
perfplot.show(
    setup=lambda n: pd.Series(np.random.choice(1000, n)),
    kernels=[
        lambda ser: ser.value_counts(sort=False).to_dict(),
        lambda ser: dict(zip(*np.unique(ser, return_counts=True))),
        lambda ser: Counter(ser),
    ],
    labels=['value_counts', 'np.unique', 'Counter'],
    n_range=[2**k for k in range(0, 15)],
    xlabel='N',
    equality_check=lambda x, y: dict(x) == dict(y)
)

# Boolean indexing with string value comparison.
perfplot.show(
    setup=lambda n: pd.DataFrame(np.random.choice(1000, (n, 2)), columns=['A','B'], dtype=str),
    kernels=[
        lambda df: df[df.A != df.B],
        lambda df: df.query('A != B'),
        lambda df: df[[x != y for x, y in zip(df.A, df.B)]],
    ],
    labels=['vectorized !=', 'query (numexpr)', 'list comp'],
    n_range=[2**k for k in range(0, 15)],
    xlabel='N',
    equality_check=None
)

# Dictionary value extraction.
ser1 = pd.Series([{'key': 'abc', 'value': 123}, {'key': 'xyz', 'value': 456}])
perfplot.show(
    setup=lambda n: pd.concat([ser1] * n, ignore_index=True),
    kernels=[
        lambda ser: ser.map(operator.itemgetter('value')),
        lambda ser: pd.Series([x.get('value') for x in ser]),
    ],
    labels=['map', 'list comprehension'],
    n_range=[2**k for k in range(0, 15)],
    xlabel='N',
    equality_check=None
)

# List positional indexing. 
ser2 = pd.Series([['a', 'b', 'c'], [1, 2], []])        
perfplot.show(
    setup=lambda n: pd.concat([ser2] * n, ignore_index=True),
    kernels=[
        lambda ser: ser.map(get_0th),
        lambda ser: ser.str[0],
        lambda ser: pd.Series([x[0] if len(x) > 0 else np.nan for x in ser]),
        lambda ser: pd.Series([get_0th(x) for x in ser]),
    ],
    labels=['map', 'str accessor', 'list comprehension', 'list comp safe'],
    n_range=[2**k for k in range(0, 15)],
    xlabel='N',
    equality_check=None
)

# Nested list flattening.
ser3 = pd.Series([['a', 'b', 'c'], ['d', 'e'], ['f', 'g']])
perfplot.show(
    setup=lambda n: pd.concat([ser2] * n, ignore_index=True),
    kernels=[
        lambda ser: pd.DataFrame(ser.tolist()).stack().reset_index(drop=True),
        lambda ser: pd.Series(list(chain.from_iterable(ser.tolist()))),
        lambda ser: pd.Series([y for x in ser for y in x]),
    ],
    labels=['stack', 'itertools.chain', 'nested list comp'],
    n_range=[2**k for k in range(0, 15)],
    xlabel='N',    
    equality_check=None

)

# Extracting strings.
ser4 = pd.Series(['foo xyz', 'test A1234', 'D3345 xtz'])
perfplot.show(
    setup=lambda n: pd.concat([ser4] * n, ignore_index=True),
    kernels=[
        lambda ser: ser.str.extract(r'(?<=[A-Z])(\d{4})', expand=False),
        lambda ser: pd.Series([matcher(x) for x in ser])
    ],
    labels=['str.extract', 'list comprehension'],
    n_range=[2**k for k in range(0, 15)],
    xlabel='N',
    equality_check=None
)

pd.Seriesএবং pd.DataFrameএখন পুনরাবৃত্তি থেকে নির্মাণ সমর্থন। এর অর্থ যে কোনও একটি প্রথমে একটি তালিকা তৈরির প্রয়োজনের চেয়ে (পাইয়ে বোঝার সাহায্যে) একটি পাইথন জেনারেটরটি কেবল কনস্ট্রাক্টর ফাংশনে পাস করতে পারে যা অনেক ক্ষেত্রে ধীর হতে পারে। তবে জেনারেটরের আউটপুটটির আকার আগে নির্ধারণ করা যায় না। আমি নিশ্চিত না যে কত সময় / স্মৃতি ওভারহেডের কারণ ঘটবে।
GZ0

@ GZ0 আইআইআরসি, পুনরাবৃত্তিকে গ্রহণ করা এপিআই-তে আরও সাম্প্রতিক সংযোজন। যেমন "এর অর্থ হল প্রথমে একটি তালিকা তৈরির প্রয়োজনের পরিবর্তে পাইথন জেনারেটরটি কনস্ট্রাক্টর ফাংশনে সহজেই পাস করা যায়", আমি একমত নই। স্মৃতি সম্ভবত, পারফরম্যান্স নং। আমার অভিজ্ঞতায় তালিকাটি তৈরি করা এবং এটিকে পাস করার জন্য এটি প্রায় সবসময় দ্রুত। তালিকা comps FTW।
CS95

@ সিএস 95 আপনি কেন ভাবেন না যে জেনারেটরগুলি কর্মক্ষমতা অর্জন করতে পারে? নাকি আপনি কোন পরীক্ষা করেছিলেন?
GZ0

@ জিজেড ০ আমি বলিনি জেনারেটরগুলি পারফরম্যান্স লাভের দিকে নিয়ে যায় না, আমি বলছি আপনি তালিকা বোধগম্যতা ব্যবহার করে যতটা অর্জন করবেন না তেমন লাভ করবেন না। জেনেক্সপগুলি আরম্ভ করার সাথে সম্পর্কিত অবস্থা বজায় রাখা ইত্যাদি সম্পর্কিত ওভারহেডগুলি রয়েছে, যা লিস্টের কমপগুলিতে নেই। তালিকার বোধগম্যতার সাথে এখানে অনেকগুলি উদাহরণ রয়েছে যা আপনি তুলনামূলকভাবে জেনারেটর বোঝার বিরুদ্ধে সময় দিতে পারেন time এখানে একটি উদাহরণ এই মুহূর্তে আপনাকে চালানো পারেন: ser = pd.Series([['a', 'b', 'c'], ['d', 'e'], ['f', 'g']] * 10000); %timeit pd.Series(y for x in ser for y in x); %timeit pd.Series([y for x in ser for y in x])
সিএস 95

@ cs95 আপনি সম্ভবত সঠিক বলেছেন। আমি কেবল কনস্ট্রাক্টর pd.Seriesকোডটিতে একটি তাত্ক্ষণিক নজর রেখেছি এবং এটি প্রমাণিত হয়েছে যে যদি পুনরাবৃত্ত আকারের আউটপুটটির আকার নির্ধারণ করা যায় না তবে যে কোনও উপায়ে পুনরাবৃত্ত থেকে একটি তালিকা তৈরি করা হবে। সুতরাং বেশিরভাগ ক্ষেত্রে জেনারেটর ব্যবহারের কোনও লাভ হবে না। তবে আমি যখন জেনারেটরের সংস্করণটি দিয়েছি তার উদাহরণগুলি ধীরে ধীরে ধীর is আমি নিশ্চিত নই যে পার্থক্যটি কোথা থেকে এসেছে।
GZ0

1

সংক্ষেপে

  • লুপ জন্য + iterrowsঅত্যন্ত ধীর। ওভারহেড ~ 1k সারিগুলিতে তাত্পর্যপূর্ণ নয়, তবে 10k + সারিগুলিতে লক্ষণীয়।
  • লুপের জন্য বা এর itertuplesচেয়ে অনেক দ্রুত ।iterrowsapply
  • ভেক্টরাইজেশন সাধারণত তুলনায় অনেক দ্রুত itertuples

মাপকাঠি এখানে চিত্র বর্ণনা লিখুন

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.