0Pricing
Pandas & NumPy Academy · Lección

Ventanas expansivas

Calcule estadísticas acumuladas que incluyan todas las filas desde el inicio hasta cada punto mediante expanding().sum().

Ventanas expansivas es una lección gratuita de Pandas & NumPy Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Pandas & NumPy Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

Partes de esta lección aún no han sido traducidas y se muestran en inglés.

What Is an Expanding Window?

An expanding window includes all rows from the very beginning of the Series up to the current row — the window grows with each new row rather than staying fixed. At row 0 it contains only one value; at row 5 it contains six values; at row 100 it contains 101 values. Expanding windows are used to compute cumulative statistics that represent the running total, running average, or running maximum from the start of the dataset to the current point in time.

import pandas as pd
import numpy as np

sales = pd.Series(
    [100, 150, 120, 200, 180, 160, 220, 190],
    index=pd.date_range('2024-01-01', periods=8)
)

# Expanding mean — grows with each row
df = pd.DataFrame({'sales': sales})
df['cumulative_mean'] = df['sales'].expanding().mean()
print(df)

The expanding() Method

Series.expanding(min_periods=1) returns an Expanding object. The min_periods parameter (default 1) sets the minimum number of observations required for the first non-NaN result. Unlike rolling(n), the window in expanding() is not fixed — it always starts at the beginning. Chain any aggregation: .sum(), .mean(), .std(), .min(), .max().

import pandas as pd
import numpy as np

np.random.seed(42)
monthly_revenue = pd.Series(
    np.random.randint(500, 1500, 12),
    index=pd.date_range('2024-01', periods=12, freq='ME'),
    name='revenue'
)

df = pd.DataFrame({'revenue': monthly_revenue})
df['cumsum'] = df['revenue'].expanding().sum()
df['cummean'] = df['revenue'].expanding().mean()
df['cummax'] = df['revenue'].expanding().max()
df['cummin'] = df['revenue'].expanding().min()
print(df)

Pandas cumsum, cumprod, cummax, cummin

For the most common cumulative statistics, Pandas provides direct methods on Series and DataFrame without needing expanding(): cumsum(), cumprod(), cummax(), and cummin(). These are optimised implementations that are slightly faster than their expanding() equivalents. Use these shortcuts for typical business metrics like year-to-date revenue (cumsum) or all-time maximum price (cummax).

import pandas as pd
import numpy as np

prices = pd.Series(
    [100, 95, 110, 105, 120, 115, 130, 125],
    index=pd.date_range('2024-01-01', periods=8)
)

df = pd.DataFrame({'price': prices})
df['cummax'] = df['price'].cummax()    # all-time high
df['cummin'] = df['price'].cummin()    # all-time low
df['cumsum'] = df['price'].cumsum()    # cumulative total
df['cumprod'] = (1 + df['price'].pct_change()).cumprod()  # growth index
print(df.round(3))

Year-to-Date Calculation with Expanding

A classic use of expanding windows is year-to-date (YTD) revenue. For each day, you want the sum of all revenue from January 1st of the current year up to that day. This is just cumsum() applied within each year group. Implement it with groupby(year).cumsum() — the cumsum resets at the start of each calendar year while accumulating through the year.

import pandas as pd
import numpy as np

np.random.seed(0)
dates = pd.date_range('2023-01-01', '2024-06-30', freq='ME')
daily = pd.DataFrame({
    'date': dates,
    'revenue': np.random.randint(100, 500, len(dates))
})

# Year-to-date cumulative revenue
daily['year'] = daily['date'].dt.year
daily['ytd_revenue'] = daily.groupby('year')['revenue'].cumsum()
daily = daily.drop(columns='year')
print(daily.tail(10).to_string(index=False))

Expanding Window Standard Deviation

expanding().std() computes the running standard deviation using all data from the start to the current row. This is useful for monitoring whether the variability of a metric is increasing or decreasing over time — for example, tracking whether a product's daily sales are becoming more or less predictable as the business matures. Unlike rolling std (which only reflects recent variability), expanding std captures the full historical dispersion.

import pandas as pd
import numpy as np

np.random.seed(42)
sales = pd.Series(
    np.concatenate([
        np.random.normal(100, 5, 30),   # stable period
        np.random.normal(100, 25, 30)   # volatile period
    ]),
    index=pd.date_range('2024-01-01', periods=60)
)

df = pd.DataFrame({'sales': sales})
df['expanding_std'] = df['sales'].expanding().std()
df['rolling_30d_std'] = df['sales'].rolling(30).std()
print(df.tail(10).round(2))

Comparing Rolling vs Expanding

The key distinction: rolling windows reflect recent performance (last n days) and are insensitive to long-ago data, while expanding windows incorporate all history and thus converge slowly toward stable values. Use rolling when you care about recent trends (a 7-day moving average is more responsive to recent changes than a 90-day one). Use expanding when you want all-time statistics or YTD metrics that must never forget earlier data.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(10)
data = pd.Series(
    np.random.randn(60).cumsum() + 50,
    index=pd.date_range('2024-01-01', periods=60)
)

df = pd.DataFrame({'value': data})
df['rolling_10'] = data.rolling(10).mean()
df['expanding'] = data.expanding().mean()

