pandasメモ

  • 277
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

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