¿Qué son las funciones de ventana?
Las funciones de ventana calculan un valor para cada fila de una Serie o DataFrame teniendo en cuenta un subconjunto de filas vecinas (la "ventana"), en lugar de toda la columna. Son el equivalente de OVER (ROWS BETWEEN ... AND ...) en SQL.
graph TB
SER[Serie temporal] -->|rolling window=n| ROL[Ventana deslizante fija]
SER -->|expanding| EXP[Ventana creciente desde inicio]
SER -->|ewm alpha=...| EWM[Pesos exponenciales]
ROL -->|.mean / .sum / .std| AGG[Agregación]
EXP --> AGG
EWM --> AGG
ROL -.->|min_periods| BORD[Control bordes]
SER -->|groupby + rolling| GBR[Ventanas por grupo]
AGG --> RES[Serie con valores deslizantes]
RES --> APP["Indicadores SMA, EMA, Bollinger Bands"]
En Pandas existen tres tipos principales:
| Tipo | Método | Descripción |
|------|--------|-------------|
| Deslizante fija | rolling(n) | Ventana de las últimas n observaciones |
| Acumulada creciente | expanding() | Ventana desde el inicio hasta la fila actual |
| Exponencialmente ponderada | ewm() | Pesos decrecientes con el tiempo |
import pandas as pd
import numpy as np
# Serie de ventas diarias para los ejemplos
np.random.seed(42)
fechas = pd.date_range("2024-01-01", periods=20, freq="D")
ventas = pd.Series(
[145, 160, 138, 172, 155, 189, 142, 165, 178, 150,
190, 168, 145, 175, 188, 162, 155, 170, 185, 195],
index=fechas,
name="ventas"
)
print(ventas.head())
rolling(): ventana deslizante fija
rolling(window) crea un objeto Rolling que aplica una función de agregación sobre las últimas window filas. El resultado tiene el mismo índice que la serie original; las primeras window-1 posiciones son NaN porque no hay suficientes datos.
# Media móvil de 3 días
media_3 = ventas.rolling(window=3).mean()
print(media_3)
# 2024-01-01 NaN
# 2024-01-02 NaN
# 2024-01-03 147.667 <- (145+160+138)/3
# ...
Funciones disponibles sobre rolling
# Suma acumulada en ventana de 5 días
suma_5 = ventas.rolling(5).sum()
# Desviación estándar móvil (volatilidad)
std_7 = ventas.rolling(7).std()
# Mínimo y máximo en ventana de 3 días
min_3 = ventas.rolling(3).min()
max_3 = ventas.rolling(3).max()
# Mediana móvil
mediana_5 = ventas.rolling(5).median()
min_periods: controlar el mínimo de observaciones requerido
Por defecto, rolling(n) exige exactamente n observaciones. Con min_periods se puede reducir ese mínimo para obtener valores parciales al principio de la serie:
# Ventana de 7 días pero con al menos 1 observación
media_parcial = ventas.rolling(7, min_periods=1).mean()
print(media_parcial.head(7))
# Los primeros valores no serán NaN: usan las observaciones disponibles
Esto es útil cuando los datos tienen valores faltantes o cuando se quiere un resultado desde el primer elemento.
center=True: ventana centrada
Por defecto la ventana incluye la observación actual y las n-1 anteriores (ventana "hacia atrás"). Con center=True la ventana se centra en la observación actual:
media_centrada = ventas.rolling(window=5, center=True).mean()
Las primeras n//2 y las últimas n//2 filas siguen siendo NaN con la ventana centrada.
rolling con ventana temporal en Series con DatetimeIndex
Cuando la Serie tiene un DatetimeIndex, window puede ser una cadena de duración en lugar de un entero. Esto permite definir la ventana en términos de tiempo real (útil con datos irregulares):
# Ventana de 7 días naturales
media_7d = ventas.rolling("7D").mean()
print(media_7d.head(10))
Con ventanas temporales Pandas incluye todas las observaciones dentro del rango de tiempo, aunque haya huecos.
min_periodssigue aplicando.
expanding(): ventana acumulada creciente
expanding() calcula estadísticas usando todas las observaciones desde el inicio de la serie hasta la fila actual. Es equivalente a rolling(window=len(serie), min_periods=1), pero más eficiente.
# Media acumulada (promedio histórico)
media_acumulada = ventas.expanding().mean()
print(media_acumulada)
# 2024-01-01 145.000 <- solo 1 obs
# 2024-01-02 152.500 <- (145+160)/2
# 2024-01-03 147.667 <- (145+160+138)/3
# ...
# Máximo histórico
maximo_hist = ventas.expanding().max()
# Suma acumulada total
suma_acumulada = ventas.expanding().sum()
Uso de min_periods en expanding
# Requiere al menos 5 observaciones para calcular la desviación estándar
std_acumulada = ventas.expanding(min_periods=5).std()
print(std_acumulada.head(7))
ewm(): media ponderada exponencialmente
ewm() (Exponentially Weighted Moving) asigna pesos decrecientes a las observaciones más antiguas. Las observaciones recientes tienen más influencia en el resultado.
El parámetro de decaimiento puede especificarse de varias formas:
| Parámetro | Significado |
|-----------|-------------|
| span=n | La ventana efectiva equivale aproximadamente a n periodos |
| com=c | Centro de masa: alpha = 1/(1+c) |
| alpha=a | Factor de suavizado directo (0 < alpha <= 1) |
| halflife=h | Semivida: tiempo para que el peso caiga a la mitad |
# EMA con span de 7 (aproxima una media de 7 periodos)
ema_7 = ventas.ewm(span=7).mean()
# EMA con alpha directo (0.3 = 30% de peso en la observación actual)
ema_alpha = ventas.ewm(alpha=0.3).mean()
# Comparar SMA, EMA y expanding
df_comparativa = pd.DataFrame({
"ventas": ventas,
"SMA_7": ventas.rolling(7).mean(),
"EMA_7": ventas.ewm(span=7).mean(),
"media_acumulada": ventas.expanding().mean()
})
print(df_comparativa)
La EMA reacciona más rápido a cambios recientes que la SMA, lo que la hace preferida en análisis financiero y detección de tendencias.
rolling con funciones personalizadas: apply()
Se puede pasar cualquier función Python a rolling().apply() para calcular estadísticas no contempladas de forma nativa:
# Rango intercuartílico (IQR) deslizante
def iqr(x):
return np.percentile(x, 75) - np.percentile(x, 25)
iqr_movil = ventas.rolling(7).apply(iqr, raw=True)
print(iqr_movil)
raw=Truepasa el array NumPy subyacente en lugar de una Serie, lo que es más eficiente para funciones numéricas puras.
rolling en DataFrames
Cuando se aplica sobre un DataFrame, rolling() opera sobre cada columna de forma independiente:
df_ventas = pd.DataFrame({
"laptop": [120, 145, 132, 158, 140, 175, 162, 148, 170, 155],
"tablet": [80, 95, 88, 102, 91, 115, 108, 98, 110, 105],
"movil": [200, 220, 215, 240, 205, 255, 238, 225, 245, 230]
}, index=pd.date_range("2024-01-01", periods=10, freq="D"))
# Media móvil de 3 días en todas las columnas
print(df_ventas.rolling(3).mean())
# Suma acumulada por columna
print(df_ventas.expanding().sum())
groupby() + rolling(): ventanas deslizantes por grupo
Combinar groupby() con rolling() permite calcular estadísticas deslizantes dentro de cada grupo, sin que las filas de un grupo afecten al cálculo del otro:
df_tiendas = pd.DataFrame({
"tienda": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"],
"semana": [1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
"ventas": [100, 110, 105, 120, 115, 90, 95, 88, 102, 98]
})
# Media móvil de 3 semanas por tienda
df_tiendas["media_movil_3"] = (
df_tiendas.groupby("tienda")["ventas"]
.transform(lambda s: s.rolling(3, min_periods=1).mean())
)
print(df_tiendas)
transform()es necesario para que el resultado tenga el mismo índice y longitud que el DataFrame original.
Indicadores financieros con funciones de ventana
Las funciones de ventana son la herramienta estándar para calcular indicadores técnicos en análisis financiero:
import pandas as pd
import numpy as np
# Precio de cierre simulado
np.random.seed(0)
precios = pd.Series(
100 + np.cumsum(np.random.randn(100) * 2),
index=pd.date_range("2024-01-02", periods=100, freq="B"), # días hábiles
name="cierre"
)
# SMA (Simple Moving Average) de 20 días
sma_20 = precios.rolling(20).mean()
# EMA (Exponential Moving Average) de 12 días
ema_12 = precios.ewm(span=12).mean()
ema_26 = precios.ewm(span=26).mean()
# MACD (diferencia de EMAs)
macd = ema_12 - ema_26
señal_macd = macd.ewm(span=9).mean()
# Bollinger Bands (±2 desviaciones estándar sobre SMA-20)
sma_20_bb = precios.rolling(20).mean()
std_20 = precios.rolling(20).std()
banda_superior = sma_20_bb + 2 * std_20
banda_inferior = sma_20_bb - 2 * std_20
indicadores = pd.DataFrame({
"precio": precios,
"SMA_20": sma_20,
"EMA_12": ema_12,
"MACD": macd,
"BB_sup": banda_superior,
"BB_inf": banda_inferior
})
print(indicadores.tail(10))
print(f"\nAncho de bandas (último día): {banda_superior.iloc[-1] - banda_inferior.iloc[-1]:.2f}")
Caso práctico: análisis de rendimiento mensual
import pandas as pd
import numpy as np
# Datos diarios de visitas a una tienda online durante 6 meses
np.random.seed(7)
fechas = pd.date_range("2024-01-01", "2024-06-30", freq="D")
visitas = pd.Series(
500 + np.cumsum(np.random.randn(len(fechas)) * 20),
index=fechas,
name="visitas"
).clip(lower=100)
analisis = pd.DataFrame({
"visitas": visitas,
"media_7d": visitas.rolling("7D").mean().round(1),
"media_30d": visitas.rolling("30D").mean().round(1),
"max_historico": visitas.expanding().max().round(1),
"zscore_30d": (
(visitas - visitas.rolling("30D").mean()) /
visitas.rolling("30D").std()
).round(2)
})
print(analisis.tail(15))
print(f"\nDía con mayor volumen: {visitas.idxmax().strftime('%Y-%m-%d')} ({visitas.max():.0f} visitas)")
print(f"Media global: {visitas.mean():.1f} visitas/día")
Las funciones de ventana son imprescindibles en análisis de series temporales, finanzas, meteorología y cualquier dominio donde sea necesario capturar tendencias, suavizar ruido o detectar anomalías en datos ordenados cronológicamente.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Pandas es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Pandas
Explora más contenido relacionado con Pandas y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Calcular estadísticas deslizantes con
rolling(window)aplicando funciones comomean(),sum(),std()ymin()/max(). - Controlar el comportamiento del borde con el parámetro
min_periodsen ventanas deslizantes. - Usar
rollingcon ventanas de tiempo en Series temporales indexadas conDatetimeIndex. - Calcular estadísticas acumuladas con
expanding()para obtener valores desde el inicio de la serie. - Aplicar medias móviles ponderadas exponencialmente con
ewm()controlando el decaimiento conspan,comyalpha. - Combinar funciones de ventana con
groupby()para calcular estadísticas deslizantes por grupo. - Crear indicadores financieros comunes (SMA, EMA, Bollinger Bands, RSI simplificado) con funciones de ventana.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje