vacia una tamaƱo matrices lista len funcion for elementos crear agregar python pandas statistics

una - matrices en python



Contando el valor positivo consecutivo en la matriz de Python (4)

¿Por qué la obsesión con la forma ultra-pitónica de hacer las cosas? legibilidad + eficiencia triunfa sobre "leet hackerz style".

Simplemente lo haría así:

a = [0,0,1,1,1,0,0,1,0,1,1] b = [0,0,0,0,0,0,0,0,0,0,0] for i in range(len(a)): if a[i] == 1: b[i] = b[i-1] + 1 else: b[i] = 0

Estoy tratando de contar días consecutivos en los datos de retorno de capital, por lo que si un día positivo es 1 y negativo es 0, una lista y=[0,0,1,1,1,0,0,1,0,1,1] debe devolver z=[0,0,1,2,3,0,0,1,0,1,2] .

He llegado a una solución que es clara en términos de número de líneas de código, pero es muy lenta:

import pandas y=pandas.Series([0,0,1,1,1,0,0,1,0,1,1]) def f(x): return reduce(lambda a,b:reduce((a+b)*b,x) z=pandas.expanding_apply(y,f)

Supongo que estoy repasando toda la lista demasiadas veces. ¿Existe una buena forma en Pythonic de lograr lo que quiero mientras solo repaso los datos una vez? Yo mismo podría escribir un bucle, pero me pregunto si hay una mejor manera.

¡Gracias!


Esto puede parecer un poco mágico, pero en realidad usa algunos modismos comunes: dado que los pandas aún no tienen un buen soporte nativo para un grupo contiguo, a menudo se encuentra necesitando algo como esto.

>>> y * (y.groupby((y != y.shift()).cumsum()).cumcount() + 1) 0 0 1 0 2 1 3 2 4 3 5 0 6 0 7 1 8 0 9 1 10 2 dtype: int64

Algunas explicaciones: primero, comparamos y con una versión modificada de sí mismo para encontrar cuándo comienzan los grupos contiguos:

>>> y != y.shift() 0 True 1 False 2 True 3 False 4 False 5 True 6 False 7 True 8 True 9 True 10 False dtype: bool

Luego (ya que False == 0 y True == 1) podemos aplicar una suma acumulada para obtener un número para los grupos:

>>> (y != y.shift()).cumsum() 0 1 1 1 2 2 3 2 4 2 5 3 6 3 7 4 8 5 9 6 10 6 dtype: int32

Podemos usar groupby y cumcount para obtener un número entero en cada grupo:

>>> y.groupby((y != y.shift()).cumsum()).cumcount() 0 0 1 1 2 0 3 1 4 2 5 0 6 1 7 0 8 0 9 0 10 1 dtype: int64

Agrega uno:

>>> y.groupby((y != y.shift()).cumsum()).cumcount() + 1 0 1 1 2 2 1 3 2 4 3 5 1 6 2 7 1 8 1 9 1 10 2 dtype: int64

Y finalmente cero los valores donde teníamos cero para empezar:

>>> y * (y.groupby((y != y.shift()).cumsum()).cumcount() + 1) 0 0 1 0 2 1 3 2 4 3 5 0 6 0 7 1 8 0 9 1 10 2 dtype: int64


Mantener las cosas simples, usando una matriz, un bucle y un condicional.

a = [0,0,1,1,1,0,0,1,0,1,1] for i in range(1, len(a)): if a[i] == 1: a[i] += a[i - 1]


Si algo está claro, es "pythonic". Francamente, ni siquiera puedo hacer que su solución original funcione. Además, si funciona, tengo curiosidad por saber si es más rápido que un bucle. ¿Comparaste?

Ahora, desde que comenzamos a hablar sobre la eficiencia, aquí hay algunas ideas.

Los bucles en Python son intrínsecamente lentos, no importa lo que hagas. Por supuesto, si está utilizando pandas, también está usando numpy debajo, con todas las ventajas de rendimiento. Simplemente no los destruyas haciendo un bucle. Esto no quiere decir que las listas de Python requieren más memoria de la que pueden pensar; potencialmente mucho más de 8 bytes * length , ya que cada entero puede ser envuelto en un objeto separado y colocado en un área separada en la memoria, y señalado por un puntero de la lista.

La vectorización proporcionada por numpy debería ser suficiente SI puede encontrar alguna forma de expresar esta función sin hacer un bucle. De hecho, me pregunto si hay alguna forma de representarlo usando expresiones como A+B*C Si puede construir esta función a partir de funciones en Lapack , entonces puede incluso vencer el código ordinario de C ++ compilado con optimización.

También puede utilizar uno de los enfoques compilados para acelerar sus bucles. Vea una solución con Numba en las matrices numpy a continuación. Otra opción es usar PyPy , aunque probablemente no puedas combinarlo adecuadamente con los pandas.

In [140]: import pandas as pd In [141]: import numpy as np In [143]: a=np.random.randint(2,size=1000000) # Try the simple approach In [147]: def simple(L): for i in range(len(L)): if L[i]==1: L[i] += L[i-1] In [148]: %time simple(L) CPU times: user 255 ms, sys: 20.8 ms, total: 275 ms Wall time: 248 ms # Just-In-Time compilation In[149]: from numba import jit @jit def faster(z): prev=0 for i in range(len(z)): cur=z[i] if cur==0: prev=0 else: prev=prev+cur z[i]=prev In [151]: %time faster(a) CPU times: user 51.9 ms, sys: 1.12 ms, total: 53 ms Wall time: 51.9 ms In [159]: list(L)==list(a) Out[159]: True

De hecho, la mayor parte del tiempo en el segundo ejemplo anterior se dedicó a la compilación Just-In-Time. En su lugar (recuerde copiar, ya que la función cambia la matriz).

b=a.copy() In [38]: %time faster(b) CPU times: user 55.1 ms, sys: 1.56 ms, total: 56.7 ms Wall time: 56.3 ms In [39]: %time faster(c) CPU times: user 10.8 ms, sys: 42 µs, total: 10.9 ms Wall time: 10.9 ms

Así que para las llamadas subsiguientes tenemos una aceleración de 25x en comparación con la versión simple. Te sugiero que leas High Performance Python si quieres saber más.