見出し画像

バズの未来を読む数理モデル – ロジスティック曲線で見える成長の行方

前回、2025年7月26日 13:37に公開した記事なんですが、こんなにビュー数が伸びるなんて予測してなかったんです。

画像

様子を見ていると、どうもGoogleなどの検索結果からの流入が多いみたい。
今ね、「国谷裕子 詐欺」とかでググってみると、俺の記事がトップ表示されるんですよ。怪しい広告を見て「これって詐欺?」とググって確かめようとすると、俺の記事にたどり着くという仕掛けでビュー数が伸びているようだ。

で、どうもトータル10,000ビューを超えると、世間的には「バズった」と評価されるらしい。
偏屈なばっかりの昭和の親父の戯言ですよ、いまさら世間的な人気者なんてありえんて!と思っていたんだけど、もしかしてバズ記事認定されるのか?
そこで今回は、数理モデルでこの記事のビュー数の伸びを検証してみようと思います。
アクセス解析だけでなく、ビジネスの成長予測や卒論のデータ分析など、いろんな場面で使えると思います。社会人ならコンサル要らず、学生さんなら卒論がんばれよ。

なお、繰り返しになりますが、国谷裕子さんをネタにした某サイトは読売新聞オンラインとは一切関係のない詐欺サイトです。絶対に引っ掛からないようにご注意ください。


ロジスティック曲線で成長の推移を可視化する

世の中の現象の多くは、時間とともに一定のパターンを描いて変化します。
たとえば、森林におけるカーボン固定量は、初期成長期にはゆるやかに増加し、葉量が増加して光合成活性が高まる中期には急速に増え、やがて成熟して収束します。
ウェブページのアクセス数や人口増加、感染症の拡大、製品や技術の普及といった社会現象でも同様に、最初は緩やか、次第に急増し、最終的に飽和するという「S字型成長」がしばしば観察されます。

こうしたS字型の変化パターンを定量的に解析する有効な手法のひとつが、ロジスティック曲線です。ロジスティック曲線は、生物学や社会科学、環境科学など幅広い分野で用いられ、加速期・変曲点・収束期を持つS字型成長プロセスを数学的に表現できます。

実測値から「最大成長量」「変化速度」「転換点」といったパラメーターを推定することで、単なるデータ記述にとどまらず、将来の動きを予測することができます。
この手法の利点は、観測データが少ない段階でも成長の上限や変曲点を推定できる点です。現象がさらに加速するのか、あるいは間もなく鈍化するのかをリアルタイムに判断できます。たとえば森林管理であれば伐採や再造林のタイミングを、ウェブ記事であればプロモーションや記事更新の時期が見極められます。

この記事では、ロジスティック曲線を用いた変化パターン解析の基本的な考え方と、実データからパラメータを推定し、成長の上限や転換点を予測する実践的手順を解説します。応用例として、ウェブ記事のアクセス数解析を取り上げながら、その具体的な使い方を紹介します。


理論

ロジスティック曲線は、成長現象を記述するための代表的な数理モデルです。時間 t における状態量 f(t) は、次の式で表されます。

f(t) = \frac{K}{1 + e^{-r(t - t_0)}}​

  • K:飽和レベル(最大成長量)

  • r:成長速度(変化がどれだけ急速に進むかを示す)

  • t_0​:変曲点(成長が加速から鈍化に転じる時間)

この関数をプロットすると、S字型の曲線になります。図中の縦の点線は変曲点 t0​ 、青の破線は飽和レベル K を示しています。曲線は、初期には指数関数的に増加し、変曲点を過ぎると増加速度が低下し、最終的に K に収束するという成長プロセスを描きます。

画像

このロジスティック曲線の形状を直感的に理解するには、成長率(時間あたりの変化量)に注目してみるとわかりやすいかもしれません。ロジスティック曲線の関数を微分すると、以下の式になります。

\frac{df}{dt} = r f(t) \left( 1 - \frac{f(t)}{K} \right)

  • \frac{df}{dt}​:時間あたりの変化率(成長速度)

  • f(t):時刻 t における状態量

  • r:成長速度パラメータ(自己増殖の強さ)

  • K:飽和レベル(最大成長量、成長の上限)

この式から、成長速度は現状の値 f(t) に比例し、上限 K に近づくほど減速することが読み取れます。これにより、ロジスティック曲線は現象の初期加速、変曲点、最終収束という3つの主要なフェーズを自然に表現できるわけです。


データの準備

ロジスティック曲線を使って現象を解析するには、まず時間の経過に伴う状態量のデータを整備する必要があります。ここからは実例として、ウェブページのアクセス数を対象に分析を行います。

記事が公開された時刻を基準として、一定間隔で累積アクセス数を記録したデータを用意します。CSV形式であれば以下のようになります。

timestamp,views
2025-07-26 13:37,0
2025-07-27 19:00,217
2025-07-29 03:56,662
2025-07-30 15:14,1206
2025-07-31 15:53,2013
2025-08-01 09:08,2180
2025-08-01 19:48,2465
2025-08-02 03:51,2796
  • timestamp:アクセス数が記録された日時

  • views:その時点での累積アクセス数

ロジスティック曲線で解析するためには、時間の単位を扱いやすくする必要があります。記事が公開された時刻を t=0 とし、その後の経過時間を日数に換算します。

t_i = \frac{timestamp_i - timestamp_0}{24*3600}

  • t_i​:記事公開からの経過時間(日)

  • timestampitimestamp_i:各観測データの記録時刻(秒単位)

  • timestamp_0​:記事公開時刻(秒単位)

Pythonで処理する場合は、以下のように計算できます。

df["days_since_start"] = (df["timestamp"] - df["timestamp"].min()).dt.total_seconds() / (24*3600)

これにより、`days_since_start` 列に「記事公開からの経過日数」が格納され、ロジスティック曲線の時間変数 t として利用できるようになります。


モデル定義とパラメータの推定

ロジスティック曲線を実データに当てはめるには、曲線が示す予測値と実際の観測値のズレを最小にする必要があります。ここでいうズレは、各観測点における予測値と実測値の差(残差)です。
すべてのデータ点での残差を二乗し、それらを合計した値が RSS(Residual Sum of Squares)です。統計的には、この RSS を最小にするパラメータを求める方法は非線形最小二乗法(nonlinear least squares) と呼ばれます。
パラメータ (K,r,t0)(K, r, t_0)(K,r,t0​) を推定するために、残差平方和(RSS)を目的関数 J() として定義し、この値が最小になるようにパラメータを調整します。

J(K, r, t_0) = \sum_{i} \Big[ f(t_i; K, r, t_0) - y_i \Big]^2

  • f(t_i;K,r,t_0) :ロジスティック曲線による予測値

  • y_i​:時刻 t_i​ における観測値

  • J(K,r,t_0):全データに対する予測誤差(二乗和)

この目的関数 J() が小さいほど、ロジスティック曲線は観測データに良く適合しています。パラメータ推定では、この RSS を最も小さくする曲線の形として以下のパラメータを求めます。

(K, r, t_0) = \arg\min_{K,,r,,t_0} J(K, r, t_0)

Pythonでは、ロジスティック関数を次のように定義し、`scipy.optimize` モジュールを読み込み、`curve_fit` 関数を使って最適なパラメータを推定します。

from scipy.optimize import curve_fit

def logistic(t, K, r, t0):
	return K / (1 + np.exp(-r * (t - t0)))

popt, _ = curve_fit(logistic, df["days_since_start"], df["views"], p0=[3000, 1, 4])
K, r, t0 = popt

この手順により、少ないデータ点でも成長の上限 K 、変化速度 r、転換点 t_0​ を数値的に求めることができます。


予測と可視化、分析

推定したパラメータ (K, r, t_0) を用いると、ロジスティック曲線を描き、将来の累積アクセス数を予測できます。予測曲線と実測データを同じグラフに表示すれば、モデルの適合度や成長の進み具合を視覚的に確認できます。
Python での例を示します。

import numpy as np
import matplotlib.pyplot as plt

# 未来の予測範囲を設定
t_future = np.linspace(df["days_since_start"].min(), df["days_since_start"].max() + 5, 300)

# 予測値を計算
views_pred = logistic(t_future, *popt) 

# グラフ描画
plt.figure(figsize=(10, 6))
plt.scatter(df["days_since_start"], df["views"], color='red', label="Actual Views")
plt.plot(t_future, views_pred, 'b--', label=f"Logistic Fit\\nK={K:.0f}, r={r:.2f}, t0={t0:.2f}")
plt.xlabel("Days Since Start")
plt.ylabel("Views")
plt.title("Logistic Curve Fit for Article Views")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

この可視化から以下の点を読み取ることができます。

  • 実測値がロジスティック曲線にどの程度一致しているか(フィットの良さ)

  • 現在が加速期なのか、鈍化しつつあるのか

  • 成長の最終到達値(上限 K)の予測

  • 変曲点 t_0 を過ぎているかどうか

