python - पांडों में SettingWithCopyWarning से निपटने के लिए कैसे?




pandas dataframe (6)

पांडस डेटाफ्रेम कॉपी चेतावनी

जब आप जाते हैं और ऐसा कुछ करते हैं:

quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]

इस मामले में pandas.ix एक नया, स्टैंड स्टैंड अकेले pandas.ix

इस डेटाफ्रेम में आप जो भी मान बदलना चाहते हैं, वह मूल डेटाफ्रेम नहीं बदलेगा।

पांडा आपको इस बारे में चेतावनी देने की कोशिश करता है।

क्यों .ix एक बुरा विचार है

.ix ऑब्जेक्ट एक से अधिक चीज़ करने की कोशिश करता है, और किसी भी व्यक्ति के लिए जिसने क्लीन कोड के बारे में कुछ भी पढ़ा है, यह एक मजबूत गंध है।

इस डेटाफ्रेम को देखते हुए:

df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})

दो व्यवहार:

dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2

व्यवहार एक: dfcopy अब स्टैंड स्टैंड अकेले dfcopy है। इसे बदलना df नहीं बदलेगा

df.ix[0, "a"] = 3

व्यवहार दो: यह मूल डेटा फ्रेम बदलता है।

इसके बजाए .loc प्रयोग करें

पांडा डेवलपर्स ने मान्यता दी कि .ix ऑब्जेक्ट काफी हद तक सुगंधित था [ .ix ] और इस प्रकार दो नई वस्तुएं .ix जो डेटा के प्रवेश और असाइनमेंट में मदद करती हैं। (दूसरा .iloc )

.loc तेज़ है, क्योंकि यह डेटा की एक प्रति बनाने की कोशिश नहीं करता है।

.loc आपके मौजूदा .loc को संशोधित करने के लिए है, जो अधिक स्मृति कुशल है।

.loc अनुमानित है, यह एक व्यवहार है।

समाधान

आप अपने कोड उदाहरण में क्या कर रहे हैं, बहुत सारे कॉलम के साथ एक बड़ी फ़ाइल लोड कर रहा है, फिर इसे छोटा करने के लिए संशोधित कर रहा है।

pd.read_csv फ़ंक्शन आपको pd.read_csv बहुत से मदद कर सकता है और फ़ाइल को बहुत तेज़ी से लोड करने में भी मदद कर सकता है।

तो ऐसा करने के बजाय

quote_df = pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
quote_df.rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]

यह करो

columns = ['STK', 'TPrice', 'TPCLOSE', 'TOpen', 'THigh', 'TLow', 'TVol', 'TAmt', 'TDate', 'TTime']
df = pd.read_csv(StringIO(str_of_all), sep=',', usecols=[0,3,2,1,4,5,8,9,30,31])
df.columns = columns

यह केवल उन स्तंभों को पढ़ेगा जिन्हें आप रुचि रखते हैं, और उन्हें सही तरीके से नाम दें। जादुई सामान करने के लिए बुराई .ix वस्तु का उपयोग करने की कोई ज़रूरत नहीं है।

पृष्ठभूमि

मैंने अभी अपने पांडों को 0.11 से 0.13.0 आरसी 1 तक अपग्रेड किया है। अब, एप्लिकेशन कई नई चेतावनियों को पॉप आउट कर रहा है। उनमें से एक इस तरह:

E:\FinReporter\FM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TVol']   = quote_df['TVol']/TVOL_SCALE

मैं जानना चाहता हूं कि इसका क्या अर्थ है? क्या मुझे कुछ बदलने की ज़रूरत है?

अगर मैं quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE का उपयोग करने का आग्रह करता हूं तो मुझे चेतावनी कैसे निलंबित करनी चाहिए?

वह कार्य जो त्रुटियों को देता है

def _decode_stock_quote(list_of_150_stk_str):
    """decode the webpage and return dataframe"""

    from cStringIO import StringIO

    str_of_all = "".join(list_of_150_stk_str)

    quote_df = pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
    quote_df.rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
    quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
    quote_df['TClose'] = quote_df['TPrice']
    quote_df['RT']     = 100 * (quote_df['TPrice']/quote_df['TPCLOSE'] - 1)
    quote_df['TVol']   = quote_df['TVol']/TVOL_SCALE
    quote_df['TAmt']   = quote_df['TAmt']/TAMT_SCALE
    quote_df['STK_ID'] = quote_df['STK'].str.slice(13,19)
    quote_df['STK_Name'] = quote_df['STK'].str.slice(21,30)#.decode('gb2312')
    quote_df['TDate']  = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])

    return quote_df

अधिक त्रुटि संदेश

E:\FinReporter\FM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TVol']   = quote_df['TVol']/TVOL_SCALE
E:\FinReporter\FM_EXT.py:450: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TAmt']   = quote_df['TAmt']/TAMT_SCALE
E:\FinReporter\FM_EXT.py:453: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TDate']  = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])

आप इस तरह की पूरी समस्या से बच सकते हैं, मुझे विश्वास है:

return (
    pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
    .rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
    .ix[:,[0,3,2,1,4,5,8,9,30,31]]
    .assign(
        TClose=lambda df: df['TPrice'],
        RT=lambda df: 100 * (df['TPrice']/quote_df['TPCLOSE'] - 1),
        TVol=lambda df: df['TVol']/TVOL_SCALE,
        TAmt=lambda df: df['TAmt']/TAMT_SCALE,
        STK_ID=lambda df: df['STK'].str.slice(13,19),
        STK_Name=lambda df: df['STK'].str.slice(21,30)#.decode('gb2312'),
        TDate=lambda df: df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10]),
    )
)

