पंडों में SettingWithCopyWarning से कैसे निपटें?


629

पृष्ठभूमि

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

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])

2
यहाँ एक संदर्भ प्रबंधक अस्थायी रूप से चेतावनी स्तर gist.github.com/notbanker/2be3ed3453c86e22ffdd88fd95ad8bc
पीटर कॉटन

2
आप df.set_valueयहां डॉक्स का उपयोग कर सकते हैं - pandas.pydata.org/pandas-docs/stable/generated/…
leonprou

1
pandas.pydata.org/pandas-docs/stable/… आधिकारिक दस्तावेज़ विस्तार से बताएं
wyx

3
@leonprou df.set_valueको पदावनत कर दिया गया है। पंडों ने अब उपयोग करने .at[]या .iat[]इसके बजाय की सिफारिश की । डॉक्स यहां pandas.pydata.org/pandas-docs/stable/generated/…
Kyle C

मुझे आश्चर्य है कि option_contextयहाँ किसी ने पंडों का उल्लेख नहीं किया है: pandas.pydata.org/pandas-docs/stable/user_guide/options.html , का उपयोग करेंwith pd.option_context("mode.chained_assignment", None): [...]
m-dz

जवाबों:


793

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

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

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

import pandas as pd
pd.options.mode.chained_assignment = None  # default='warn'

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

48
जब वे विकल्पों के लिए चारों ओर हैकिंग करते हैं, तो लोगों को चेतावनी देने की कोशिश करना थोड़ा असुविधाजनक है। एक्सेस के लिए नए-शैली के पंडों के तरीकों (सुधार .ix, सुधार .iloc, आदि) को निश्चित रूप से "प्राथमिक तरीका" के रूप में देखा जा सकता है, बिना किसी अन्य तरीके के सभी के बारे में चेतावनी दिए। इसके बजाय उन्हें बड़े होने दें और यदि वे जंजीर असाइनमेंट करना चाहते हैं, तो ऐसा ही हो। मेरे दो सेंट वैसे भी। पंडों देवों की असंतुष्ट टिप्पणियों को अक्सर यहां देखा जाता है जब जंजीर असाइनमेंट एक समस्या को हल करने के लिए काम करेंगे, लेकिन ऐसा करने के लिए "प्राथमिक" तरीका नहीं माना जाएगा।
ely

8
@EM समस्या यह है कि यह हमेशा उस कोड से स्पष्ट नहीं होता है जहां एक कॉपी बनाम एक दृश्य बनाया जा रहा है और इस मुद्दे से कई बग / भ्रम पैदा होते हैं। हम स्वचालित रूप से कॉन्फ़िगरेशन करने के लिए एक आरसी फ़ाइल / विकल्पों में डालने पर विचार कर रहे थे, जो कि अधिक उपयोगी हो सकता है कि कॉपी चेतावनी के साथ सेटिंग कैसे काम कर रही है।
जेफ ट्रैटनर

3
चेतावनी देने का कारण पुराने कोड को अपग्रेड करने वाले लोगों के लिए है। और मुझे निश्चित रूप से एक चेतावनी की आवश्यकता है, क्योंकि मैं कुछ बहुत ही बदसूरत पुराने कोड के साथ काम कर रहा हूं।
थॉमस एंड्रयूज

15
एक साइड नोट पर, मैंने पाया कि chained_assignment चेतावनी को अक्षम करना: pd.options.mode.chained_assignment = Noneमेरे कोड के परिणामस्वरूप लगभग 6 गुना तेजी से चल रहा है। किसी और को भी इसी तरह के परिणाम का अनुभव?
मून

209

SettingWithCopyWarningपंडों से कैसे निपटें ?

यह पोस्ट उन पाठकों के लिए है जो

  1. समझना चाहेंगे कि इस चेतावनी का क्या मतलब है
  2. इस चेतावनी को दबाने के विभिन्न तरीकों को समझना चाहेंगे
  3. भविष्य में इस चेतावनी से बचने के लिए अपने कोड को बेहतर बनाने और अच्छी प्रथाओं का पालन करने के तरीके को समझना चाहेंगे।

सेट अप

np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (3, 5)), columns=list('ABCDE'))
df
   A  B  C  D  E
0  5  0  3  3  7
1  9  3  5  2  4
2  7  6  8  8  1

क्या है SettingWithCopyWarning?

इस चेतावनी से निपटने के तरीके को जानने के लिए, यह समझना महत्वपूर्ण है कि इसका क्या मतलब है और इसे पहली जगह में क्यों उठाया गया है।

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

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

df[df.A > 5]['B']

1    3
2    6
Name: B, dtype: int64

तथा,

df.loc[df.A > 5, 'B']

1    3
2    6
Name: B, dtype: int64

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

df.loc[df.A > 5, 'B'] = 4
# becomes
df.__setitem__((df.A > 5, 'B'), 4)

के साथ एक __setitem__कॉल करने के लिए df। OTOH, इस कोड पर विचार करें:

df[df.A > 5]['B'] = 4
# becomes
df.__getitem__(df.A > 5).__setitem__('B", 4)

अब, इस पर निर्भर करता है कि __getitem__कोई दृश्य लौटाया गया या कॉपी किया गया, __setitem__ऑपरेशन काम नहीं कर सकता है

सामान्य तौर पर, आपको locलेबल-आधारित असाइनमेंट के ilocलिए और पूर्णांक / स्थिति आधारित असाइनमेंट के लिए उपयोग करना चाहिए , क्योंकि यह गारंटी देता है कि वे हमेशा मूल पर काम करते हैं। इसके अतिरिक्त, एकल कक्ष सेट करने के लिए, आपको उपयोग करना चाहिए atऔर iat

प्रलेखन में अधिक पाया जा सकता है ।

नोट
सभी बूलियन अनुक्रमण संचालन के साथ locभी किया जा सकता है iloc। अंतर केवल इतना है कि ilocइंडेक्स के लिए पूर्णांक / स्थिति या बूलियन मानों की एक सुपीरियर सरणी, और कॉलम के लिए पूर्णांक / स्थिति इंडेक्स की अपेक्षा करता है।

उदाहरण के लिए,

df.loc[df.A > 5, 'B'] = 4

नास लिखा जा सकता है

df.iloc[(df.A > 5).values, 1] = 4

तथा,

df.loc[1, 'A'] = 100

के रूप में लिखा जा सकता है

df.iloc[1, 0] = 100

और इसी तरह।


बस मुझे बताओ कि चेतावनी को कैसे दबाएं!

के "ए" कॉलम पर एक सरल ऑपरेशन पर विचार करें df। "ए" का चयन करना और 2 से विभाजित करना चेतावनी को बढ़ाएगा, लेकिन ऑपरेशन काम करेगा।

df2 = df[['A']]
df2['A'] /= 2
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/__main__.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

df2
     A
0  2.5
1  4.5
2  3.5

इस चेतावनी को सीधे चुप करने के कुछ तरीके हैं:

  1. एक बनाओ deepcopy

    df2 = df[['A']].copy(deep=True)
    df2['A'] /= 2
  2. बदलेंpd.options.mode.chained_assignment
    करने के लिए सेट किया जा सकता है None, "warn"या "raise""warn"डिफ़ॉल्ट है। Noneपूरी तरह से चेतावनी को दबा देगा, और "raise"एक को फेंक देगा SettingWithCopyError, ऑपरेशन को रोकने से।

    pd.options.mode.chained_assignment = None
    df2['A'] /= 2

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

class ChainedAssignent:
    def __init__(self, chained=None):
        acceptable = [None, 'warn', 'raise']
        assert chained in acceptable, "chained must be in " + str(acceptable)
        self.swcw = chained

    def __enter__(self):
        self.saved_swcw = pd.options.mode.chained_assignment
        pd.options.mode.chained_assignment = self.swcw
        return self

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = self.saved_swcw

उपयोग निम्नानुसार है:

# some code here
with ChainedAssignent():
    df2['A'] /= 2
# more code follows

या, अपवाद को बढ़ाने के लिए

with ChainedAssignent(chained='raise'):
    df2['A'] /= 2

SettingWithCopyError: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

"XY समस्या": मैं गलत क्या कर रहा हूँ?

बहुत बार, उपयोगकर्ता इस अपवाद को दबाने के तरीकों को पूरी तरह से समझने के लिए प्रयास करते हैं कि इसे पहली जगह में क्यों उठाया गया था। यह एक XY समस्या का एक अच्छा उदाहरण है , जहां उपयोगकर्ता "Y" समस्या को हल करने का प्रयास करते हैं जो वास्तव में एक गहरी जड़ वाली समस्या "X" का लक्षण है। इस चेतावनी का सामना करने वाली सामान्य समस्याओं के आधार पर प्रश्न उठाए जाएंगे और फिर समाधान प्रस्तुत किए जाएंगे।

प्रश्न 1
मेरे पास डेटाफ़्रेम है

df
       A  B  C  D  E
    0  5  0  3  3  7
    1  9  3  5  2  4
    2  7  6  8  8  1

मैं col "A"> 5 से 1000 में मान निर्दिष्ट करना चाहता हूं। मेरा अपेक्षित आउटपुट है

      A  B  C  D  E
0     5  0  3  3  7
1  1000  3  5  2  4
2  1000  6  8  8  1

ऐसा करने का गलत तरीका:

df.A[df.A > 5] = 1000         # works, because df.A returns a view
df[df.A > 5]['A'] = 1000      # does not work
df.loc[df.A  5]['A'] = 1000   # does not work

उपयोग करने का सही तरीका loc:

df.loc[df.A > 5, 'A'] = 1000


प्रश्न 2 1
मैं सेल में मूल्य (1, 'D') को 12345 पर सेट करने का प्रयास कर रहा हूं। मेरा अपेक्षित आउटपुट है

   A  B  C      D  E
0  5  0  3      3  7
1  9  3  5  12345  4
2  7  6  8      8  1

मैंने इस सेल तक पहुँचने के विभिन्न तरीकों की कोशिश की है, जैसे कि df['D'][1]। इसे करने का बेहतरीन तरीका क्या है?

1. यह प्रश्न विशेष रूप से चेतावनी से संबंधित नहीं है, लेकिन यह समझना अच्छा है कि इस विशेष ऑपरेशन को सही तरीके से कैसे किया जाए ताकि उन स्थितियों से बचा जा सके जहां चेतावनी भविष्य में संभावित रूप से उत्पन्न हो सकती है।

ऐसा करने के लिए आप निम्न विधियों में से किसी का उपयोग कर सकते हैं।

df.loc[1, 'D'] = 12345
df.iloc[1, 3] = 12345
df.at[1, 'D'] = 12345
df.iat[1, 3] = 12345


प्रश्न 3
मैं कुछ शर्त के आधार पर मूल्यों को कम करने की कोशिश कर रहा हूं। मेरे पास एक DataFrame है

   A  B  C  D  E
1  9  3  5  2  4
2  7  6  8  8  1

मैं "D" में मानों को 123 तक असाइन करना चाहूंगा जैसे कि "C" == 5. मैंने कोशिश की

df2.loc[df2.C == 5, 'D'] = 123

जो ठीक लगता है लेकिन मुझे अभी भी हो रहा है SettingWithCopyWarning! मैं यह कैसे तय करुं?

यह वास्तव में शायद इसलिए है क्योंकि आपके पाइपलाइन में कोड अधिक है। क्या आपने df2कुछ बड़े से बनाया है, जैसे

df2 = df[df.A > 5]

? इस मामले में, बूलियन इंडेक्सिंग एक दृश्य लौटाएगा, इसलिए df2मूल को संदर्भित करेगा। आपको कॉपीdf2 करने के लिए क्या करना होगा :

df2 = df[df.A > 5].copy()
# Or,
# df2 = df.loc[df.A > 5, :]


प्रश्न 4
मैं कॉलम "C" को इन-प्लेस से छोड़ने की कोशिश कर रहा हूं

   A  B  C  D  E
1  9  3  5  2  4
2  7  6  8  8  1

लेकिन उपयोग कर रहा है

df2.drop('C', axis=1, inplace=True)

फेंकता है SettingWithCopyWarning। ये क्यों हो रहा है?

ऐसा इसलिए है क्योंकि df2इसे किसी अन्य स्लाइसिंग ऑपरेशन से एक दृश्य के रूप में बनाया गया होगा, जैसे कि

df2 = df[df.A > 5]

यहाँ समाधान या तो एक बनाने के लिए है copy()की df, या उपयोग loc, पहले की तरह।


7
पुनश्च: मुझे बताएं कि क्या आपकी स्थिति अनुभाग 3 के प्रश्नों की सूची में शामिल नहीं है। मैं अपने पद में संशोधन करूंगा।
cs95

150

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

यहाँ एक और विकल्प है:

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

जानकारी और चर्चा के लिए धन्यवाद, मैं इस कंसोल को चुप रहने देने के लिए चेतावनी को बंद कर देता हूं। यह SQL डेटाबेस में दृश्य और तालिका जैसा लगता है। मुझे 'कॉपी' की अवधारणा के परिचय के लाभ के बारे में अधिक जानने की जरूरत है, लेकिन IMHO यह कुछ हद तक सूक्ष्म अर्थ
tle

19
मैं प्रतिलिपि से सहमत हूं (); यह स्पष्ट है और इसने मेरा मुद्दा तय किया (जो कि एक गलत सकारात्मक था)।
rdchambers 14:25 बजे

5
अपडेट के बाद 0.16मैंने कई और झूठी सकारात्मकताएं देखीं, झूठी सकारात्मकता के साथ समस्या यह है कि इसे अनदेखा करना सीखता है, भले ही कभी-कभी यह वैध हो।
ढिलाई

3
@ डैडी आप बिंदु को याद कर रहे हैं। कभी-कभी शायद सबसे अधिक समय भी यह काम कर सकता है। लेकिन यह उदाहरण के लिए हो सकता है यदि फ्रेम बड़ा / छोटा है या आप एक कॉलम जोड़ते हैं एक अलग dtype का कहना है कि यह काम नहीं करता है। यही तो बात है। आप कुछ ऐसा कर रहे हैं जो काम कर सकता है लेकिन इसकी गारंटी नहीं है। यह डिप्रेशन चेतावनियों से बहुत अलग है। यदि आप इसका उपयोग जारी रखना चाहते हैं और यह बढ़िया है। लेकिन पूर्वाभास हो।
जेफ

3
@ जेफ अब समझ में आता है, इसलिए यह एक undefinedव्यवहार है। अगर कुछ भी इसे एक त्रुटि फेंकनी चाहिए (तो देखा नुकसान से बचने के लिए C), क्योंकि apiजमे हुए है चेतावनी के वर्तमान व्यवहार पिछड़े संगतता के लिए समझ में आता है। और मैं उन्हें अपने उत्पादन कोड ( warnings.filterwarnings('error', r'SettingWithCopyWarning) में त्रुटियों के रूप में पकड़ने के लिए फेंक दूंगा । इसके अलावा .locकभी-कभी उपयोग करने का सुझाव भी मदद नहीं करता है (यदि यह एक समूह में है)।
डैशी जूल

41

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

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

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

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अब एक स्टैंड अलोन डेटाफ्रेम है। इसे बदलने से बदलाव नहीं होगाdf

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

व्यवहार दो: यह मूल डेटाफ़्रेम में परिवर्तन करता है।


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

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

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

.loc का अर्थ है कि आपके मौजूदा डेटाफ्रेम को निष्क्रिय करना, जो कि अधिक मेमोरी कुशल है।

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


समाधान

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

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जादुई सामान करने के लिए बुरी वस्तु का उपयोग करने की आवश्यकता नहीं है ।


"पांडा डेवलपर्स ने माना कि .ix वस्तु काफी बदबूदार [सट्टा] थी और इस तरह दो नई वस्तुओं का निर्माण हुआ" - दूसरा क्या है?
15:32 बजे jf328

3
@ jf328 .iloc मुझे लगता है
ब्रायन बीन

1
हाँ, यह है .iloc। पांडा डेटा संरचनाओं को अनुक्रमित करने के लिए ये दो प्राथमिक तरीके हैं। प्रलेखन में अधिक पढ़ें।
निन्जाकन्नन

डेट टाइम ऑब्जेक्ट या स्ट्रिंग के साथ टाइमस्टैम्प में डेटाफ्रैम कॉलम को कैसे बदलना चाहिए?
बोल्डनिक

@boldnik इस उत्तर की जाँच करें stackoverflow.com/a/37453925/3730397
firelynx

20

यहां मैं सीधे सवाल का जवाब देता हूं। इसका सामना कैसे करें?

.copy(deep=False)स्लाइस करने के बाद बना लें । Pandas.DataFrame.copy देखें

रुको, एक टुकड़ा एक प्रति वापस नहीं करता है? आखिरकार, यह चेतावनी संदेश क्या कहना चाह रहा है? पढ़ें लंबा जवाब:

import pandas as pd
df = pd.DataFrame({'x':[1,2,3]})

यह एक चेतावनी देता है:

df0 = df[df.x>2]
df0['foo'] = 'bar'

यह नहीं करता:

df1 = df[df.x>2].copy(deep=False)
df1['foo'] = 'bar'

दोनों df0और df1कर रहे हैं DataFrameवस्तुओं, लेकिन उनके बारे में कुछ अलग पांडा सक्षम बनाता है कि चेतावनी मुद्रित करने के लिए है। आइए जानें कि यह क्या है।

import inspect
slice= df[df.x>2]
slice_copy = df[df.x>2].copy(deep=False)
inspect.getmembers(slice)
inspect.getmembers(slice_copy)

अपनी पसंद के भिन्न उपकरण का उपयोग करते हुए, आप देखेंगे कि पते के एक जोड़े से परे, केवल भौतिक अंतर यह है:

|          | slice   | slice_copy |
| _is_copy | weakref | None       |

विधि को सचेत करने के लिए कि क्या निर्णय लेता है कि है DataFrame._check_setitem_copyजो चेक _is_copy। तो यहाँ तुम जाओ। ऐसा बनाएं copyजिससे आपका DataFrame न हो_is_copy

चेतावनी का उपयोग करने का सुझाव दे रहा है .loc, लेकिन यदि आप .locएक फ्रेम पर उपयोग करते हैं _is_copy, तो आपको अभी भी वही चेतावनी मिलेगी। गुमराह करने वाले? हाँ। कष्टप्रद? बिलकुल। सहायक? संभावित रूप से, जब जंजीर असाइनमेंट का उपयोग किया जाता है। लेकिन यह चेन असाइनमेंट का सही पता नहीं लगा सकता है और चेतावनी को अंधाधुंध प्रिंट करता है।


11

यह विषय वास्तव में पंडों के साथ भ्रमित करने वाला है। सौभाग्य से, इसका अपेक्षाकृत सरल समाधान है।

समस्या यह है कि यह हमेशा स्पष्ट नहीं होता है कि डेटा फ़िल्टरिंग ऑपरेशन (जैसे कि लोकल) एक कॉपी या डेटाफ़्रेम का एक दृश्य लौटाता है या नहीं। इस तरह के फ़िल्टर किए गए DataFrame का आगे उपयोग भ्रमित हो सकता है।

सरल समाधान है (जब तक आपको डेटा के बहुत बड़े सेट के साथ काम करने की आवश्यकता न हो):

जब भी आपको किसी भी मान को अपडेट करने की आवश्यकता होती है, तो हमेशा यह सुनिश्चित करें कि आप असाइनमेंट से पहले डेटाफ़्रेम को आसानी से कॉपी कर लें।

df  # Some DataFrame
df = df.loc[:, 0:2]  # Some filtering (unsure whether a view or copy is returned)
df = df.copy()  # Ensuring a copy is made
df[df["Name"] == "John"] = "Johny"  # Assignment can be done now (no warning)

एक टाइपो है: स्पष्ट रूप से स्पष्ट रूप से होना चाहिए
s9527

7

किसी भी संदेह को दूर करने के लिए, मेरा समाधान नियमित कॉपी के बजाय स्लाइस की एक गहरी प्रति बनाना था। यह आपके संदर्भ के आधार पर लागू नहीं हो सकता है (मेमोरी की कमी / स्लाइस के आकार, प्रदर्शन में गिरावट की संभावना - खासकर अगर कॉपी एक लूप में होती है जैसे कि यह मेरे लिए किया था, आदि ...)

स्पष्ट होने के लिए, यहाँ मुझे चेतावनी मिली है:

/opt/anaconda3/lib/python3.6/site-packages/ipykernel/__main__.py:54:
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

चित्रण

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

उदाहरण 1: मूल पर एक कॉलम छोड़ने से कॉपी प्रभावित होती है

हमें पता था कि पहले से ही लेकिन यह एक स्वस्थ अनुस्मारक है। यह वह जगह है नहीं क्या चेतावनी के बारे में है।

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123


>> df2 = df1
>> df2

A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 affects df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    B
0   121
1   122
2   123

Df2 को प्रभावित करने के लिए df1 पर किए गए परिवर्तनों से बचना संभव है

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2
A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 does not affect df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    A   B
0   111 121
1   112 122
2   113 123

उदाहरण 2: कॉपी पर एक कॉलम छोड़ने से मूल प्रभावित हो सकता है

यह वास्तव में चेतावनी को दर्शाता है।

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> df2 = df1
>> df2

    A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df2 can affect df1
# No slice involved here, but I believe the principle remains the same?
# Let me know if not
>> df2.drop('A', axis=1, inplace=True)
>> df1

B
0   121
1   122
2   123

Df1 को प्रभावित करने के लिए df2 पर किए गए परिवर्तनों से बचना संभव है

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2

A   B
0   111 121
1   112 122
2   113 123

>> df2.drop('A', axis=1, inplace=True)
>> df1

A   B
0   111 121
1   112 122
2   113 123

चीयर्स!


4

यह काम करना चाहिए:

quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE

4

कुछ लोग बस चेतावनी को दबाना चाहते हैं:

class SupressSettingWithCopyWarning:
    def __enter__(self):
        pd.options.mode.chained_assignment = None

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = 'warn'

with SupressSettingWithCopyWarning():
    #code that produces warning

3

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

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

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

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

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


यह 9 बार अधिक महंगा है, फिर df ["B"] = मूल्य
क्लाउडीयू क्रेन्गा

क्या आप इसे अधिक गहराई से समझा सकते हैं @ClaudiuCreanga?
gies0r

2

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

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

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]

चूँकि अपडेट_रो विधि में पंक्तियाँ वास्तव में टाइप हैं Series, मैंने लाइन को इसके साथ बदल दिया:

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

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

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


2

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

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]),
    )
)

