ローパスフィルタとしての移動平均：周期性の除去と長期トレンドの可視化

# **user**:

次のPython関数は、行列 A に基づいて M 個の正弦関数を定義し、それらの合計を返します。このPython関数をベースに、正弦関数ではなくシグモイド関数の合計を返すようにコードを改変してください。

・ユーザは行列Aを入力する。
・行列AはM行N列の行列である。
・N=3であり、シグモイド関数 y = B / (1 + exp(-(C x + D)) のB・C・Dを表す３つの数値が入る。
・Mはユーザが任意に設定可能で、Mの数だけシグモイド関数を定義できる。
・Aで表されたM個のシグモイド関数の和が、求める関数である。

```python
import numpy as np

def sum_of_sin(A, x):
    """
    A: M×3の行列 (numpy.ndarray)
       - A[:, 0] -> 振幅 (Amplitude)
       - A[:, 1] -> 周期 (Period)
       - A[:, 2] -> 位相 (Phase)
    x: 計算する点 (float または numpy.ndarray)

    返り値:
        M個のsin関数の和
    """
    A = np.asarray(A)  # 念のため ndarray に変換
    if A.shape[1] != 3:
        raise ValueError("A は M×3 の行列でなければなりません。")
    
    amplitudes = A[:, 0]
    periods = A[:, 1]
    phases = A[:, 2]
    
    # M個の sin 関数を計算し、それらの合計を返す
    return np.sum(amplitudes[:, None] * np.sin(2 * np.pi * x / periods[:, None] + phases[:, None]), axis=0)

# 使用例
A = np.array([[1, 2, 0],  # 振幅1, 周期2, 位相0
              [0.5, 1, np.pi/4],  # 振幅0.5, 周期1, 位相π/4
              [2, 3, np.pi/2]])  # 振幅2, 周期3, 位相π/2

x = np.linspace(0, 10, 100)  # 0から10までの100点
y = sum_of_sin(A, x)

import matplotlib.pyplot as plt
plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Sum of Multiple Sinusoidal Functions")
plt.grid()
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/f0336b8f-e434-41a4-a0cc-b5a7607a6ffd.png)

# **assistant**:

以下のコードでは、正弦関数の代わりにシグモイド関数を使用し、M個のシグモイド関数の合計を計算するように変更しました。

```python
import numpy as np
import matplotlib.pyplot as plt

def sum_of_sigmoids(A, x):
    """
    A: M×3の行列 (numpy.ndarray)
       - A[:, 0] -> B (スケール)
       - A[:, 1] -> C (傾き)
       - A[:, 2] -> D (シフト)
    x: 計算する点 (float または numpy.ndarray)

    返り値:
        M個のシグモイド関数の和
    """
    A = np.asarray(A)  # 念のため ndarray に変換
    if A.shape[1] != 3:
        raise ValueError("A は M×3 の行列でなければなりません。")
    
    B = A[:, 0]
    C = A[:, 1]
    D = A[:, 2]
    
    # M個のシグモイド関数を計算し、それらの合計を返す
    return np.sum(B[:, None] / (1 + np.exp(-(C[:, None] * x + D[:, None]))), axis=0)

# 使用例
A = np.array([[1, 2, -1],  # B=1, C=2, D=-1
              [0.5, -1, 3],  # B=0.5, C=-1, D=3
              [2, 0.5, 0]])  # B=2, C=0.5, D=0

x = np.linspace(-10, 10, 100)  # -10から10までの100点
y = sum_of_sigmoids(A, x)

plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Sum of Multiple Sigmoid Functions")
plt.grid()
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/4ee9d33c-667d-403a-bb5a-a1e86a3e095c.png)


このコードは、各行列 $ A $ の行が $B, C, D$ のパラメータを持つシグモイド関数を定義し、それらの合計を計算するようになっています。可視化のため、`matplotlib` を用いてプロットも行っています。

# **user**:

このシグモイド関数の変曲点のx座標は -D/C になるという理解で合ってますか？

# **assistant**:

はい、その理解で合っています。

シグモイド関数の一般的な形は次のようになります：

$$y = \frac{B}{1 + \exp(-(C x + D))}$$

