こんにちは、たねやつです。
前回は、Pythonと正規表現を駆使して、ぴよログのテキストデータをプログラムで扱える「構造化データ(JSON形式)」に変換しました。これでデータ活用の第一歩を踏み出せました。
しかし、現在のデータはまだ「生の」状態です。例えば、ミルクの量が 240ml
という文字列のままだったり、日付と時刻が別の項目になっていたりと、このままではAIがデータの意味を正確に理解し、計算などに使うことが困難です。
そこで今回は、データ分析の必須ライブラリである Pandas を使って、この生のデータをさらに洗練された形式に整える「データ前処理」を行います。AIにとって分かりやすく、栄養価の高い"食事"を用意してあげるようなイメージですね!
前の記事
この記事でできること
- 前回作成したJSONデータを、Pythonのデータ分析ライブラリ Pandas の DataFrame という形式で読み込む方法がわかる。
- 日付と時刻の文字列を結合し、時系列分析に不可欠な
datetime
型に変換する方法を習得できる。
- 「240ml」や「5分」のような文字列から、数値だけを抽出して計算可能な形式にクレンジングする方法がわかる。
- データをより扱いやすくするために、データ型を適切に変換する作業の重要性が理解できる。
事前に必要なもの
- Pythonの実行環境
- Pandasライブラリ: 本記事の主役です。インストールされていない場合は、ターミナル(コマンドプロンプト)で以下のコマンドを実行してください。
python -m venv venv
.\venv\Scripts\activate
pip install pandas
- 前回の成果物:
parse_piyolog.py
と、それによって生成されたJSONデータ。今回はこのJSONデータを入力として使います。
Pandas DataFrameにデータを読み込む
まずは、前回作成したパーサー (parse_piyolog.py
) の末尾を少し改造して、解析結果をJSONファイルとして出力するようにしましょう。そして、そのJSONファイルをPandasで読み込みます。
1. parse_piyolog.py
を修正してJSONファイルを出力
if __name__ == '__main__':
ブロックを以下のように変更します。標準出力に表示していた部分を、ファイルに書き出す処理に置き換えるだけです。
if __name__ == '__main__':
logs_directory = 'logs'
structured_data = parse_all_logs(logs_directory)
structured_data.sort(key=lambda x: (x['date'], x['time']))
with open('piyolog_structured.json', 'w', encoding='utf-8') as f:
json.dump(structured_data, f, indent=2, ensure_ascii=False)
print(f"'{logs_directory}' ディレクトリのログを解析し、piyolog_structured.json に保存しました。")
このスクリプトを再度実行すると、piyolog_structured.json
というファイルがプロジェクトフォルダ内に生成されます。
2. 新しいスクリプトでJSONをDataFrameに読み込む
ここからが本番です。preprocess_data.py
という新しいファイルを作成し、Pandasを使ってデータを読み込みます。
import pandas as pd
df = pd.read_json('piyolog_structured.json')
print(df.head())
これを実行すると、スプレッドシートのような表形式でデータが表示されるはずです。この表が DataFrame で、Pandasでデータを扱う際の基本単位となります。
date time event detail memo
0 2022/05/24 22:15 おしっこ None None
1 2022/05/24 22:15 うんち None None
2 2022/05/24 22:30 ミルク 40ml None
3 2022/05/24 22:40 寝る None None
4 2022/06/01 00:55 起きる (1時間55分) None
データ前処理の実装
ここから、このDataFrameを本格的に加工していきます。AIが理解しやすいように、イベントごとに情報を整理し、新しいカラムを追加していくのが目標です。
1. 日付と時刻を datetime
型に変換
まずは基本のキ。date
と time
カラムは現在ただの文字列(object型)です。これらを結合して、Pandasが時刻として認識できる datetime
型に変換します。これにより、後々「2時間後のイベント」のような時間計算が簡単にできるようになります。
df['datetime'] = pd.to_datetime(df['date'] + ' ' + df['time'], errors='coerce')
df = df.drop(columns=['date', 'time'])
df = df.set_index('datetime')
print("--- datetime変換後 ---")
print(df.info())
print(df.head())
df.info()
の出力を見ると、インデックスが DatetimeIndex
という型に変わっていることが確認できます。これで時系列データを扱う準備が整いました。
2. detail
カラムをイベントごとに解析
次に、本丸である detail
カラムのクレンジングです。このカラムには (1時間55分)
や 左14分 ◀ 右7分
のように、イベントの種類によって全く異なる形式の情報が入っています。
そこで、イベントの種類 (event
カラムの値) に応じて、それぞれ専用の解析処理を適用し、意味のある新しいカラム(sleep_duration_min
や milk_ml
など)を作成していきます。
ヘルパー関数を定義する
まずは、特定の文字列から数値を抽出するための小さな関数をいくつか定義します。正規表現が再び活躍します。
import re
def get_sleep_duration(detail):
""" 'xx時間yy分' から合計分を計算する """
if pd.isna(detail): return None
h_match = re.search(r'(\d+)時間', str(detail))
m_match = re.search(r'(\d+)分', str(detail))
hours = int(h_match.group(1)) if h_match else 0
minutes = int(m_match.group(1)) if m_match else 0
return hours * 60 + minutes
def get_milk_volume(detail):
""" 'xxxml' から数値(xxx)を抽出する """
if pd.isna(detail): return None
match = re.search(r'(\d+)ml', str(detail))
return int(match.group(1)) if match else None
def get_breast_milk_time(detail, side):
""" '左x分 / 右y分' などから左右それぞれの時間を抽出する """
if pd.isna(detail): return 0
pattern = r'左(\d+)分' if side == 'left' else r'右(\d+)分'
match = re.search(pattern, str(detail))
return int(match.group(1)) if match else 0
Pandasの apply
を使ってデータを一括処理
次に、これらのヘルパー関数を使って、新しいカラムにデータを格納していきます。
is_wakeup = df['event'] == '起きる'
df.loc[is_wakeup, 'sleep_duration_min'] = df.loc[is_wakeup, 'detail'].apply(get_sleep_duration)
is_milk = df['event'] == 'ミルク'
df.loc[is_milk, 'milk_ml'] = df.loc[is_milk, 'detail'].apply(get_milk_volume)
is_breast_milk = df['event'] == '母乳'
df.loc[is_breast_milk, 'breast_milk_left_min'] = df.loc[is_breast_milk, 'detail'].apply(get_breast_milk_time, side='left')
df.loc[is_breast_milk, 'breast_milk_right_min'] = df.loc[is_breast_milk, 'detail'].apply(get_breast_milk_time, side='right')
df = df.drop(columns=['detail', 'memo'])
print("\n--- detail解析後 ---")
print(df[is_breast_milk | is_wakeup].head())
df['event'] == '起きる'
のような条件で、処理対象の行を絞り込みます(Boolean Indexing)。
.loc[行の条件, 列名]
で対象のデータを選択し、.apply(関数)
を使って一括で変換処理を適用しています。
- これにより、イベントごとに最適化されたデータクレンジングが実現できました。
コード全体像
preprocess_data.py
の最終的なコードは以下のようになります。
import pandas as pd
import re
def get_sleep_duration(detail):
""" 'xx時間yy分' から合計分を計算する """
if pd.isna(detail): return None
h_match = re.search(r'(\d+)時間', str(detail))
m_match = re.search(r'(\d+)分', str(detail))
hours = int(h_match.group(1)) if h_match else 0
minutes = int(m_match.group(1)) if m_match else 0
return hours * 60 + minutes
def get_milk_volume(detail):
""" 'xxxml' から数値(xxx)を抽出する """
if pd.isna(detail): return None
match = re.search(r'(\d+)ml', str(detail))
return int(match.group(1)) if match else None
def get_breast_milk_time(detail, side):
""" '左x分 / 右y分' などから左右それぞれの時間を抽出する """
if pd.isna(detail): return 0
pattern = r'左(\d+)分' if side == 'left' else r'右(\d+)分'
match = re.search(pattern, str(detail))
return int(match.group(1)) if match else 0
df = pd.read_json('piyolog_structured.json')
df['datetime'] = pd.to_datetime(df['date'].astype(str) + ' ' + df['time'].astype(str), errors='coerce')
df = df.drop(columns=['date', 'time'])
df = df.set_index('datetime')
is_wakeup = df['event'] == '起きる'
df.loc[is_wakeup, 'sleep_duration_min'] = df.loc[is_wakeup, 'detail'].apply(get_sleep_duration)
is_milk = df['event'] == 'ミルク'
df.loc[is_milk, 'milk_ml'] = df.loc[is_milk, 'detail'].apply(get_milk_volume)
is_breast_milk = df['event'] == '母乳'
df.loc[is_breast_milk, 'breast_milk_left_min'] = df.loc[is_breast_milk, 'detail'].apply(get_breast_milk_time, side='left')
df.loc[is_breast_milk, 'breast_milk_right_min'] = df.loc[is_breast_milk, 'detail'].apply(get_breast_milk_time, side='right')
df = df.drop(columns=['detail', 'memo'])
df = df.sort_index()
df.to_csv('piyolog_preprocessed.csv')
print("--- 最終的なDataFrame ---")
print(df.head())
print("\n--- データ型 ---")
print(df.info())
print("\npiyolog_preprocessed.csv に保存しました。")
最後に
今回は、データ分析の土台作りとして非常に重要な「データ前処理」のプロセスを、Pandasを使って実践しました。生のデータをそのまま使うのではなく、このように一手間加えてあげることで、後続のAIモデルのパフォーマンスは劇的に向上します。
- 文字列を
datetime
型に変換して、時間の概念をコンピュータに理解させる。
- イベントごとに文字列から数値や単位を分離して、計算可能なデータと意味のあるカテゴリに分ける。
これらの作業は、RAGシステムに限らず、あらゆるデータサイエンスのタスクで基本となる考え方です。
さて、これでようやくAIに入力するための「きれいなデータ」が準備できました。次回は、このデータをRAGシステムで使えるように、意味のある塊(チャンク)に分割する「チャンキング」というステップに進みます。
次の記事