असाइनमेंट का उपयोग करना। से प्रलेखन एक DataFrame असाइन नए कॉलम, नए लोगों को के अलावा सभी मूल स्तंभों के साथ एक नई वस्तु (एक प्रतिलिपि) लौटने:।

पंडों में विधि चाइनिंग पर टॉम ऑग्सपर्गर का लेख देखें: https://tomaugspurger.github.io/method-chaining


2

फॉलोअप शुरुआत प्रश्न / टिप्पणी

शायद मेरे जैसे अन्य शुरुआती लोगों के लिए एक स्पष्टीकरण (मैं आर से आता हूं जो हुड के नीचे थोड़ा अलग काम करता है)। निम्नलिखित हानिरहित-दिखने वाले और कार्यात्मक कोड SettingWithCopy चेतावनी का उत्पादन करते रहे, और मैं यह पता नहीं लगा सका कि क्यों। मैंने "जंजीर अनुक्रमण" के साथ जारी किया गया था, दोनों को पढ़ा और समझा था, लेकिन मेरे कोड में कोई भी शामिल नहीं है:

def plot(pdb, df, title, **kw):
    df['target'] = (df['ogg'] + df['ugg']) / 2
    # ...

लेकिन फिर, बाद में, बहुत देर हो चुकी है, मैंने देखा कि प्लॉट () फ़ंक्शन कहाँ कहा जाता है:

    df = data[data['anz_emw'] > 0]
    pixbuf = plot(pdb, df, title)

