Saya telah memperhatikan kinerja yang sangat buruk saat menggunakan iterrow dari panda.
Apakah ini sesuatu yang dialami oleh orang lain? Apakah ini khusus untuk iterrow dan haruskah fungsi ini dihindari untuk data dengan ukuran tertentu (saya bekerja dengan 2-3 juta baris)?
Diskusi di GitHub ini membuat saya percaya bahwa ini disebabkan saat mencampurkan dtypes di dataframe, namun contoh sederhana di bawah ini menunjukkan bahwa itu ada bahkan ketika menggunakan satu dtype (float64). Ini membutuhkan 36 detik di mesin saya:
import pandas as pd
import numpy as np
import time
s1 = np.random.randn(2000000)
s2 = np.random.randn(2000000)
dfa = pd.DataFrame({'s1': s1, 's2': s2})
start = time.time()
i=0
for rowindex, row in dfa.iterrows():
i+=1
end = time.time()
print end - start
Mengapa operasi vektorisasi seperti diterapkan jauh lebih cepat? Saya membayangkan pasti ada beberapa perulangan baris demi baris yang terjadi di sana juga.
Saya tidak tahu bagaimana tidak menggunakan iterrow dalam kasus saya (ini akan saya simpan untuk pertanyaan mendatang). Oleh karena itu, saya akan sangat menghargai jika Anda secara konsisten dapat menghindari pengulangan ini. Saya membuat perhitungan berdasarkan data dalam kerangka data terpisah. Terima kasih!
--- Edit: versi sederhana dari apa yang ingin saya jalankan telah ditambahkan di bawah ---
import pandas as pd
import numpy as np
#%% Create the original tables
t1 = {'letter':['a','b'],
'number1':[50,-10]}
t2 = {'letter':['a','a','b','b'],
'number2':[0.2,0.5,0.1,0.4]}
table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)
#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=[0])
#%% Iterate through filtering relevant data, optimizing, returning info
for row_index, row in table1.iterrows():
t2info = table2[table2.letter == row['letter']].reset_index()
table3.ix[row_index,] = optimize(t2info,row['number1'])
#%% Define optimization
def optimize(t2info, t1info):
calculation = []
for index, r in t2info.iterrows():
calculation.append(r['number2']*t1info)
maxrow = calculation.index(max(calculation))
return t2info.ix[maxrow]
sumber
apply
TIDAK vektorisasi.iterrows
bahkan lebih buruk karena kotak semuanya (yaitu 'the perf diff withapply
). Anda sebaiknya hanya menggunakaniterrows
sangat sedikit situasi. IMHO tidak pernah. Tunjukkan apa yang sebenarnya Anda lakukaniterrows
.DatetimeIndex
keTimestamps
(diimplementasikan dalam ruang python), dan ini telah jauh ditingkatkan di master.Jawaban:
Umumnya,
iterrows
sebaiknya hanya digunakan dalam kasus yang sangat, sangat spesifik. Ini adalah urutan prioritas umum untuk kinerja berbagai operasi:1) vectorization 2) using a custom cython routine 3) apply a) reductions that can be performed in cython b) iteration in python space 4) itertuples 5) iterrows 6) updating an empty frame (e.g. using loc one-row-at-a-time)
Menggunakan rutinitas Cython khusus biasanya terlalu rumit, jadi mari kita lewati itu untuk saat ini.
1) Vektorisasi SELALU, SELALU pilihan pertama dan terbaik. Namun, ada sekumpulan kecil kasus (biasanya melibatkan kekambuhan) yang tidak dapat di-vektorisasi dengan cara yang jelas. Selanjutnya, pada yang bertubuh kecil
DataFrame
, mungkin lebih cepat menggunakan metode lain.3)
apply
biasanya dapat ditangani oleh iterator di ruang Cython. Ini ditangani secara internal oleh panda, meskipun bergantung pada apa yang terjadi di dalamapply
ekspresi. Misalnya,df.apply(lambda x: np.sum(x))
akan dieksekusi dengan cukup cepat, meskipun tentu saja,df.sum(1)
lebih baik. Namun sesuatu seperti itudf.apply(lambda x: x['b'] + 1)
akan dijalankan dalam ruang Python, dan akibatnya jauh lebih lambat.4)
itertuples
tidak mengotakkan data menjadi aSeries
. Itu hanya mengembalikan data dalam bentuk tupel.5)
iterrows
MELAKUKAN kotak data menjadi aSeries
. Kecuali Anda benar-benar membutuhkan ini, gunakan metode lain.6) Memperbarui bingkai kosong satu baris satu per satu. Saya telah melihat metode ini menggunakan JAUH terlalu banyak. Sejauh ini, ini paling lambat. Ini mungkin tempat yang umum (dan cukup cepat untuk beberapa struktur python), tetapi a
DataFrame
melakukan cukup banyak pemeriksaan pada pengindeksan, jadi ini akan selalu sangat lambat untuk memperbarui baris dalam satu waktu. Jauh lebih baik untuk membuat struktur baru danconcat
.sumber
itertuples
lebih cepat dariapply
:(pd.DataFrame.apply
seringkali lebih lambat dariitertuples
. Selain itu, ada baiknya mempertimbangkan pemahaman daftar``map
dinamai dengan buruknp.vectorize
dannumba
(tanpa urutan tertentu) untuk kalkulasi yang tidak dapat vektor , misalnya lihat jawaban ini .Operasi vektor di Numpy dan pandas jauh lebih cepat daripada operasi skalar di vanilla Python karena beberapa alasan:
Pencarian jenis diamortisasi : Python adalah bahasa yang diketik secara dinamis, jadi ada overhead waktu proses untuk setiap elemen dalam larik. Namun, Numpy (dan karenanya pandas) melakukan kalkulasi dalam C (seringkali melalui Cython). Jenis larik ditentukan hanya di awal iterasi; tabungan ini sendiri adalah salah satu kemenangan terbesar.
Caching yang lebih baik : Iterasi melalui array C ramah-cache dan karenanya sangat cepat. DataFrame pandas adalah "tabel berorientasi kolom", yang berarti bahwa setiap kolom sebenarnya hanya sebuah array. Jadi tindakan asli yang dapat Anda lakukan pada DataFrame (seperti menjumlahkan semua elemen dalam kolom) akan memiliki beberapa cache yang terlewat.
Lebih banyak peluang untuk paralelisme : Array C sederhana dapat dioperasikan melalui instruksi SIMD. Beberapa bagian dari Numpy mengaktifkan SIMD, tergantung pada CPU dan proses instalasi Anda. Manfaat paralelisme tidak akan sedramatis pengetikan statis dan caching yang lebih baik, tetapi ini masih merupakan kemenangan yang solid.
Moral dari cerita: gunakan operasi vektor di Numpy dan pandas. Mereka lebih cepat daripada operasi skalar dengan Python karena alasan sederhana bahwa operasi ini persis seperti yang ditulis oleh programmer C dengan tangan. (Kecuali bahwa gagasan array jauh lebih mudah dibaca daripada loop eksplisit dengan instruksi SIMD yang disematkan.)
sumber
Inilah cara untuk menyelesaikan masalah Anda. Ini semua vektorisasi.
In [58]: df = table1.merge(table2,on='letter') In [59]: df['calc'] = df['number1']*df['number2'] In [60]: df Out[60]: letter number1 number2 calc 0 a 50 0.2 10 1 a 50 0.5 25 2 b -10 0.1 -1 3 b -10 0.4 -4 In [61]: df.groupby('letter')['calc'].max() Out[61]: letter a 25 b -1 Name: calc, dtype: float64 In [62]: df.groupby('letter')['calc'].idxmax() Out[62]: letter a 1 b 2 Name: calc, dtype: int64 In [63]: df.loc[df.groupby('letter')['calc'].idxmax()] Out[63]: letter number1 number2 calc 1 a 50 0.5 25 2 b -10 0.1 -1
sumber
Pilihan lainnya adalah menggunakan
to_records()
, yang lebih cepat dari keduanyaitertuples
daniterrows
.Namun untuk kasus Anda, ada banyak ruang untuk jenis perbaikan lainnya.
Ini versi terakhir saya yang dioptimalkan
def iterthrough(): ret = [] grouped = table2.groupby('letter', sort=False) t2info = table2.to_records() for index, letter, n1 in table1.to_records(): t2 = t2info[grouped.groups[letter].values] # np.multiply is in general faster than "x * y" maxrow = np.multiply(t2.number2, n1).argmax() # `[1:]` removes the index column ret.append(t2[maxrow].tolist()[1:]) global table3 table3 = pd.DataFrame(ret, columns=('letter', 'number2'))
Tes benchmark:
-- iterrows() -- 100 loops, best of 3: 12.7 ms per loop letter number2 0 a 0.5 1 b 0.1 2 c 5.0 3 d 4.0 -- itertuple() -- 100 loops, best of 3: 12.3 ms per loop -- to_records() -- 100 loops, best of 3: 7.29 ms per loop -- Use group by -- 100 loops, best of 3: 4.07 ms per loop letter number2 1 a 0.5 2 b 0.1 4 c 5.0 5 d 4.0 -- Avoid multiplication -- 1000 loops, best of 3: 1.39 ms per loop letter number2 0 a 0.5 1 b 0.1 2 c 5.0 3 d 4.0
Kode lengkap:
import pandas as pd import numpy as np #%% Create the original tables t1 = {'letter':['a','b','c','d'], 'number1':[50,-10,.5,3]} t2 = {'letter':['a','a','b','b','c','d','c'], 'number2':[0.2,0.5,0.1,0.4,5,4,1]} table1 = pd.DataFrame(t1) table2 = pd.DataFrame(t2) #%% Create the body of the new table table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=table1.index) print('\n-- iterrows() --') def optimize(t2info, t1info): calculation = [] for index, r in t2info.iterrows(): calculation.append(r['number2'] * t1info) maxrow_in_t2 = calculation.index(max(calculation)) return t2info.loc[maxrow_in_t2] #%% Iterate through filtering relevant data, optimizing, returning info def iterthrough(): for row_index, row in table1.iterrows(): t2info = table2[table2.letter == row['letter']].reset_index() table3.iloc[row_index,:] = optimize(t2info, row['number1']) %timeit iterthrough() print(table3) print('\n-- itertuple() --') def optimize(t2info, n1): calculation = [] for index, letter, n2 in t2info.itertuples(): calculation.append(n2 * n1) maxrow = calculation.index(max(calculation)) return t2info.iloc[maxrow] def iterthrough(): for row_index, letter, n1 in table1.itertuples(): t2info = table2[table2.letter == letter] table3.iloc[row_index,:] = optimize(t2info, n1) %timeit iterthrough() print('\n-- to_records() --') def optimize(t2info, n1): calculation = [] for index, letter, n2 in t2info.to_records(): calculation.append(n2 * n1) maxrow = calculation.index(max(calculation)) return t2info.iloc[maxrow] def iterthrough(): for row_index, letter, n1 in table1.to_records(): t2info = table2[table2.letter == letter] table3.iloc[row_index,:] = optimize(t2info, n1) %timeit iterthrough() print('\n-- Use group by --') def iterthrough(): ret = [] grouped = table2.groupby('letter', sort=False) for index, letter, n1 in table1.to_records(): t2 = table2.iloc[grouped.groups[letter]] calculation = t2.number2 * n1 maxrow = calculation.argsort().iloc[-1] ret.append(t2.iloc[maxrow]) global table3 table3 = pd.DataFrame(ret) %timeit iterthrough() print(table3) print('\n-- Even Faster --') def iterthrough(): ret = [] grouped = table2.groupby('letter', sort=False) t2info = table2.to_records() for index, letter, n1 in table1.to_records(): t2 = t2info[grouped.groups[letter].values] maxrow = np.multiply(t2.number2, n1).argmax() # `[1:]` removes the index column ret.append(t2[maxrow].tolist()[1:]) global table3 table3 = pd.DataFrame(ret, columns=('letter', 'number2')) %timeit iterthrough() print(table3)
Versi terakhir hampir 10x lebih cepat dari kode aslinya. Strateginya adalah:
groupby
untuk menghindari perbandingan nilai yang berulang.to_records
untuk mengakses objek numpy.records mentah.sumber
Ya, Pandas itertuples () lebih cepat dari iterrows (). Anda dapat merujuk dokumentasi: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iterrows.html
"Untuk mempertahankan dtypes saat melakukan iterasi pada baris, lebih baik menggunakan itertuple () yang mengembalikan namtuple dari nilai dan yang umumnya lebih cepat daripada iterrow."
sumber
Detail dalam video ini
Tolok ukur
sumber