पांडा के लिए छोरों वास्तव में खराब हैं? मुझे कब ध्यान देना चाहिए?


106

क्या forलूप्स वास्तव में "खराब" हैं? यदि नहीं, तो किस स्थिति में वे अधिक परंपरागत "वेक्टरकृत" दृष्टिकोण का उपयोग करने से बेहतर होंगे? 1

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

हालांकि, मैं बहुत सारे कोड (स्टैक ओवरफ्लो पर जवाब सहित) समस्याओं को हल करने में आश्चर्यचकित हूं, जिसमें forलूप और लिस्ट कॉम्प्रिहेंशन का उपयोग करके डेटा के माध्यम से लूपिंग शामिल है । प्रलेखन और एपीआई का कहना है कि लूप्स "खराब" हैं, और किसी को सरणियों, श्रृंखला या डेटाफ्रेम पर "कभी नहीं" पुनरावृति करना चाहिए। तो, मैं कभी-कभी उपयोगकर्ताओं को लूप-आधारित समाधानों का सुझाव देते हुए कैसे देख सकता हूं?


1 - जबकि यह सच है कि सवाल कुछ व्यापक लगता है, सच्चाई यह है कि बहुत विशिष्ट परिस्थितियां हैं जब forलूप आमतौर पर डेटा पर परंपरागत रूप से पुनरावृत्ति करने से बेहतर होते हैं। इस पोस्ट का उद्देश्य पोस्टरिटी के लिए इस पर कब्जा करना है।

जवाबों:


143

TLDR; नहीं, forलूप कंबल "खराब" नहीं हैं, कम से कम, हमेशा नहीं। यह कहना शायद अधिक सटीक है कि कुछ सदिश किए गए ऑपरेशन पुनरावृति की तुलना में धीमे होते हैं , या यह कहते हुए कि पुनरावृत्ति कुछ सदिश परिचालनों की तुलना में तेज़ है। यह जानना कि आपके कोड से सबसे अधिक प्रदर्शन कब और क्यों महत्वपूर्ण है। संक्षेप में, ये वे परिस्थितियाँ हैं जहाँ यह वेक्टरकृत पांडा कार्यों के विकल्प पर विचार करने के लायक है:

  1. जब आपका डेटा छोटा हो (... जो आप कर रहे हैं उसके आधार पर),
  2. जब objectमिश्रित dtypes के साथ काम कर रहे हो
  3. str/ Regex accessor फ़ंक्शन का उपयोग करते समय

आइए इन स्थितियों की व्यक्तिगत रूप से जांच करें।


लघु डेटा पर विचलन v / s वेक्टरकरण

पंडों ने अपने एपीआई डिजाइन में "कन्वेंशन ओवर कॉन्फ़िगरेशन" दृष्टिकोण का अनुसरण किया । इसका मतलब है कि डेटा और उपयोग के मामलों की एक विस्तृत श्रृंखला को पूरा करने के लिए एक ही एपीआई को फिट किया गया है।

जब एक पंडों के समारोह को बुलाया जाता है, तो काम को सुनिश्चित करने के लिए निम्नलिखित चीजों (दूसरों के बीच) को आंतरिक रूप से फ़ंक्शन द्वारा नियंत्रित किया जाना चाहिए

  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मध्यम आकार के एन के लिए सूची समझ से बाहर प्रदर्शन , और यहां तक ​​कि बेहतर प्रदर्शन वेक्टर बेहतर छोटे एन के लिए तुलना नहीं करता है। दुर्भाग्य से, सूची व्यापक रूप से स्केल समझती है, इसलिए यह बड़े एन के लिए अधिक प्रदर्शन लाभ प्रदान नहीं करता है।

नोट
यह ध्यान देने योग्य है कि सूची समझ का बहुत अधिक लाभ सूचकांक संरेखण के बारे में चिंता न करने से आता है, लेकिन इसका मतलब है कि यदि आपका कोड अनुक्रमण संरेखण पर निर्भर है, तो यह टूट जाएगा। कुछ मामलों में, अंतर्निहित 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) की एक बड़ी रेंज के लिए दोनों सदिश तरीकों पर जीतता है।

नोट
अधिक सामान्य ज्ञान (सौजन्य @ user2357112) Counterएक साथ लागू किया गया है सी त्वरक , यह अभी भी काम करना पड़ता है इसलिए जब अजगर अंतर्निहित सी डेटाटाइप्स के बजाय वस्तुओं के साथ, यह अभी भी तेजी से एक से है 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

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

