Ada beberapa cara untuk memilih baris dari bingkai data panda:
- Pengindeksan boolean (
df[df['col'] == value
])
- Pengindeksan posisi (
df.iloc[...]
)
- Pengindeksan label (
df.xs(...)
)
df.query(...)
API
Di bawah ini saya tunjukkan contoh masing-masing, dengan saran kapan harus menggunakan teknik tertentu. Asumsikan kriteria kami adalah kolom 'A'
=='foo'
(Catatan tentang kinerja: Untuk setiap jenis basis, kita dapat menjaga hal-hal sederhana dengan menggunakan API panda atau kita dapat menjelajah di luar API, biasanya ke dalam numpy
, dan mempercepatnya.)
Pengaturan
Hal pertama yang kita perlukan adalah mengidentifikasi suatu kondisi yang akan bertindak sebagai kriteria kita untuk memilih baris. Kami akan mulai dengan kasus OP column_name == some_value
, dan menyertakan beberapa kasus penggunaan umum lainnya.
Meminjam dari @unutbu:
import pandas as pd, numpy as np
df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
'B': 'one one two three two two one three'.split(),
'C': np.arange(8), 'D': np.arange(8) * 2})
1. Pengindeksan Boolean
... Pengindeksan boolean membutuhkan menemukan nilai sebenarnya dari setiap 'A'
kolom baris yang sama 'foo'
, kemudian menggunakan nilai-nilai kebenaran untuk mengidentifikasi baris mana yang harus dipertahankan. Biasanya, kami akan menamai seri ini, array nilai kebenaran mask
,. Kami akan melakukannya di sini juga.
mask = df['A'] == 'foo'
Kami kemudian dapat menggunakan topeng ini untuk mengiris atau mengindeks bingkai data
df[mask]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Ini adalah salah satu cara paling sederhana untuk menyelesaikan tugas ini dan jika kinerja atau intuitifitas tidak menjadi masalah, ini harus menjadi metode yang Anda pilih. Namun, jika kinerja merupakan masalah, maka Anda mungkin ingin mempertimbangkan cara alternatif untuk membuatnya mask
.
2. Pengindeksan posisi
Pengindeksan posisi ( df.iloc[...]
) memiliki kasus penggunaannya, tetapi ini bukan salah satunya. Untuk mengidentifikasi di mana harus mengiris, pertama-tama kita perlu melakukan analisis boolean yang sama seperti yang kita lakukan di atas. Ini membuat kami melakukan satu langkah ekstra untuk menyelesaikan tugas yang sama.
mask = df['A'] == 'foo'
pos = np.flatnonzero(mask)
df.iloc[pos]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
3. Pengindeksan label
Pengindeksan label bisa sangat berguna, tetapi dalam hal ini, kami kembali melakukan lebih banyak pekerjaan tanpa manfaat
df.set_index('A', append=True, drop=False).xs('foo', level=1)
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
4. df.query()
API
pd.DataFrame.query
adalah cara yang sangat elegan / intuitif untuk melakukan tugas ini, tetapi seringkali lebih lambat. Namun , jika Anda memperhatikan timing di bawah ini, untuk data besar, kueri ini sangat efisien. Lebih dari pendekatan standar dan besarnya serupa sebagai saran terbaik saya.
df.query('A == "foo"')
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Preferensi saya adalah menggunakan Boolean
mask
Perbaikan yang sebenarnya dapat dilakukan dengan memodifikasi cara kami membuat Boolean
mask
.
mask
alternatif 1
Gunakan numpy
array yang mendasari dan lupakan overhead untuk membuat yang lainpd.Series
mask = df['A'].values == 'foo'
Saya akan menunjukkan tes waktu yang lebih lengkap di bagian akhir, tetapi lihat saja peningkatan kinerja yang kami dapatkan menggunakan kerangka data sampel. Pertama, kita melihat perbedaan dalam menciptakanmask
%timeit mask = df['A'].values == 'foo'
%timeit mask = df['A'] == 'foo'
5.84 µs ± 195 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
166 µs ± 4.45 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Mengevaluasi mask
dengan numpy
array adalah ~ 30 kali lebih cepat. Ini sebagian karena numpy
evaluasi sering lebih cepat. Ini juga sebagian karena kurangnya overhead yang diperlukan untuk membangun indeks dan pd.Series
objek yang sesuai .
Selanjutnya, kita akan melihat waktu untuk mengiris dengan yang satu mask
versus yang lain.
mask = df['A'].values == 'foo'
%timeit df[mask]
mask = df['A'] == 'foo'
%timeit df[mask]
219 µs ± 12.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
239 µs ± 7.03 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Keuntungan kinerja tidak diucapkan. Kita akan melihat apakah ini tahan terhadap pengujian yang lebih kuat.
mask
alternatif 2
Kita bisa merekonstruksi bingkai data juga. Ada peringatan besar ketika merekonstruksi kerangka data — Anda harus berhati-hati dtypes
ketika melakukannya!
Alih-alih df[mask]
kita akan melakukan ini
pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)
Jika bingkai data adalah tipe campuran, yang merupakan contoh kita, maka ketika kita mendapatkan df.values
array yang dihasilkan adalah dtype
object
dan akibatnya, semua kolom dari bingkai data baru akan menjadi dtype
object
. Dengan demikian membutuhkan astype(df.dtypes)
dan membunuh keuntungan kinerja potensial.
%timeit df[m]
%timeit pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)
216 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1.43 ms ± 39.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Namun, jika frame data bukan tipe campuran, ini adalah cara yang sangat berguna untuk melakukannya.
Diberikan
np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))
d1
A B C D E
0 0 2 7 3 8
1 7 0 6 8 6
2 0 2 0 4 9
3 7 3 2 4 3
4 3 6 7 7 4
5 5 3 7 5 9
6 8 7 6 4 7
7 6 2 6 6 5
8 2 8 7 5 8
9 4 7 6 1 5
%%timeit
mask = d1['A'].values == 7
d1[mask]
179 µs ± 8.73 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Melawan
%%timeit
mask = d1['A'].values == 7
pd.DataFrame(d1.values[mask], d1.index[mask], d1.columns)
87 µs ± 5.12 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Kami memotong waktu menjadi dua.
mask
alternatif 3
@unutbu juga menunjukkan kepada kita cara menggunakan pd.Series.isin
untuk memperhitungkan setiap elemen df['A']
dalam serangkaian nilai. Ini mengevaluasi ke hal yang sama jika set nilai kami adalah satu set nilai, yaitu 'foo'
. Tetapi juga menggeneralisasi untuk memasukkan set nilai yang lebih besar jika diperlukan. Ternyata, ini masih cukup cepat walaupun itu adalah solusi yang lebih umum. Satu-satunya kerugian nyata adalah pada intuisi bagi mereka yang tidak terbiasa dengan konsep tersebut.
mask = df['A'].isin(['foo'])
df[mask]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Namun, seperti sebelumnya, kita dapat memanfaatkan numpy
untuk meningkatkan kinerja tanpa mengorbankan apa pun. Kami akan gunakannp.in1d
mask = np.in1d(df['A'].values, ['foo'])
df[mask]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Waktu
saya akan menyertakan konsep lain yang disebutkan dalam posting lain juga untuk referensi.
Kode di bawah ini
Setiap kolom dalam tabel ini mewakili kerangka data panjang yang berbeda di mana kami menguji setiap fungsi. Setiap kolom menunjukkan waktu relatif yang diambil, dengan fungsi tercepat diberi indeks dasar 1.0
.
res.div(res.min())
10 30 100 300 1000 3000 10000 30000
mask_standard 2.156872 1.850663 2.034149 2.166312 2.164541 3.090372 2.981326 3.131151
mask_standard_loc 1.879035 1.782366 1.988823 2.338112 2.361391 3.036131 2.998112 2.990103
mask_with_values 1.010166 1.000000 1.005113 1.026363 1.028698 1.293741 1.007824 1.016919
mask_with_values_loc 1.196843 1.300228 1.000000 1.000000 1.038989 1.219233 1.037020 1.000000
query 4.997304 4.765554 5.934096 4.500559 2.997924 2.397013 1.680447 1.398190
xs_label 4.124597 4.272363 5.596152 4.295331 4.676591 5.710680 6.032809 8.950255
mask_with_isin 1.674055 1.679935 1.847972 1.724183 1.345111 1.405231 1.253554 1.264760
mask_with_in1d 1.000000 1.083807 1.220493 1.101929 1.000000 1.000000 1.000000 1.144175
Anda akan melihat bahwa waktu tercepat tampaknya dibagi antara mask_with_values
danmask_with_in1d
res.T.plot(loglog=True)
Fungsi
def mask_standard(df):
mask = df['A'] == 'foo'
return df[mask]
def mask_standard_loc(df):
mask = df['A'] == 'foo'
return df.loc[mask]
def mask_with_values(df):
mask = df['A'].values == 'foo'
return df[mask]
def mask_with_values_loc(df):
mask = df['A'].values == 'foo'
return df.loc[mask]
def query(df):
return df.query('A == "foo"')
def xs_label(df):
return df.set_index('A', append=True, drop=False).xs('foo', level=-1)
def mask_with_isin(df):
mask = df['A'].isin(['foo'])
return df[mask]
def mask_with_in1d(df):
mask = np.in1d(df['A'].values, ['foo'])
return df[mask]
Pengujian
res = pd.DataFrame(
index=[
'mask_standard', 'mask_standard_loc', 'mask_with_values', 'mask_with_values_loc',
'query', 'xs_label', 'mask_with_isin', 'mask_with_in1d'
],
columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
dtype=float
)
for j in res.columns:
d = pd.concat([df] * j, ignore_index=True)
for i in res.index:a
stmt = '{}(d)'.format(i)
setp = 'from __main__ import d, {}'.format(i)
res.at[i, j] = timeit(stmt, setp, number=50)
Penentuan Waktu Khusus
Melihat kasus khusus ketika kami memiliki satu non-objek dtype
untuk seluruh bingkai data.
Kode di bawah ini
spec.div(spec.min())
10 30 100 300 1000 3000 10000 30000
mask_with_values 1.009030 1.000000 1.194276 1.000000 1.236892 1.095343 1.000000 1.000000
mask_with_in1d 1.104638 1.094524 1.156930 1.072094 1.000000 1.000000 1.040043 1.027100
reconstruct 1.000000 1.142838 1.000000 1.355440 1.650270 2.222181 2.294913 3.406735
Ternyata, rekonstruksi tidak layak melewati beberapa ratus baris.
spec.T.plot(loglog=True)
Fungsi
np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))
def mask_with_values(df):
mask = df['A'].values == 'foo'
return df[mask]
def mask_with_in1d(df):
mask = np.in1d(df['A'].values, ['foo'])
return df[mask]
def reconstruct(df):
v = df.values
mask = np.in1d(df['A'].values, ['foo'])
return pd.DataFrame(v[mask], df.index[mask], df.columns)
spec = pd.DataFrame(
index=['mask_with_values', 'mask_with_in1d', 'reconstruct'],
columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
dtype=float
)
Pengujian
for j in spec.columns:
d = pd.concat([df] * j, ignore_index=True)
for i in spec.index:
stmt = '{}(d)'.format(i)
setp = 'from __main__ import d, {}'.format(i)
spec.at[i, j] = timeit(stmt, setp, number=50)