Pandas: .loc [...] = ترجع القيمة SettingWithCopyWarning

تم إنشاؤها على ٨ سبتمبر ٢٠١٧  ·  8تعليقات  ·  مصدر: pandas-dev/pandas

نموذج التعليمات البرمجية

# My code
df.loc[0, 'column_name'] = 'foo bar'

وصف المشكلة

هذا الرمز في Pandas 20.3 يلقي SettingWithCopyWarning ويقترح

"حاول استخدام .loc[row_indexer,col_indexer] = value بدلاً من ذلك".

أنا أفعل ذلك بالفعل ، يبدو أن هناك القليل من الأخطاء. أنا استخدم Jupyter.
شكرا لك! :)

ناتج pd.show_versions()


الالتزام: لا شيء
بيثون: 3.6.1.final.0
بتات الثعبان: 64
نظام التشغيل: Windows
إصدار نظام التشغيل: 8.1
الجهاز: AMD64
المعالج: Intel64 Family 6 Model 61 Stepping 4، GenuineIntel
byteorder: قليلا
LC_ALL: لا شيء
لانغ: لا شيء
LOCALE: لا شيء

الباندا: 0.20.1
بيتيست: 3.0.7
النقطة: 9.0.1
أدوات الإعداد: 35.0.2
سايثون: 0.25.2
numpy: 1.12.1
scipy: 0.19.0
xarray: لا شيء
IPython: 5.3.0
أبو الهول: 1.5.6
باتسي: 0.4.1
داتوتيل: ​​2.6.0
بيتز: 2017.2
blosc: لا شيء
عنق الزجاجة: 1.2.1
الجداول: 3.2.2
numexpr: 2.6.2
الريش: لا شيء
matplotlib: 2.0.2
openpyxl: لا شيء
xlrd: 1.0.0
xlwt: 1.2.0
xlsxwriter: 0.9.6
lxml: 3.7.3
BS4: 4.6.0
html5lib: 0.999
sqlalchemy: 1.1.9
pymysql: لا شيء
psycopg2: لا شيء
jinja2: 2.9.6
s3fs: لا شيء
pandas_gbq: لا شيء
pandas_datareader: لا شيء

Indexing Usage Question

التعليق الأكثر فائدة

تكمن المشكلة هنا في أنك تقوم بتقطيع إطار البيانات أولاً باستخدام .loc في السطر 4. محاولة تعيين قيم لتلك الشريحة.

df_c = df.loc[df.encountry == country, :]

Pandas ليس متأكدًا بنسبة 100٪ إذا كنت تريد تعيين قيم لشريحة df_c ، أو جعلها تتكاثر على طول الطريق إلى df الأصلي. لتجنب ذلك عند تعيين df_c لأول مرة ، تأكد من إخبار الباندا أنه إطار بيانات خاص به (وليس شريحة) باستخدام

df_c = df.loc[df.encountry == country, :].copy()

القيام بذلك سوف يصلح الخطأ الخاص بك. سأتناول مثالًا موجزًا ​​للمساعدة في شرح ما سبق لأنني لاحظت أن الكثير من المستخدمين يشعرون بالارتباك من الباندا في هذا الجانب.

مثال مع البيانات المختلقة

>>> import pandas as pd
>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df
   A  B
0  1  Q
1  2  Q
2  3  Q
3  4  C
4  5  C
>>> df.loc[df['B'] == 'Q', 'new_col'] = 'hello'
>>> df
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q   hello
3  4  C     NaN
4  5  C     NaN

لذلك ما سبق يعمل كما نتوقع! لنجرب الآن مثالاً يعكس ما حاولت فعله ببياناتك.

>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df_q = df.loc[df['B'] == 'Q']
>>> df_q
   A  B
0  1  Q
1  2  Q
2  3  Q
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
/Users/riddellcd/anaconda/lib/python3.6/site-packages/pandas/core/indexing.py:337: 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

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  self.obj[key] = _infer_fill_value(value)

>>> df_q
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

يبدو أننا وصلنا نفس الخطأ! لكنها تغيرت df_q كما توقعنا! هذا لأن df_q شريحة من df لذلك ، على الرغم من أننا نستخدم الباندا .loc [] df_q يحذرنا من أنه لن ينشر التغييرات إلى df . لتجنب ذلك ، نحتاج إلى أن نكون أكثر وضوحًا وأن نقول إن df_q هو إطار بيانات خاص به ، منفصل عن df بإعلان ذلك صراحة.

لنبدأ مرة أخرى من df_q لكن استخدم .copy() هذه المرة.

