0
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DataFrameの操作まとめ

Last updated at Posted at 2021-03-09

はじめに

Pythonでデータ分析をする際に使う操作を備忘録としてまとめた記事です。
まとめることで定着するかなと思ったので。(毎回ググるのは効率が悪い)
使うごとに更新していこうと思うので網羅率は悪いです。
基礎的なものからちょっとしたテクニックまで色々記載して日々増やしていければと思います。
項目が増えてきたら分類もできる限り整理していきます。

データ

みんな大好きタイタニックのデータを使って説明できるものは説明し、
難しい場合は別途データを定義して説明します。

import pandas as pd
# Titanicのデータを読み込む
df = pd.read_csv("data/train.csv")

image.png

テクニック

基本

基本情報を表示

型が数値の列に対し、データ数、平均値、標準偏差、最小値、1/4番目の値、中央値、3/4番目の値、最大値を計算する。

df.describe()

image.png

特定の列の平均を求める

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)

image.png

四捨五入

round関数を利用。正数が小数点第n位、負数がnの位を四捨五入。

df["Fare"].mean().round(2)
# 32.2

各列の欠損値の数を求める

df.isnull().sum()

image.png

データを結合

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()

image.png

重複を消す

ある列に重複の値があった場合は重複を消す。
下記例の場合、"Survived"と"Pclass"で同じ組み合わせのデータは1つしかない。
個人的にはすっきりさせたいので、reset_indexでインデックスをリセットさせる。

df.drop_duplicates(subset=["Survived", "Pclass"]).reset_index(drop=True)

image.png

データ型に関する処理

各列の型を表示

df.dtypes

image.png

日付に変換

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

image.png

データ抽出

複数条件でデータを抽出

ポイントは条件の中身を()で囲うところ。
また演算子はPythonだがand&or|を使うことに注意。

# 男性でかつ20歳以上
df[(df["Sex"] == "male") & (df["Age"] >= 20)].head()

image.png

日付データの絞り込み

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)"
)

image.png

リストのいずれかの値が含まれる行を抽出

# 年齢がゾロ目フラグを追加
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()

image.png

特定の文字列を含むか

# 名前にJohnを含む人だけ抽出
df[df["Name"].str.contains("John")].head()

image.png

ある列の文字列が別の列の文字列に含まれるか否か

# 父親と同じ名前の人を抽出
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)]

image.png

時間データの絞り込み

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)]

image.png

集計

条件を絞ってデータを集計

# 性別ごとに年齢と料金の平均値を算出
df.groupby("Sex").agg({"Age": "mean", "Fare": "mean"}).head()

image.png

Groupbyして算出した情報をもとのDataFrameに追加

Groupbyした結果を元のデータに結合したい時に有効

# Pclassごとに平均チケット料金を求めて元のデータに付与
df["PclassMean"] = df.groupby(["Pclass"]).transform(np.mean)["Fare"]
df.head()

image.png

列・列同士の計算

列同士の計算結果を新しい列に追加

# (特に意味はないけど) 年齢 x 料金 をスコアとして新しい列に追加
df["Score"] = df["Age"] * df["Fare"]
df.head()

image.png

文字列抽出して新しい列に追加

# (特に意味はないけど) Ticket列の下2桁を抽出
df["TicketSuffix"] = df["Ticket"].str.extract("(..)$")
df.head()

image.png

条件に合致する場合に値を代入

# チケット代が30以上の場合、expensive_flagカラムに1を付与
df["expensive_flag"] = 0
df["expensive_flag"] = df["expensive_flag"].mask(df["Fare"] >= 30, 1)
df.head()

image.png

時間操作

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

image.png

その他

dummy変数化するけど元のカラムを残したい

df_dummy = pd.get_dummies(df, columns=["Sex"])
df = pd.concat([df_dummy, df["Sex"]], axis=1)
df.head()

image.png

マルチインデックスカラムを統合する

# グループ化
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

image.png

もっとスマートな方法があれば教えて下さい。。。

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()

image.png

関数を適用

# チケット料金のランクを追加
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()

image.png

0
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?