Latar Belakang
Saya baru saja memutakhirkan Panda saya dari 0,11 ke 0,13.0rc1. Sekarang, aplikasi tersebut mengeluarkan banyak peringatan baru. Salah satunya seperti ini:
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
Saya ingin tahu apa artinya sebenarnya? Apakah saya perlu mengubah sesuatu?
Bagaimana saya harus menangguhkan peringatan jika saya bersikeras untuk menggunakan quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
?
Fungsi yang memberikan kesalahan
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
Lebih banyak pesan kesalahan
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])
df.set_value
, dokumentasi di sini - pandas.pydata.org/pandas-docs/stable/generated/…df.set_value
telah ditinggalkan. Panda sekarang merekomendasikan untuk digunakan.at[]
atau.iat[]
sebagai gantinya. docs here pandas.pydata.org/pandas-docs/stable/generated/…option_context
sini: pandas.pydata.org/pandas-docs/stable/user_guide/options.html , gunakan sebagaiwith pd.option_context("mode.chained_assignment", None): [...]
Jawaban:
Itu
SettingWithCopyWarning
dibuat untuk menandai tugas yang berpotensi membingungkan "dirantai", seperti berikut ini, yang tidak selalu berfungsi seperti yang diharapkan, terutama ketika pilihan pertama mengembalikan salinan . [lihat GH5390 dan GH5597 untuk diskusi latar belakang.]Peringatan itu menawarkan saran untuk menulis ulang sebagai berikut:
Namun, ini tidak sesuai dengan penggunaan Anda, yang setara dengan:
Meskipun jelas bahwa Anda tidak peduli tentang penulisan yang membuatnya kembali ke bingkai asli (karena Anda menimpa referensi untuk itu), sayangnya pola ini tidak dapat dibedakan dari contoh tugas berantai pertama. Karena itu peringatan (false positive). Potensi untuk false positive dibahas dalam dokumen pengindeksan , jika Anda ingin membaca lebih lanjut. Anda dapat dengan aman menonaktifkan peringatan baru ini dengan tugas berikut.
sumber
.ix
, ditingkatkan.iloc
, dll.) Pasti dapat dilihat sebagai "cara utama" tanpa memperingatkan semua orang tanpa henti tentang cara lain. Alih-alih biarkan mereka menjadi orang dewasa dan jika mereka ingin melakukan tugas dirantai, maka jadilah itu. Lagi pula, dua sen saya. Seseorang melihat komentar yang tidak puas dari para devs Pandas di sini sering ketika tugas dirantai akan bekerja untuk memecahkan masalah, tetapi tidak akan dianggap sebagai cara "utama" untuk melakukannya.pd.options.mode.chained_assignment = None
telah menghasilkan kode saya berjalan sekitar 6 kali lebih cepat. Adakah yang mengalami hasil serupa?Posting ini dimaksudkan untuk pembaca yang,
Mendirikan
Apa itu
SettingWithCopyWarning
?Untuk mengetahui bagaimana menangani peringatan ini, penting untuk memahami apa artinya dan mengapa hal itu diangkat.
Saat memfilter DataFrames, dimungkinkan mengiris / mengindeks bingkai untuk mengembalikan tampilan , atau salinan , tergantung pada tata letak internal dan berbagai detail implementasi. "Tampilan" adalah, seperti istilah yang disarankan, tampilan ke data asli, sehingga memodifikasi tampilan dapat mengubah objek asli. Di sisi lain, "salinan" adalah replikasi data dari aslinya, dan memodifikasi salinan tidak berpengaruh pada aslinya.
Seperti disebutkan oleh jawaban lain,
SettingWithCopyWarning
itu dibuat untuk menandai operasi "tugas dirantai". Pertimbangkandf
dalam pengaturan di atas. Misalkan Anda ingin memilih semua nilai dalam kolom "B" di mana nilai dalam kolom "A" adalah> 5. Panda memungkinkan Anda untuk melakukan ini dengan cara yang berbeda, beberapa lebih benar daripada yang lain. Sebagai contoh,Dan,
Ini mengembalikan hasil yang sama, jadi jika Anda hanya membaca nilai-nilai ini, tidak ada bedanya. Jadi, apa masalahnya? Masalah dengan tugas berantai, adalah bahwa umumnya sulit untuk memprediksi apakah tampilan atau salinan dikembalikan, jadi ini sebagian besar menjadi masalah ketika Anda mencoba untuk menetapkan nilai kembali. Untuk membangun contoh sebelumnya, pertimbangkan bagaimana kode ini dijalankan oleh penerjemah:
Dengan satu
__setitem__
panggilan kedf
. OTOH, pertimbangkan kode ini:Sekarang, tergantung pada apakah
__getitem__
mengembalikan tampilan atau salinan,__setitem__
operasi mungkin tidak berfungsi .Secara umum, Anda harus menggunakan
loc
untuk penugasan berbasis-label, daniloc
untuk penugasan berbasis integer / positional, karena spec menjamin bahwa mereka selalu beroperasi pada yang asli. Selain itu, untuk mengatur sel tunggal, Anda harus menggunakanat
daniat
.Lebih banyak dapat ditemukan dalam dokumentasi .
Katakan saja padaku bagaimana menekan peringatan itu!
Pertimbangkan operasi sederhana pada kolom "A" di
df
. Memilih "A" dan membaginya dengan 2 akan memunculkan peringatan, tetapi operasi akan berhasil.Ada beberapa cara untuk langsung membungkam peringatan ini:
Membuat
deepcopy
Ganti
pd.options.mode.chained_assignment
Dapat diatur untuk
None
,"warn"
, atau"raise"
."warn"
adalah standarnya.None
akan sepenuhnya menekan peringatan, dan"raise"
akan melemparSettingWithCopyError
, mencegah operasi lewat.@Peter Cotton dalam komentar, muncul dengan cara yang baik untuk tidak mengubah mode secara intrusi (dimodifikasi dari intisari ini ) menggunakan manajer konteks, untuk mengatur mode hanya selama diperlukan, dan mengatur ulang kembali ke keadaan asli saat selesai.
Penggunaannya adalah sebagai berikut:
Atau, untuk meningkatkan pengecualian
"Masalah XY": Apa yang saya lakukan salah?
Sering kali, pengguna berusaha mencari cara untuk menekan pengecualian ini tanpa sepenuhnya memahami mengapa hal itu muncul sejak awal. Ini adalah contoh yang baik dari masalah XY , di mana pengguna berusaha memecahkan masalah "Y" yang sebenarnya merupakan gejala dari masalah yang berakar lebih dalam "X". Pertanyaan akan diajukan berdasarkan masalah umum yang menemui peringatan ini, dan solusi kemudian akan disajikan.
Cara yang salah untuk melakukan ini:
Cara yang benar menggunakan
loc
:Anda dapat menggunakan salah satu metode berikut untuk melakukan ini.
Ini sebenarnya mungkin karena kode lebih tinggi di dalam pipa Anda. Apakah Anda membuat
df2
dari sesuatu yang lebih besar, seperti? Dalam hal ini, pengindeksan boolean akan mengembalikan tampilan, sehingga
df2
akan referensi aslinya. Apa yang Anda akan perlu lakukan adalah menetapkandf2
untuk copy :Ini karena
df2
harus dibuat sebagai tampilan dari beberapa operasi pemotongan lain, sepertiSolusi di sini adalah baik membuat
copy()
daridf
, atau penggunaanloc
, seperti sebelumnya.sumber
Secara umum intinya
SettingWithCopyWarning
adalah untuk menunjukkan kepada pengguna (dan terutama pengguna baru) bahwa mereka dapat beroperasi pada salinan dan bukan yang asli seperti yang mereka pikirkan. Ada yang positif palsu (TKI jika Anda tahu apa yang Anda lakukan bisa ok ). Salah satu kemungkinan adalah hanya untuk mematikan (secara default memperingatkan ) peringatan sebagai @Garrett menyarankan.Berikut ini pilihan lain:
Anda dapat mengatur
is_copy
tanda untukFalse
, yang secara efektif akan mematikan tanda centang, untuk objek itu :Jika Anda menyalin secara eksplisit maka tidak ada peringatan lebih lanjut yang akan terjadi:
Kode yang ditunjukkan OP di atas, walaupun sah, dan mungkin sesuatu yang saya lakukan juga, secara teknis merupakan kasus untuk peringatan ini, dan bukan positif palsu. Cara lain untuk tidak memiliki peringatan adalah dengan melakukan operasi seleksi via
reindex
, misAtau,
sumber
0.16
saya melihat lebih banyak positif palsu, masalah dengan false positive adalah orang belajar untuk mengabaikannya, meskipun terkadang itu sah.undefined
perilaku. Jika ada kesalahan maka itu harus (kemudian untuk menghindari jebakan dilihatC
), karenaapi
dibekukan perilaku peringatan saat ini masuk akal untuk kompatibilitas mundur. Dan saya akan membuat mereka membuang untuk menangkap mereka sebagai kesalahan dalam kode produksi saya (warnings.filterwarnings('error', r'SettingWithCopyWarning
). Juga saran untuk menggunakan.loc
kadang-kadang juga tidak membantu (jika ada dalam kelompok).Peringatan salinan bingkai data Pandas
Ketika Anda pergi dan melakukan sesuatu seperti ini:
pandas.ix
dalam hal ini mengembalikan bingkai data baru yang berdiri sendiri.Nilai apa pun yang Anda putuskan untuk diubah dalam kerangka data ini, tidak akan mengubah kerangka data asli.
Inilah yang mencoba memperingatkan panda tentang Anda.
Mengapa
.ix
itu ide yang burukThe
.ix
objek mencoba untuk melakukan lebih dari satu hal, dan untuk siapa saja yang telah membaca apa-apa tentang kode yang bersih, ini adalah bau yang kuat.Dengan kerangka data ini:
Dua perilaku:
Perilaku satu:
dfcopy
sekarang merupakan kerangka data yang berdiri sendiri. Mengubahnya tidak akan berubahdf
Perilaku dua: Ini mengubah kerangka data asli.
Gunakan
.loc
sebagai gantinyaPara pengembang panda menyadari bahwa
.ix
objek itu sangat berbau [spekulatif] dan dengan demikian menciptakan dua objek baru yang membantu dalam aksesi dan penugasan data. (Yang lainnya.iloc
).loc
lebih cepat, karena tidak mencoba membuat salinan data..loc
dimaksudkan untuk memodifikasi kerangka data yang ada di tempat Anda, yang lebih hemat memori..loc
dapat diprediksi, ia memiliki satu perilaku.Solusinya
Apa yang Anda lakukan dalam contoh kode Anda adalah memuat file besar dengan banyak kolom, kemudian memodifikasinya menjadi lebih kecil.
The
pd.read_csv
Fungsi dapat membantu Anda keluar dengan banyak ini dan juga membuat pemuatan file jauh lebih cepat.Jadi, bukannya melakukan ini
Melakukan hal ini
Ini hanya akan membaca kolom yang Anda minati, dan memberi nama dengan benar. Tidak perlu menggunakan
.ix
objek jahat untuk melakukan hal-hal ajaib.sumber
.iloc
. Ini adalah dua metode utama untuk mengindeks struktur data panda. Baca lebih lanjut di dokumentasi.Di sini saya menjawab pertanyaan secara langsung. Bagaimana cara mengatasinya?
Buat
.copy(deep=False)
setelah Anda mengiris. Lihat pandas . DataFrame.copy .Tunggu, bukankah sepotong mengembalikan salinan? Lagipula, inilah yang ingin disampaikan oleh pesan peringatan? Baca jawaban panjangnya:
Ini memberi peringatan:
Ini tidak:
Kedua
df0
dandf1
yangDataFrame
objek, tetapi sesuatu tentang mereka berbeda yang memungkinkan panda untuk mencetak peringatan. Ayo cari tahu apa itu.Dengan menggunakan alat pilihan pilihan Anda, Anda akan melihat bahwa di luar beberapa alamat, satu-satunya perbedaan material adalah ini:
Metode yang memutuskan apakah akan memperingatkan adalah
DataFrame._check_setitem_copy
yang memeriksa_is_copy
. Jadi ini dia. Buatcopy
agar DataFrame Anda tidak_is_copy
.Peringatan itu menyarankan untuk digunakan
.loc
, tetapi jika Anda menggunakan.loc
pada frame itu_is_copy
, Anda akan tetap mendapatkan peringatan yang sama. Menyesatkan? Iya. Mengganggu? Anda bertaruh. Bermanfaat? Berpotensi, ketika tugas dirantai digunakan. Tapi itu tidak bisa dengan benar mendeteksi penugasan berantai dan mencetak peringatan tanpa pandang bulu.sumber
Topik ini sangat membingungkan dengan Panda. Untungnya, ia memiliki solusi yang relatif sederhana.
Masalahnya adalah bahwa tidak selalu jelas apakah operasi penyaringan data (misalnya loc) mengembalikan salinan atau tampilan DataFrame. Oleh karena itu, penggunaan lebih lanjut dari DataFrame yang disaring tersebut dapat membingungkan.
Solusi sederhananya adalah (kecuali jika Anda perlu bekerja dengan set data yang sangat besar):
Setiap kali Anda perlu memperbarui nilai apa pun, selalu pastikan bahwa Anda secara tidak sah menyalin DataFrame sebelum penugasan.
sumber
Untuk menghilangkan keraguan, solusi saya adalah membuat salinan slice yang dalam dan bukan salinan biasa. Ini mungkin tidak berlaku tergantung pada konteks Anda (Memori kendala / ukuran irisan, potensi penurunan kinerja - terutama jika salinan terjadi dalam satu lingkaran seperti yang terjadi pada saya, dll ...)
Agar lebih jelas, inilah peringatan yang saya terima:
Ilustrasi
Saya ragu bahwa peringatan itu dilemparkan karena kolom saya menjatuhkan salinan salinan. Meskipun tidak secara teknis mencoba menetapkan nilai dalam salinan slice, itu masih merupakan modifikasi dari salinan slice. Di bawah ini adalah langkah-langkah (sederhana) yang telah saya ambil untuk mengkonfirmasi kecurigaan itu, saya harap ini akan membantu kita yang mencoba memahami peringatan itu.
Contoh 1: menjatuhkan kolom pada dokumen asli memengaruhi salinan
Kami sudah tahu itu tapi ini pengingat yang sehat. Ini BUKAN tentang peringatan itu.
Dimungkinkan untuk menghindari perubahan yang dilakukan pada df1 untuk mempengaruhi df2
Contoh 2: menjatuhkan kolom pada salinan dapat memengaruhi yang asli
Ini sebenarnya menggambarkan peringatan itu.
Dimungkinkan untuk menghindari perubahan yang dilakukan pada df2 untuk mempengaruhi df1
Bersulang!
sumber
Ini seharusnya bekerja:
sumber
Beberapa mungkin hanya ingin menekan peringatan:
sumber
Jika Anda telah menetapkan slice ke variabel dan ingin mengatur menggunakan variabel seperti berikut ini:
Dan Anda tidak ingin menggunakan solusi Jeffs karena kondisi komputer Anda terlalu
df2
lama atau karena alasan lain, maka Anda dapat menggunakan yang berikut:df2.index.tolist()
mengembalikan indeks dari semua entri dalam df2, yang kemudian akan digunakan untuk mengatur kolom B dalam kerangka data asli.sumber
Bagi saya masalah ini terjadi pada <contoh berikut yang disederhanakan. Dan saya juga bisa menyelesaikannya (semoga dengan solusi yang benar):
kode lama dengan peringatan:
Ini dicetak peringatan untuk garis
old_row[field] = new_row[field]
Karena baris dalam metode update_row sebenarnya adalah tipe
Series
, saya mengganti baris dengan:yaitu metode untuk mengakses / mencari
Series
. Walaupun keduanya bekerja dengan baik dan hasilnya sama, dengan cara ini saya tidak harus menonaktifkan peringatan (= simpan untuk masalah pengindeksan berantai lainnya di tempat lain).Saya harap ini dapat membantu seseorang.
sumber
Anda dapat menghindari seluruh masalah seperti ini, saya percaya:
Menggunakan Assign. Dari dokumentasi : Tetapkan kolom baru ke DataFrame, mengembalikan objek baru (salinan) dengan semua kolom asli selain yang baru.
Lihat artikel Tom Augspurger tentang metode chaining di panda: https://tomaugspurger.github.io/method-chaining
sumber
Tindak lanjut pertanyaan / komentar pemula
Mungkin klarifikasi untuk pemula lain seperti saya (saya berasal dari R yang tampaknya bekerja sedikit berbeda di bawah tenda). Kode fungsional dan tampak tidak berbahaya berikut terus menghasilkan peringatan SettingWithCopy, dan saya tidak tahu mengapa. Saya telah membaca dan memahami dikeluarkan dengan "pengindeksan berantai", tetapi kode saya tidak mengandung:
Tapi kemudian, kemudian, sangat terlambat, saya melihat di mana fungsi plot () dipanggil:
Jadi "df" bukan bingkai data tetapi objek yang entah bagaimana mengingat bahwa itu dibuat dengan mengindeks bingkai data (jadi apakah itu tampilan?) Yang akan membuat baris dalam plot ()
setara dengan
yang merupakan pengindeksan berantai. Apakah saya benar?
Bagaimanapun,
memperbaikinya.
sumber
Karena pertanyaan ini sudah sepenuhnya dijelaskan dan didiskusikan dalam jawaban yang sudah ada, saya hanya akan memberikan
pandas
pendekatan yang rapi kepada manajer konteks menggunakanpandas.option_context
(tautan ke dokumen dan contoh ) - sama sekali tidak perlu membuat kelas khusus dengan semua metode dunder dan bel lainnya dan peluit.Pertama kode manajer konteks itu sendiri:
Lalu sebuah contoh:
Yang perlu diperhatikan adalah bahwa kedua pendekatan tidak memodifikasi
a
, yang agak mengejutkan bagi saya, dan bahkan salinan df dangkal dengan.copy(deep=False)
akan mencegah peringatan ini dinaikkan (sejauh yang saya mengerti salinan dangkal setidaknya harus memodifikasia
juga, tetapi tidak 't.pandas
sihir.).sumber
Saya telah mendapatkan masalah ini
.apply()
ketika menetapkan kerangka data baru dari kerangka data yang sudah ada sebelumnya di mana saya telah menggunakan.query()
metode ini. Contohnya:Akan mengembalikan kesalahan ini. Perbaikan yang tampaknya menyelesaikan kesalahan dalam kasus ini adalah dengan mengubah ini menjadi:
Namun, ini TIDAK efisien terutama ketika menggunakan kerangka data yang besar, karena harus membuat salinan baru.
Jika Anda menggunakan
.apply()
metode dalam menghasilkan kolom baru dan nilainya, perbaikan yang mengatasi kesalahan dan lebih efisien adalah dengan menambahkan.reset_index(drop=True)
:sumber