>>> df_q = df.loc[df['B'] == 'Q'].copy()
>>> df_q
   A  B
0  1  Q
1  2  Q
2  3  Q

Lets try to reassign our value now!
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
>>> df_q
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

يعمل هذا بدون خطأ لأننا أخبرنا الباندا أن df_q منفصل عن df

إذا كنت تريد في الواقع أن تنتشر هذه التغييرات إلى df_c حتى df فهذه نقطة أخرى تمامًا وستجيب إذا أردت.

ال 8 كومينتر

NadiaRom هل يمكنك تقديم مثال كامل؟ من الصعب الجزم بذلك ، لكنني أظن أن df أتى من عملية قد تكون عرضًا أو نسخة. فمثلا:

In [8]: df = pd.DataFrame({"A": [1, 2], "B": [3, 4], "C": [4, 5]})

In [9]: df1 = df[['A', 'B']]

In [10]: df1.loc[0, 'A'] = 5
/Users/taugspurger/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/indexing.py:180: 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
  self._setitem_with_indexer(indexer, value)
/Users/taugspurger/Envs/pandas-dev/bin/ipython:1: 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
  #!/Users/taugspurger/Envs/pandas-dev/bin/python3.6

لذلك نحن نقوم بتحديث df1 بشكل صحيح. الغموض هو ما إذا كان سيتم تحديث df أم لا. أعتقد أن شيئًا مشابهًا يحدث لك ، لكن بدون مثال قابل للتكرار ، يصعب الجزم بذلك.

TomAugspurger ها هو الكود ، بشكل عام ، لا

df = pd.read_csv('df_unicities.tsv', sep='\t')
df.replace({'|': '--'}, inplace=True)

df_c = df.loc[df.encountry == country, : ]

df_c['sort'] = (df_c.encities_ua == 'all').astype(int) # new column
df_c['sort'] += (df_c.encities_foreign == 'all').astype(int)
df_c.sort_values(by='sort', inplace=True)

# ---end of chunk, everything is fine ---

if df_c.encities_foreign.str.contains('all').sum() < len(df_c):
    df_c.loc[df_c.encities_foreign.str.contains('all'), 'encities_foreign'] = 'other'
    df_c.loc[df_c.cities_foreign.str.contains('всі'), 'cities_foreign'] = 'інші'
else:
    df_c.loc[df_c.encities_foreign.str.contains('all'), 'encities_foreign'] = country
    df_c.loc[df_c.cities_foreign.str.contains('всі'), 'cities_foreign'] = df_c.country.iloc[0]

if df_c.encities_ua.str.contains('all').sum() < len(df_c):
    df_c.loc[df_c.encities_ua.str.contains('all'), 'encities_ua'] = 'other'
    df_c.loc[df_c.cities_ua.str.contains('всі'), 'cities_ua'] = 'інші'
else:
    df_c.loc[df_c.encities_ua.str.contains('all'), 'encities_ua'] = 'Ukraine'
    df_c.loc[df_c.cities_ua.str.contains('всі'), 'cities_ua'] = 'Україна'

# Warning after it

شكرا لك على الرد السريع!

تكمن المشكلة هنا في أنك تقوم بتقطيع إطار البيانات أولاً باستخدام .loc في السطر 4. محاولة تعيين قيم لتلك الشريحة.

df_c = df.loc[df.encountry == country, :]

Pandas ليس متأكدًا بنسبة 100٪ إذا كنت تريد تعيين قيم لشريحة df_c ، أو جعلها تتكاثر على طول الطريق إلى df الأصلي. لتجنب ذلك عند تعيين df_c لأول مرة ، تأكد من إخبار الباندا أنه إطار بيانات خاص به (وليس شريحة) باستخدام

df_c = df.loc[df.encountry == country, :].copy()

القيام بذلك سوف يصلح الخطأ الخاص بك. سأتناول مثالًا موجزًا ​​للمساعدة في شرح ما سبق لأنني لاحظت أن الكثير من المستخدمين يشعرون بالارتباك من الباندا في هذا الجانب.

مثال مع البيانات المختلقة

>>> import pandas as pd
>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df
   A  B
0  1  Q
1  2  Q
2  3  Q
3  4  C
4  5  C
>>> df.loc[df['B'] == 'Q', 'new_col'] = 'hello'
>>> df
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q   hello
3  4  C     NaN
4  5  C     NaN

لذلك ما سبق يعمل كما نتوقع! لنجرب الآن مثالاً يعكس ما حاولت فعله ببياناتك.

>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df_q = df.loc[df['B'] == 'Q']
>>> df_q
   A  B