नंबा बहुत शक्तिशाली वेक्टर कोड के लिए लूप पीथॉन कोड का जेआईटी संकलन प्रदान करता है। सुंबा काम बनाने के तरीके को समझना एक सीखने की अवस्था है।


मिश्रित / objectdtypes के साथ संचालन

स्ट्रिंग-आधारित तुलना
पहले खंड से फ़िल्टरिंग उदाहरण को फिर से देखना, क्या होगा यदि स्तंभों की तुलना की जा रही है तार? ऊपर दिए गए 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

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

तो, क्या बदला? यहां ध्यान देने वाली बात यह है कि स्ट्रिंग ऑपरेशन को वेक्टर करना मुश्किल है। पंडाल वस्तुओं के रूप में तारों का व्यवहार करते हैं, और वस्तुओं पर सभी संचालन एक धीमी, ढलान पर लागू होते हैं।

अब, क्योंकि यह ढलान कार्यान्वयन उपर्युक्त सभी ओवरहेड से घिरा हुआ है, इन समाधानों के बीच एक निरंतर परिमाण अंतर है, भले ही वे समान पैमाने पर हों।

जब यह परिवर्तनशील / जटिल वस्तुओं के संचालन की बात आती है, तो कोई तुलना नहीं है। सूची समझ से बाहर सभी कार्यों dicts और सूचियों को शामिल।

कुंजी द्वारा डिक्शनरी वैल्यू एक्सेस करना
यहाँ दो ऑपरेशंस के लिए टाइमिंग है जो शब्दकोशों के एक कॉलम से एक वैल्यू निकालते हैं: mapऔर लिस्ट कॉम्प्रिहेंशन। सेटअप "कोड स्निपेट्स" शीर्षक के तहत परिशिष्ट में है।

# 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सॉल्यूशन की तुलना में बहुत बेहतर हैं।

ये समय इस तथ्य का एक मजबूत संकेत है कि पांडा मिश्रित dtypes के साथ काम करने के लिए सुसज्जित नहीं है, और आपको ऐसा करने के लिए उपयोग करने से शायद बचना चाहिए। जहाँ भी संभव हो, डेटा को अलग-अलग कॉलम में अदिश मान (इन्टस / फ्लोट्स / स्ट्रिंग्स) के रूप में मौजूद होना चाहिए।

अंत में, इन समाधानों की प्रयोज्यता आपके डेटा पर व्यापक रूप से निर्भर करती है। तो, सबसे अच्छी बात यह है कि इन आंकड़ों का परीक्षण करने से पहले यह तय कर लें कि क्या करना है। ध्यान दें कि मैंने applyइन समाधानों पर कैसे समय नहीं दिया है , क्योंकि यह ग्राफ को तिरछा करेगा (हाँ, यह धीमा है)।


रेगेक्स ऑपरेशंस, और एक्सेसर .strमेथड्स

