Pandas: .loc [...] = nilai mengembalikan SettingWithCopyWarning

Dibuat pada 8 Sep 2017  ·  8Komentar  ·  Sumber: pandas-dev/pandas

Contoh Kode

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

Deskripsi masalah

Kode di Pandas 20.3 ini melempar SettingWithCopyWarning dan menyarankan untuk

"Coba gunakan .loc[row_indexer,col_indexer] = value sebagai gantinya".

Saya sudah melakukannya, sepertinya ada bug kecil. Saya menggunakan Jupyter.
Terima kasih! :)

Keluaran pd.show_versions()


commit: Tidak ada
python: 3.6.1.final.0
python-bits: 64
OS: Windows
Rilis OS: 8.1
mesin: AMD64
prosesor: Intel64 Family 6 Model 61 Stepping 4, GenuineIntel
byteorder: sedikit
LC_ALL: Tidak ada
LANG: Tidak ada
LOCALE: Tidak ada. Tidak ada

panda: 0.20.1
pytest: 3.0.7
pip: 9.0.1
setuptools: 35.0.2
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.19.0
xarray: Tidak ada
IPython: 5.3.0
sphinx: 1.5.6
patsy: 0.4.1
dateutil: 2.6.0.0
pytz: 2017.2
blosc: Tidak ada
kemacetan: 1.2.1
tabel: 3.2.2
numexpr: 2.6.2
bulu: Tidak ada
matplotlib: 2.0.2
openpyxl: Tidak ada
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: Tidak ada
psycopg2: Tidak ada
jinja2: 2.9.6
s3fs: Tidak ada
pandas_gbq: Tidak ada
pandas_datareader: Tidak ada

Indexing Usage Question

Komentar yang paling membantu

Masalahnya di sini adalah Anda mengiris dataframe Anda terlebih dahulu dengan .loc di baris 4. Mencoba menetapkan nilai ke potongan itu.

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

Panda tidak 100% yakin jika Anda ingin menetapkan nilai hanya ke potongan df_c , atau menyebarkannya kembali ke aslinya df . Untuk menghindari hal ini saat pertama kali menetapkan df_c pastikan Anda memberi tahu panda bahwa itu adalah bingkai datanya sendiri (dan bukan potongan) dengan menggunakan

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

Melakukan ini akan memperbaiki kesalahan Anda. Saya akan memberikan contoh singkat untuk membantu menjelaskan hal di atas karena saya telah memperhatikan banyak pengguna yang bingung dengan panda dalam aspek ini.

Contoh dengan data yang dibuat-buat

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

Jadi di atas berfungsi seperti yang kami harapkan! Sekarang mari kita coba contoh yang mencerminkan apa yang Anda coba lakukan dengan data Anda.

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

Sepertinya kita mengalami kesalahan yang sama! Tapi itu berubah df_q seperti yang kami harapkan! Ini karena df_q adalah potongan dari df jadi, meskipun kita menggunakan .loc [] df_q pandas memperingatkan kita bahwa itu tidak akan menyebarkan perubahan menjadi df . Untuk menghindari hal ini, kita perlu lebih eksplisit dan mengatakan bahwa df_q adalah kerangka datanya sendiri, terpisah dari df dengan menyatakannya secara eksplisit.

Mari kita mulai kembali dari df_q tetapi gunakan .copy() kali ini.

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

Ini berfungsi tanpa kesalahan karena kami telah memberi tahu panda bahwa df_q terpisah dari df

Jika Anda sebenarnya ingin perubahan ini ke df_c menyebar hingga df poin lain sepenuhnya dan akan menjawab jika Anda mau.

Semua 8 komentar

@NadiaRom Bisakah Anda memberikan contoh lengkap? Sulit untuk mengatakan dengan pasti, tetapi saya menduga bahwa df berasal dari operasi yang mungkin berupa tampilan atau salinan. Sebagai contoh:

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

Jadi kami memperbarui df1 dengan benar. Kerancuannya adalah apakah df akan diperbarui juga atau tidak. Saya pikir hal serupa terjadi pada Anda, tetapi tanpa contoh yang dapat direproduksi, sulit untuk mengatakan dengan pasti.

@TomAugspurger Ini kodenya, secara umum, saya tidak pernah memberikan nilai ke panda tanpa .loc

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

Terima kasih atas jawaban cepatnya!

Masalahnya di sini adalah Anda mengiris dataframe Anda terlebih dahulu dengan .loc di baris 4. Mencoba menetapkan nilai ke potongan itu.

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

Panda tidak 100% yakin jika Anda ingin menetapkan nilai hanya ke potongan df_c , atau menyebarkannya kembali ke aslinya df . Untuk menghindari hal ini saat pertama kali menetapkan df_c pastikan Anda memberi tahu panda bahwa itu adalah bingkai datanya sendiri (dan bukan potongan) dengan menggunakan

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