तो "df" एक डेटा फ्रेम नहीं है, लेकिन एक वस्तु है जो किसी भी तरह से याद करती है कि यह एक डेटा फ्रेम को अनुक्रमित करके बनाया गया था (इसलिए यह एक दृश्य है?) जो भूखंड में रेखा बना देगा ()

 df['target'] = ...

के बराबर

 data[data['anz_emw'] > 0]['target'] = ...

जो एक जंजीर अनुक्रमण है। क्या मैने इसे सही समझा?

वैसे भी,

def plot(pdb, df, title, **kw):
    df.loc[:,'target'] = (df['ogg'] + df['ugg']) / 2

ठीक कर दिया।


1

जैसा कि यह प्रश्न पहले से ही पूरी तरह से समझाया गया है और मौजूदा उत्तरों में चर्चा की गई है, मैं सिर्फ pandasसंदर्भ प्रबंधक pandas.option_context(लिंक से) का उपयोग करने के लिए एक स्वच्छ दृष्टिकोण प्रदान करूंगा डॉक्स और उदाहरण के ) - सभी डंडे विधियों और अन्य घंटियों के साथ एक कस्टम वर्ग बनाने की कोई आवश्यकता नहीं है और सीटी।

पहले संदर्भ प्रबंधक कोड ही:

from contextlib import contextmanager

@contextmanager
def SuppressPandasWarning():
    with pd.option_context("mode.chained_assignment", None):
        yield

फिर एक उदाहरण:

import pandas as pd
from string import ascii_letters

a = pd.DataFrame({"A": list(ascii_letters[0:4]), "B": range(0,4)})

mask = a["A"].isin(["c", "d"])
# Even shallow copy below is enough to not raise the warning, but why is a mystery to me.
b = a.loc[mask]  # .copy(deep=False)

# Raises the `SettingWithCopyWarning`
b["B"] = b["B"] * 2

# Does not!
with SuppressPandasWarning():
    b["B"] = b["B"] * 2

वर्थ नोटिंग यह है कि दोनों अनुमोदन संशोधित नहीं करते हैं a, जो मेरे लिए थोड़ा आश्चर्य की बात है, और यहां तक ​​कि एक उथली डीएफ कॉपी भी .copy(deep=False)इस चेतावनी को बढ़ाने से रोकती है (जहां तक ​​मुझे समझ में आता है कि उथली प्रति कम से कम संशोधित होनी चाहिए a, लेकिन यह नहीं है 't। pandasजादू।)।


हम्म, मैं इसे समझता हूं कि अगर चेतावनी दी गई है तो यह स्पष्ट रूप से गलत है, इसलिए बेहतर है कि इसे चेतावनी की तरह चेतावनी से बचें, तो आप क्या सोचते हैं?
जजराएल

नहीं, चेतावनी सिर्फ चेतावनी है। यहाँ की तरह, यह आपको कुछ चेतावनी दे सकता है गलत सकता है, जिसे जानना बहुत अच्छा है, लेकिन अगर आप जानते हैं कि आप क्या और क्यों कर रहे हैं, तो उनमें से कुछ को दबाने के लिए यह बिल्कुल ठीक है। संदर्भों को पुन: प्रस्तुत करने के बारे में stackoverflow.com/a/20627316/4272484 में स्पष्टीकरण देखें ।
-dz

1

मैं इस मुद्दे को .apply()पहले से मौजूद डेटाफ्रेम से एक नया डेटाफ्रेम असाइन करते समय प्राप्त कर रहा था, जिस पर मैंने .query()विधि का उपयोग किया है। उदाहरण के लिए:

prop_df = df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)

इस त्रुटि को वापस करेगा। इस मामले में त्रुटि को हल करने के लिए लगता है कि यह करने के लिए इसे बदल रहा है:

prop_df = df.copy(deep=True)
prop_df = prop_df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)

हालाँकि, यह विशेष रूप से तब प्रभावी नहीं होता है जब बड़ी कॉपीफ्रेम का उपयोग करते हुए, एक नई प्रतिलिपि बनाने के कारण।

यदि आप .apply()एक नए कॉलम और उसके मानों को बनाने में विधि का उपयोग कर रहे हैं, तो एक समाधान जो त्रुटि को हल करता है और जो अधिक कुशल है वह जोड़कर है .reset_index(drop=True):

prop_df = df.query('column == "value"').reset_index(drop=True)
prop_df['new_column'] = prop_df.apply(function, axis=1)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.