【Python】【pandas入門】値の抽出 ~locを中心とした世界~
脳に支障がありlocについてすぐ忘れるのでまとめておく。今回はiloc()
は対象外。
サンプルデータ作成
3000年後の高校の高校課程共通科目の期末テストについてのサンプルデータを作成する。
import numpy as np
import pandas as pd
# 学校の各教科の母数
school_criteria = {
"でんぷん高等学校": {
"量子文学": {"mean": 50, "std": 20},
"エンターテイメント数学論": {"mean": 60, "std": 15},
"モンスター育成学": {"mean": 70, "std": 10},
"ハイパー睡眠学": {"mean": 80, "std": 10},
"エコロジカル・ディストピア学": {"mean": 45, "std": 12},
},
"おにぎり工業高等学校": {
"量子文学": {"mean": 55, "std": 18},
"エンターテイメント数学論": {"mean": 65, "std": 12},
"モンスター育成学": {"mean": 60, "std": 15},
"ハイパー睡眠学": {"mean": 85, "std": 8},
"エコロジカル・ディストピア学": {"mean": 50, "std": 15},
},
}
# 学年・組・生徒数
grades = ["1年", "2年", "3年"]
classes = ["A組", "B組", "C組"]
students_per_class = 20
# 教科
subjects = [
"量子文学",
"エンターテイメント数学論",
"モンスター育成学",
"ハイパー睡眠学",
"エコロジカル・ディストピア学"
]
# 試験結果を格納するリスト
exam_results = []
# 各学校の試験結果を生成
for school in school_criteria:
for grade in grades:
for clazz in classes:
for student_id in range(1, students_per_class + 1):
scores = {}
for subject in subjects:
mean = school_criteria[school][subject]["mean"]
std = school_criteria[school][subject]["std"]
score = int(np.clip(np.random.normal(mean, std), 0, 100))
scores[subject] = score
exam_results.append({
"学校": school,
"学年": grade,
"学級": clazz,
"出席番号": student_id,
**scores
})
# pandas DataFrameに変換
df_orig = pd.DataFrame(exam_results)
df_orig.head()
学校 | 学年 | 学級 | 出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|---|---|---|
でんぷん高等学校 | 1年 | A組 | 1 | 58 | 88 | 59 | 83 | 47 |
でんぷん高等学校 | 1年 | A組 | 2 | 53 | 55 | 67 | 68 | 57 |
でんぷん高等学校 | 1年 | A組 | 3 | 28 | 65 | 57 | 86 | 37 |
でんぷん高等学校 | 1年 | A組 | 4 | 56 | 50 | 68 | 81 | 64 |
でんぷん高等学校 | 1年 | A組 | 5 | 46 | 59 | 53 | 58 | 46 |
色々なデータ抽出
非マルチインデックス
pandas.Series
Seriesデータを作成、でんぷん高等学校の1年A組の量子文学の点数をSeries形式で取得。
ser = df_orig.loc[
(df_orig["学校"] == "でんぷん高等学校")
& (df_orig["学年"] == "1年")
& (df_orig["学級"] == "A組")
].set_index("出席番号")["量子文学"]
ser.head()
出席番号
1 58
2 53
3 28
4 56
5 46
Name: 量子文学, dtype: int64
インデックス(出席番号)で抽出する。
ser.loc[1]
58
インデックス(出席番号)でスライスを使って抽出する。
ser.loc[3:9:2]
出席番号
3 28
5 46
7 31
9 61
Name: 量子文学, dtype: int64
インデックス(出席番号)で配列を使って抽出する。
ser.loc[[1, 3, 5]]
出席番号
1 58
3 28
5 46
Name: 量子文学, dtype: int64
値に応じて動的に抽出する。
ser.loc[ser > 60]
出席番号
6 65
9 61
15 78
19 80
20 100
Name: 量子文学, dtype: int64
pandas.DataFrame
DataFrameを作成する。
df = df_orig.loc[
(df_orig["学校"] == "でんぷん高等学校")
& (df_orig["学年"] == "1年")
& (df_orig["学級"] == "A組")
, "出席番号":].set_index("出席番号")
df.head()
出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|
1 | 58 | 88 | 59 | 83 | 47 |
2 | 53 | 55 | 67 | 68 | 57 |
3 | 28 | 65 | 57 | 86 | 37 |
4 | 56 | 50 | 68 | 81 | 64 |
5 | 46 | 59 | 53 | 58 | 46 |
loc[インデックス, カラム名]で要素を抽出する。
df.loc[2, "量子文学"]
53
特定の列だけ抽出する。
df.loc[:,"量子文学"]
出席番号
1 58
2 53
3 28
4 56
5 46
6 65
7 31
8 45
9 61
10 53
11 57
12 49
13 54
14 58
15 78
16 0
17 35
18 47
19 80
20 100
Name: 量子文学, dtype: int64
特定の行だけ抽出する。
df.loc[4, :]
量子文学 56
エンターテイメント数学論 50
モンスター育成学 68
ハイパー睡眠学 81
エコロジカル・ディストピア学 64
Name: 4, dtype: int64
スライスで抽出する。
df.loc[0:20:2, "エンターテイメント数学論":"ハイパー睡眠学"]
出席番号 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 |
---|---|---|---|
1 | 88 | 59 | 83 |
3 | 65 | 57 | 86 |
5 | 59 | 53 | 58 |
7 | 64 | 78 | 87 |
9 | 61 | 83 | 82 |
11 | 72 | 91 | 77 |
13 | 100 | 72 | 62 |
15 | 47 | 75 | 73 |
17 | 86 | 74 | 85 |
19 | 47 | 69 | 88 |
リストで特定要素を抽出する。
df.loc[[1, 4, 6, 8, 9], ["量子文学", "モンスター育成学"]]
出席番号 | 量子文学 | モンスター育成学 |
---|---|---|
1 | 58 | 59 |
4 | 56 | 68 |
6 | 65 | 70 |
8 | 45 | 73 |
9 | 61 | 83 |
値に応じて動的に抽出する。
df.loc[df["ハイパー睡眠学"] > 87]
出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|
16 | 0 | 79 | 73 | 89 | 45 |
18 | 47 | 93 | 57 | 91 | 28 |
19 | 80 | 47 | 69 | 88 | 29 |
複数条件の場合、演算子の優先順位の都合上、必ず各条件を()
でくくる。ビット演算のほうが比較演算より優先度が高い。&
や |
のビット演算を使用するところも注意。and
や or
ではない。
df.loc[(df["ハイパー睡眠学"] > 87) & (df["量子文学"] > 40)]
出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|
18 | 47 | 93 | 57 | 91 | 28 |
19 | 80 | 47 | 69 | 88 | 29 |
マルチインデックス
pandas.Series
マルチインデックスのSeriesを作成
ser = df_orig.set_index(["学校", "学年", "学級", "出席番号"])["量子文学"]
ser.head()
学校 学年 学級 出席番号
でんぷん高等学校 1年 A組 1 58
2 53
3 28
4 56
5 46
Name: 量子文学, dtype: int64
マルチインデックスの場合、タプルで抽出対象を指定する。
ser.loc[("でんぷん高等学校", "1年", "A組", 3)]
28
タプルの配列で複数の抽出対象を指定する。
ser.loc[
[
("でんぷん高等学校", "1年", "A組", 3),
("でんぷん高等学校", "2年", "A組", 3)
]
]
学校 学年 学級 出席番号
でんぷん高等学校 1年 A組 3 28
2年 A組 3 57
Name: 量子文学, dtype: int64
タプルをスライスする場合、事前にsort_index()
を行わないとUnsortedIndexError
が発生する。
ser.sort_index().loc[
("でんぷん高等学校", "1年", "A組", 19):("でんぷん高等学校", "1年", "B組", 2)
]
学校 学年 学級 出席番号
でんぷん高等学校 1年 A組 19 80
20 100
B組 1 58
2 61
Name: 量子文学, dtype: int64
Pythonの構文上、タプル内にスライスは記載できない。
ser.loc[("でんぷん高等学校", "2年":"3年", "A組", 2:6:2)]
Cell In[479], line 2
ser.loc[("でんぷん高等学校", "2年":"3年", "A組", 2:6:2)]
^
SyntaxError: invalid syntax
このような場合はslice()
関数が使える。
ser.sort_index().loc[
(
"でんぷん高等学校",
slice("2年", "3年"),
"A組",
slice(2, 6, 2)
)
]
学校 学年 学級 出席番号
でんぷん高等学校 2年 A組 2 46
4 59
6 49
3年 A組 2 63
4 27
6 40
Name: 量子文学, dtype: int64
IndexSlice
クラスを利用することで、スライス記法も使えるようになる。
ser.sort_index().loc[
(
"でんぷん高等学校",
pd.IndexSlice["2年":"3年"],
"A組",
pd.IndexSlice[2:6:2]
)
]
学校 学年 学級 出席番号
でんぷん高等学校 2年 A組 2 46
4 59
6 49
3年 A組 2 63
4 27
6 40
Name: 量子文学, dtype: int64
IndexSlice
クラスの添え字には bool インデックスが渡せるので、値に応じた動的な抽出ができる。
ser.sort_index().loc[
(
"でんぷん高等学校",
pd.IndexSlice["2年":"3年"],
"A組",
pd.IndexSlice[ser.sort_index() > 75]
)
]
学校 学年 学級 出席番号
でんぷん高等学校 2年 A組 8 92
13 76
15 78
18 95
3年 A組 19 80
20 80
Name: 量子文学, dtype: int64
xs()
メソッドも使える。ちなみに、xsとはクロスセクションのこと。クロスセクションとは核物理や散乱理論では粒子が相互作用を起こすときの反応のしやすさを示す値のことで、核分裂反応断面積や中性子捕獲断面積など色々と種類があるが、このクロスセクションはその断面積ではなく、交差系列のことだと思われる。
「おにぎり工業高等学校」のみを抽出する。
ser.xs("おにぎり工業高等学校")
学年 学級 出席番号
1年 A組 1 52
2 61
3 33
4 47
5 40
..
3年 C組 16 57
17 56
18 60
19 42
20 54
Name: 量子文学, Length: 180, dtype: int64
「2年」のみを抽出する。
ser.xs("2年", level=1)
学校 学級 出席番号
でんぷん高等学校 A組 1 54
2 46
3 57
4 59
5 72
..
おにぎり工業高等学校 C組 16 75
17 54
18 55
19 33
20 46
Name: 量子文学, Length: 120, dtype: int64
「C組」のみを抽出する。
ser.xs("C組", level=2)
学校 学年 出席番号
でんぷん高等学校 1年 1 68
2 92
3 58
4 55
5 54
..
おにぎり工業高等学校 3年 16 57
17 56
18 60
19 42
20 54
Name: 量子文学, Length: 120, dtype: int64
出席番後「13」のみを抽出する。
ser.xs(13, level=3)
学校 学年 学級
でんぷん高等学校 1年 A組 54
B組 5
C組 46
2年 A組 76
B組 44
C組 26
3年 A組 55
B組 39
C組 20
おにぎり工業高等学校 1年 A組 69
B組 78
C組 36
2年 A組 23
B組 4
C組 57
3年 A組 78
B組 74
C組 72
Name: 量子文学, dtype: int64
drop_level=False
で、出力時に抽出対象としたインデックスの情報を残す。
ser.xs(13, level=3, drop_level=False)
学校 学年 学級 出席番号
でんぷん高等学校 1年 A組 13 54
B組 13 5
C組 13 46
2年 A組 13 76
B組 13 44
C組 13 26
3年 A組 13 55
B組 13 39
C組 13 20
おにぎり工業高等学校 1年 A組 13 69
B組 13 78
C組 13 36
2年 A組 13 23
B組 13 4
C組 13 57
3年 A組 13 78
B組 13 74
C組 13 72
Name: 量子文学, dtype: int64
タプルで複数階層を指定して抽出できる。
ser.xs(("おにぎり工業高等学校", "C組", 8), level=[0, 2, 3], drop_level=False)
学校 学年 学級 出席番号
おにぎり工業高等学校 1年 C組 8 57
2年 C組 8 54
3年 C組 8 42
Name: 量子文学, dtype: int64
値に応じた動的な抽出を行う。
ser.loc[ser > 90]
学校 学年 学級 出席番号
でんぷん高等学校 1年 A組 20 100
B組 18 91
C組 2 92
2年 A組 8 92
18 95
おにぎり工業高等学校 1年 A組 8 96
2年 A組 11 100
Name: 量子文学, dtype: int64
複数条件での値に応じた抽出。
ser.loc[(ser > 90) | (ser < 6)]
学校 学年 学級 出席番号
でんぷん高等学校 1年 A組 16 0
20 100
B組 13 5
18 91
C組 2 92
2年 A組 8 92
9 0
18 95
おにぎり工業高等学校 1年 A組 8 96
2年 A組 11 100
B組 13 4
Name: 量子文学, dtype: int64
Seriesの場合、マルチインデックスを「,」区切りで指定することができる。データ構造によっては、ぱっと見DataFrameの指定とごちゃるので、この方法は紛らわしいと思う。
ser.sort_index().loc["おにぎり工業高等学校", "2年", "C組", 4:7]
学校 学年 学級 出席番号
おにぎり工業高等学校 2年 C組 4 54
5 66
6 47
7 62
Name: 量子文学, dtype: int64
pandas.DataFrame
Seriesと基本、変わらない。マルチインデックスのDataFrameを作成
df = df_orig.set_index(["学校", "学年", "学級", "出席番号"])
df.head()
学校 | 学年 | 学級 | 出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|---|---|---|
でんぷん高等学校 | 1年 | A組 | 1 | 58 | 88 | 59 | 83 | 47 |
2 | 53 | 55 | 67 | 68 | 57 | |||
3 | 28 | 65 | 57 | 86 | 37 | |||
4 | 56 | 50 | 68 | 81 | 64 | |||
5 | 46 | 59 | 53 | 58 | 46 |
タプルで要素を指定して抽出する。
df.loc[("でんぷん高等学校", "1年", "A組", 3)]
量子文学 28
エンターテイメント数学論 65
モンスター育成学 57
ハイパー睡眠学 86
エコロジカル・ディストピア学 37
Name: (でんぷん高等学校, 1年, A組, 3), dtype: int64
タプルの配列で複数の抽出対象を指定する。
df.loc[
[
("でんぷん高等学校", "1年", "A組", 3),
("でんぷん高等学校", "2年", "A組", 3)
]
]
学校 | 学年 | 学級 | 出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|---|---|---|
でんぷん高等学校 | 1年 | A組 | 3 | 28 | 65 | 57 | 86 | 37 |
2年 | A組 | 3 | 57 | 53 | 92 | 85 | 47 |
タプルをスライスする場合、事前にsort_index()
を行わないとUnsortedIndexError
が発生する。
df.sort_index().loc[
("でんぷん高等学校", "1年", "A組", 19):("でんぷん高等学校", "1年", "B組", 2)
]
学校 | 学年 | 学級 | 出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|---|---|---|
でんぷん高等学校 | 1年 | A組 | 19 | 80 | 47 | 69 | 88 | 29 |
20 | 100 | 47 | 62 | 76 | 50 | |||
B組 | 1 | 58 | 49 | 66 | 79 | 65 | ||
2 | 61 | 48 | 73 | 65 | 40 |
slice()
関数を使う。
df.sort_index().loc[
(
"でんぷん高等学校",
slice("2年", "3年"),
"A組",
slice(2, 6, 2)
)
]
学校 | 学年 | 学級 | 出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|---|---|---|
でんぷん高等学校 | 2年 | A組 | 2 | 46 | 23 | 70 | 69 | 25 |
4 | 59 | 74 | 78 | 68 | 55 | |||
6 | 49 | 79 | 63 | 79 | 42 | |||
3年 | A組 | 2 | 63 | 35 | 68 | 65 | 55 | |
4 | 27 | 77 | 75 | 78 | 58 | |||
6 | 40 | 53 | 73 | 78 | 52 |
IndexSliceクラスを利用する。
df.sort_index().loc[
(
"でんぷん高等学校",
pd.IndexSlice["2年":"3年"],
"A組",
pd.IndexSlice[2:6:2]
)
]
学校 | 学年 | 学級 | 出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|---|---|---|
でんぷん高等学校 | 2年 | A組 | 2 | 46 | 23 | 70 | 69 | 25 |
4 | 59 | 74 | 78 | 68 | 55 | |||
6 | 49 | 79 | 63 | 79 | 42 | |||
3年 | A組 | 2 | 63 | 35 | 68 | 65 | 55 | |
4 | 27 | 77 | 75 | 78 | 58 | |||
6 | 40 | 53 | 73 | 78 | 52 |
IndexSliceクラスで値に応じた動的な抽出をする。
df.sort_index().loc[
(
"でんぷん高等学校",
pd.IndexSlice["2年":"3年"],
"A組",
pd.IndexSlice[df.sort_index()["量子文学"] > 75]
)
]
学校 | 学年 | 学級 | 出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|---|---|---|
でんぷん高等学校 | 2年 | A組 | 8 | 92 | 59 | 76 | 83 | 44 |
13 | 76 | 34 | 58 | 81 | 57 | |||
15 | 78 | 75 | 78 | 100 | 25 | |||
18 | 95 | 48 | 63 | 84 | 46 | |||
3年 | A組 | 19 | 80 | 63 | 71 | 96 | 39 | |
20 | 80 | 40 | 79 | 79 | 36 |
xs()
メソッドを使う。
df.xs("おにぎり工業高等学校")
学年 | 学級 | 出席番号 | 量子文学 | エンターテイメント数学論 | モンスター育成学 | ハイパー睡眠学 | エコロジカル・ディストピア学 |
---|---|---|---|---|---|---|---|
1年 | A組 | 1 | 52 | 73 | 86 | 97 | 22 |
2 | 61 | 64 | 46 | 100 | 54 | ||
3 | 33 | 83 | 46 | 91 | 52 | ||
4 | 47 | 66 | 77 | 78 | 32 | ||
5 | 40 | 82 | 52 | 60 | 68 | ||
... | ... | ... | ... | ... | ... | ... | ... |
3年 | C組 | 16 | 57 | 66 | 85 | 93 | 53 |
17 | 56 | 62 | 63 | 91 | 42 | ||
18 | 60 | 68 | 13 | 84 | 34 | ||
19 | 42 | 72 | 67 | 100 | 38 | ||
20 | 54 | 84 | 55 | 96 | 46 |
DataFrameの場合、xs()
メソッドに引数axis=1
を指定することで、列のSerirsを取得できる
df.xs("量子文学", axis=1)
学校 学年 学級 出席番号
でんぷん高等学校 1年 A組 1 58
2 53
3 28
4 56
5 46
..
おにぎり工業高等学校 3年 C組 16 57
17 56
18 60
19 42
20 54
Name: 量子文学, Length: 360, dtype: int64
xs()
メソッドで行と列を指定するが、locじゃダメなのか?
df.xs(("B組", 10), level=(2, 3), axis=0, drop_level=False).xs("モンスター育成学", axis=1)
学校 学年 学級 出席番号
でんぷん高等学校 1年 B組 10 63
2年 B組 10 60
3年 B組 10 46
おにぎり工業高等学校 1年 B組 10 74
2年 B組 10 61
3年 B組 10 55
Name: モンスター育成学, dtype: int64
まとめ
-
loc[]
には、インデックスの型のリテラル、リスト、スライスが利用可能。 -
loc[]
で複数条件を指定する場合は各条件を()
でくくる。ビット演算(&
や|
)を用いる。 - マルチインデックスの場合
- タプルでインデックスを指定する。
- タプル内でスライスを使いたい場合
slice()
関数やpandas.IndexSlice
クラスが使える。 - スライスなどで複数行抽出する場合は
sort_index()
でソートする。
Discussion