時系列パート(基礎編:Pandasの基礎)#
使用データについて#
今回はカフェの顧客データ(cafe_customers.csv)を使用します.これは時系列データの典型例で,頻繁に扱うデータ形式です.
必要なライブラリのインポート#
pandas (pd)
目的: データの読み込み,加工,分析
特徴: ExcelのようなTable形式データ(DataFrame)を扱う
研究での用途: 実験データの前処理,統計分析,可視化用データ準備
まずは,今回のノートブックの実行に使用するライブラリをインポートします.
import pandas as pd
from datetime import timedelta
# 警告メッセージを非表示にするライブラリ・設定
import warnings
warnings.filterwarnings('ignore')
データの読み込み#
時系列データ分析の第一歩は「データを理解すること」から始まります.
どんなに高度な分析手法を使っても、データの中身を知らなければ意味のある結果は得られません.
今回使用するデータセット(cafe_customers.csv)をpandasライブラリを用いて読み込みます.
# CSVファイルの読み込み
df = pd.read_csv('../data/raw/cafe_customers.csv')
# データの最初の5行を表示
print(df.head())
データの形状とタイプの確認#
データ分析の最重要ステップの一つです.
データの「形」と「型」を理解せずに分析を進めると,誤った結論に至る可能性があります.
なぜshapeの確認が重要なのか?#
df.shape は (行数, 列数) を返します:
サンプルサイズの把握: 統計的有意性の判断材料
処理時間の予測: 大規模データでは処理方法を変える必要がある
メモリ使用量の推定: 大きすぎるデータは分割処理が必要
# データの形状を表示
print(df.shape)
データ型(dtypes)の重要性#
df.dtypes でデータ型を確認する理由:
適切な分析手法の選択:
int64/float64: 数値分析(平均,相関など)object: カテゴリ分析(クロス集計など)datetime64: 時系列分析
メモリ効率の最適化:
int32vsint64: メモリ使用量が半分カテゴリデータを
category型にすると大幅な節約
エラーの予防:
数値として扱いたいのに
object型 → 計算エラー日付として扱いたいのに
string型 → 時系列分析不可
# タイプの確認
print(df.dtypes)
欠損値の確認#
欠損値(Missing Values)は,適切に処理しないと,結果が大きく歪む可能性があります.
欠損値が研究に与える影響#
統計的バイアス: 欠損が偶然でない場合,結果が偏る
サンプルサイズの減少: 分析に使えるデータが減る
手法の制限: 多くの機械学習アルゴリズムは欠損値を扱えない
欠損値の対処法#
完全削除: 欠損があるレコードを除外(最も安全だが,データ損失大)
平均値補完: 数値データを平均値で埋める(簡単だが,分散が小さくなる)
前方補完/後方補完: 時系列データで前後の値を使用
# 欠損値の確認
print(df.isnull().sum())
時系列データの扱い方#
時系列データは,時間の経過とともに変化するデータのことで,IoT(気温,湿度など),Web解析(アクセス数など),金融(株価など),医療(心拍数など)といったあらゆる分野で活用されています.
なぜ特別な扱いが必要なのか?#
時間順序の意味: データの順番に意味がある
季節性・周期性: 曜日,月,季節による規則的な変動パターン
トレンド: 長期的な増加・減少傾向
自己相関: 過去の値が現在の値に影響する
文字列 → datetime型への変換の重要性#
「2024-01-15」のままでは,ただの文字であり,時間的な計算ができない.datetime型に変換することで,プログラムが実際の日時として認識し,曜日抽出,期間計算,時系列分析が可能になる.
# 文字列から日時型に変換する前のタイプの確認
print("変換前のタイプ: " + str(df["Timestamp"].dtype))
# 文字列から日時型に変換
df['Timestamp'] = pd.to_datetime(df['Timestamp'])
# 文字列から日時型に変換後のタイプの確認
print("変換後のタイプ: " + str(df["Timestamp"].dtype))
時系列から新しい特徴量を生成する#
曜日: 人間の行動パターンは曜日に大きく依存
時間: 日内変動(朝昼夜)のパターン分析
日付: トレンド分析,特定日の影響評価
print("--- 追加前のデータ ---")
print(df.head())
# 時系列データから新しい特徴量を抽出・追加
df['Date'] = df['Timestamp'].dt.date # 日付
df['Hour'] = df['Timestamp'].dt.hour # 時間
df['DayOfWeek'] = df['Timestamp'].dt.dayofweek # 曜日(0:月曜日 ~ 6:日曜日)
df['WeekdayName'] = df['Timestamp'].dt.day_name() # 曜日名
print("\n--- 追加後のデータ ---")
print(df.head())
基本的な統計情報の取得#
統計情報は,詳細な分析に入る前に必ず確認すべき重要な項目です.
describe()で一気に把握する重要指標#
各統計量の意味:
count (件数): サンプルサイズ
mean (平均): データの中心傾向
std (標準偏差): データのばらつき
min/max: 最小・最大値
25%/50%/75% (四分位数): データ分布の形状
# 基本的な統計情報をまとめて取得(デフォルトは数値列のみ)
print(df.describe())
# 基本的な統計情報を個別に取得(例:Customer列)
print("件数: " + str(df["Customers"].count()))
print("平均: " + str(df["Customers"].mean()))
print("最小値: " + str(df["Customers"].min()))
print("第1四分位数: " + str(df["Customers"].quantile(0.25)))
print("中央値(第2四分位数): " + str(df["Customers"].median()))
print("第3四分位数: " + str(df["Customers"].quantile(0.75)))
print("最大値: " + str(df["Customers"].max()))
print("標準偏差: " + str(df["Customers"].std()))
データのフィルタリングとソート#
フィルタリングとソートは,膨大なデータから一部を抽出し,パターンを見つける際に使用します.
フィルタリングの研究における重要性#
ノイズ除去: 分析に不要なデータを除外
条件別分析: 特定の条件下での現象を調査
異常値除去: 測定エラーや外れ値を排除
サブグループ分析: 性別,年齢層,時間帯などによる層別分析
営業時間フィルタリング#
目的: 店舗が実際に営業している時間のデータのみ分析
応用: 実験時間内のデータ,授業時間内の学習データなど
重要性: 全データを使うと,夜間(客数=0)が平均を下げて誤った結論に
# 営業時間(7時-21時)のデータをフィルタリング
business_hours = df[(df['Hour'] >= 7) & (df['Hour'] <= 21)]
print("営業時間内のデータ割合(%): " + str(len(business_hours) / len(df) * 100))
ソート(並び替え)#
上位・下位の特定: 最も効果的・非効果的な条件を発見
ランキング作成: 重要度順,効果順の評価
パターン発見: データを並び替えることで隠れた傾向を発見
# 客数が多い順にソート
sorted_df = df.sort_values(by='Customers', ascending=False)
print("客数が多いランキング(上位5件):")
print(sorted_df.head())
# 特定の曜日(土曜日)のデータを抽出
saturday_data = df[df['DayOfWeek'] == 5] # 5 = 土曜日
print("土曜日のデータ割合(%): " + str(len(saturday_data) / len(df) * 100))
print("土曜日の平均客数(人):", saturday_data['Customers'].mean())
# 複数条件でのフィルタリング例(週末のランチタイム)
weekend_lunch = df[
(df['DayOfWeek'].isin([5, 6])) & # 土日
(df['Hour'] >= 11) & (df['Hour'] <= 14) # ランチタイム
]
print("週末ランチタイムのデータ割合(%): " + str(len(weekend_lunch) / len(df) * 100))
print("週末ランチタイムの平均客数(人):", weekend_lunch['Customers'].mean())
グループ化と集約#
グループ化(groupby)は,「全体の傾向」ではなく「グループ別の特徴」を理解することで,より深い洞察が得られます.
なぜグループ化が重要なのか?#
単純集計 vs グループ別集計の違い:
単純: 「全体の平均客数は50人」→ 表面的な情報
グループ別: 「平日昼12時の平均客数は80人,深夜3時は5人」→ 具体的な行動指針
時間帯別分析 (groupby('Hour'))#
目的: 日内変動パターンの把握
応用例: 学習効率の時間変動,システム負荷の時間分析
価値: ピーク時間の特定,リソース配分の最適化
# 時間帯別の平均客数を計算
hourly_avg = df.groupby('Hour')['Customers'].mean()
print(hourly_avg)
曜日別分析 (groupby('WeekdayName'))#
目的: 週次サイクルの理解
応用例: ユーザー行動の曜日差,実験参加者の曜日による違い
価値: 曜日効果の定量化,スケジューリングの最適化
# 曜日別の平均客数
daily_avg = df.groupby('WeekdayName')['Customers'].mean()
print(daily_avg)
日次集計 (groupby('Date'))#
目的: トレンド分析,長期変動の把握
応用例: 売上推移,学習進捗,システム利用量の変化
価値: 成長率の測定,異常日の検出
# 日付別の合計客数(日次集計)
daily_total = df.groupby('Date')['Customers'].sum()
print(daily_total.head())
groupby(['WeekdayName', 'Hour'])#
意味: 「月曜日の9時」「金曜日の18時」など,具体的なシーン分析
価値: 単一軸では見えない複雑なパターンを可視化
研究応用: 実験条件の組み合わせ効果,多要因分析
unstack()メソッド#
ピボットテーブル形式への変換:
縦長データ → 横長データ(Excel的な見やすさ)
行:時間,列:曜日 → 一目で全パターンを比較可能
研究での使用: 論文の表作成,プレゼン資料の作成
# 時間×曜日のクロス統計
cross_stats = df.groupby(['WeekdayName', 'Hour'])['Customers'].mean().unstack(level=0)
print(cross_stats.head())
インデックスの操作#
インデックス操作は,特に時系列データ分析において極めて重要なスキルです.適切なインデックス設定により,データの抽出・操作が簡単になります.
なぜTimestampをインデックスにするのか?#
メリット:
日付ベースのスライシング: 「2024年1月のデータ」を簡単抽出
リサンプリング機能: 時間・日・月単位での自動集約
時系列プロット: matplotlib/seabornでの時系列グラフが美しく描画
期間計算: 「初日から7日間」のような相対的期間指定
# データフレームのコピーを作成
df_indexed = df.copy()
# インデックス操作前のデータを表示
print("--- 追加前のデータ ---")
print(df_indexed.head())
# Timestampをインデックスに設定
df_indexed = df_indexed.set_index('Timestamp')
print("\n--- 追加後のデータ ---")
print(df_indexed.head())
日付範囲での絞り込み#
実験期間中のデータのみ抽出
特定イベント前後の比較分析
季節・月別の傾向分析
# 利用可能な日付範囲を確認
print(f"開始日: {df_indexed.index.min().date()}")
print(f"終了日: {df_indexed.index.max().date()}")
# 初日のデータを抽出
first_timestamp = df_indexed.index.min() # 最初のタイムスタンプ
first_date = first_timestamp.date() # 最初の日付
# 初日の全データを取得
print(df_indexed[df_indexed.index.date == first_date])
# 初週(最初の7日間)のデータを抽出
end_date = first_date + timedelta(days=6) # 最初の日付から6日後まで
first_week = df_indexed[(df_indexed.index.date >= first_date) & (df_indexed.index.date <= end_date)]
リサンプリング#
‘H’: 1時間ごと → 時間別パターン分析
‘D’: 1日ごと → 日次トレンド分析
‘W’: 1週間ごと → 週次変動の把握
‘M’: 1月ごと → 長期トレンド分析
# リサンプリング(1時間ごとの客数合計)
hourly_data = df_indexed.resample('H')['Customers'].sum()
# 1時間ごとの客数(最初の24時間)を表示
print(hourly_data.head(24))
演習問題#
演習1: 営業時間外のデータを除外して、1時間ごとの平均客数を計算してください#
💡ヒント:
営業時間(7:00-21:00)でフィルタリング
Timestampをインデックスに設定
resample(‘H’)で1時間ごとに集約
# 回答欄
演習2: 平日と週末それぞれの時間帯別平均客数を比較してください#
# 回答欄