rolling_mean rolling panda has example attribute python pandas numpy dataframe cython

panda - rolling window python



¿Cómo funcionan los objetos panda Rolling? (1)

Editar: condensé esta pregunta dado que probablemente era demasiado complicado para empezar. La carne de la pregunta está en negrita a continuación.

Me gustaría obtener más información sobre el objeto que se crea realmente al usar DataFrame.rolling o Series.rolling :

print(type(df.rolling)) <class ''pandas.core.window.Rolling''>

Algunos antecedentes: considere la alternativa frecuentemente utilizada con np.as_strided . Este fragmento de código en sí no es importante, pero su resultado es mi punto de referencia al hacer esta pregunta.

def rwindows(a, window): if a.ndim == 1: a = a.reshape(-1, 1) shape = a.shape[0] - window + 1, window, a.shape[-1] strides = (a.strides[0],) + a.strides windows = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides) return np.squeeze(windows)

Aquí rwindows tomará una rwindows 1d o 2d y ndarray "bloques" rodantes igual al tamaño de ventana especificado (como se muestra a continuación). ¿Cómo se .rolling un objeto .rolling con el resultado ndarray continuación? ¿Es un iterador, con ciertos atributos almacenados para cada bloque? ¿O algo completamente diferente? He intentado jugar con la terminación de pestañas en el objeto con atributos / métodos como __dict__ y _get_index() y no me dicen mucho. También he visto un método _create_blocks en pandas: ¿se parece en strided método strided ?

# as_strided version a = np.arange(5) print(rwindows(a, 3)) # 1d input [[0 1 2] [1 2 3] [2 3 4]] b = np.arange(10).reshape(5,2) print(rwindows(b, 4)) # 2d input [[[0 1] [2 3] [4 5] [6 7]] [[2 3] [4 5] [6 7] [8 9]]]

Parte 2, crédito adicional

El uso del enfoque NumPy anterior (implementación de OLS here ) es necesario por el hecho de que el func dentro de pandas.core.window.Rolling.apply debe

producir un valor único a partir de una entrada ndarray * args y ** kwargs se pasan a la función

Entonces el argumento no puede ser otro objeto rodante. Es decir

def prod(a, b): return a * b df.rolling(3).apply(prod, args=((df + 2).rolling(3),)) ----------------------------------------------------------------------- ... TypeError: unsupported operand type(s) for *: ''float'' and ''Rolling''

Así que esto es realmente de donde surge mi pregunta anterior. ¿Por qué es que la función pasada debe usar una matriz NumPy y producir un único valor escalar, y qué tiene esto que ver con el diseño de un objeto .rolling ?


Te sugiero que eches un vistazo al código fuente para entrar en el meollo de lo que hace rodando. En particular, sugiero que eche un vistazo a las funciones de rolling en window.py y window.py . Desde allí puede echar un vistazo a la clase Window que se usa si especifica un tipo de ventana o la clase Rolling predeterminada. El último hereda de _Rolling_and_Expanding y finalmente _Rolling y _Window .

Dicho esto, daré mi granito de apply_along_axis : el mecanismo completo de Pandas se basa en la función apply_along_axis . En particular, se usa here en pandas. Se usa junto con el módulo cython windows.pyx . En su serie, sale la ventana acumulada. Para las funciones de agregación típicas, las maneja de manera eficiente, pero para las personalizadas (usando apply() ) usa un roll_generic() en windows.pyx .

La función de balanceo en pandas funciona en las columnas de marcos de datos de pandas de manera independiente. No es un iterador de Python , y está cargado de forma diferida, lo que significa que no se computa nada hasta que se le aplica una función de agregación. Las funciones que realmente aplican la ventana de datos rodante no se utilizan hasta justo antes de que se realice una agregación.

Una fuente de confusión podría ser que piense en el objeto rodante como un marco de datos. (Usted ha nombrado el objeto rodante df en su último fragmento de código). Realmente no lo es. Es un objeto que puede producir marcos de datos mediante la aplicación de agregaciones sobre la lógica de ventana que alberga.

La lambda que está suministrando se aplica para cada celda de su nuevo marco de datos. Toma una ventana hacia atrás (a lo largo de cada columna) en su marco de datos anterior, y lo agrega a una sola celda en el nuevo marco de datos. La agregación puede ser cosas como sum , mean , algo personalizado que haya hecho, etc., sobre un tamaño de ventana, digamos 3. Estos son algunos ejemplos:

a = np.arange(5) df = pd.DataFrame(a, columns=[''a'']) df.rolling(3).mean().dropna()

... que también puede ser hecho por:

df.rolling(3).apply(np.mean).dropna()

... y produce:

a 2 3.0 3 6.0 4 9.0

(La primera columna es el valor del índice y se puede ignorar aquí, y para los ejemplos siguientes).

Observe cómo suministramos una función de agregación numpy existente. Esa es la idea. Se supone que podemos proporcionar todo lo que queramos siempre que se ajuste a las funciones de agregación, es decir, tome un vector de valores y genere un único valor. Aquí hay otro en el que creamos una función de agregación personalizada, en este caso, la norma L2 de la ventana:

df.rolling(3).apply(lambda x: np.sqrt(x.dot(x))).dropna()

si no está familiarizado con las funciones de lambda, esto es lo mismo que:

def euclidean_dist(x): return np.sqrt(x.dot(x)) df.rolling(3).apply(euclidean_dist).dropna()

... cediendo:

a 2 2.236068 3 3.741657 4 5.385165

