python - شرح - كيفية التعامل مع SettingWithCopy تحذير في Pandas؟




pandas python شرح (6)

خلفية

أنا فقط ترقية بلدي الباندا من 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])

الباندا dataframe نسخة تحذير

عندما تذهب وتفعل شيئًا كهذا:

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

pandas.ix في هذه الحالة بإرجاع dataframe جديدة ومستقلة.

أي القيم التي تقرر تغييرها في هذا dataframe ، لن تتغير في مخطط البيانات الأصلي.

هذا هو ما تحاول الباندا أن تحذرك عنه.

لماذا .ix فكرة سيئة

يحاول كائن .ix القيام بأكثر من شيء واحد ، وبالنسبة لأي شخص قرأ أي شيء عن الشفرة النظيفة ، فإن هذه رائحة قوية.

نظرا لهذا dataframe:

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

سلوكان:

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

السلوك الأول: dfcopy الآن قائم بذاته dataframe. تغييره لن يتغير df

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

السلوك الثاني: هذا يغير مخطط البيانات الأصلي.

استخدم .loc بدلاً من ذلك

أدرك مطورو .ix الباندا أن الكائن .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 سحرية.


إذا قمت بتعيين الشريحة إلى متغير وتريد تعيينها باستخدام المتغير كما يلي:

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

وأنت لا تريد استخدام حل Jeffs نظرًا لأن حالة الكمبيوتر df2 هي طويلة أو لسبب آخر ، فيمكنك استخدام ما يلي:

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

df2.index.tolist() الفهارس من كافة الإدخالات في df2 ، والتي سيتم استخدامها بعد ذلك لتعيين العمود B في مخطط البيانات الأصلي.


بشكل عام ، تتمثل SettingWithCopyWarning في إظهار المستخدمين (واسم المستخدمين الجدد) أنهم قد يعملون على نسخة وليس النسخة الأصلية كما يظنون. هناك ايجابيات كاذبة (IOW تعلمون ما تقوم به ، لذلك على ما يرام ). أحد الاحتمالات هو ببساطة إيقاف التحذير (عن طريق التحذير الافتراضي) كما يقترحGarrett.

هنا هو نور ، لكل خيار.

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

إذا كنت نسخة explicity ثم تعرف ما تقوم به ، لذلك لن يحدث أي تحذير آخر.

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 للإبلاغ عن التفضيلات المحتملة "المتسلسلة" ، مثل التالية ، والتي لا تعمل دائمًا كما هو متوقع ، خاصةً عند قيام التحديد الأول بإرجاع نسخة . [راجع 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

في حين أنه من الواضح أنك لا تهتم بالكتب التي تعيدها إلى الإطار الأصلي (نظرًا لأنك قمت بالاكتفاء بالإشارة إليها) ، للأسف لا يمكن تمييز هذا النمط عن المثال الأول للمهمة المقيدة ، ومن ثم التحذير (الإيجابي الخاطئ). يتم تناول إمكانات النتائج الإيجابية الخاطئة في مستندات الفهرسة ، إذا كنت ترغب في مزيد من القراءة. يمكنك تعطيل هذا التحذير الجديد بأمان مع الواجب التالي.

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

يجب أن يعمل هذا:

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

يمكنك تجنب مشكلة كاملة مثل هذا ، وأعتقد:

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 : قم بتعيين أعمدة جديدة إلى DataFrame ، وإرجاع كائن جديد (نسخة) مع جميع الأعمدة الأصلية بالإضافة إلى الأعمدة الجديدة.

انظر مقال Tom Augspurger عن طريقة تسلسل في الباندا: https://tomaugspurger.github.io/method-chaining





chained-assignment