はじめに
Pythonでデータ分析をする際に使う操作を備忘録としてまとめた記事です。
まとめることで定着するかなと思ったので。(毎回ググるのは効率が悪い)
使うごとに更新していこうと思うので網羅率は悪いです。
基礎的なものからちょっとしたテクニックまで色々記載して日々増やしていければと思います。
項目が増えてきたら分類もできる限り整理していきます。
データ
みんな大好きタイタニックのデータを使って説明できるものは説明し、
難しい場合は別途データを定義して説明します。
import pandas as pd
# Titanicのデータを読み込む
df = pd.read_csv("data/train.csv")
テクニック
基本
基本情報を表示
型が数値の列に対し、データ数、平均値、標準偏差、最小値、1/4番目の値、中央値、3/4番目の値、最大値を計算する。
df.describe()
特定の列の平均を求める
df_time["Age"].mean()
# 29.69911764705882
列の最大値(最小値)を求める
# 最大値
df_time["Age"].max()
# 80.0
# 最小値
df_time["Age"].min()
# 0.42
列の最大値(最小値)のインデックスを取得
# 最大値のインデックス
df["Age"].idxmax()
# 630
# 最小値のインデックス
df["Age"].idxmin()
# 803
特定の列のUniqueの値をすべて表示
df["Sex"].unique()
# array(['male', 'female'], dtype=object)
特定の列のユニーク数(異なり数)を取得
df["Sex"].nunique()
# 2
並び替え
デフォルトは昇順のため、降順にしたい場合はascending=False
にする。
df.sort_values("Fare", ascending=False).head(5)
四捨五入
round
関数を利用。正数が小数点第n位、負数がnの位を四捨五入。
df["Fare"].mean().round(2)
# 32.2
各列の欠損値の数を求める
df.isnull().sum()
データを結合
df_test = pd.DataFrame(
[
[892, 0, 2, "John Han", "male", 30, 0, 0, "84202", 23, "C147", "C"],
[893, 1, 1, "Hannah May", "female", 22, 1, 0, "84205", 10.0, "B41", "S"],
],
columns=[
"PassengerId",
"Survived",
"Pclass",
"Name",
"Sex",
"Age",
"SibSp",
"Parch",
"Ticket",
"Fare",
"Cabin",
"Embarked",
],
)
df_merge = pd.concat([df, df_test], ignore_index=True)
df_merge.tail()
重複を消す
ある列に重複の値があった場合は重複を消す。
下記例の場合、"Survived"と"Pclass"で同じ組み合わせのデータは1つしかない。
個人的にはすっきりさせたいので、reset_index
でインデックスをリセットさせる。
df.drop_duplicates(subset=["Survived", "Pclass"]).reset_index(drop=True)
データ型に関する処理
各列の型を表示
df.dtypes
日付に変換
to_datetimeを利用。formatで表示形式を指定できる。
df_test = pd.DataFrame(
[
["20200101", "001", "foo"],
["20200102", "002", "hoge"],
["20200103", "003", "foge"],
],
columns=["date", "id", "value"],
)
df_test["date"] = pd.to_datetime(df_test["date"], format="%Y/%m/%d")
df_test
データ抽出
複数条件でデータを抽出
ポイントは条件の中身を()で囲うところ。
また演算子はPythonだがand
は&
、or
は|
を使うことに注意。
# 男性でかつ20歳以上
df[(df["Sex"] == "male") & (df["Age"] >= 20)].head()
日付データの絞り込み
import datetime
df_test = pd.DataFrame(
[
["20200101", "001", "foo"],
["20200102", "002", "hoge"],
["20200103", "003", "foge"],
],
columns=["date", "id", "value"],
)
df_test["date"] = pd.to_datetime(df_test["date"], format="%Y/%m/%d")
# 2020年1月のデータのみを抽出
df_test[(datetime.datetime(2020,1,1) <= df_test["date"]) & (df_test["date"] < datetime.datetime(2020,2,1))]
個人的にはすっきりしているのでBETWEEN的に使う場合は下記の書き方のほうが好き。
df_test.query(
"datetime.datetime(2020,1,1) <= date < datetime.datetime(2020,2,1)"
)
リストのいずれかの値が含まれる行を抽出
# 年齢がゾロ目フラグを追加
zorome = [11, 22, 33, 44, 55, 66, 77, 88, 99]
df["zorome_flag"] = 0
df["zorome_flag"] = df["zorome_flag"].mask(
df["Age"].isin(zorome),
1,
)
df[df["zorome_flag"] == 1].head()
特定の文字列を含むか
# 名前にJohnを含む人だけ抽出
df[df["Name"].str.contains("John")].head()
ある列の文字列が別の列の文字列に含まれるか否か
# 父親と同じ名前の人を抽出
df_test = df.head()
df_test["FatherName"] = ["Owen", "James", "Ryan", "David", "William"]
df_test[df_test.apply(lambda x: x.FatherName in x.Name, axis=1)]
時間データの絞り込み
import datetime
df_test = pd.DataFrame(
[
["20200101 09:05:00", "20200101 19:35:00", "001", "foo"],
["20200102 10:32:50", "20200102 17:11:16", "002", "hoge"],
["20200103 09:16:12", "20200103 16:54:43", "003", "foge"],
["20200201 10:21:42", "20200201 21:22:21", "004", "geho"],
["20200202 11:22:11", "20200202 18:31:01", "005", "foho"],
],
columns=["time1", "time2", "id", "value"],
)
# Timedeltaに変換
df_test["time1"] = pd.to_datetime(df_test["time1"])
df_test["time2"] = pd.to_datetime(df_test["time2"])
# 時間の差を算出
df_test["time_diff"] = df_test["time2"] - df_test["time1"]
# 10時間以内だけに絞り込む
df_test[df_test["time_diff"] <= datetime.timedelta(hours=10)]
集計
条件を絞ってデータを集計
# 性別ごとに年齢と料金の平均値を算出
df.groupby("Sex").agg({"Age": "mean", "Fare": "mean"}).head()
Groupbyして算出した情報をもとのDataFrameに追加
Groupbyした結果を元のデータに結合したい時に有効
# Pclassごとに平均チケット料金を求めて元のデータに付与
df["PclassMean"] = df.groupby(["Pclass"]).transform(np.mean)["Fare"]
df.head()
列・列同士の計算
列同士の計算結果を新しい列に追加
# (特に意味はないけど) 年齢 x 料金 をスコアとして新しい列に追加
df["Score"] = df["Age"] * df["Fare"]
df.head()
文字列抽出して新しい列に追加
# (特に意味はないけど) Ticket列の下2桁を抽出
df["TicketSuffix"] = df["Ticket"].str.extract("(..)$")
df.head()
条件に合致する場合に値を代入
# チケット代が30以上の場合、expensive_flagカラムに1を付与
df["expensive_flag"] = 0
df["expensive_flag"] = df["expensive_flag"].mask(df["Fare"] >= 30, 1)
df.head()
時間操作
timedeltaを秒とか時間に変換
import datetime
import numpy as np
df_test = pd.DataFrame(
[
["20200101 09:05:00", "20200101 19:35:00", "001", "foo"],
["20200102 10:32:50", "20200102 17:11:16", "002", "hoge"],
["20200103 09:16:12", "20200103 16:54:43", "003", "foge"],
["20200201 10:21:42", "20200201 21:22:21", "004", "geho"],
["20200202 11:22:11", "20200202 18:31:01", "005", "foho"],
],
columns=["time1", "time2", "id", "value"],
)
# Timedeltaに変換
df_test["time1"] = pd.to_datetime(df_test["time1"])
df_test["time2"] = pd.to_datetime(df_test["time2"])
# 時間の差を算出
df_test["time_diff"] = df_test["time2"] - df_test["time1"]
# 分に変更
df_test["time_diff"] = df_test["time_diff"] / np.timedelta64(1, "m")
df_test
その他
dummy変数化するけど元のカラムを残したい
df_dummy = pd.get_dummies(df, columns=["Sex"])
df = pd.concat([df_dummy, df["Sex"]], axis=1)
df.head()
マルチインデックスカラムを統合する
# グループ化
df_group = df.groupby("Sex").agg({"Age": ["max", "min"], "Fare": ["max", "min"]}).reset_index()
# ここでハイフンでつなげる
df_group.columns = ["_".join(x) for x in df_group.columns.ravel()]
# マルチインデックスでないところにもハイフンがついてしまうのでリネームする
df_group = df_group.rename(columns={"Sex_": "Sex"})
df_group
もっとスマートな方法があれば教えて下さい。。。
Dataframe同士で重複している行を削除
特定のDataFrame以外の行を抽出したい!という時に便利です。
import numpy as np
df_test = pd.DataFrame(
[
[
1,
0,
3,
"Braund, Mr. Owen Harris",
"male",
22.0,
1,
0,
"A/5 21171",
7.2500,
np.nan,
"S",
],
[
3,
1,
3,
"Heikkinen, Miss. Laina",
"female",
26.0,
0,
0,
"STON/O2. 3101282",
7.9250,
np.nan,
"S",
],
],
columns=[
"PassengerId",
"Survived",
"Pclass",
"Name",
"Sex",
"Age",
"SibSp",
"Parch",
"Ticket",
"Fare",
"Cabin",
"Embarked",
],
)
# PassengerIdの1,3が重複しているので削除される
pd.concat([df, df_test]).drop_duplicates(keep=False).head()
関数を適用
# チケット料金のランクを追加
def fare_rank(price):
if price < 10:
return "cheap"
elif price < 30:
return "normal"
else:
return "expensive"
df["fare_rank"] = df["Fare"].apply(fare_rank)
df.head()