El uso de la indexación booleana con datos de muestra funcionó bien, pero a medida que aumenté el tamaño de los datos, el tiempo de cálculo se está exponencialmente largo (ejemplo a continuación). ¿Alguien sabe una manera de aumentar la velocidad de ese indexador booleano en particular?

import pandas as pd
import numpy as np
a = pd.date_range('2019-01-01', '2019-12-31',freq = '1T')
b = np.random.normal(size = len(a), loc = 50)
c = pd.DataFrame(index = a, data = b, columns = ['price'])

1500 filas:

z = c.head(1500)
z[z.index.map(lambda x : 8 <= x.hour <= 16 ) & z.index.map(lambda x : x.weekday() < 5 )]

CPU times: user 149 ms, sys: 8.71 ms, total: 158 ms Wall time: 157 ms

5000 filas:

z = c.head(5000)
z[z.index.map(lambda x : 8 <= x.hour <= 16 ) & z.index.map(lambda x : x.weekday() < 5 )]

CPU times: user 14.1 s, sys: 9.07 s, total: 23.2 s Wall time: 23.2 s

Intenté con z = c.head(10000), pero me está tomando más de 15 minutos calcular, así que paré ... El tamaño de los datos en los que quiero usar ese indexador es de aproximadamente 30000 filas.

1
jim basquiat 6 ene. 2020 a las 02:19

2 respuestas

La mejor respuesta

La razón por la que esto no funciona rápido es porque realiza una asignación con una expresión lambda, lo que significa que para cada elemento, se realizará una llamada a la función. Por lo general, esta no es una buena idea si desea procesar datos en forma masiva. Puede acelerar esto con:

hour = z.index.hour
z[(8 <= hour) & (hour <= 16) & (z.index.weekday < 5)]

Con z = c (un total de 524'161 filas), obtenemos los siguientes tiempos:

>>> z = c
>>> timeit(lambda: z[(8 <= z.index.hour) & (z.index.hour <= 16) & (z.index.weekday < 5)], number=100)
11.825318349001464

Entonces esto se ejecuta en un total de ~ 118 milisegundos por ejecución.

Cuando usamos las primeras 5'000 filas, obtenemos:

>>> z = c.head(5000)
>>> timeit(lambda: z[(8 <= z.index.hour) & (z.index.hour <= 16) & (z.index.weekday < 5)], number=100)
0.1542488380218856

Entonces esto se ejecuta en 1.5 milisegundos por ejecución.

1
Willem Van Onsem 5 ene. 2020 a las 23:34

Tanto z.index.map(lambda x : 8 <= x.hour <= 16) como z.index.map(lambda x: x.weekday() < 5) se ejecutan casi al instante. El problema ocurre cuando los combina con el operador bit a bit y &.

Pd.Index.map devuelve otro objeto pd.Index. Y el operador & en los objetos Index realmente establece la intersección; no es "elemento sabio y". Si observa el resultado, verá que no es lo que espera, es 5000 True s. La razón por la que está tardando tanto es que estas comparaciones devuelven valores booleanos que, por supuesto, están duplicados y la intersección del índice falla en esa situación.

La forma correcta de manejar esto es, por supuesto, utilizando operaciones vectorizadas, pero si de alguna manera necesita comparar elementos de dos objetos pd.Index, puede hacerlo convirtiéndolos en matrices numpy:

res1 = z.index.map(lambda x : 8 <= x.hour <= 16 ).to_numpy()
res2 = z.index.map(lambda x : x.weekday() < 5 ).to_numpy()
z[res1 & res2]
2
ayhan 6 ene. 2020 a las 00:01