Python
pandas
More than 5 years have passed since last update.

Python for Data Analysisの日本語版発売記念に

よく使いそうなものとか詰まりそうなところとか

めちゃくちゃ長くなってしまったので目次


複数のSeriesを結合してDataFrameに

concatを使ってでaxis=1にすれば良い

s1 = pd.Series([1,2,3,4,5], index=[0,1,2,3,4])

s2 = pd.Series([10,20,30,40,50], index=[10,11,12,13,14])
s3 = pd.Series([100,200,300,400,500], index=[0,2,4,6,8])
print pd.concat([s1, s2, s3], axis=1)
"""
0 1 2
0 1 NaN 100
1 2 NaN NaN
2 3 NaN 200
3 4 NaN NaN
4 5 NaN 300
6 NaN NaN 400
8 NaN NaN 500
10 NaN 10 NaN
11 NaN 20 NaN
12 NaN 30 NaN
13 NaN 40 NaN
14 NaN 50 NaN
"""


DataFrameのインデックス参照

色々用意されていて混乱する

めんどくさいけどixによる参照が一番わかり易いと思う

# DataFrameを用意

df = DataFrame(arange(30).reshape((6, 5)), index=list("ASDFGH"), columns=list("abcde"))
print df
"""
a b c d e
A 0 1 2 3 4
S 5 6 7 8 9
D 10 11 12 13 14
F 15 16 17 18 19
G 20 21 22 23 24
H 25 26 27 28 29
"""


columnの参照

カラムの参照はカラム名を指定する

複数指定する場合はリスト形式にして指定

指定するカラムが1つの場合はインスタンス変数がカラム名で用意されているのでそれを指定しても良い

# カラム名を指定

print df["a"]
"""
A 0
S 5
D 10
F 15
G 20
H 25
Name: a, dtype: int64
"""

# 複数のカラム名を指定
print df[["a", "c", "e"]]
"""
a c e
A 0 2 4
S 5 7 9
D 10 12 14
F 15 17 19
G 20 22 24
H 25 27 29
"""

# カラム名のインスタンス変数を指定
print df.a
"""
A 0
S 5
D 10
F 15
G 20
H 25
"""


rowの参照

スライシングなどによって指定

行に付いている名前 (インデックスラベル) による指定は後述

print df[0:3]

"""
a b c d e
A 0 1 2 3 4
S 5 6 7 8 9
D 10 11 12 13 14
"""

# 負のインデックス指定
print df[-2:]
"""
a b c d e
G 20 21 22 23 24
H 25 26 27 28 29
"""


行と列を同時に範囲指定して参照 (ixによる参照)

ixアトリビュートをスライシングする事で参照が可能

実際これだけ覚えておけばどんな時にも同じ形式で使えるし便利

指定はNumPyの2次元配列のスライシングのように[行, 列]で行う

インデックスラベルによる指定も可能

ただしインデックスラベルによるスライシングの場合、後ろのインデックスラベルは範囲に含まれる事に注意

# インデックスによる指定

print df.ix[0, 3]
"""
3
"""

# インデックスによるスライシング
print df.ix[0:3, 0:2]
"""
a b
A 0 1
S 5 6
D 10 11
"""

# 離れた行または列を複数インデックスで指定
print df.ix[[0, 2, 4], [0, 2, 4]]
"""
a c e
A 0 2 4
D 10 12 14
G 20 22 24
"""

# インデックスラベルによる指定
print df.ix["D", "a"]
"""
10
"""

# インデックスラベルによる範囲指定
print df.ix["A":"D", "a":"b"]
"""
a b
A 0 1
S 5 6
D 10 11
"""

# 離れた行または列を複数インデックスラベルで指定
print df.ix[["A", "H", "S"], ["c", "b", "a"]]
"""
c b a
A 2 1 0
H 27 26 25
S 7 6 5
"""


Seriesのインデックス参照

DataFrameをやったのでついでにSeriesも

DataFrameと違って最初からインデックスによる指定も、

インデックスラベルによる指定も可能なので特につまる所は無いと思う

スライシングも可能

# Seriesを用意

