Tengo el siguiente marco de datos:
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
Exigir:
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
se deriva 2015-01-31
tomando value
de D
.
Entonces necesito usar el value
de C
para 2015-01-31
y se multiplican por el value
de A
el 2015-02-01
y añadir B
.
He intentado un apply
y un shift
uso if else
de esto da un error clave.
A
yB
?A
y columnaD
?apply()
dónde la función del usuario puede acceder a uno o más valores de la fila anterior como parte de su cálculo o al menos devolver un valor que luego se pasa 'a sí mismo' en la siguiente iteración. ¿No permitiría esto algunas ganancias de eficiencia en comparación con un bucle for?numba
suele ser una buena opción aquí.Respuestas:
Primero, cree el valor derivado:
df.loc[0, 'C'] = df.loc[0, 'D']
Luego, recorra las filas restantes y complete los valores calculados:
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
fuente
apply
con una función que hace el mismo cálculo que el bucle, pero detrás de escena esto también sería un bucle. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…C
?Dada una columna de números:
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
Puede hacer referencia a la fila anterior con 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
fuente
numba
Para cálculos recursivos que no son vectorizables,
numba
que utiliza compilación JIT y trabaja con objetos de nivel inferior, a menudo produce grandes mejoras de rendimiento. Solo necesita definir unfor
bucle regular y usar el decorador@njit
o (para versiones anteriores)@jit(nopython=True)
:Para un marco de datos de tamaño razonable, esto proporciona una mejora de rendimiento de ~ 30 veces en comparación con un
for
bucle normal :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
fuente
Aplicar la función recursiva en matrices numpy será más rápido que la respuesta actual.
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
Salida
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
fuente
Aunque ha pasado un tiempo desde que se hizo esta pregunta, publicaré mi respuesta con la esperanza de que ayude a alguien.
Descargo de responsabilidad: sé que esta solución no es estándar , pero creo que funciona bien.
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
Básicamente, usamos un
apply
from pandas y la ayuda de una variable global que realiza un seguimiento del valor calculado anterior.Comparación de tiempo con un
for
bucle: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 por bucle (media ± desviación estándar de 7 corridas, 1 bucle cada una)
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 por bucle (media ± desarrollo estándar de 7 corridas, 1 bucle cada una)
Entonces, 0.57 veces más rápido en promedio.
fuente
En general, la clave para evitar un bucle explícito sería unir (fusionar) 2 instancias del marco de datos en rowindex-1 == rowindex.
Entonces tendría un gran marco de datos que contiene filas de r y r-1, desde donde podría hacer una función df.apply ().
Sin embargo, la sobrecarga de crear un gran conjunto de datos puede contrarrestar los beneficios del procesamiento paralelo ...
HTH Martin
fuente