これにより、現象が今後どこまで伸びるか、どのタイミングで成長が鈍化するかを視覚的に把握できます。今回のデータをロジスティック曲線で図示してみましょう。

画像

図は、実際の累積アクセス数データ(赤い点)と、ロジスティック曲線をフィッティングした結果(青の破線)を示しています。青の点線は予測される成長の上限 K、灰色の点線は変曲点 t_0​ を表します。

  • K:成長の上限(飽和レベル)≈ 3,476

  • r:成長速度 ≈ 0.71

  • t_0​:成長が加速から鈍化に転じる変曲点 ≈ 4.86 日

この結果から以下のことがわかります。

  • モデルがデータによく適合しており、記事のアクセス数はロジスティック成長パターンに従っている

  • 成長の上限は K ≈ 3,476 で、最終的には 3,500 程度で飽和する予測

  • 変曲点は記事公開から約 4.9 日後で、この時点を境にアクセス数の増加が鈍化

  • 現在(2025-08-02 03:51)のアクセス数(2,796)は既に上限に近づいており、今後は緩やかな伸びが予想

このように、図を使うことでデータの成長過程と将来の動きを視覚的に把握でき、追加のプロモーションや記事更新の必要性を判断する材料となります。


(2025年8月4日追記)
なお、ロジスティック曲線は少ないデータ点でも初期的な成長パターンを捉えることができますが、データが偏っていると推定結果が不安定になる場合があります。特に、加速期から鈍化期への移行がまだ観測されていない段階では、予測される上限値 K が実際より大きく、または小さく見積もられることがあります。
そのため、一定期間ごとにデータを追加し、再推定を行うことが望ましいです。今回の解析でも、8月3日、4日と新しいデータを追加するたびに、成長上限や変曲点の推定値が以下のように変化しました。

  • 2025年8月3日 09:03 時点のデータ:3,257ビュー

    • K≈3,791

    • r≈0.66

    • t_0≈5.15日

  • 2025年8月4日 05:09 時点のデータ:3,354ビュー

    • K≈3,680

    • r≈0.68

    • t_0≈5.05日

画像

今回のデータも、今後の経緯を継続的にウォッチし、都度予測値を更新していこうと思っています。


スクリプト

  • logistic_fit.py

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

# Logistic function
def logistic(t, K, r, t0):
    return K / (1 + np.exp(-r * (t - t0)))

# Load data
df = pd.read_csv("article_views.csv", parse_dates=["timestamp"])
df["days_since_start"] = (df["timestamp"] - df["timestamp"].min()).dt.total_seconds() / (24*3600)

# Fit parameters
popt, _ = curve_fit(logistic, df["days_since_start"], df["views"], p0=[3000, 1, 4])
K, r, t0 = popt

# Predict future views
t_future = np.linspace(df["days_since_start"].min(), df["days_since_start"].max() + 5, 300)
views_pred = logistic(t_future, *popt)

# Plot
plt.figure(figsize=(10, 6))
plt.scatter(df["days_since_start"], df["views"], color='red', label="Actual Views")
plt.plot(t_future, views_pred, 'b--', label=f"Logistic Fit\nK={K:.0f}, r={r:.2f}, t0={t0:.2f}")
plt.axhline(K, color='blue', linestyle=':', label="Carrying Capacity (K)")
plt.axvline(t0, color='gray', linestyle='--', linewidth=2, zorder=5, label="Inflection Point (t0)")
plt.xlabel("Days Since Start")
plt.ylabel("Views")
plt.title("Logistic Curve Fit for Article Views")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

CSVサンプル:article_views.csv

timestamp,views
2025-07-26 13:37,0
2025-07-27 19:00,217
2025-07-29 03:56,662
2025-07-30 15:14,1206
2025-07-31 15:53,2013
2025-08-01 09:08,2180
2025-08-01 19:48,2465
2025-08-02 03:51,2796

画像

向かって右の人は良く知らない。
ということで次回に続く。

いいなと思ったら応援しよう!

ピックアップされています

コゴト・コクゾ・スグ

  • 24本

コメント

コメントするには、 ログイン または 会員登録 をお願いします。
#うすき林業 #杣人工房うすき #林業 #山林伐採 #丸太販売 #木工 #林学 #社会人大学院
バズの未来を読む数理モデル – ロジスティック曲線で見える成長の行方|Yusuke Goto
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1