s1 = pd.Series(range(6), index=list("abcdef"))
print s1
"""
a 0
b 1
c 2
d 3
e 4
f 5
dtype: int64
"""

# インデックスによる指定
print s1[0:3]
"""
a 0
b 1
c 2
dtype: int64
"""

# インデックスラベルによる指定
print ["b":"e"]
"""
b 1
c 2
d 3
e 4
dtype: int64
"""


bool値によるマスキング

NumPyのndarrayのようにSeriesまたはDataFrameに対して評価を行うと、それぞれの要素に対してその評価が行われたboolのSeriesまたはDataFrameがかえってくる

print df > 5

"""
a b c d e
A False False False False False
S False True True True True
D True True True True True
F True True True True True
G True True True True True
H True True True True True
"""

print s1 > 3
"""
a False
b False
c False
d False
e True
f True
dtype: bool
"""

返ってきたもので元のSeriesまたはDataFrameをマスクすることができる

# DataFrameのマスク

print df[df > 5]
"""
a b c d e
A NaN NaN NaN NaN NaN
S NaN 6 7 8 9
D 10 11 12 13 14
F 15 16 17 18 19
G 20 21 22 23 24
H 25 26 27 28 29
"""

# Seriesのマスク
print s1[s1 > 3]
"""
e 4
f 5
dtype: int64
"""


Series同士, DataFrame同士の演算

同じインデックスラベル同士で演算が行われる

同じインデックスラベルがないものはNaNになる

NaNにしたくない場合はfill_valueでNaNに0を充ててから演算すればよい

# Seriesを用意

s1 = pd.Series(range(6), index=list("abcdef"))
s2 = pd.Series([10,20,30,40,50], index=list("asdfg"))

print s1
"""
a 0
b 1
c 2
d 3
e 4
f 5
dtype: int64
"""

print s2
"""
a 10
s 20
d 30
f 40
g 50
dtype: int64
"""

# s1 + s2でもよい
print s1.add(s2)
"""
a 10
b NaN
c NaN
d 33
e NaN
f 45
g NaN
s NaN
dtype: float64
"""

# NaNを0に置換してから演算
print s1.add(s2, fill_value=0)
"""
a 10
b 1
c 2
d 33
e 4
f 45
g 50
s 20
dtype: float64
"""

# DataFrameを準備
df1 = DataFrame(arange(30).reshape((6,5)), index=list("ASDFGH"), columns=list("abcde"))
df2 = DataFrame(arange(42).reshape((7,6)), index=list("ASDFGHJ"), columns=list("abcdef"))

print df1
"""
a b c d e
A 0 1 2 3 4
S 5 6 7 8 9
D 10 11 12 13 14
F 15 16 17 18 19
G 20 21 22 23 24
H 25 26 27 28 29
"""

print df2
"""
a b c d e f
A 0 1 2 3 4 5
S 6 7 8 9 10 11
D 12 13 14 15 16 17
F 18 19 20 21 22 23
G 24 25 26 27 28 29
H 30 31 32 33 34 35
J 36 37 38 39 40 41
"""

# df1 + df2でもよい
print df1.add(df2)
"""
a b c d e f
A 0 2 4 6 8 NaN
D 22 24 26 28 30 NaN
F 33 35 37 39 41 NaN
G 44 46 48 50 52 NaN
H 55 57 59 61 63 NaN
J NaN NaN NaN NaN NaN NaN
S 11 13 15 17 19 NaN
"""

# NaNを0に置換してから演算
print df1.add(df2, fill_value=0)
"""
a b c d e f
A 0 2 4 6 8 5
D 22 24 26 28 30 17
F 33 35 37 39 41 23
G 44 46 48 50 52 29
H 55 57 59 61 63 35
J 36 37 38 39 40 41
S 11 13 15 17 19 11
"""

また、演算するデータの片方のインデックスラベルがもう片方のデータのインデックスラベルに完全に含まれる場合はreindexでfill_valueしてから演算をしても良い

# 上のdf1とdf2を例に

# df1のインデックスラベルはdf2に完全に含まれている
print df1.index.isin(df2.index).all(), df1.columns.isin(df2.columns).all()
"""
True True
"""