df.plot(figsize=(12, 5), title='Rolling 10-day Mean vs. Expanding Mean')
plt.ylabel('Value')
plt.show()

Expanding Apply for Custom Cumulative Stats

expanding().apply(func) applies a custom function to all data from the start to the current row, just like rolling().apply(). This enables cumulative statistics not available as built-ins — for example, the cumulative coefficient of variation (std/mean), the running median, or a cumulative Sharpe ratio. The function receives a NumPy array of all values seen so far and must return a scalar.

import pandas as pd
import numpy as np

np.random.seed(0)
returns = pd.Series(
    np.random.normal(0.001, 0.02, 60),
    index=pd.date_range('2024-01-01', periods=60)
)

# Cumulative Sharpe ratio (mean/std of returns)
def sharpe(arr):
    if len(arr) < 2:
        return float('nan')
    return arr.mean() / arr.std() * (252 ** 0.5)  # annualised

df = pd.DataFrame({'returns': returns})
df['cum_sharpe'] = df['returns'].expanding().apply(sharpe, raw=True)
print(df.tail(8).round(4))

Expanding Windows with NaN Handling

Expanding windows skip NaN values by default — a NaN in the middle of the Series is ignored when computing the cumulative sum or mean. You can check this with skipna=True (the default in most Pandas aggregations). When you have time series with genuine missing values (e.g. no trading on weekends), expanding statistics will be computed only from the non-NaN values seen so far, which is usually the desired behaviour.

import pandas as pd
import numpy as np

data = pd.Series([10, np.nan, 20, 30, np.nan, 40, 50])

# cumsum skips NaN by default
df = pd.DataFrame({'value': data})
df['cumsum'] = df['value'].cumsum()      # NaN propagates in cumsum!
df['expanding_sum'] = df['value'].expanding().sum()  # NaN skipped

print(df)
print('\nNote: cumsum propagates NaN; expanding().sum() skips it.')

Expanding Windows for Running Benchmarks

Expanding windows are ideal for computing running benchmarks: the all-time maximum, the best-quarter-ever, or the lowest error rate since launch. These KPIs require remembering all historical values — a rolling window would forget old records. Using cummax() or cummin() returns the running best (or worst) at each point, making it easy to track when new records were set.

import pandas as pd
import numpy as np

np.random.seed(5)
sales = pd.Series(
    np.random.randint(100, 300, 20),
    index=pd.date_range('2024-01-01', periods=20),
    name='daily_sales'
)

df = pd.DataFrame({'sales': sales})
df['all_time_max'] = df['sales'].cummax()
df['new_record'] = df['sales'] == df['all_time_max']

print('Days where a new sales record was set:')
print(df[df['new_record']].drop(columns='new_record'))

Practical Example: Cumulative Returns

In finance, cumulative returns show how much an investment has grown from the start date to each subsequent date. Starting from daily percentage returns, the cumulative product of (1 + daily_return) gives the total growth factor. An expanding product is more natural than a rolling product here — you want to track total growth since inception, not just over the last n days.

import pandas as pd
import numpy as np

np.random.seed(7)
start_price = 100
daily_returns = pd.Series(
    np.random.normal(0.0005, 0.015, 252),
    index=pd.date_range('2024-01-01', periods=252)
)

# Cumulative return = product of (1 + r_i)
cum_returns = (1 + daily_returns).cumprod()
portfolio_value = start_price * cum_returns

print(f'Start value: ${start_price:.2f}')
print(f'End value:   ${portfolio_value.iloc[-1]:.2f}')
print(f'Total return: {(portfolio_value.iloc[-1]/start_price - 1)*100:.1f}%')

When to Use Expanding vs Rolling vs cumsum

Guidelines for choosing the right approach:

  • Use cumsum() / cummax() / cummin() for simple cumulative aggregations — they are the fastest option.
  • Use expanding().mean() / std() when you need cumulative running averages or variance.
  • Use expanding().apply(custom_func) for complex all-time statistics not available as built-ins.
  • Use rolling(n) instead of expanding when only recent history should influence the current value.

Quick Check

Test your understanding of expanding windows from this lesson.

Lesson Recap

In this lesson you learned: expanding() computes cumulative statistics over a growing window from the start of the Series, cumsum/cummax/cummin are optimised shortcuts for common cumulative operations, and expanding windows are ideal for all-time records, YTD metrics, and cumulative investment returns. Next up we explore exponentially weighted moving averages (EWMA) — giving more weight to recent observations.

Preguntas frecuentes

¿La lección «Ventanas expansivas» es gratis?

Sí — el texto completo de «Ventanas expansivas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Pandas & NumPy Academy, actualiza a CoddyKit PRO. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Ventanas expansivas»?

Calcule estadísticas acumuladas que incluyan todas las filas desde el inicio hasta cada punto mediante expanding().sum(). Practicas Pandas & NumPy Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Pandas & NumPy Academy?

No se requiere experiencia previa. Pandas & NumPy Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Ventanas expansivas»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Pandas & NumPy Academy?

Sí. Cada lección de Pandas & NumPy Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Ventanas móviles
  2. Ventanas expansivas
  3. Media móvil ponderada exponencialmente
  4. Rangos y percentiles dentro de grupos
← Volver a Pandas & NumPy Academy