Saya memiliki dataframe berikut:
Index_Date A B C D
===============================
2015-01-31 10 10 Nan 10
2015-02-01 2 3 Nan 22
2015-02-02 10 60 Nan 280
2015-02-03 10 100 Nan 250
Memerlukan:
Index_Date A B C D
===============================
2015-01-31 10 10 10 10
2015-02-01 2 3 23 22
2015-02-02 10 60 290 280
2015-02-03 10 100 3000 250
Column C
berasal untuk 2015-01-31
dengan mengambil value
dari D
.
Kemudian saya perlu menggunakan value
dari C
untuk 2015-01-31
dan kalikan dengan value
dari A
pada 2015-02-01
dan menambahkan B
.
Saya telah mencoba apply
dan shift
menggunakan if else
oleh ini memberikan kesalahan kunci.
A
danB
?A
dan kolomD
?apply()
mana fungsi pengguna dapat mengakses satu atau beberapa nilai dari baris sebelumnya sebagai bagian dari penghitungannya atau setidaknya mengembalikan nilai yang kemudian diteruskan 'ke dirinya sendiri' pada iterasi berikutnya. Bukankah ini memungkinkan beberapa peningkatan efisiensi dibandingkan dengan loop for?numba
sering kali merupakan pilihan yang baik di sini.Jawaban:
Pertama, buat nilai turunan:
df.loc[0, 'C'] = df.loc[0, 'D']
Kemudian lakukan iterasi melalui baris yang tersisa dan isi nilai yang dihitung:
for i in range(1, len(df)): df.loc[i, 'C'] = df.loc[i-1, 'C'] * df.loc[i, 'A'] + df.loc[i, 'B'] Index_Date A B C D 0 2015-01-31 10 10 10 10 1 2015-02-01 2 3 23 22 2 2015-02-02 10 60 290 280
sumber
apply
fungsi yang melakukan kalkulasi yang sama seperti loop, tetapi di balik layar ini juga akan menjadi loop. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…C
?Diberikan kolom angka:
lst = [] cols = ['A'] for a in range(100, 105): lst.append([a]) df = pd.DataFrame(lst, columns=cols, index=range(5)) df A 0 100 1 101 2 102 3 103 4 104
Anda dapat mereferensikan baris sebelumnya dengan shift:
df['Change'] = df.A - df.A.shift(1) df A Change 0 100 NaN 1 101 1.0 2 102 1.0 3 103 1.0 4 104 1.0
sumber
numba
Untuk penghitungan rekursif yang tidak dapat vektor,
numba
yang menggunakan kompilasi JIT dan bekerja dengan objek tingkat yang lebih rendah, sering kali menghasilkan peningkatan kinerja yang besar. Anda hanya perlu menentukanfor
loop biasa dan menggunakan dekorator@njit
atau (untuk versi yang lebih lama)@jit(nopython=True)
:Untuk kerangka data ukuran yang wajar, ini memberikan peningkatan kinerja ~ 30x dibandingkan dengan
for
loop biasa :from numba import jit @jit(nopython=True) def calculator_nb(a, b, d): res = np.empty(d.shape) res[0] = d[0] for i in range(1, res.shape[0]): res[i] = res[i-1] * a[i] + b[i] return res df['C'] = calculator_nb(*df[list('ABD')].values.T) n = 10**5 df = pd.concat([df]*n, ignore_index=True) # benchmarking on Python 3.6.0, Pandas 0.19.2, NumPy 1.11.3, Numba 0.30.1 # calculator() is same as calculator_nb() but without @jit decorator %timeit calculator_nb(*df[list('ABD')].values.T) # 14.1 ms per loop %timeit calculator(*df[list('ABD')].values.T) # 444 ms per loop
sumber
Menerapkan fungsi rekursif pada array numpy akan lebih cepat daripada jawaban saat ini.
df = pd.DataFrame(np.repeat(np.arange(2, 6),3).reshape(4,3), columns=['A', 'B', 'D']) new = [df.D.values[0]] for i in range(1, len(df.index)): new.append(new[i-1]*df.A.values[i]+df.B.values[i]) df['C'] = new
Keluaran
A B D C 0 1 1 1 1 1 2 2 2 4 2 3 3 3 15 3 4 4 4 64 4 5 5 5 325
sumber
Meskipun sudah lama sejak pertanyaan ini diajukan, saya akan memposting jawaban saya dengan harapan dapat membantu seseorang.
Penafian: Saya tahu solusi ini tidak standar , tetapi menurut saya solusi ini berfungsi dengan baik.
import pandas as pd import numpy as np data = np.array([[10, 2, 10, 10], [10, 3, 60, 100], [np.nan] * 4, [10, 22, 280, 250]]).T idx = pd.date_range('20150131', end='20150203') df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx) df A B C D ================================= 2015-01-31 10 10 NaN 10 2015-02-01 2 3 NaN 22 2015-02-02 10 60 NaN 280 2015-02-03 10 100 NaN 250 def calculate(mul, add): global value value = value * mul + add return value value = df.loc['2015-01-31', 'D'] df.loc['2015-01-31', 'C'] = value df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1) df A B C D ================================= 2015-01-31 10 10 10 10 2015-02-01 2 3 23 22 2015-02-02 10 60 290 280 2015-02-03 10 100 3000 250
Jadi pada dasarnya kami menggunakan a
apply
from pandas dan bantuan variabel global yang melacak nilai yang dihitung sebelumnya.Perbandingan waktu dengan
for
loop:data = np.random.random(size=(1000, 4)) idx = pd.date_range('20150131', end='20171026') df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx) df.C = np.nan df.loc['2015-01-31', 'C'] = df.loc['2015-01-31', 'D'] %%timeit for i in df.loc['2015-02-01':].index.date: df.loc[i, 'C'] = df.loc[(i - pd.DateOffset(days=1)).date(), 'C'] * df.loc[i, 'A'] + df.loc[i, 'B']
3,2 s ± 114 ms per loop (rata-rata ± std. Dev. Dari 7 run, masing-masing 1 loop)
data = np.random.random(size=(1000, 4)) idx = pd.date_range('20150131', end='20171026') df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx) df.C = np.nan def calculate(mul, add): global value value = value * mul + add return value value = df.loc['2015-01-31', 'D'] df.loc['2015-01-31', 'C'] = value %%timeit df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1)
1,82 s ± 64,4 ms per loop (rata-rata ± std. Dev. Dari 7 run, masing-masing 1 loop)
Jadi rata-rata 0,57 kali lebih cepat.
sumber
Secara umum, kunci untuk menghindari pengulangan eksplisit adalah menggabungkan (menggabungkan) 2 instance dataframe pada rowindex-1 == rowindex.
Kemudian Anda akan memiliki kerangka data besar yang berisi baris r dan r-1, dari mana Anda dapat melakukan fungsi df.apply ().
Namun, overhead pembuatan kumpulan data besar dapat mengimbangi manfaat pemrosesan paralel ...
HTH Martin
sumber