print df1.reindex(index=df2.index, columns=df2.columns, fill_value=0) + df2
"""
a b c d e f
A 0 2 4 6 8 5
S 11 13 15 17 19 11
D 22 24 26 28 30 17
F 33 35 37 39 41 23
G 44 46 48 50 52 29
H 55 57 59 61 63 35
J 36 37 38 39 40 41
"""


階層的インデックス

一つの行または列に対して複数のインデックスラベルをつけることができる

階層的インデックスのラベルでソートする場合はsortlevelメソッドを使う

# Series

s = Series(range(10), index=[list("aaabbbccdd"),[1,2,3,2,1,3,1,2,1,2]])
print s
"""
a 1 0
2 1
3 2
b 2 3
1 4
3 5
c 1 6
2 7
d 1 8
2 9
dtype: int64
"""

# 外側のインデックスラベルを参照
print s["a"]
"""
1 0
2 1
3 2
dtype: int64
"""

# 内側のインデックスラベルを参照
print s[:, 1]
"""
a 0
b 4
c 6
d 8
dtype: int64
"""

# インデックスラベルの階層を変更
# 0階層目のインデックスラベルと1階層目のインデックスラベルを入れ替え
# s.swaplevel(1, 0)でもよい
print s.swaplevel(0, 1)
"""
1 a 0
2 a 1
3 a 2
2 b 3
1 b 4
3 b 5
1 c 6
2 c 7
1 d 8
2 d 9
dtype: int64
"""

# 外側のインデックスラベルでソート
print s.swaplevel(0,1).sortlevel(0)
"""
1 a 0
b 4
c 6
d 8
2 a 1
b 3
c 7
d 9
3 a 2
b 5
dtype: int64
"""


stackメソッドとunstackメソッド

stackは列から行へのピボット変換

unstackは行から列へのピボット変換

行と列の変換なのでDataFrameか階層的インデックスラベルを持つSeriesでないと使えない

# 階層的インデックスラベルをもつSeriesオブジェクト

print s
"""
a 1 0
2 1
3 2
b 2 3
1 4
3 5
c 1 6
2 7
d 1 8
2 9
dtype: int64
"""

# 行を列へ変換
print s.unstack()
"""
1 2 3
a 0 1 2
b 4 3 5
c 6 7 NaN
d 8 9 NaN
"""

print df
"""
a b c d e
A 0 1 2 3 4
S 5 6 7 8 9
D 10 11 12 13 14
F 15 16 17 18 19
G 20 21 22 23 24
H 25 26 27 28 29
"""

# 列を行へ
print df.stack()
"""
A a 0
b 1
c 2
d 3
e 4
S a 5
b 6
c 7
d 8
e 9
D a 10
b 11
c 12
d 13
e 14
F a 15
b 16
c 17
d 18
e 19
G a 20
b 21
c 22
d 23
e 24
H a 25
b 26
c 27
d 28
e 29
dtype: int64
"""

# 行を列へ
print df.unstack()
"""
a A 0
S 5
D 10
F 15
G 20
H 25
b A 1
S 6
D 11
F 16
G 21
H 26
c A 2
S 7
D 12
F 17
G 22
H 27
d A 3
S 8
D 13
F 18
G 23
H 28
e A 4
S 9
D 14
F 19
G 24
H 29
dtype: int64
"""


名前付け


行または列の名前付け

行の場合はindex、列の場合はcolumns属性を上書きすると名前を変えることができる

もしくはrenameメソッドを使って変更前の名前と変更後の名前を辞書形式で渡す

renameメソッドを使うとデータのコピーが返され、元のデータは変更されない

print df

"""
a b c d e
A 0 1 2 3 4
S 5 6 7 8 9
D 10 11 12 13 14
F 15 16 17 18 19
G 20 21 22 23 24
H 25 26 27 28 29
"""

# 行名を変更
df.index = ["AAA", "SSS", "DDD", "FFF", "GGG", "HHH"]
# 列名を変更
df.columns=["one", "two", "three", "four", "five"]