この関数の変曲点（曲率が変わる点）は、シグモイド関数の微分が最大になる点であり、指数関数の変曲点と一致します。指数関数の変曲点は、指数の中身がゼロになる点で発生するため、

$$
C x + D = 0
$$

を解くと、

$$
x = -\frac{D}{C}
$$

となります。したがって、各シグモイド関数の変曲点の $ x $ 座標は $ -D/C $ で正しいです。

# **user**:

ありがとうございます。では、上記の関数をベースに、次のように改変してください。

・ユーザは行列Aを入力する。
・行列AはM行N列の行列である。
・N=3であり、シグモイド関数を y = B / (1 + exp(-(C x + D)) としたときのB・C・-D/Cを表す３つの数値が入る。
・Mはユーザが任意に設定可能で、Mの数だけシグモイド関数を定義できる。
・Aで表されたM個のシグモイド関数の和が、求める関数である。

# **assistant**:

変更を加えました。行列 $ A $ の3列目には変曲点の $ x $ 座標 $ ( -D/C )$ を指定し、それをもとに $ D $ を計算するようになっています。これで意図した形になっているはずです！

```python
import numpy as np
import matplotlib.pyplot as plt

def sum_of_sigmoids(A, x):
    """
    A: M×3の行列 (numpy.ndarray)
       - A[:, 0] -> B (スケール)
       - A[:, 1] -> C (傾き)
       - A[:, 2] -> -D/C (変曲点のx座標)
    x: 計算する点 (float または numpy.ndarray)

    返り値:
        M個のシグモイド関数の和
    """
    A = np.asarray(A)  # 念のため ndarray に変換
    if A.shape[1] != 3:
        raise ValueError("A は M×3 の行列でなければなりません。")
    
    B = A[:, 0]
    C = A[:, 1]
    x_inflection = A[:, 2]  # 変曲点のx座標 (-D/C)
    D = -C * x_inflection  # Dを計算
    
    # M個のシグモイド関数を計算し、それらの合計を返す
    return np.sum(B[:, None] / (1 + np.exp(-(C[:, None] * x + D[:, None]))), axis=0)

# 使用例
A = np.array([[1, 2, -0.5],  # B=1, C=2, 変曲点x=-0.5 → D=-C*x=1
              [0.5, -1, 3],  # B=0.5, C=-1, 変曲点x=3 → D=-C*x=-3
              [2, 0.5, 0]])  # B=2, C=0.5, 変曲点x=0 → D=0

x = np.linspace(-10, 10, 100)  # -10から10までの100点
y = sum_of_sigmoids(A, x)

plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Sum of Multiple Sigmoid Functions")
plt.grid()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/a0ef605c-fd3c-4ede-8601-9eff925f2398.png)

# **user**:

ありがとうございます。変曲点をユーザーが直接入力できるようになったことで、使いやすくなりました。

```python
# 使用例
A = np.array([[1, 0.1, 100],  
              [0.5, -1, 200], 
              [2, 0.2, 300]])

x = np.linspace(0, 365, 365)  
y = sum_of_sigmoids(A, x)

plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Sum of Multiple Sigmoid Functions")
plt.grid()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/e01eda4d-9d9d-4725-b9d4-8237b6602e6a.png)

# **assistant**:

こちらこそ、良いフィードバックをありがとうございます！✨  

この設定なら、例えば年間の成長トレンドや季節性のあるデータのモデリングにも活用しやすくなりそうですね。もしさらに拡張したい機能や可視化の工夫があれば、ぜひ教えてください！😊

# **user**:

ありがとうございます。正弦関数の和を返す関数と組み合わせると、面白そうな時系列データを作れるようになりました。

```python
# 使用例
A = np.array([[1, 0.1, 100],  
              [0.5, -1, 200], 
              [2, 0.2, 300]])

B = np.array([[0.1, 7, 0],  
              [0.2, 31, np.pi/4],  
              [0.3, 365, np.pi/2]]) 

x = np.linspace(0, 365, 365)  
y = sum_of_sigmoids(A, x) + sum_of_sin(B, x)

plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Sum of Multiple Sigmoid+Sinusoidal Functions")
plt.grid()
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/871d1282-8adc-49c0-ab8c-6deb72a232b8.png)

# **assistant**:

おお、それは面白いですね！✨  

シグモイド関数で長期トレンドを、正弦関数で周期的な変動を表現できるので、例えば **季節性＋成長トレンドを持つデータ**（売上、気温、ユーザー数の推移など）をシミュレーションするのにぴったりですね！📈🌊  

もしノイズを加えてよりリアルな時系列データを生成したり、パラメータの自動調整（最適化）をしたい場合も、お手伝いできますよ！😃

# **user**:

ありがとうございます。今回はこの人工時系列データを材料に、ローパスフィルタ（移動平均）による周期的成分の除去を行いたいと考えています。Pythonコードを書いていただけませんか？

# **assistant**:

了解しました！  
ローパスフィルタとして **移動平均** を使い、周期的な変動（正弦関数成分）を除去するコードを作成します。

```python
import numpy as np
import matplotlib.pyplot as plt

def sum_of_sigmoids(A, x):
    """
    A: M×3の行列 (numpy.ndarray)
       - A[:, 0] -> B (スケール)
       - A[:, 1] -> C (傾き)
       - A[:, 2] -> -D/C (変曲点のx座標)
    x: 計算する点 (float または numpy.ndarray)

    返り値:
        M個のシグモイド関数の和
    """
    A = np.asarray(A)  # 念のため ndarray に変換
    if A.shape[1] != 3:
        raise ValueError("A は M×3 の行列でなければなりません。")
    
    B = A[:, 0]
    C = A[:, 1]
    x_inflection = A[:, 2]  # 変曲点のx座標 (-D/C)
    D = -C * x_inflection  # Dを計算
    
    # M個のシグモイド関数を計算し、それらの合計を返す
    return np.sum(B[:, None] / (1 + np.exp(-(C[:, None] * x + D[:, None]))), axis=0)

def sum_of_sin(A, x):
    """
    A: M×3の行列 (numpy.ndarray)
       - A[:, 0] -> 振幅 (Amplitude)
       - A[:, 1] -> 周期 (Period)
       - A[:, 2] -> 位相 (Phase)
    x: 計算する点 (float または numpy.ndarray)

    返り値:
        M個のsin関数の和
    """
    A = np.asarray(A)  # 念のため ndarray に変換
    if A.shape[1] != 3:
        raise ValueError("A は M×3 の行列でなければなりません。")
    
    amplitudes = A[:, 0]
    periods = A[:, 1]
    phases = A[:, 2]
    
    # M個の sin 関数を計算し、それらの合計を返す
    return np.sum(amplitudes[:, None] * np.sin(2 * np.pi * x / periods[:, None] + phases[:, None]), axis=0)

def moving_average(y, window_size):
    """
    移動平均を計算する関数
    y: 平滑化するデータ (numpy.ndarray)
    window_size: 移動平均のウィンドウサイズ (int)
    """
    return np.convolve(y, np.ones(window_size)/window_size, mode='valid')

# 使用例
A = np.array([[1, 0.1, 100],  
              [0.5, -1, 200], 
              [2, 0.2, 300]])

B = np.array([[0.1, 7, 0],  
              [0.2, 31, np.pi/4],  
              [0.3, 365, np.pi/2]]) 

x = np.linspace(0, 365, 365)  
y = sum_of_sigmoids(A, x) + sum_of_sin(B, x)

# 移動平均を適用
window_size = 30  # 30日移動平均
y_smooth = moving_average(y, window_size)
x_smooth = x[:len(y_smooth)]  # xの長さを合わせる

plt.plot(x, y, label="Original Data", alpha=0.5)
plt.plot(x_smooth, y_smooth, label="Smoothed Data (Moving Average)", linewidth=2)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Sum of Multiple Sigmoid+Sinusoidal Functions with Moving Average")
plt.legend()
plt.grid()
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/bf7da5b4-6dd1-441a-a9ad-6461211c0352.png)

追加しました！  
このコードでは `moving_average` 関数を使い、30日間の移動平均を適用して周期的成分（正弦波の揺らぎ）を除去しています。必要に応じて `window_size` を調整してください！📉✨

# **user**:

ありがとうございます。次のように7日移動平均を考えてみると、得られる結果も違って面白いですね。