Melakukan ini akan memperbaiki kesalahan Anda. Saya akan memberikan contoh singkat untuk membantu menjelaskan hal di atas karena saya telah memperhatikan banyak pengguna yang bingung dengan panda dalam aspek ini.

Contoh dengan data yang dibuat-buat

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

Jadi di atas berfungsi seperti yang kami harapkan! Sekarang mari kita coba contoh yang mencerminkan apa yang Anda coba lakukan dengan data Anda.

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

Sepertinya kita mengalami kesalahan yang sama! Tapi itu berubah df_q seperti yang kami harapkan! Ini karena df_q adalah potongan dari df jadi, meskipun kita menggunakan .loc [] df_q pandas memperingatkan kita bahwa itu tidak akan menyebarkan perubahan menjadi df . Untuk menghindari hal ini, kita perlu lebih eksplisit dan mengatakan bahwa df_q adalah kerangka datanya sendiri, terpisah dari df dengan menyatakannya secara eksplisit.

Mari kita mulai kembali dari df_q tetapi gunakan .copy() kali ini.

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

Ini berfungsi tanpa kesalahan karena kami telah memberi tahu panda bahwa df_q terpisah dari df

Jika Anda sebenarnya ingin perubahan ini ke df_c menyebar hingga df poin lain sepenuhnya dan akan menjawab jika Anda mau.

@CRiddler Hebat, terima kasih !
Seperti yang Anda sebutkan, dirantai .loc tidak pernah memberikan hasil yang tidak diharapkan. Seperti yang saya pahami, .copy() memastikan Pandas yang kami perlakukan dipilih df_sliced_once sebagai objek terpisah dan tidak bermaksud untuk mengubah awal df . Harap perbaiki jika saya campur aduk.

Dokumentasinya ada di sini http://pandas.pydata.org/pandas-docs/stable/indexing.html#returning -a-view-versus-a-copy dan @CRiddler punya inplace sama sekali.

Jika Anda sebenarnya ingin perubahan ini ke df_c menyebar hingga df poin lain sepenuhnya dan akan menjawab jika Anda mau.

@ CRiddler Terima kasih, jawaban Anda lebih baik daripada yang ada di Stack Overflow, bisakah Anda menambahkan ketika Anda ingin menyebarkan ke kerangka data awal atau memberikan indikasi bagaimana hal itu dilakukan?

@persep Secara umum saya tidak suka mengubah masalah menjadi utas stackoverflow untuk mendapatkan bantuan, tetapi tampaknya masalah ini telah mendapat sedikit perhatian sejak posting terakhir jadi saya akan melanjutkan dan memposting metode saya untuk menangani jenis masalah ini di panda. Saya biasanya melakukan ini dengan tidak men-subset kerangka data menjadi variabel terpisah, tetapi saya malah mengubah topeng menjadi variabel- kemudian menggabungkan topeng sesuai kebutuhan dan menetapkan nilai berdasarkan topeng tersebut untuk memastikan perubahan terjadi dalam kerangka data asli, dan bukan pada beberapa salinan yang beredar .

Data asli:

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

Ingatlah bahwa membuat kerangka data sementara TIDAK akan menyebarkan perubahan
Seperti yang ditunjukkan pada contoh sebelumnya, ini membuat perubahan hanya menjadi df_q dan memunculkan peringatan pandas (tidak disalin / ditempel di sini). DAN TIDAK menyebarkan perubahan apa pun ke 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

Sepengetahuan saya, tidak ada cara untuk menggunakan kode yang sama seperti di atas dan memaksa perubahan untuk disebarkan kembali ke kerangka data asli.

Namun, jika kita sedikit mengubah pemikiran kita dan bekerja dengan topeng alih-alih subset penuh, kita dapat mencapai hasil yang diinginkan. Meskipun ini tidak selalu "menyebarkan" perubahan ke dataframe asli dari subset, kami memastikan bahwa setiap perubahan yang kami lakukan terjadi pada dataframe asli df . Untuk melakukan ini, kita membuat topeng terlebih dahulu, lalu menerapkannya ketika kita ingin membuat perubahan ke subset 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

Terakhir, jika kami ingin melihat seperti apa df_q, kami selalu dapat membuat subset dari dataframe asli menggunakan q_mask

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

Meskipun ini belum tentu "menyebarkan" perubahan dari df_q menjadi df kami mencapai hasil yang sama. Propagasi yang sebenarnya perlu dilakukan secara eksplisit dan akan kurang efisien daripada hanya bekerja dengan topeng.

@CRiddler Terima kasih, Anda telah sangat membantu

Apakah halaman ini membantu?
0 / 5 - 0 peringkat