0Pricing
Pandas & NumPy Academy · レッスン

指数加重移動平均

ewm(span=)で新しい値ほど重くなる指数平滑化を適用します。これは金融分析で標準的に使われる手法です。

「指数加重移動平均」はCoddyKit上の無料Pandas & NumPy Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPandas & NumPy Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

このレッスンの一部はまだ翻訳されておらず、英語で表示されています。

The Problem with Simple Moving Averages

A simple moving average treats all n observations in the window equally — a price from 30 days ago contributes the same as yesterday's price. But for predicting near-term values, recent observations are more informative than old ones. An Exponentially Weighted Moving Average (EWMA) solves this by assigning exponentially decreasing weights to older observations: yesterday counts the most, the day before counts slightly less, and so on back to the beginning of the series.

import pandas as pd
import numpy as np

np.random.seed(42)
prices = pd.Series(
    100 + np.cumsum(np.random.normal(0.5, 2, 30)),
    index=pd.date_range('2024-01-01', periods=30)
)

df = pd.DataFrame({'price': prices})
df['SMA_7'] = df['price'].rolling(7).mean()
df['EWM_span7'] = df['price'].ewm(span=7).mean()
print(df.tail(5).round(2))

How EWMA Weighting Works

EWMA computes each value as a weighted combination of the current observation and the previous EWMA: EWM_t = alpha * x_t + (1 - alpha) * EWM_{t-1}. The alpha (smoothing factor) determines how quickly weights decay — a large alpha (close to 1) responds quickly to recent changes (less smoothing), a small alpha (close to 0) is slow to respond (more smoothing). The weights form a geometric series: the most recent value has weight alpha, the previous has alpha*(1-alpha), and so on.

import numpy as np

# Manually show EWMA weights
alpha = 0.3
n_points = 8
weights = [alpha * (1 - alpha)**i for i in range(n_points)]

print(f'Alpha = {alpha}')
print(f'Weights (most recent first):')
for i, w in enumerate(weights):
    print(f'  t-{i}: {w:.4f}')
print(f'Sum of shown weights: {sum(weights):.4f} (converges to 1.0)')

The ewm() Method and span Parameter

Pandas implements EWMA through Series.ewm(). The most intuitive parameter is span, which is analogous to a simple moving average window: alpha = 2 / (span + 1). A span of 10 approximately corresponds to a 10-period SMA but with exponentially decaying weights instead of equal weights. Use ewm(span=n).mean() to get the EWMA, and ewm(span=n).std() for exponentially weighted volatility.

import pandas as pd
import numpy as np

np.random.seed(0)
data = pd.Series(
    np.random.normal(100, 10, 50),
    index=pd.date_range('2024-01-01', periods=50)
)

df = pd.DataFrame({'value': data})
df['EWM_3'] = df['value'].ewm(span=3).mean()
df['EWM_10'] = df['value'].ewm(span=10).mean()
df['EWM_20'] = df['value'].ewm(span=20).mean()

print('Alpha values:')
for span in [3, 10, 20]:
    alpha = 2 / (span + 1)
    print(f'  span={span} → alpha={alpha:.3f}')
print(df.tail(5).round(2))

The halflife Parameter

The halflife parameter defines how many periods it takes for the weight to decrease by half. If halflife=5, a value from 5 periods ago has half the weight of the current value. The relationship is: alpha = 1 - exp(log(0.5) / halflife). Halflife is often more intuitive than span or alpha because it directly expresses the 'memory' of the average in business terms — for example, 'prices from 2 weeks ago should have half the influence of today's price'.

import pandas as pd
import numpy as np

np.random.seed(5)
prices = pd.Series(
    100 + np.cumsum(np.random.normal(0, 2, 30)),
    index=pd.date_range('2024-01-01', periods=30)
)

df = pd.DataFrame({'price': prices})
# halflife=5: weight halves every 5 days
df['EWM_hl5'] = df['price'].ewm(halflife=5).mean()
# halflife=14: weight halves every 14 days (longer memory)
df['EWM_hl14'] = df['price'].ewm(halflife=14).mean()

print(df.tail(5).round(2))

The alpha Parameter Directly

You can also set the smoothing factor directly with ewm(alpha=). Alpha must be between 0 and 1 (exclusive). A high alpha like 0.8 gives very little smoothing — the EWMA tracks the raw signal closely. A low alpha like 0.1 gives heavy smoothing — the EWMA changes slowly and lags behind sudden shifts. Choosing the right alpha depends on the trade-off between responsiveness (detecting real trend changes quickly) and stability (ignoring random noise).

import pandas as pd
import numpy as np

np.random.seed(3)
data = pd.Series(
    [100]*20 + [120]*20,  # sudden level shift at row 20
    dtype=float
) + np.random.normal(0, 2, 40)

df = pd.DataFrame({'signal': data})
df['alpha_0.1'] = df['signal'].ewm(alpha=0.1).mean()  # slow
df['alpha_0.5'] = df['signal'].ewm(alpha=0.5).mean()  # medium
df['alpha_0.9'] = df['signal'].ewm(alpha=0.9).mean()  # fast

print('After the level shift (rows 20-25):')
print(df.iloc[18:26].round(2))

EWMA for Volatility: ewm().std()

ewm(span=).std() computes an exponentially weighted standard deviation. This is the basis of the EWMA volatility model used in financial risk management (also known as the RiskMetrics model with decay factor lambda=0.94). Exponentially weighted volatility responds faster to volatility spikes than a simple rolling std because it assigns more weight to recent high-variance observations.

import pandas as pd
import numpy as np

np.random.seed(42)
# Simulate returns: quiet period then volatile period
returns = pd.Series(
    np.concatenate([
        np.random.normal(0, 0.01, 60),
        np.random.normal(0, 0.04, 60)  # 4x more volatile
    ]),
    index=pd.date_range('2024-01-01', periods=120)
)