0  1  Q
1  2  Q
2  3  Q
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
/Users/riddellcd/anaconda/lib/python3.6/site-packages/pandas/core/indexing.py:337: 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

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  self.obj[key] = _infer_fill_value(value)

>>> df_q
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

يبدو أننا وصلنا نفس الخطأ! لكنها تغيرت df_q كما توقعنا! هذا لأن df_q شريحة من df لذلك ، على الرغم من أننا نستخدم الباندا .loc [] df_q يحذرنا من أنه لن ينشر التغييرات إلى df . لتجنب ذلك ، نحتاج إلى أن نكون أكثر وضوحًا وأن نقول إن df_q هو إطار بيانات خاص به ، منفصل عن df بإعلان ذلك صراحة.

لنبدأ مرة أخرى من df_q لكن استخدم .copy() هذه المرة.

>>> df_q = df.loc[df['B'] == 'Q'].copy()
>>> df_q
   A  B
0  1  Q
1  2  Q
2  3  Q

Lets try to reassign our value now!
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
>>> df_q
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

يعمل هذا بدون خطأ لأننا أخبرنا الباندا أن df_q منفصل عن df

إذا كنت تريد في الواقع أن تنتشر هذه التغييرات إلى df_c حتى df فهذه نقطة أخرى تمامًا وستجيب إذا أردت.

@ CRiddler عظيم ، شكرا لك !
كما ذكرت ، فإن المتسلسلة .loc لم تُرجع أبدًا نتائج غير متوقعة. كما أفهم ، يضمن .copy() Pandas أننا نتعامل مع df_sliced_once ككائن منفصل ولا ننوي تغيير df بالكامل. من فضلك صحح إذا خلطت

التوثيق هنا http://pandas.pydata.org/pandas-docs/stable/indexing.html#returning -a-view-versus-a-copy و CRiddler لديه شرح لطيف. يجب بشكل عام عدم استخدام inplace على الإطلاق.

إذا كنت تريد في الواقع نشر هذه التغييرات إلى df_c حتى df فهذه نقطة أخرى تمامًا وستجيب إذا أردت.

CRiddler شكرًا على إجابتك أفضل من تلك الموجودة في Stack Overflow ، فهل يمكنك إضافتها عندما تريد النشر إلى إطار البيانات الأولي أو إعطاء إشارة إلى كيفية القيام بذلك؟

persep بشكل عام ، لا أحب تحويل المشكلات إلى سلاسل عمليات stackoverflow للحصول على المساعدة ، ولكن يبدو أن هذه المشكلة قد حظيت بقدر لا بأس به من الاهتمام منذ آخر عملية نشر ، لذا

البيانات الأصلية:

>>>import pandas as pd
>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df
   A  B
0  1  Q
1  2  Q
2  3  Q
3  4  C
4  5  C

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

>>> df_q = df.loc[df["B"] == "Q"]
>>> df_q.loc[df["A"] < 3, "new_column"] = "hello"

# df remains unchanged because we only made changes to `df_q`
>>> df
   A  B
0  1  Q
1  2  Q
2  3  Q
3  4  C
4  5  C

على حد علمي ، لا توجد طريقة لاستخدام نفس الكود المذكور أعلاه وإجبار التغييرات على الانتشار مرة أخرى إلى إطار البيانات الأصلي.

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

>>> q_mask = df["B"] == "Q"
>>> a_mask = df["A"] < 3

# Combine masks (in this case we used "&") to achieve what a nested subset would look like
#  In the same step we add in our item assignment. Instructing pandas to create a new column in `df` and assign
#  the value "hello" to the rows in `df` where `q_mask` & `a_mask` overlap.
>>> df.loc[q_mask & a_mask, "new_col"] = "hello"

# Successful "propagation" of new values to the original dataframe
>>> df
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN
3  4  C     NaN
4  5  C     NaN

أخيرًا ، إذا أردنا يومًا ما أن نرى كيف سيبدو df_q ، فيمكننا دائمًا تقسيمه من إطار البيانات الأصلي باستخدام q_mask

>>> df.loc[q_mask, :]
   A  B new_col
0  1  Q   hello
1  2  Q   hello
2  3  Q     NaN

في حين أن هذا ليس بالضرورة "نشر" التغييرات من df_q إلى df نحقق نفس النتيجة. يجب أن يتم التكاثر الفعلي بشكل صريح وسيكون أقل كفاءة من مجرد العمل مع الأقنعة.

@ CRiddler شكرًا ، لقد كنت مفيدًا جدًا

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات