SettingWithCopyWarning
पंडों से कैसे निपटें ?
यह पोस्ट उन पाठकों के लिए है जो
- समझना चाहेंगे कि इस चेतावनी का क्या मतलब है
- इस चेतावनी को दबाने के विभिन्न तरीकों को समझना चाहेंगे
- भविष्य में इस चेतावनी से बचने के लिए अपने कोड को बेहतर बनाने और अच्छी प्रथाओं का पालन करने के तरीके को समझना चाहेंगे।
सेट अप
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
इस चेतावनी को सीधे चुप करने के कुछ तरीके हैं:
एक बनाओ deepcopy
df2 = df[['A']].copy(deep=True)
df2['A'] /= 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
, पहले की तरह।