Estoy usando marcos de datos de Pandas y quiero crear una nueva columna en función de las columnas existentes. No he visto una buena discusión sobre la diferencia de velocidad entre df.apply()y np.vectorize(), así que pensé en preguntar aquí.
La apply()función Pandas es lenta. Por lo que medí (que se muestra a continuación en algunos experimentos), usar np.vectorize()es 25 veces más rápido (o más) que usar la función DataFrame apply(), al menos en mi MacBook Pro 2016. ¿Es este un resultado esperado y por qué?
Por ejemplo, supongamos que tengo el siguiente marco de datos con Nfilas:
N = 10
A_list = np.random.randint(1, 100, N)
B_list = np.random.randint(1, 100, N)
df = pd.DataFrame({'A': A_list, 'B': B_list})
df.head()
# A B
# 0 78 50
# 1 23 91
# 2 55 62
# 3 82 64
# 4 99 80
Supongamos además que quiero crear una nueva columna en función de las dos columnas Ay B. En el siguiente ejemplo, usaré una función simple divide(). Para aplicar la función, puedo usar df.apply()o np.vectorize():
def divide(a, b):
if b == 0:
return 0.0
return float(a)/b
df['result'] = df.apply(lambda row: divide(row['A'], row['B']), axis=1)
df['result2'] = np.vectorize(divide)(df['A'], df['B'])
df.head()
# A B result result2
# 0 78 50 1.560000 1.560000
# 1 23 91 0.252747 0.252747
# 2 55 62 0.887097 0.887097
# 3 82 64 1.281250 1.281250
# 4 99 80 1.237500 1.237500
Si aumento Na tamaños del mundo real como 1 millón o más, entonces observo que np.vectorize()es 25 veces más rápido o más que df.apply().
A continuación se muestra un código completo de evaluación comparativa:
import pandas as pd
import numpy as np
import time
def divide(a, b):
if b == 0:
return 0.0
return float(a)/b
for N in [1000, 10000, 100000, 1000000, 10000000]:
print ''
A_list = np.random.randint(1, 100, N)
B_list = np.random.randint(1, 100, N)
df = pd.DataFrame({'A': A_list, 'B': B_list})
start_epoch_sec = int(time.time())
df['result'] = df.apply(lambda row: divide(row['A'], row['B']), axis=1)
end_epoch_sec = int(time.time())
result_apply = end_epoch_sec - start_epoch_sec
start_epoch_sec = int(time.time())
df['result2'] = np.vectorize(divide)(df['A'], df['B'])
end_epoch_sec = int(time.time())
result_vectorize = end_epoch_sec - start_epoch_sec
print 'N=%d, df.apply: %d sec, np.vectorize: %d sec' % \
(N, result_apply, result_vectorize)
# Make sure results from df.apply and np.vectorize match.
assert(df['result'].equals(df['result2']))
Los resultados se muestran a continuación:
N=1000, df.apply: 0 sec, np.vectorize: 0 sec
N=10000, df.apply: 1 sec, np.vectorize: 0 sec
N=100000, df.apply: 2 sec, np.vectorize: 0 sec
N=1000000, df.apply: 24 sec, np.vectorize: 1 sec
N=10000000, df.apply: 262 sec, np.vectorize: 4 sec
Si np.vectorize()en general siempre es más rápido que df.apply(), ¿por qué np.vectorize()no se menciona más? Solo veo publicaciones de StackOverflow relacionadas con df.apply(), como:
los pandas crean una nueva columna basada en valores de otras columnas
¿Cómo uso la función 'aplicar' de Pandas a varias columnas?
Cómo aplicar una función a dos columnas del marco de datos de Pandas
np.vectorizees básicamente unforbucle de Python (es un método de conveniencia) yapplycon una lambda también está en tiempo de Python