```python
# 移動平均を適用
window_size = 7  # 7日移動平均
y_smooth = moving_average(y, window_size)
x_smooth = x[:len(y_smooth)]  # xの長さを合わせる

plt.plot(x, y, label="Original Data", alpha=0.5)
plt.plot(x_smooth, y_smooth, label="Smoothed Data (Moving Average)", linewidth=2)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Sum of Multiple Sigmoid+Sinusoidal Functions with Moving Average")
plt.legend()
plt.grid()
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/0318a03e-1948-429f-880e-d4caa7ce58d6.png)

# **assistant**:

いいですね！✨  
7日移動平均なら **週間周期の変動はある程度残しつつ、短期的なノイズを抑える** ことができますし、30日移動平均なら **より滑らかに長期トレンドを強調** できます。  

もしかすると、**異なるウィンドウサイズの移動平均を重ねて可視化** すると、どのスケールの変動がどの程度影響しているか見やすくなるかもしれませんね！📊💡

# **user**:

ありがとうございます。今回の内容をブログに書きたいんですけど、タイトル案を列挙していただけませんか？

# **assistant**:

いいですね！ブログのタイトル案をいくつか挙げてみますね。  

### シンプルで分かりやすい系  
1. **シグモイド関数＋正弦関数でリアルな時系列データを作る方法**  
2. **Pythonで作る人工時系列データ：トレンド＋周期性の組み合わせ**  
3. **移動平均で周期成分を除去！Pythonで時系列データを処理する**  

### キャッチーな系  
4. **時系列データをシミュレート！シグモイド＆正弦波の合わせ技**  
5. **データサイエンス実験室：人工データ×ローパスフィルタの世界**  
6. **Pythonで「それっぽい」時系列データを作って処理してみた**  

### ちょっと専門的な系  
7. **時系列データのモデリング：シグモイド＋正弦波＋移動平均の活用**  
8. **ローパスフィルタとしての移動平均：周期性の除去と長期トレンドの可視化**  
9. **Pythonで実装するトレンド＋季節性の分解と平滑化**  

どれかピンとくるものがあれば、微調整して使ってください！😊

# **user**:

「ローパスフィルタとしての移動平均：周期性の除去と長期トレンドの可視化」にしたいと思います。

# **assistant**:

いいタイトルですね！✨ **専門的だけど分かりやすく、内容がしっかり伝わる** 良いバランスだと思います。  

もしブログ記事の構成や説明の補足が必要なら、いつでも相談してくださいね！😊

# **user**:

ありがとうございます。最後に、今回のまとめを書いてください。

# **assistant**:

### **まとめ：ローパスフィルタとしての移動平均 – 周期性の除去と長期トレンドの可視化**  

今回の内容では、**シグモイド関数と正弦関数を組み合わせた人工時系列データを作成し、それを移動平均フィルタで平滑化する** 方法を解説しました。  

#### **1. 人工時系列データの作成**  
- **シグモイド関数** を用いて **長期トレンド**（成長や変化）を表現。  
- **正弦関数** を用いて **周期的な変動**（季節性やサイクル）を加える。  
- これにより、現実のデータに近い特性を持つ時系列をシミュレート。  

#### **2. ローパスフィルタとしての移動平均**  
- **移動平均を適用することで、周期成分（短期変動）を除去し、長期トレンドを強調**。  
- **ウィンドウサイズの選び方** によって、どのスケールの変動を残すかを調整可能。  
  - 例: 7日移動平均 → 週間の変動は残しつつ短期ノイズを抑制  
  - 30日移動平均 → 長期的なトレンドをより滑らかに強調  

#### **3. 実験結果の可視化**  
- 元の時系列データと移動平均後のデータを比較し、周期成分の除去効果を確認。  
- 異なるウィンドウサイズでの平滑化結果を試し、用途に応じたデータ処理の重要性を実感。  

### **今後の展開**  
- **ノイズを加えてよりリアルなデータを生成する**  
- **移動平均以外の平滑化手法（指数平滑、ロバストフィルタなど）を試す**  
- **トレンドと周期成分を分離する「時系列分解（seasonal decomposition）」を実装する**  

今回の手法は、**売上予測、天気データ解析、株価分析など幅広い分野で活用可能** です。ぜひ、自分のデータに応用してみてください！😊