7. 時系列データ分析¶
第 7 回では、時系列データの分析方法について学びます。
時系列データは、時間の経過に沿って収集されたデータです。株価分析や、気象データの予測、販売データの解析、トレンド予測など、さまざまな分野で活用されています。
7.1 時系列データの基本¶
7.1.1 時系列データの定義¶
時系列データとは、各データポイントが時間に対応しているデータのことであり、主に以下の特徴を持ちます。
- データが時間軸に沿って配置されている。
- データが連続的または離散的(定期的)に収集されている。
- 過去のデータが未来のデータに影響を与えることがある。
7.1.2 時系列データ分析の目的¶
時系列データは多くの分野で使用されており、その主要な分析目的としては以下のようなものがあります。
- 過去のパターンの理解
- 将来の値の予測
- 異常値の検出
- トレンドや周期性の把握
7.2 pandas による時系列データの基本¶
時系列データ処理には、主に pandas
と numpy
ライブラリを使用します。
7.2.1 時系列データの生成¶
日付範囲のデータを生成するには、pandas.date_range()
関数を使用し、引数で開始日 (start
) やデータ数 (periods
)、頻度 (freq
) などを指定します。
import pandas as pd
import numpy as np
# データ数
n = 7
# 日付範囲の生成
date_range = pd.date_range(start="2025-06-03", periods=n, freq="D")
# 時系列データの生成
data = np.random.randn(n) # ランダムなデータ生成
ts = pd.Series(data, index=date_range)
# 時系列データの表示
print(ts)
2025-06-03 -0.957430
2025-06-04 -0.519095
2025-06-05 0.429354
2025-06-06 -0.982983
2025-06-07 -0.704216
2025-06-08 0.185925
2025-06-09 -1.301902
Freq: D, dtype: float64
頻度パラメータについて¶
pandas.date_range()
関数の頻度 (freq
) は、デフォルトで D
(1 日毎) が指定されていますが、以下のようにさまざま変更することができます。
# 毎週金曜日(W-FRI)
pd.date_range(start="2025-06-03", periods=4, freq="W-FRI")
# 3日毎
pd.date_range(start="2025-06-03", periods=10, freq="3D")
# 2時間毎
pd.date_range(start="2025-06-03", periods=12, freq="2h")
その他の頻度パラメータの詳細については、こちら (Offset aliases) を確認するようにしてください。
7.2.2 CSV ファイルから時系列データの作成¶
CSV ファイルから時系列データを作成する方法を見ていきましょう。以下のような時系列情報を含む CSV ファイルがあるとします。
date,time[s],sessions
2025-04-15,6987,162
2025-04-16,20116,310
2025-04-17,1078,5
2025-04-18,12787,194
2025-04-19,2276,5
...
これを DataFrame として読み込むには、以前にも紹介したように、pandas.read_csv()
関数を用います。しかし、そのままでは先頭の日付の列 (date
) が文字列として認識されてしまい、時系列データとして扱うことができません。このような場合、以下のような手順で時系列データに変換します。
pandas.to_datetime()
関数で日付の列をdatetime
型に変換DataFrame.set_index()
関数で日付の列を DataFrame のインデックスに設定
実際のコード例は以下のようになります。
import pandas as pd
# CSV ファイルの読み込み
df = pd.read_csv("log_0000001.csv")
# 時系列データに変換
df["date"] = pd.to_datetime(df["date"])
# インデックスを date に設定
df = df.set_index("date")
# 先頭行の表示
print(df.head())
time[s] sessions
date
2025-04-15 6987 162
2025-04-16 20116 310
2025-04-17 1078 5
2025-04-18 12787 194
2025-04-19 2276 5
CSV の読込と同時に時系列データ作成¶
はじめから CSV のどの列が日時を表すかわかっている場合は、pandas.read_csv()
関数の引数で parse_dates=["列名"]
を指定することで、特定の列を自動的に datetime
型に変換したうえで読み込みます。以下は、date
列を datetime
型として読み込む例です。
さらに、インデックスを date
列にしたい場合は、以下のように index_col
を指定します。
df = pd.read_csv("log_0000001.csv", parse_dates=["date"], index_col="date")
Note
データが日付順になっていない場合は、DataFrame.sort_index()
関数で並び替えることができます。このとき、引数で ascending=False
を指定すると、降順に並び替えることができます。
7.2.3 データの選択とスライシング¶
通常の DataFrame と同様に、DataFrame.loc
インデクサを用いることで、時系列データから特定の日時の選択や、スライシングを行うことができます。
# 特定の日付を選択
print(df.loc["2025-04-15"])
# 特定の日付範囲を抽出(スライシング)
print(df.loc["2025-04-15":"2025-04-22"])
time[s] 6987
sessions 162
Name: 2025-04-15 00:00:00, dtype: int64
time[s] sessions
date
2025-04-15 6987 162
2025-04-16 20116 310
2025-04-17 1078 5
2025-04-18 12787 194
2025-04-19 2276 5
2025-04-20 8045 85
2025-04-21 10033 119
2025-04-22 9928 182
7.3 日付と時間のデータ型と変換¶
時系列データを正しく扱うために、日付と時間を適切に扱うための方法を学びましょう。
7.3.1 日付と時間のデータ型¶
Python で日付と時間の効率的に取り扱うためには、datetime
モジュールを使います。このモジュールに含まれる datetime
クラスは、日付と時間の両方を扱うことができます。
datetime
モジュールから datetime
クラスをインポートするには、以下のようにします。
datetime.now()
関数を用いると、現在の日時を取得することができます。
datetime
クラスの他にも、日付のみを扱う date
クラスや、時間のみを扱う time
クラスも用意されています。
7.3.2 文字列から datetime
への変換¶
文字列を datetime
型(datetime
クラスのオブジェクト)に変換するには、strptime()
関数を用います。
from datetime import datetime
date_string = "2025-05-30 14:30:00"
dt = datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S")
print("変換された日時: ", dt)
ただし、pandas
を使用している場合、基本的には pandas.to_datetime()
関数を使用したほうが柔軟性が高く便利です。
7.3.3 datetime
から文字列への変換¶
datetime
型のオブジェクトを文字列に変換するには、strftime()
関数を用います。このとき、引数で書式コードを指定することで、変換後の日時表記のスタイルを調整することができます。
from datetime import datetime
# 現在日時の取得
now = datetime.now()
# datetime を文字列に変換
formatted_date = now.strftime("%Y年%m月%d日 %H時%M分%S秒")
print("フォーマット前の日時:" , now)
print("フォーマット後の日時:" , formatted_date)
書式の詳細は、Python 公式ドキュメントの「strftime() と strptime() の書式コード」を参照するようにしてください。
7.3.4 タイムゾーンの取り扱い¶
datetime
型のオブジェクトは、デフォルトではタイムゾーンの情報を含みません (naive datetime)。
datetime
モジュールで任意のタイムゾーンを設定するには、zoneinfo
モジュールを用います。例えば、タイムゾーンを JST (日本時間, UTC+0900) にするには、以下のようにします。
from datetime import datetime
from zoneinfo import ZoneInfo
# 現在日時の取得(タイムゾーン: JST)
now = datetime.now(ZoneInfo("Asia/Tokyo"))
print(now)
pandas
でタイムゾーンを設定するには、DataFrame.tz_localize()
関数を用います。タイムゾーンを JST にするには、引数で "Asia/Tokyo"
を指定します。以下は、日時の文字列を datetime
に変換し、インデックスに設定したうえで、タイムゾーンを JST にするコードの例です。
import pandas as pd
# CSV の読み込み & 時系列データに変換 (index = naive timezone)
df = pd.read_csv("log_0000001.csv", parse_dates=["date"], index_col="date")
# タイムゾーンを JST に設定
df.index = df.index.tz_localize("Asia/Tokyo")
print(df.index[0])
また、既に設定されているタイムゾーンを別のタイムゾーンに変換するには、DataFrame.tz_convert()
関数を用います。
7.4 時系列データの可視化¶
時系列データを可視化する方法を見ていきましょう。
7.4.1 Matplotlib による時系列データ可視化¶
時系列データはこれまで学んできた方法と同様、Matplotlib や Seaborn で可視化することができます。Matplotlib による基本的な折れ線グラフを描くには、以下のように pyplot.plot()
関数を用います。
import pandas as pd
import matplotlib.pyplot as plt
# CSV の読み込み & 時系列データに変換
df = pd.read_csv("log_0000001.csv", parse_dates=["date"], index_col="date")
# 折れ線グラフで可視化
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(df["time[s]"])
ax.set_ylabel("時間(秒)")
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
7.4.2 Matplotlib による時系列データの軸調整¶
前項では pyplot.plot()
関数による時系列データの可視化を行いましたが、日付軸のフォーマットや、軸の間隔を調整することで、より見やすいグラフにすることができます。
日付軸のフォーマット設定には、matplotlib.dates
モジュールに含まれる dates.DateFormatter
や dates.DayLocator
が役に立ちます。例えば、ax
という名前の Axes
オブジェクトがあるとき、これらは以下のようにして使うことができます。
import matplotlib.dates as mdates
# x軸の日付を「2025/04/13」→「4/13」のようにフォーマット
ax.xaxis.set_major_formatter(mdates.DateFormatter("%-m/%-d"))
# x軸の日付の間隔を3日毎に設定
ax.xaxis.set_major_locator(mdates.DayLocator(interval=3))
日付の軸ラベルが「4/13」のようにシンプルになり、3 日毎に配置されるようになりました。
プログラムは少しややこしく感じられるかもしれませんが、はじめのうちはこういうものであると思い、フォーマット指定部分や数字の部分のみを変更するくらいでも問題ありません。以下は、上記コードのフォーマット設定を構成する要素の概説です。
Axes.xaxis
:Axes
オブジェクトの中にある x 軸に関するプロパティです。axis.set_major_formatter()
: 主ラベル (major ticks) のフォーマットを指定します。axis.set_major_locator()
: 主ラベル (major ticks) を置く位置を指定します。dates.DateFormatter()
: 日付フォーマットのオブジェクトです。dates.DayLocator()
: 日付の配置に関するオブジェクトです。
7.4.3 複合グラフの描画¶
Matplotlib を使用して、複合グラフを描いてみましょう。複合グラフとは、複数の異なる種類のグラフを組み合わせて描画するグラフのことで、例えば「折れ線グラフ」と「棒グラフ」を組み合わせたものなどです。
試しに、これまでの方法を組み合わせて折れ線グラフを棒グラフを同時に描いてみましょう。以下のコード例では、折れ線グラフに time[s]
(活動時間(秒))を、棒グラフに sessions
(セッション数)を指定しています。
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# CSV読み込み & 日付をインデックスに
df = pd.read_csv("log_0000001.csv", parse_dates=["date"], index_col="date")
# 図と左y軸を作成
fig, ax = plt.subplots(figsize=(10, 4))
# 折れ線グラフ: time[s]
ax.plot(
df.index,
df["time[s]"],
color="royalblue",
marker="o",
label="time [s]"
)
ax.set_ylabel("活動時間(秒)")
# 折れ線グラフ: sessions
ax.bar(
df.index,
df["sessions"],
color="red",
alpha=0.4,
label="sessions"
)
ax.set_ylabel("セッション数")
# x軸フォーマットと間隔
ax.xaxis.set_major_formatter(mdates.DateFormatter("%-m/%-d"))
ax.xaxis.set_major_locator(mdates.DayLocator(interval=3))
# レイアウト調整・表示
fig.tight_layout()
plt.show()
折れ線グラフと棒グラフを同時に表示することができました。しかし、2 つのグラフの軸が共有されているため、数値のスケールが合っておらず、セッション数の棒グラフの様子が見えにくくなってしまっています。このようなときは、グラフの左側と右側で、それぞれ異なるスケールの軸を設定することが望ましいです。
x 軸を共有して、y 軸を別に作成するためには、Axes.twinx()
関数を用います。そうすることで、以下のように、スケールが異なっていても視覚的にわかりやすい複合グラフを作成することができます。
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# CSV読み込み & 日付をインデックスに
df = pd.read_csv("log_0000001.csv", parse_dates=["date"], index_col="date")
# 図と左y軸を作成
fig, ax1 = plt.subplots(figsize=(10, 4))
# 折れ線グラフ: time[s]
line = ax1.plot(
df.index,
df["time[s]"],
color="royalblue",
marker="o",
label="time [s]"
)
ax1.set_ylabel("活動時間(秒)")
ax1.set_ylim(0)
# 右y軸を作成
ax2 = ax1.twinx()
# 折れ線グラフ: sessions
bar = ax2.bar(
df.index,
df["sessions"],
color="red",
alpha=0.4,
label="sessions"
)
ax2.set_ylabel("セッション数(回)")
# x軸フォーマットと間隔
ax1.xaxis.set_major_formatter(mdates.DateFormatter("%-m/%-d"))
ax1.xaxis.set_major_locator(mdates.DayLocator(interval=3))
# 凡例をまとめて表示(折れ線とバー両方)
ax1.legend([line[0], bar], ["活動時間(秒)", "セッション数(回)"], loc="upper left")
# レイアウト調整・表示
fig.tight_layout()
plt.show()
複数の凡例をまとめて表示
ここでは折れ線グラフを line
、棒グラフを bar
という変数に格納しており、Axes.legend()
関数にラベル情報を併せてリストで渡すことで、複数の凡例をまとめて表示させています。なお、pyplot.plot()
関数はリストを返すため、line
には [0]
をつけて先頭のデータを渡しています。
7.5 時系列データの前処理¶
本章の最後に、時系列データに対する重要な前処理のテクニックを幾つか見ていきましょう。
7.5.1 リサンプリング¶
リサンプリングとは、時系列データの頻度(間隔)を変換する操作のことです。高頻度のデータを低頻度に変換することを ダウンサンプリング、低頻度のデータを高頻度に変換することを アップサンプリング と呼びます。
ダウンサンプリング¶
ダウンサンプリングでは、一般的にデータを集約することで、頻度を減らします。例えば、1 日単位のデータを 1 週間単位に変換する場合、DataFrame.resample()
関数を用いて以下のように書きます。
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# CSV読み込み & 日付をインデックスに
df = pd.read_csv("log_0000001.csv", parse_dates=["date"], index_col="date")
# 一週間単位のデータを集約(平均化)
resampled = df.resample("7D").mean()
# 図と左y軸を作成
fig, ax = plt.subplots(figsize=(10, 4))
# 折れ線グラフ: time[s]
ax.plot(
resampled.index,
resampled["time[s]"],
color="royalblue",
marker="o",
label="time [s]"
)
ax.set_ylabel("活動時間(秒)")
ax.set_ylim(0)
# x軸フォーマットと間隔
ax.xaxis.set_major_formatter(mdates.DateFormatter("%-m/%-d"))
ax.xaxis.set_major_locator(mdates.DayLocator(interval=3))
# レイアウト調整・表示
fig.tight_layout()
plt.show()
時系列データの大局的な変動を表すグラフになりました。ダウンサンプリングの集約処理としては、主に以下の関数が用いられます。
mean()
: 平均値を計算sum()
: 合計値を計算min()
: 最小値を計算max()
: 最大値を計算
アップサンプリング¶
アップサンプリングでは、新たにデータポイントを補完して、データの頻度を増やします。DataFrame.resample("[freq]").asfreq()
とすることで、指定した頻度 (freq
) のデータポイントが追加され、NaN
で補完されます。同時に欠損値処理を行う場合は、続けて DataFrame.fillna()
関数などを使用します。
その他の時系列データ分析
本講義では取り扱いませんが、時系列データを分析するうえで重要なキーワードとして、「移動平均」「トレンド抽出」「季節性抽出」「自己回帰」「ラグプロット」などがあります。高度な分析に興味のある人は、これらのキーワードを参考に調べてみてください。
演習¶
演習 7-1
活動ログデータの CSV ファイルを読み込み、セッション効率(1セッションあたりの活動時間)を計算してください。その後、欠損値処理をしたうえで、セッション効率の時間変化を折れ線グラフで可視化してください。
演習 7-2
活動ログデータの CSV ファイルを読み込み、曜日別の活動時間を計算してください。その後、曜日ごとの活動時間の平均を棒グラフで可視化してください。
曜日情報取得のヒント
日付データから曜日情報を取得するには、DatetimeIndex.day_name()
関数または DatetimeIndex.dayofweek
プロパティを使用します。例えば、インデックスに日付が入った df
という名前の DataFrame
があり、各日付の曜日を取得して "weekday"
という名前の列にその情報を格納したい場合は、以下のようにします。
活動ログデータ¶
活動ログデータについて
CHIKUWA Editor の「統計情報」のウィンドウ下部から、以下の3つの CHIKUAW Editor 利用ログデータをダウンロードできるようになっていますので、演習や課題に活用してください。
- 自分のログデータ: あなた自身の CHIKUWA Editor 利用ログです。日別の「活動時間(秒)」と「セッション数(回)」を CSV 形式でダウンロードできます。ここで「セッション数」は、「保存」や「実行」など、エディタで何らかの行動を起こした回数に相当します。また、「活動時間」は推定値となっており、実際の活動時間とは異なる可能性があります。
- ID: 0000001 のログデータ: 「ID: 0000001」(加納)の CHIKUWA Editor 利用ログです。日別の「活動時間」と「セッション数」が含まれており、学習目的であれば自由に利用可能です。
- 全体のログデータ(日別総計): 日別の「活動時間」「セッション数」の総計、および「アクティブユーザ数(人)」が含まれる、匿名化処理済みのデータです。匿名性を担保するため、アクティブユーザ数が3人以下の場合は「アクティブユーザ数」を
NaN
としています。
ログデータの利用にあたって
ログデータには、あなた自身の CHIKUWA Editor 利用ログや、クラス全体の統計情報(総計、アクティブユーザ数)が含まれています。このデータは、学習目的(分析・可視化・傾向の考察)においてのみに使用してください。クラス全体の統計情報は、個人を特定できないように集計されていますが、他人を推測したり評価するような使い方は決してしないようにしてください。
なお、このデータは「自分の学習傾向を振り返る」「集団の傾向と比較する」ために用意されています。他者との競争ではなく、より良い学びのヒントを見つける材料として活用してください。
※ 授業外では CHIKUWA Editor をあまり使用していない場合は、自身のログデータの分析が難しいかもしれません。そのような場合は、ID: 0000001(加納)のログデータをダウンロードして分析の練習に活用してください。