print df
"""
one two three four five
AAA 0 1 2 3 4
SSS 5 6 7 8 9
DDD 10 11 12 13 14
FFF 15 16 17 18 19
GGG 20 21 22 23 24
HHH 25 26 27 28 29
"""

# renameを使う
print df.rename(index={"AAA":"a", "SSS":"s", "DDD":"d", "FFF":"f", "GGG":"g", "HHH":"h"},
columns={"one":1, "two":2, "three":3, "four":4, "five":5})
"""
1 2 3 4 5
a 0 1 2 3 4
s 5 6 7 8 9
d 10 11 12 13 14
f 15 16 17 18 19
g 20 21 22 23 24
h 25 26 27 28 29
"""


インデックスラベル自体の名前付け

インデックスラベル自体に名前を付ける事がができる

付けた名前はswaplevelやunstackなどの軸の変換をするときに使うことができる

# インデックスの名前を変更

df.index.names = ["UPPER"]
# カラムの名前を変更
df.columns.names = ["lower"]

print df
"""
lower one two three four five
UPPER
AAA 0 1 2 3 4
SSS 5 6 7 8 9
DDD 10 11 12 13 14
FFF 15 16 17 18 19
GGG 20 21 22 23 24
HHH 25 26 27 28 29
"""


ビンニング

(ビンとはヒストグラムの柱のこと)

pd.cutメソッドを使うとデータの値をビンに分割することができる

# 年齢と性別のデータ

df = DataFrame([[20,"F"],[22,"M"],[25,"M"],[27,"M"],[21,"F"],[23,"M"],[37,"F"],[31,"M"],[61,"F"],[45,"M"],[41,"F"],[32,"M"]], columns=["age", "sex"])
print df
"""
age sex
0 20 F
1 22 M
2 25 M
3 27 M
4 21 F
5 23 M
6 37 F
7 31 M
8 61 F
9 45 M
10 41 F
11 32 M
"""

# ビンに分割するときの値
bins = [18, 25, 35, 60, 100]
# ビンの名前
group_names = ["Youth", "YoungAdult", "MiddleAged", "Senior"]
# ビン化
print pd.cut(df.age, bins, labels=group_names)
"""
Categorical:
[Youth, Youth, Youth, YoungAdult, Youth, Youth, nan, YoungAdult, nan, nan, nan, YoungAdult]
Levels (4): Index(['Youth', 'YoungAdult', 'MiddleAged', 'Senior'], dtype=object)
"""

# dfにビンの列を追加
df["bin"] = pd.cut(df.age, bins, labels=group_names)
print df
"""
age sex bin
0 20 F Youth
1 22 M Youth
2 25 M Youth
3 27 M YoungAdult
4 21 F Youth
5 23 M Youth
6 37 F MiddleAged
7 31 M YoungAdult
8 61 F Senior
9 45 M MiddleAged
10 41 F MiddleAged
11 32 M YoungAdult
"""


列とインデックスの変換

特定の列のみをインデックスに変換したい場合はDataFrameのset_indexメソッドを使う

print df

"""
age sex bin
0 20 F Youth
1 22 M Youth
2 25 M Youth
3 27 M YoungAdult
4 21 F Youth
5 23 M Youth
6 37 F MiddleAged
7 31 M YoungAdult
8 61 F Senior
9 45 M MiddleAged
10 41 F MiddleAged
11 32 M YoungAdult
"""

# bin列をインデックスに変換
print df.set_index("bin")
"""
age sex
bin
Youth 20 F
Youth 22 M
Youth 25 M
YoungAdult 27 M
Youth 21 F
Youth 23 M
MiddleAged 37 F
YoungAdult 31 M
Senior 61 F
MiddleAged 45 M
MiddleAged 41 F
YoungAdult 32 M
"""

# 複数の列を階層的インデックスに変換するにはリストを引数に与える
print df.set_index(["bin", "sex"])
"""
age
bin sex
Youth F 20
M 22
M 25
YoungAdult M 27
Youth F 21
M 23
MiddleAged F 37
YoungAdult M 31
Senior F 61
MiddleAged M 45
F 41
YoungAdult M 32
"""