असाइन का उपयोग करना। documentation : नए कॉलम को नए डेटा के साथ सभी मूल कॉलम के साथ एक नया ऑब्जेक्ट (प्रतिलिपि) लौटाकर, डेटाफ्रेम पर नए कॉलम असाइन करें।

पांडा में विधि श्रृंखला पर टॉम Augspurger का आलेख देखें: https://tomaugspurger.github.io/method-chaining


मेरे लिए यह समस्या निम्नलिखित> सरलीकृत <उदाहरण में हुई। और मैं इसे हल करने में भी सक्षम था (उम्मीद है कि एक सही समाधान के साथ):

चेतावनी के साथ पुराना कोड:

def update_old_dataframe(old_dataframe, new_dataframe):
    for new_index, new_row in new_dataframe.iterrorws():
        old_dataframe.loc[new_index] = update_row(old_dataframe.loc[new_index], new_row)

def update_row(old_row, new_row):
    for field in [list_of_columns]:
        # line with warning because of chain indexing old_dataframe[new_index][field]
        old_row[field] = new_row[field]  
    return old_row

इसने old_row[field] = new_row[field] लाइन के लिए चेतावनी मुद्रित की

चूंकि update_row विधि में पंक्तियां वास्तव में Series टाइप करती हैं, इसलिए मैंने लाइन को प्रतिस्थापित किया है:

old_row.at[field] = new_row.at[field]

यानी एक Series लिए एक्सेस / लुकअप के लिए method । घटनाक्रम दोनों ठीक काम करता है और नतीजा एक जैसा है, इस तरह मुझे चेतावनियों को अक्षम करने की आवश्यकता नहीं है (= उन्हें अन्य चेन इंडेक्सिंग मुद्दों के लिए कहीं और रखें)।

मुझे उम्मीद है कि यह किसी की मदद कर सकता है।


यदि आपने स्लाइस को एक चर में असाइन किया है और चर के रूप में निम्न का उपयोग करना चाहते हैं:

df2 = df[df['A'] > 2]
df2['B'] = value

और आप जेफ्स समाधान का उपयोग नहीं करना चाहते हैं क्योंकि आपकी हालत कंप्यूटिंग df2 लंबी या किसी अन्य कारण से है, तो आप निम्न का उपयोग कर सकते हैं:

df.loc[df2.index.tolist(), 'B'] = value

df2.index.tolist() df2 में सभी प्रविष्टियों से सूचकांक लौटाता है, जिसका उपयोग तब मूल डेटाफ्रेम में कॉलम बी सेट करने के लिए किया जाएगा।


सामान्य रूप से SettingWithCopyWarning का बिंदु उपयोगकर्ताओं को दिखाता है (और नए उपयोगकर्ताओं को esp) कि वे एक प्रतिलिपि पर काम कर रहे हैं, न कि मूल के रूप में वे सोचते हैं। झूठी सकारात्मक हैं (अब आप जानते हैं कि आप क्या कर रहे हैं, तो यह ठीक है )। @ गारेटेट के सुझाव के रूप में चेतावनी (डिफ़ॉल्ट चेतावनी द्वारा) चेतावनी को बंद करने की संभावना एक संभावना है।

यहां एक विकल्प है, प्रति विकल्प।

In [1]: df = DataFrame(np.random.randn(5,2),columns=list('AB'))

In [2]: dfa = df.ix[:,[1,0]]

In [3]: dfa.is_copy
Out[3]: True

In [4]: dfa['A'] /= 2
/usr/local/bin/ipython:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  #!/usr/local/bin/python

आप is_copy ध्वज को False सेट कर सकते हैं, जो प्रभावी रूप से चेक को बंद कर देगा, * उस ऑब्जेक्ट के लिए

In [5]: dfa.is_copy = False

In [6]: dfa['A'] /= 2

यदि आप अन्वेषण प्रतिलिपि करते हैं तो आप जानते हैं कि आप क्या कर रहे हैं , इसलिए कोई और चेतावनी नहीं होगी।

In [7]: dfa = df.ix[:,[1,0]].copy()

In [8]: dfa['A'] /= 2

ओपी कोड ऊपर दिखा रहा है, जबकि वैध, और शायद मैं कुछ भी करता हूं, तकनीकी रूप से इस चेतावनी का मामला है, न कि झूठी सकारात्मक। चेतावनी नहीं होने का एक और तरीका reindex माध्यम से चयन ऑपरेशन करना होगा, उदाहरण के लिए

quote_df = quote_df.reindex(columns=['STK',.......])

या,

quote_df = quote_df.reindex(['STK',.......], axis=1) # v.0.21

SettingWithCopyWarning संभावित रूप से भ्रमित "जंजीर" असाइनमेंट को ध्वजांकित करने के लिए बनाया गया था, जैसे कि निम्न, जो हमेशा अपेक्षित काम नहीं करते हैं, खासकर जब पहला चयन प्रतिलिपि देता है। [पृष्ठभूमि चर्चा के लिए GH5597 GH5390 और GH5597 GH5390 देखें।]

df[df['A'] > 2]['B'] = new_val  # new_val not set in df

चेतावनी निम्नानुसार पुनर्लेखन के लिए एक सुझाव प्रदान करती है:

df.loc[df['A'] > 2, 'B'] = new_val

हालांकि, यह आपके उपयोग में फिट नहीं है, जो इसके बराबर है:

df = df[df['A'] > 2]
df['B'] = new_val

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

pd.options.mode.chained_assignment = None  # default='warn'




chained-assignment