df = pd.DataFrame({'returns': returns})
df['rolling_vol'] = df['returns'].rolling(20).std()
df['ewm_vol'] = df['returns'].ewm(span=20).std()

print('Volatility comparison (transition at row 60):')
print(df.iloc[58:65].round(5))

MACD: Combining Two EWMAs

The MACD (Moving Average Convergence Divergence) is a classic financial indicator built from two EWMAs. It is the difference between a fast (short-span) EWMA and a slow (long-span) EWMA of the price. When the MACD crosses zero from below, it signals potential upward momentum (a buy signal); crossing from above signals downward momentum. MACD is a perfect example of applying two EWMAs to derive a derived indicator.

import pandas as pd
import numpy as np

np.random.seed(7)
prices = pd.Series(
    100 + np.cumsum(np.random.normal(0.3, 2, 100)),
    index=pd.date_range('2024-01-01', periods=100)
)

df = pd.DataFrame({'price': prices})
df['EMA_12'] = df['price'].ewm(span=12).mean()  # fast
df['EMA_26'] = df['price'].ewm(span=26).mean()  # slow
df['MACD'] = df['EMA_12'] - df['EMA_26']
df['Signal'] = df['MACD'].ewm(span=9).mean()   # signal line

print('MACD values (last 8 rows):')
print(df[['price', 'MACD', 'Signal']].tail(8).round(2))

EWMA with Time-Based Halflife

Pandas 1.1+ supports specifying halflife, span, and com as time offsets (e.g. halflife='7D') when working with a DatetimeIndex. This enables proper EWMA on non-uniform time series — for example, if you have hourly data with gaps on weekends, a halflife of '7D' means the weight truly halves every 7 calendar days regardless of the number of rows in that period.

import pandas as pd
import numpy as np

np.random.seed(0)
# Business-day data (no weekends)
biz_dates = pd.bdate_range('2024-01-01', periods=20)
prices = pd.Series(100 + np.cumsum(np.random.randn(20)), index=biz_dates)

# halflife as a time offset (7 calendar days)
ewm_7d = prices.ewm(halflife='7D', times=biz_dates).mean()
# halflife as row count (7 rows)
ewm_7rows = prices.ewm(halflife=7).mean()

df = pd.DataFrame({'price': prices, 'hl_7D': ewm_7d, 'hl_7rows': ewm_7rows})
print(df.round(2))

Choosing Between EWMA, Rolling, and Expanding

Summary of when to use each:

  • Rolling(n).mean(): all n recent observations equally weighted — good for simple trailing metrics and when all recent periods matter equally.
  • ewm(span=n).mean(): exponentially weighted — best when you want smoothing that responds more to recent changes, commonly used in finance and signal processing.
  • expanding().mean(): all historical data with equal weight — best for YTD or all-time statistics where forgetting old data is not acceptable.

Visualising EWMA vs SMA

Plotting EWMA and SMA together on a time series chart makes the difference in their responsiveness visible. The EWMA reacts more quickly to sudden level changes because it has not 'forgotten' all previous observations — the short-span EWMA will turn faster than an SMA with the same nominal window. This makes EWMA preferable in forecasting applications where capturing trend direction quickly is more valuable than maximum noise reduction.

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

np.random.seed(42)
prices = pd.Series(
    np.concatenate([
        100 + np.cumsum(np.random.normal(0.3, 1.5, 50)),
        130 + np.cumsum(np.random.normal(-0.3, 1.5, 50))
    ]),
    index=pd.date_range('2024-01-01', periods=100)
)

df = pd.DataFrame({'price': prices})
df['SMA_10'] = df['price'].rolling(10).mean()
df['EWMA_10'] = df['price'].ewm(span=10).mean()

df.plot(figsize=(12, 5), title='SMA vs EWMA — EWMA reacts faster to trend reversals')
plt.ylabel('Price')
plt.axvline(x=df.index[50], color='red', linestyle='--', label='Trend change')
plt.legend()
plt.show()

EWMA in Business Applications

Beyond finance, EWMA is widely used in product analytics and operations: customer lifetime value smoothing (recent behaviour predicts future spend better than old behaviour), server latency monitoring (an exponentially smoothed latency metric sounds false alarms faster than a rolling mean), and inventory demand forecasting (recent sales weeks should outweigh data from six months ago). The universal principle is: wherever recency matters, EWMA outperforms simple averaging.

Quick Check

Test your understanding of exponentially weighted moving averages from this lesson.

Lesson Recap

In this lesson you learned: ewm(span=) applies exponentially decaying weights so recent observations matter more, alpha controls responsiveness (high = fast, low = smooth), and the halflife parameter expresses memory in business-meaningful terms. EWMA outperforms SMA when detecting trend reversals quickly is important. Next up we explore rank and percentile calculations within groups using groupby().rank() and pd.qcut.

よくある質問

「指数加重移動平均」レッスンは無料ですか?

はい。「指数加重移動平均」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Pandas & NumPy Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

「指数加重移動平均」で何を学びますか?

ewm(span=)で新しい値ほど重くなる指数平滑化を適用します。これは金融分析で標準的に使われる手法です。 ブラウザで直接実行するハンズオンコードでPandas & NumPy Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Pandas & NumPy Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPandas & NumPy Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「指数加重移動平均」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPandas & NumPy Academyレッスンでコードを書いて実行できますか?

はい。すべてのPandas & NumPy Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ローリングウィンドウ
  2. 拡張ウィンドウ
  3. 指数加重移動平均
  4. グループ内の順位とパーセンタイル
← Pandas & NumPy Academyに戻る