# ソート
print df.set_index(["bin", "sex"]).sortlevel("bin")
"""
age
bin sex
MiddleAged F 37
F 41
M 45
Senior F 61
YoungAdult M 27
M 31
M 32
Youth F 20
F 21
M 22
M 25
M 23
"""


ランダムサンプリング

DataFrameのtakeを使うと必要な行だけ取り出すことができる

これを利用してランダムに列を取り出せばランダムサンプリングすることができる

# サンプリングする行を指定

print df.take([1, 3, 5])
"""
age sex bin
1 22 M Youth
3 27 M YoungAdult
5 23 M Youth
"""

# ランダムに5つサンプリング
sampler = np.random.permutation(len(df))
print df.take(sampler[:5])
"""
age sex bin
8 61 F Senior
2 25 M Youth
7 31 M YoungAdult
1 22 M Youth
0 20 F Youth
"""


カテゴリカルデータをダミー変数化

pandasのget_dummiesメソッドを使うとカテゴリカルデータ (質的データ) をダミー変数に変換することができる

数量化I類とかするときに使うアレ

# 性別columnをダミー変数化

dum = pd.get_dummies(df["sex"])
print dum
"""
F M
0 1 0
1 0 1
2 0 1
3 0 1
4 1 0
5 0 1
6 1 0
7 0 1
8 1 0
9 0 1
10 1 0
11 0 1
"""

# dfに追加
df = pd.concat((df, dum), axis=1)
df = df.drop("sex", axis=1)
print df
"""
age bin F M
0 20 Youth 1 0
1 22 Youth 0 1
2 25 Youth 0 1
3 27 YoungAdult 0 1
4 21 Youth 1 0
5 23 Youth 0 1
6 37 MiddleAged 1 0
7 31 YoungAdult 0 1
8 61 Senior 1 0
9 45 MiddleAged 0 1
10 41 MiddleAged 1 0
11 32 YoungAdult 0 1
"""


グルーピング

groupbyを使うことで値によるグルーピングが行える

それぞれのグループに同じ処理を適用するにはgroupbyオブジェクトのapplyメソッドを使う

print df.groupby("bin")

"""
<pandas.core.groupby.DataFrameGroupBy object at 0x106af9a90>
"""

# グループごとにmeanを計算
f = lambda x: x.mean()
print df.groupby("bin").apply(f)
"""
age F M
bin
MiddleAged 41.0 0.666667 0.333333
Senior 61.0 1.000000 0.000000
YoungAdult 30.0 0.000000 1.000000
Youth 22.2 0.400000 0.600000
"""

例: ビン分析

ビンの値グルーピングし、それぞれのビンごとに統計量を出す

frame = DataFrame({"data1": np.random.randn(1000), "data2":np.random.randn(1000)})

factor = pd.cut(frame.data1, 4) # data1の値の範囲を均等に4つのビンに分割

# applyするための関数を定義
def get_stats(group):
return {"min": group.min(), "max": group.max(), "count": group.count(), "mean": group.mean()}

# data2の値の統計量をdata1のビンごとに出す
grouped = frame.data2.groupby(factor)
print grouped.apply(get_stats).unstack()
"""
count max mean min
data1
(-3.205, -1.673] 44 2.544910 -0.108558 -1.984124
(-1.673, -0.147] 414 3.420818 0.003078 -3.168528
(-0.147, 1.379] 447 2.563359 -0.018062 -3.175148
(1.379, 2.905] 95 2.498611 0.140166 -2.439985
"""

aggメソッドを使うとGroupByオブジェクトのカラムごとに同じ関数を適用できる

df["random"] = [random.choice((0,1)) for i in range(len(df))]

print df
"""
age sex random
0 20 F 1
1 22 M 1
2 25 M 1
3 27 M 0
4 21 F 1
5 23 M 1
6 37 F 1
7 31 M 0
8 61 F 1
9 45 M 1
10 41 F 0
11 32 M 0
"""

print df.groupby("sex").agg([np.max, np.min, np.mean])
"""
age random
amax amin mean amax amin mean
sex
F 61 20 36.000000 1 0 0.800000
M 45 22 29.285714 1 0 0.571429
"""