手元にあるデータを扱いやすい形にするためには、データ読み込みと前処理が必要です。
この記事では、csvファイルの読み込みと型変換、欠損処理の方法を紹介します。
作業環境は、vscode を使用しています。
練習用 CSV を準備する
この記事では以下のサンプルデータを使います。
下のテキストをコピーして、テキストエディタ(メモ帳など)に貼り付け、sample_data.csv という名前で保存してください。
顧客ID,日付,カテゴリ,売上,担当者
00123,2024/01/05,食品,12000,田中
00456,2024/01/07,日用品,N/A,佐藤
00789,2024/01/10,食品,8500,田中
01011,2024/01/12,,5200,
01234,2024/01/15,日用品3400,鈴木
01567,2024/01/18,食品,15000,佐藤
01890,2024/01/20,家電,N/A,田中
02112,2024/01/22,日用品,9800,
02345,2024/01/25,家電,42000,鈴木
02678,2024/01/28,食品,,田中
02901,2024/02/01,家電,88000,佐藤
03124,2024/02/03,日用品,1200,鈴木
03457,2024/02/05,食品,N/A,
03680,2024/02/08,家電,67000,田中
03903,2024/02/10,食品,4300,佐藤
データを読み込む:read_csv
まず CSV ファイルを DataFrame として読み込みます。
import pandas as pd
df = pd.read_csv('sample_data.csv')
print(df.head())
print('---')
print(df.dtypes)
出力
顧客ID 日付 カテゴリ 売上 担当者
0 123 2024/01/05 食品 12000 田中
1 456 2024/01/07 日用品 NaN 佐藤
2 789 2024/01/10 食品 8500 田中
3 1011 2024/01/12 NaN 5200 NaN
4 1234 2024/01/15 日用品 3400 鈴木
---
顧客ID int64
日付 str
カテゴリ str
売上 str
担当者 str
dtype: object
出力を見ると顧客IDの先頭の0が消えるなど、いくつか意図通りにならない結果になりました。
よくある問題と対処
意図しない挙動を以下の表にまとめました。
| 問題 | 内容 |
|---|---|
| 先頭ゼロのある顧客ID | 00123 など。そのまま読むと 123 になってしまう |
| 日付が文字列 | 2024/01/05 形式。そのままでは日付として扱えない |
| 全角文字が売上 | 文字列が混在 |
| 欠損値 | 売上・カテゴリ・担当者に空欄あり |
型変換
上記で取り上げた問題を解決していきます。
文字列への変換:dtype
先頭にゼロがある顧客IDをそのまま扱うには、read_csv の引数で dtype で文字列で指定します。
df = pd.read_csv(
'sample_data.csv',
dtype={'顧客ID': str}, # 先頭ゼロが消えるのを防ぐ
)
df['顧客ID'].head()
出力
0 00123
1 00456
2 00789
3 01011
4 01234
Name: 顧客ID, dtype: str
日付への変換:to_datetime
日付は pd.to_datetime を使うと文字列を日付型に変換できます。
df["日付"] = pd.to_datetime(df["日付"], format="%Y/%m/%d") # 日付列を日付型に変換
df["日付"].head()
出力
0 2024-01-05
1 2024-01-07
2 2024-01-10
3 2024-01-12
4 2024-01-15
Name: 日付, dtype: datetime64[us]
変換後は .dt アクセサで年・月・日・曜日を取り出せます。
print(df["日付"].dt.year.head())
print(df["日付"].dt.month.head())
print(df["日付"].dt.day.head())
print(df["日付"].dt.day_name().head())
出力
0 2024
1 2024
2 2024
3 2024
4 2024
Name: 日付, dtype: int32
0 1
1 1
2 1
3 1
4 1
Name: 日付, dtype: int32
0 5
1 7
2 10
3 12
4 15
Name: 日付, dtype: int32
0 Friday
1 Sunday
2 Wednesday
3 Friday
4 Monday
Name: 日付, dtype: str
数値への変換:to_numeric
売上は pd.to_numeric を使って数値に変換しますが、まずは全角文字を半角に変換するために str.maketrans を使用します。
ZENKAKU = str.maketrans('0123456789', '0123456789')
df['売上'] = df['売上'].str.translate(ZENKAKU)
print(df['売上'].head())
print('---')
df['売上'] = pd.to_numeric(df['売上'])
print(df['売上'].head())
出力
0 12000
1 NaN
2 8500
3 5200
4 3400
Name: 売上, dtype: str
---
0 12000.0
1 NaN
2 8500.0
3 5200.0
4 3400.0
Name: 売上, dtype: float64
欠損処理:dropna/ fillna
欠損値(NaN)を放置すると集計結果がずれたりエラーになったりします。
方針は「削除するか埋めるか」の二択です。
欠損の確認
まず欠損の状況を把握してから方針を決めます。
print(df.isnull().sum()) # 列ごとの欠損件数
顧客ID 0
日付 0
カテゴリ 1
売上 4
担当者 3
dtype: int64
削除:dropna
欠損を含む行ごと取り除く場合は dropna を使用しますが、重要な列だけを subset で指定するのが安全です。
print(df.dropna().head()) # 1つでも NaN がある行を削除(カテゴリ、売上、担当者)
print('---')
print(df.dropna(subset=['売上']).head()) # 特定の列に NaN がある行だけ削除
出力
print(df.dropna().head()) # 1つでも NaN がある行を削除(カテゴリ、売上、担当者)
print('---')
print(df.dropna(subset=['売上']).head()) # 特定の列に NaN がある行だけ削除
穴埋め:fillna
欠損を特定の値で置き換える場合は、fillna で欠損が分かるような0や不明、もしくは統計に影響が小さい平均値を埋めます。
print(df["売上"].fillna(0)) # 数値列は 0 で埋める
print('---')
print(df["カテゴリ"].fillna("不明")) # 文字列列は固定値で埋める
print('---')
print(df["売上"].fillna(df["売上"].mean())) # 平均値で埋める
出力
0 12000.0
1 0.0
2 8500.0
3 5200.0
4 3400.0
5 15000.0
6 0.0
7 9800.0
8 42000.0
9 0.0
10 88000.0
11 1200.0
12 0.0
13 67000.0
14 4300.0
Name: 売上, dtype: float64
---
0 食品
1 日用品
2 食品
3 不明
4 日用品
5 食品
6 家電
7 日用品
8 家電
9 食品
10 家電
11 日用品
12 食品
13 家電
14 食品
Name: カテゴリ, dtype: str
---
0 12000.000000
1 23309.090909
2 8500.000000
3 5200.000000
4 3400.000000
5 15000.000000
6 23309.090909
7 9800.000000
8 42000.000000
9 23309.090909
10 88000.000000
11 1200.000000
12 23309.090909
13 67000.000000
14 4300.000000
Name: 売上, dtype: float64
まとめ:前処理の基本の流れ
この記事ではデータ読み込みから前処理までを紹介しました。
最後に記事内で使用したコードと前処理後データをまとめました。
import pandas as pd
# 1. 読み込み
df = pd.read_csv('sample_data.csv', dtype={'顧客ID': str})
# 2. 型変換
df["日付"] = pd.to_datetime(df["日付"], format="%Y/%m/%d")
#
ZENKAKU = str.maketrans('0123456789', '0123456789')
df['売上'] = df['売上'].str.translate(ZENKAKU)
df['売上'] = pd.to_numeric(df['売上'])
# 3. 欠損処理
df['カテゴリ'] = df['カテゴリ'].fillna('不明')
df['売上'] = df['売上'].fillna(df['売上'].mean())
df['担当者'] = df['担当者'].fillna('不明')
# 4. 確認
print(df.dtypes)
print('---')
print(df.isnull().sum())
顧客ID,日付,カテゴリ,売上,担当者
00123,2024-01-05,食品,12000.0,田中
00456,2024-01-07,日用品,23309.090909090908,佐藤
00789,2024-01-10,食品,8500.0,田中
01011,2024-01-12,不明,5200.0,不明
01234,2024-01-15,日用品,3400.0,鈴木
01567,2024-01-18,食品,15000.0,佐藤
01890,2024-01-20,家電,23309.090909090908,田中
02112,2024-01-22,日用品,9800.0,不明
02345,2024-01-25,家電,42000.0,鈴木
02678,2024-01-28,食品,23309.090909090908,田中
02901,2024-02-01,家電,88000.0,佐藤
03124,2024-02-03,日用品,1200.0,鈴木
03457,2024-02-05,食品,23309.090909090908,不明
03680,2024-02-08,家電,67000.0,田中
03903,2024-02-10,食品,4300.0,佐藤
データが整ったら集計に進みましょう

コメント