Solo para asegurarnos, podemos verificar manualmente que np.sqrt(0**2 + 1**2 + 2**2) sea ​​realmente 2.236068 .

[En su edición original, en el] último fragmento de código, es probable que su código falle antes de lo esperado. Está fallando antes de la invocación de df.apply(...) Está intentando agregar un objeto rodante llamado df al número 2 antes de pasarlo a df.apply(...) . El objeto rodante no es algo sobre lo que haces operaciones. La función de agregación que ha suministrado también no se ajusta a una función de agregación en general. La a es una lista con los valores de una ventana, b sería un parámetro extra constante que usted ingresa. Puede ser un objeto rodante si lo desea, pero normalmente no sería algo que le gustaría hacer. Para hacerlo más claro, aquí hay algo que es similar a lo que estabas haciendo en tu edición original, pero funciona:

a = np.arange(8) df = pd.DataFrame(a, columns=[''a'']) n = 4 rol = df.rolling(n) def prod(window_list, constant_rol): return window_list.dot(constant_rol.sum().dropna().head(n)) rol.apply(prod, args=(rol,)).dropna() # [92.0, 140.0, 188.0, 236.0, 284.0]

Es un ejemplo inventado, pero lo estoy mostrando para demostrar que puedes pasar lo que quieras como una constante, incluso el objeto rodante que estás utilizando. La parte dinámica es el primer argumento a en tu caso o window_list en mi caso. Todas las ventanas definidas, en forma de listas individuales, se pasan a esa función una por una.

En función de sus comentarios de seguimiento, esto podría ser lo que está buscando:

import numpy as np import pandas as pd n = 3 a = np.arange(5) df = pd.DataFrame(a, columns=[''a'']) def keep(window, windows): windows.append(window.copy()) return window[-1] windows = list() df[''a''].rolling(n).apply(keep, args=(windows,)) df = df.tail(n) df[''a_window''] = windows

que agrega matrices / vectores a cada bloque rodante produciendo así:

a a_window 2 2 [0.0, 1.0, 2.0] 3 3 [1.0, 2.0, 3.0] 4 4 [2.0, 3.0, 4.0]

Tenga en cuenta que solo funciona si lo hace en una columna a la vez. Si quiere hacer algunas operaciones matemáticas en la ventana antes de guardarlas en keep eso también está bien.

Dicho esto, sin más información sobre lo que intentas lograr es difícil construir un ejemplo que se adapte a tus necesidades.

Si su objetivo final es crear un marco de datos de variables retrasadas, entonces utilizaría las columnas reales usando shift() :

import numpy as np import pandas as pd a = np.arange(5) df = pd.DataFrame(a, columns=[''a'']) for i in range(1,3): df[''a-%s'' % i] = df[''a''].shift(i) df.dropna()

... dando:

a a-1 a-2 2 2 1.0 0.0 3 3 2.0 1.0 4 4 3.0 2.0

(Puede haber alguna forma más hermosa de hacerlo, pero hace el trabajo bien).

Con respecto a su variable b en su primer fragmento de código, recuerde que los DataFrames en pandas generalmente no se manejan como tensores de dimensiones / objetos arbitrarios. Probablemente puedas meter lo que quieras en él, pero en última instancia, cuerdas, objetos de tiempo, ints y flotantes es lo que se espera. Esa podría ser la razón por la cual los diseñadores de pandas no se han molestado en permitir la agregación progresiva a valores no escalares. Ni siquiera parece que se permita una cadena simple como salida de la función de agregación.

De todos modos, espero que esto responda algunas de sus preguntas. Si no me lo dices, intentaré ayudarte en los comentarios o en una actualización.

Nota final sobre la función _create_blocks() de los objetos rodantes.

La función _create_blocks() maneja la reindexación y el binning cuando usa el argumento freq de rolling .

Si usa freq con, digamos, semanas tales que freq=W :

import pandas as pd a = np.arange(50) df = pd.DataFrame(a, columns=[''a'']) df.index = pd.to_datetime(''2016-01-01'') + pd.to_timedelta(df[''a''], ''D'') blocks, obj, index = df.rolling(4, freq=''W'')._create_blocks(how=None) for b in blocks: print(b)

... luego obtenemos los datos originales agrupados (sin rodar) semana por semana:

a a 2016-01-03 2.0 2016-01-10 9.0 2016-01-17 16.0 2016-01-24 23.0 2016-01-31 30.0 2016-02-07 37.0 2016-02-14 44.0 2016-02-21 NaN

Tenga en cuenta que este no es el resultado del balanceo agregado. Esto es simplemente los nuevos bloques en los que trabaja. Después de este. Hacemos una agregación como sum y obtenemos:

a a 2016-01-03 NaN 2016-01-10 NaN 2016-01-17 NaN 2016-01-24 50.0 2016-01-31 78.0 2016-02-07 106.0 2016-02-14 134.0 2016-02-21 NaN

... que verifica con una suma de prueba: 50 = 2 + 9 + 16 + 23.

Si no usa freq como argumento, simplemente devuelve la estructura de datos original:

import pandas as pd a = np.arange(5) df = pd.DataFrame(a, columns=[''a'']) blocks, obj, index = df.rolling(3)._create_blocks(how=None) for b in blocks: print(b)

... que produce ...

a a 2016-01-01 0 2016-01-02 1 2016-01-03 2 2016-01-04 3 2016-01-05 4

... y se usa para agregar la agregación de ventanas.