Mengubah subset baris dalam bingkai data panda

143

Asumsikan saya memiliki panda DataFrame dengan dua kolom, A dan B. Saya ingin memodifikasi DataFrame ini (atau membuat salinan) sehingga B selalu NaN setiap kali A adalah 0. Bagaimana saya mencapainya?

Saya mencoba yang berikut ini

df['A'==0]['B'] = np.nan

dan

df['A'==0]['B'].values.fill(np.nan)

tanpa keberhasilan.

Arthur B.
sumber
Jika Anda mencari solusi yang sangat cepat, gunakan NumPy whereseperti yang terlihat dalam solusi di bawah ini
Ted Petrou

Jawaban:

243

Gunakan .locuntuk pengindeksan berbasis label:

df.loc[df.A==0, 'B'] = np.nan

The df.A==0ekspresi menciptakan serangkaian boolean yang indeks baris, 'B'memilih kolom. Anda juga dapat menggunakan ini untuk mengubah subset kolom, misalnya:

df.loc[df.A==0, 'B'] = df.loc[df.A==0, 'B'] / 2

Saya tidak cukup tahu tentang internal panda untuk tahu persis mengapa itu bekerja, tetapi masalah dasarnya adalah bahwa kadang-kadang indeks ke DataFrame mengembalikan salinan hasil, dan kadang-kadang mengembalikan pandangan pada objek asli. Menurut dokumentasi di sini , perilaku ini tergantung pada perilaku numpy yang mendasarinya. Saya menemukan bahwa mengakses segala sesuatu dalam satu operasi (bukan [satu] [dua]) lebih mungkin berfungsi untuk pengaturan.

BrenBarn
sumber
Bagian kedua dari ini adalah jawaban yang bagus untuk pertanyaan yang bahkan tidak ditanyakan ;-) Saya bertanya-tanya apakah ini masih merupakan jawaban panda kanonik, khususnya b / c itu merupakan pelanggaran KERING yang jelas, meskipun saya menganggapnya ada di fakta perlu untuk melanggar KERING mengingat kendala panda internal? (Saya dapat memposting pertanyaan semacam ini, lebih terinci, tetapi ingin melihat apakah Anda memiliki jawaban cepat sebelum saya melakukannya)
JohnE
Bagaimana subset Dataframe yang tidak memiliki nama kolom, bagaimana subset df hanya dengan indeks? df.loc [df [0] == 0] tidak berfungsi ... Apa alternatifnya? Terima Kasih
amipro
89

Berikut ini dari panda docs pada pengindeksan lanjutan:

Bagian ini akan menjelaskan dengan tepat apa yang Anda butuhkan! Ternyata df.loc(seperti .ix telah ditinggalkan - seperti yang telah ditunjukkan banyak orang di bawah ini) dapat digunakan untuk mengiris / mending bingkai data. Dan. Dapat juga digunakan untuk mengatur sesuatu.

df.loc[selection criteria, columns I want] = value

Jadi jawaban Bren mengatakan 'temukan aku semua tempat di mana df.A == 0, pilih kolom Bdan atur ke np.nan'

badgley
sumber
2
Anda membuat hari saya. Penjelasan yang jelas.
TwinPenguins
1
Ya, entah bagaimana loc[selection criteria, columns I want]tetap melekat di benak Anda ...
EmE
29

Mulai dari panda 0,20 ix sudah usang . Cara yang benar adalah dengan menggunakan df.loc

di sini adalah contoh kerja

>>> import pandas as pd 
>>> import numpy as np 
>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN
>>> 

Penjelasan:

Seperti yang dijelaskan dalam dokumen di sini , pada .loc dasarnya berbasis label, tetapi juga dapat digunakan dengan array boolean .

Jadi, apa yang kami lakukan di atas berlaku df.loc[row_index, column_index]dengan:

  • Mengeksploitasi fakta yang locdapat menggunakan array boolean sebagai topeng yang memberi tahu panda bagian dari baris yang ingin kita ubahrow_index
  • Mengeksploitasi fakta locjuga berdasarkan label untuk memilih kolom menggunakan label 'B'dicolumn_index

Kita dapat menggunakan logika, kondisi atau operasi apa pun yang mengembalikan serangkaian boolean untuk membangun array boolean. Dalam contoh di atas, kami ingin semua rowsyang mengandung a 0, untuk itu kami dapat menggunakan df.A == 0, seperti yang Anda lihat dalam contoh di bawah ini, ini mengembalikan serangkaian boolean.

>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df 
   A  B
0  0  2
1  1  0
2  0  5
>>> df.A == 0 
0     True
1    False
2     True
Name: A, dtype: bool
>>> 

Kemudian, kami menggunakan array boolean di atas untuk memilih dan mengubah baris yang diperlukan:

>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN

Untuk informasi lebih lanjut, periksa dokumentasi pengindeksan lanjutan di sini .

Mohamed Ali JAMAOUI
sumber
11

Untuk peningkatan kecepatan besar, gunakan fungsi NumPy where.

Mempersiapkan

Buat DataFrame dua kolom dengan 100.000 baris dengan beberapa nol.

df = pd.DataFrame(np.random.randint(0,3, (100000,2)), columns=list('ab'))

Solusi cepat dengan numpy.where

df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)

Pengaturan waktu

%timeit df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)
685 µs ± 6.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.loc[df['a'] == 0, 'b'] = np.nan
3.11 ms ± 17.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy wheresekitar 4x lebih cepat

Ted Petrou
sumber
Saya ingin tahu tentang ini jadi saya mengujinya sendiri dan perbedaannya bahkan lebih besar menggunakan parameter lain. Numpy hampir 10 kali lebih cepat untuk mengganti 0s dengan integer daripada np.nan. Saya bertanya-tanya apa yang perlu waktu ekstra.
Alexander
Apakah perlu untuk menggunakan .valuesdi np.where(df.a.values == 0, np.nan, df.b.values)? Sepertinya np.where(df.a == 0, np.nan, df.b)juga berfungsi?
hsl
4

Untuk mengganti kolom multipel, ubah ke array numpy menggunakan .values:

df.loc[df.A==0, ['B', 'C']] = df.loc[df.A==0, ['B', 'C']].values / 2
Adrien Renaud
sumber