पांडा जैसे regex संचालन आवेदन कर सकते हैं str.contains, str.extractऔर str.extractallसाथ ही अन्य "vectorized" स्ट्रिंग आपरेशन (जैसे, str.split, str.find ,str.translate`, और इसी तरह) स्ट्रिंग स्तंभों पर। ये कार्य सूची समझ की तुलना में धीमी हैं, और किसी भी चीज़ की तुलना में अधिक सुविधा वाले कार्य हैं।

यह आमतौर पर एक रेगाक्स पैटर्न को प्री-कंपाइल करने और आपके डेटा पर पुनरावृति करने के लिए बहुत तेज़ है re.compile(यह भी देखें कि क्या यह पायथन के 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

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

अधिक उदाहरण
पूर्ण प्रकटीकरण - मैं नीचे सूचीबद्ध इन पदों का लेखक (आंशिक या संपूर्ण) हूं।


निष्कर्ष

जैसा कि ऊपर दिए गए उदाहरणों से दिखाया गया है, डेटाफ़्रेम, मिश्रित डेटाैटिप्स और नियमित अभिव्यक्तियों की छोटी पंक्तियों के साथ काम करते समय चलना चमकता है।

आपको मिलने वाला स्पीडअप आपके डेटा और आपकी समस्या पर निर्भर करता है, इसलिए आपका माइलेज अलग-अलग हो सकता है। सबसे अच्छी बात यह है कि सावधानीपूर्वक परीक्षण चलाएं और देखें कि क्या पेआउट प्रयास के लायक है।

"वेक्टराइज्ड" फ़ंक्शन उनकी सादगी और पठनीयता में चमकते हैं, इसलिए यदि प्रदर्शन महत्वपूर्ण नहीं है, तो आपको निश्चित रूप से उन लोगों को पसंद करना चाहिए।

एक अन्य पक्ष ध्यान दें, कुछ स्ट्रिंग ऑपरेशन, उन बाधाओं से निपटते हैं, जो न्यूमपी के उपयोग के पक्ष में हैं। यहाँ दो उदाहरण हैं जहाँ सावधान NumPy वैश्वीकरण outperforms python:

इसके अतिरिक्त, कभी-कभी सिर्फ़ .valuesया डेटाफ़्रेम पर विरोध के रूप में अंतर्निहित सरणियों पर काम करने से अधिकांश सामान्य परिदृश्यों के लिए एक स्वस्थ पर्याप्त स्पीडअप की पेशकश की जा सकती है ( ऊपर संख्यात्मक तुलना में नोट देखें )। उदाहरण के लिए, उदाहरण के लिए, तत्काल प्रदर्शन बढ़ेगा । उपयोग करना हर स्थिति में उचित नहीं हो सकता है, लेकिन यह जानने के लिए एक उपयोगी हैक है।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 IIRC, पुनरावृत्तियों को स्वीकार करना एपीआई के लिए एक और हालिया जोड़ है। जैसा कि "का अर्थ है कि कोई व्यक्ति पहले से ही एक सूची बनाने के लिए आवश्यकता के बजाय निर्माण कार्यों के लिए एक पायथन जनरेटर को पारित कर सकता है", मैं असहमत हूं। मेमोरी शायद, प्रदर्शन नं। मेरे अनुभव में यह सूची बनाने और इसे पारित करने के लिए आमतौर पर लगभग हमेशा तेज होता है। सूची comps FTW।
सीएस 95

@ cs95 आपको क्यों नहीं लगता कि जनरेटर प्रदर्शन का नेतृत्व कर सकते हैं? या आपने उस पर कोई परीक्षण किया था?
GZ0

@ GZ0 मैंने यह नहीं कहा कि जनरेटर एक प्रदर्शन लाभ के लिए नेतृत्व नहीं करते हैं, मैं कह रहा हूं कि आप उतना नहीं हासिल करते हैं जितना आप सूची समझ का उपयोग करेंगे। प्रारंभिक जीनोमैप्स से संबंधित ओवरहेड्स हैं, जो राज्य को बनाए रखते हैं, आदि, जो सूची comps नहीं है। लिस्ट कॉम्प्रिहेंशन के साथ यहां बहुत सारे उदाहरण हैं जिनकी तुलना करने के लिए आप जेनरेटर कॉम्प्रिहेंशन के खिलाफ समय निकाल सकते हैं। यहाँ एक उदाहरण है जिसे आप अभी चला सकते हैं 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

@ सीएस 95 आप शायद सही हैं। मुझे अभी के कंस्ट्रक्टर कोड में एक त्वरित नज़र थी pd.Seriesऔर यह पता चला है कि अगर पुनरावृत्त आउटपुट का आकार निर्धारित नहीं किया जा सकता है तो वैसे भी चलने योग्य से एक सूची का निर्माण किया जाएगा। इसलिए ज्यादातर मामलों में जनरेटर का उपयोग करने के लिए कोई लाभ नहीं होगा। हालाँकि जब मैंने उदाहरण दिए तो आपने जेनरेटर संस्करण दिया था वास्तव में धीमा है। मुझे यकीन नहीं है कि अंतर कहां से आता है।
GZ0

1

संक्षेप में

  • लूप + के iterrowsलिए बेहद धीमी है। ओवरहेड ~ 1k पंक्तियों पर महत्वपूर्ण नहीं है, लेकिन 10k + पंक्तियों पर ध्यान देने योग्य है।
  • लूप + के लिए या की itertuplesतुलना में बहुत तेज है ।iterrowsapply
  • वैश्वीकरण आमतौर पर की तुलना में बहुत तेज है itertuples

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

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