概要
https://openbook4.me/projects/183
にまとめていた内容のうち基礎的な部分を移動
pandas
pythonでデータ解析とかするのに便利なlibrary.
numpyも使うので, ここでは両方共importした状態にしておく.
import numpy as np
import pandas as pd
Series
軸にラベルを付けた1次元の配列.
>> pd.Series([1,2,3])
0 1
1 2
2 3
dtype: int64
>> s = pd.Series([1,2,3], index=['a','b','c'])
>> s
a 1
b 2
c 3
dtype: int64
>> s.index
Index([u'a', u'b', u'c'], dtype='object')
>> s.max() # 最大値
3
>> s.min() # 最小値
1
>> s.mean() # 平均値
2.0
>> s.median() # 中央値
2.0
>> s.var() # 分散
1.0
>> s.sum() # 合計値
6
>> s.mod(2) # modの計算, この場合2で割った余り
a 1
b 0
c 1
>> s.cumsum() # 累積にする. 累積のグラフを作るとき便利
a 1
b 3
c 6
dtype: int64
>> s.apply(lambda x: x*3) # 特定の関数を各値に対して適応
a 3
b 6
c 9
dtype: int64
>> s.map({1: 10, 2: 200}) # 引数で与えた値に対応する値を変換
a 10
b 200
c NaN
dtype: float64
>> s.argmax() # 最大値のindexを取得
'c'
>> s.argmin() # 最小値のindexを取得
'a'
>> s.tolist() # listに変更
[1, 2, 3]
>> s.to_dict() # dictonaryに変更
{'a': 1, 'b': 2, 'c': 3}
>> s.to_json # jsonに変更
'{"a":1,"b":2,"c":3}'
DataFrame
DataFrameの作成
df = pd.DataFrame([[1,4,7],[2,5,8],[3,6,9]],
index=['i1','i2','i3'],
columns=list("abc"))
>> df
a b c
i1 1 4 7
i2 2 5 8
i3 3 6 9
値の操作
範囲を指定して取り出す
行に関連する操作は、.ixを使う。
>> df
a b c
i1 1 4 7
i2 2 5 8
i3 3 6 9
# 指定した行の取り出し。index名かindex番号で与えられる。
>> df.ix['i1']
a 1
b 4
c 7
Name: i1, dtype: int64
>> df.ix[1]
a 2
b 5
c 8
Name: i2, dtype: int64
# 2つ目のparameterに列を渡すことで、行を取得できる。
>> df.ix['i1','a']
1
# : は全指定する。(R, scilabと対応)
>> df.ix[:, 'a']
i1 1
i2 2
i3 3
Name: a, dtype: int64
# 複数の指定も配列で可能。
>> df.ix[[1,2], ['b','c']]
b c
i2 5 8
i3 6 9
>> df.ix[[1,2], [1,2]]
b c
i2 5 8
i3 6 9
列に関する操作は[column名]で渡す。
>> df['a']
i1 1
i2 2
i3 3
Name: a, dtype: int64
# arrayとして取得する。
>> df['a'].values
array([1, 2, 3])
# さらにindex名を指定することで値として取得できる。
>> df['a']['i3']
3
DataFrameをtableとみなして、位置指定から値を明示的に取る方法。
>> df.iloc[0,0]
1
(df.ix[0,0]と同じ。)
# 列の取得
>> df.iloc[2]
a 3
b 6
c 9
Name: i3, dtype: int64
条件を満たすに要素に値を代入
例えば行列において、1より大きい値を全部1にしたい場合
>> a = pd.DataFrame(np.random.randint(3, size=(5,3)))
0 1 2
0 0 2 1
1 2 0 1
2 0 0 2
3 2 1 0
>> b[b>1] # 条件を満たすものだけを抽出したDataFrame
0 1 2
0 NaN 2 NaN
1 2 NaN NaN
2 NaN NaN 2
3 2 NaN NaN
>> b[b>1] = -1 # 条件をみたすものに1を代入
>> b
0 1 2
0 0 1 1
1 1 0 1
2 0 0 1
3 1 1 0
NaNの処理
NaNの削除
dropnaで削除できる
>> df
a b c
0 1 3 5
1 2 NaN 6
2 NaN 4 7
>> df.dropna() # NaNを含む行を削除
a b c
0 1 3 5
>> df.dropna(axis=1) # NaNを含む列を削除
c
0 5
1 6
2 7
>> df.dropna(subset=['b']) # 特定カラムを指定することも可能
a b c
0 1 3 5
2 NaN 4 7
NaNを埋める
fillnaを使う。fillnaで指定した値で埋めることができる以外に、
直前の値で補完(pad/ffill)、直後の値で補完(bfill)などがある。
>> df
a b c
0 1 3 5
1 2 NaN 6
2 NaN 4 7
>> df.fillna(-1)
a b c
0 1 3 5
1 2 -1 6
2 -1 4 7
>> df.fillna(method='pad')
a b c
0 1 3 5
1 2 3 6
2 2 4 7
>> df.fillna(method='bfill')
a b c
0 1 3 5
1 2 4 6
2 NaN 4 7
misssing valueの前後の線形の値で埋めたい場合は pd.Series.interpolate をapplyしてやる。
>> df
a b
0 1 2
1 2 NaN
2 NaN 3
>> df.apply(pd.Series.interpolate)
a b
0 1 2.0
1 2 2.5
2 2 3.0
重複した値の処理
duplicatedで重複を調べれる。重複を調査するcolumnを指定することも可能。
>> df
0 1
0 A 1
1 A 1
2 A 2
3 B 3
4 B 4
>> df.duplicated()
0 False
1 True
2 False
3 False
4 False
dtype: bool
>> df.duplicated(0)
0 False
1 True
2 True
3 False
4 True
dtype: bool
>> df.duplicated(1)
0 False
1 True
2 False
3 False
4 False
dtype: bool
drop_duplicatesで重複した値を削除できる。duplicatedと同様にcolumn指定も可能。
take_last=Trueオプションで重複した場合に最後の値を取ることができる。
>> df.drop_duplicates()
0 1
0 A 1
2 A 2
3 B 3
4 B 4
>> df.drop_duplicates(0)
0 1
0 A 1
3 B 3
>> df.drop_duplicates(0, take_last=True)
0 1
2 A 2
4 B 4
Pandas: DataFrameの変形
ここではDataFrameの形を変える方法を順に見ていく。
次のようなdfについて処理する。
df = pd.DataFrame(np.reshape(np.arange(9), (3,3)),
columns=['a', 'b', 'c'])
>> df
a b c
0 0 1 2
1 3 4 5
2 6 7 8
DataFrameの一部を取る
n行を取る。
# 最初のn行を取る。
>> df.head(2)
a b c
0 0 1 2
1 3 4 5
# 終わりn行取る。
>> df.tail(2)
a b c
1 3 4 5
2 6 7 8
index, columnを取る。
>> df.index
Int64Index([0, 1, 2], dtype='int64')
>> df.columns
Index([u'a', u'b', u'c'], dtype='object')
DataFrameの形式を変える。
indexとcolumnを逆にする。
>> df.T
0 1 2
a 0 3 6
b 1 4 7
c 2 5 8
この操作は非破壊的なので、df自体が反転するわけではない。
(使用するときは、rdf = df.Tのようにすればよい。)
index, columnの順番を反転させる。
# columnについての順番を逆転させる。
>> df.sort_index(axis=1, ascending=False)
c b a
0 2 1 0
1 5 4 3
2 8 7 6
index, columnに対して、値の順番を並び替える。
# 大きい順に並び替える。(今回だと全部逆転したように見えるけれど...)
>> df.sort(columns='b', ascending=False)
a b c
2 6 7 8
1 3 4 5
0 0 1 2
# 小さい順に並び替える。(今回だと変わらないけれど...)
>> df.sort(columns='b')
a b c
0 0 1 2
1 3 4 5
2 6 7 8
各値を取る
df[],df.loc[],df.iloc[],...
などなど前章でやった!
マスクする(ほしい条件のもとで、dfから選びとる。)
# df["条件文をかく"]という形。
>> df.a
0 0
1 3
2 6
# に対して、2以上の1,2とついたindexのみ取ってくる。
>> df[df.a>2]
a b c
1 3 4 5
2 6 7 8
全体に対するoperationだと条件に合うもの、合わないもの(NaN)を表示する。
# 3より大きい所をとってくる
>> df[df > 3]
a b c
0 NaN NaN NaN
1 NaN 4 5
2 6 7 8
# 3より大きいところだけ、2倍する。
>> df[df > 3] = df*2
>> df
a b c
0 0 1 2
1 3 8 10
2 12 14 16
columnの追加
よく使うので重要です!
>> df['e'] = ['one','two','three']
>> df
a b c e
0 0 1 2 one
1 3 4 5 two
2 6 7 8 three
NaNのdataを含むものを消去する。
# NaNのものを消去する。
>> df[df > 3].dropna(how='any')
a b c
2 6 7 8
# NaNを別のもので埋める。
>> df[df > 3].fillna('Ice')
a b c
0 Ice Ice Ice
1 Ice 4 5
2 6 7 8
DataFrameをくっつける
df2を作成する。
>> df2 = pd.DataFrame(np.reshape(np.arange(6), (3,2)),
columns=['e', 'f'])
>> df2
e f
0 0 1
1 2 3
2 4 5
concat
列でくっつける。
>> newdf = pd.concat([df, df2],axis=1)
>> newdf
a b c e f
0 0 1 2 0 1
1 3 4 5 2 3
2 6 7 8 4 5
行についてくっつける。
>> newdf = pd.concat([df, df2],axis=0)
>> newdf
a b c e f
0 0 1 2 NaN NaN
1 3 4 5 NaN NaN
2 6 7 8 NaN NaN
0 NaN NaN NaN 0 1
1 NaN NaN NaN 2 3
2 NaN NaN NaN 4 5
append
行に対して追加する。(dataを増やす)
>> result = df.append(df2)
>> result
a b c e f
0 0 1 2 NaN NaN
1 3 4 5 NaN NaN
2 6 7 8 NaN NaN
0 NaN NaN NaN 0 1
1 NaN NaN NaN 2 3
2 NaN NaN NaN 4 5
DataFrameのindex, columnを大きく変える。
pivot table
pivot_tableでは、既存のカラムから新しくindex、columnsを定義したDataFrameを作成できる。
>> df = pd.DataFrame({'col1' : ['one', 'two', 'three', 'four'] * 3,
'col2' : ['A', 'B', 'C'] * 4,
'col3' : ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2,
'col4' : np.arange(1, 13),
'col5' : np.arange(13, 25)})
>> df
col1 col2 col3 col4 col5
0 one A foo 1 13
1 two B foo 2 14
2 three C foo 3 15
3 four A bar 4 16
4 one B bar 5 17
5 two C bar 6 18
6 three A foo 7 19
7 four B foo 8 20
8 one C foo 9 21
9 two A bar 10 22
10 three B bar 11 23
11 four C bar 12 24
col1をindex、col2をcolumnsでcol4を値としたdfは以下のように作る。
>> df.pivot_table(values='col4', index='col1', columns='col2')
col2 A B C
col1
four 4 8 12
one 1 5 9
three 7 11 3
two 10 2 6
df.pivot_tableでなく、pd.pivot_tableでも同様の操作ができる。
>> pd.pivot_table(df, values='col4', index='col1', columns='col2')
col2 A B C
col1
four 4 8 12
one 1 5 9
three 7 11 3
two 10 2 6
pivot_tableでは、indexやcolumnを階層化することができる。
ここでは、col1とcol2をmulti-index、col3をcolumn, col4を表の値としたtableに作り変える。
>> df.pivot_table(values='col4', index=['col1', 'col2'], columns=['col3'])
col3 bar foo
col1 col2
four A 4.0 NaN
B NaN 8.0
C 12.0 NaN
one A NaN 1.0
B 5.0 NaN
C NaN 9.0
three A NaN 7.0
B 11.0 NaN
C NaN 3.0
two A 10.0 NaN
B NaN 2.0
C 6.0 NaN
pivot_tableにて、一つのセルに値が複数入る場合がある。
この場合、デフォルトではmean、つまり平均値が集計される。
もし集計方法を変えたい場合はaggfuncオプションに関数を与えれば良い。
>> df.pivot_table(values='col4', index='col2', columns='col3')
col3 bar foo
col2
A 7 4
B 8 5
C 9 6
>> df.pivot_table(values='col4', index='col2', columns='col3', aggfunc=np.sum)
col3 bar foo
col2
A 14 8
B 16 10
C 18 12
>> df.pivot_table(values='col4', index='col2', columns='col3', aggfunc=np.max)
col3 bar foo
col2
A 10 7
B 11 8
C 12 9
pivot
pivot_tableでは値の候補が複数ある場合の処理を気にしなければならないが、
値がかぶらないことが保証されているときはpivotで処理するのが簡単である。
pivotではpivot_tableと同様に新しくindexとcolumnとvalueになるcolumnを指定する。
>> df.pivot(values='col4', index='col1', columns='col2')
col2 A B C
col1
four 4 8 12
one 1 5 9
three 7 11 3
two 10 2 6
pivotの場合、値の候補が複数ある場合は、エラーがでる。
>> df.pivot(values='col4', index='col2', columns='col3')
ValueError: Index contains duplicate entries, cannot reshape
stack
Multi-indexのDataFrameを作成する。
>> tuples = list(zip(*[['bar', 'bar', 'baz', 'baz',
'foo', 'foo', 'qux', 'qux'],
['one', 'two', 'one', 'two',
'one', 'two', 'one', 'two']]))
>> tuples
[('bar', 'one'),
('bar', 'two'),
('baz', 'one'),
('baz', 'two'),
('foo', 'one'),
('foo', 'two'),
('qux', 'one'),
('qux', 'two')]
>> index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
>> index
MultiIndex(levels=[[u'bar', u'baz', u'foo', u'qux'], [u'one', u'two']],
labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
names=[u'first', u'second'])
>> df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B'])
>> df
A B
first second
bar one 0.201808 0.617744
two -0.272022 1.012488
baz one 0.944162 -0.243223
two 0.259391 0.064546
foo one 0.222640 0.283878
two -0.975535 0.461281
qux one -0.437626 0.366836
two -0.029409 0.141013
これをcolumnについて並列化する。
>> stacked = df.stack()
>> stacked
first second
bar one A 0.201808
B 0.617744
two A -0.272022
B 1.012488
baz one A 0.944162
B -0.243223
two A 0.259391
B 0.064546
foo one A 0.222640
B 0.283878
two A -0.975535
B 0.461281
qux one A -0.437626
B 0.366836
two A -0.029409
B 0.141013
>> stacked[0:3]
first second
bar one A 0.201808
B 0.617744
two A -0.272022
A, Bというlabelを合わせてぐっちゃぐちゃで縦に処理できる。
unstack
stackの逆。戻すレベルを指定する。
>> stacked_unstacked = stacked.unstack(0)
>> stacked_unstacked
first bar baz foo qux
second
one A 0.201808 0.944162 0.222640 -0.437626
B 0.617744 -0.243223 0.283878 0.366836
two A -0.272022 0.259391 -0.975535 -0.029409
B 1.012488 0.064546 0.461281 0.141013
>> stacked_unstacked = stacked.unstack(1)
>> stacked_unstacked
second one two
first
bar A 0.201808 -0.272022
B 0.617744 1.012488
baz A 0.944162 0.259391
B -0.243223 0.064546
foo A 0.222640 -0.975535
B 0.283878 0.461281
qux A -0.437626 -0.029409
B 0.366836 0.141013
get_dummies
ダミー変数を作成する。カテゴリカルデータで機械学習などをしたいときなどに便利。
>> df = pd.DataFrame({'A' : ['one', 'one', 'two', 'three'] * 3,
'B' : ['A', 'B', 'C'] * 4,
'C' : ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2})
>> df
A B C
0 one A foo
1 one B foo
2 two C foo
3 three A bar
4 one B bar
5 one C bar
6 two A foo
7 three B foo
8 one C foo
9 one A bar
10 two B bar
11 three C bar
>> df.C.str.get_dummies()
bar foo
0 0 1
1 0 1
2 0 1
3 1 0
4 1 0
5 1 0
6 0 1
7 0 1
8 0 1
9 1 0
10 1 0
11 1 0
もしくは、
>> pd.get_dummies(df.C)
bar foo
0 0 1
1 0 1
2 0 1
3 1 0
4 1 0
5 1 0
6 0 1
7 0 1
8 0 1
9 1 0
10 1 0
11 1 0
df.Cにはbar, fooという文字列が入っており、それぞれbar, fooというカラムを
もつ、DataFrameを作成している。
値は、0, 1が入っているが、各インデックスに対して、barならbarカラムを1にしfooカラムを0にし、
fooならばfooカラムを1としbarカラムを0としている。
つまり、各indexがbarに属しているかfooに属しているかを0, 1に変更している。
そのままの値をカラム名としたくない場合はprefixをつけることも可能。
>> pd.get_dummies(df.C, prefix='category')
category_bar category_foo
0 0 1
1 0 1
2 0 1
3 1 0
4 1 0
5 1 0
6 0 1
7 0 1
8 0 1
9 1 0
10 1 0
11 1 0
Pandas: DataFrameに数式をあてはめる
前回までと同じく
>> df = pd.DataFrame(np.reshape(np.arange(9), (3,3)),
columns=['a', 'b', 'c'])
>> df
a b c
0 0 1 2
1 3 4 5
2 6 7 8
を使って数式による計算を適用してみる。
apply
とてもよくお世話になるメソッド。
累積(np.cumsum)
columnについて累積をとる。
>> df.apply(np.cumsum)
a b c
0 0 1 2
1 3 5 7
2 9 12 15
根号(np.sqrt)
>> df.apply(np.sqrt)
a b c
0 0.000000 1.000000 1.414214
1 1.732051 2.000000 2.236068
2 2.449490 2.645751 2.828427
lambda関数
# 各列ごとの最大と最小の差
>> df.apply(lambda x: x.max() - x.min())
a 6
b 6
c 6
基礎統計
平均を取る
列について
>> df.mean(0)
a 3
b 4
c 5
行について
>> df.mean(1)
0 1
1 4
2 7
ファイル操作
ファイルからデータを読み込んだり、ファイルにデータを吐き出すことがよくあるので、
ファイル入出力の説明
ファイル出力
Series
to_csv関数を使う.
>> s
a 1
b 2
c 3
dtype: int64
>> s.to_csv('output.csv')
とすると, index付きのファイルが出力される.
a,1
b,2
c,3
indexを出力したくない場合は, index=False を指定する.
>> s.to_csv('output.csv', index=False)
tsvなど他の形式にしたい場合はsep(separatorの略)で区切り文字を変更する.
>> s.to_csv('output.tsv', sep='\t')
DataFrame
Seriesと同様にto_csvを使う.
>> df
a b c
0 1 3 5
1 2 4 6
>> df.to_csv('output.csv')
headerも含めて出力される.
,a,b,c
0,1,3,5
1,2,4,6
headerを省略したい場合は, header=False を指定する.
>> df.to_csv('output.csv', header=False)
0,1,3,5
1,2,4,6
Seriesの場合と同様に, index=False も指定できる.
>> df.to_csv('output.csv', index=False)
a,b,c
1,3,5
2,4,6
区切り文字も同様に sep にて変更。
>> df.to_csv('output.dat', sep='|')
|a|b|c
0|1|3|5
1|2|4|6
ファイル入力
デフォルトではseparatorがcommaになるので、tsvファイルの場合などはsep="\t"と設定する必要がある。
headerがないファイルの場合はheader=Noneと指定しておかないと1行目が勝手にheaderとして認識されてしまうので注意。
pd.read_csv("data.tsv", sep="\t", header=None)
明示的にheaderを指定してもよい
pd.read_csv("data.tsv", sep="\t", names=["column1", "column2"])
indexが入っているファイル場合, indexが入っている列の番号を指定する.
pd.read_csv("data.csv", index_col=0)
入出力例
indexつきheaderつきのファイルの入出力
>> df = pd.DataFrame({'a': [1,2], 'b': [3,4], 'c': [5,6]})
>> df
a b c
0 1 3 5
1 2 4 6
>> df.to_csv('sample.csv')
これでsample.csvができる.
,a,b,c
0,1,3,5
1,2,4,6
sample.csvを読み込んでもとのDataFrameに戻すには, index_colを指定して読み込む.
>> pd.read_csv('sample.csv', index_col=0)
a b c
0 1 3 5
1 2 4 6
このときindex_colを指定しないと別途indexが指定されてしまう。
>> pd.read_csv('sample.csv')
Unnamed: 0 a b c
0 0 1 3 5
1 1 2 4 6
データベース連携
実務上では, データベースからデータを取得して処理したいことがある。
ここではpandasでどのようにデータを取得して、DataFrameに格納するかを説明する。
postgresqlからデータを取得する
pandasにはsqlに接続するための pandas.io.sql がある。
これにpostgresql接続用のライブラリpsycopg2を利用して接続する。
import pandas as pd
import psycopg2
connection = psycopg2.connect(host='host', dbname='database', user='username', password='password')
result = pd.read_sql("SELECT * FROM users", connection)
MySQLを使用する場合
MySQLを使用する場合は MySQLdb を利用する。connectionの部分をMySQLdb用に変更する.
import pandas as pd
import MySQLdb
connection = MySQLdb.connect(host='host', dbname='database', user='username', password='password')
result = pd.read_sql("SELECT * FROM users", connection)
read_sql
データベースに接続しその結果をtsvで吐き出す
import pyodbc
import pandas.io.sql as psql
connection = pyodbc.connect(uid=user, dsn='instance', database='database', pwd='password')
column_data = psql.read_sql("SELECT * FROM hoge", connection)
print "\t".join(map(str,list(column_data.ix[0])))
ここでsqlの部分を
SELECT COUNT(fuga), MAX(fuga), MIN(fuga) FROM hoge;
のようにすると
AssertionError: Wrong number of items passed (3 vs 1)
というerrorが出ることがある.
これは
SELECT COUNT(fuga), MAX(fuga) AS max_value, MIN(fuga) AS min_value FROM hoge;
のように抽出後のcolumn名を指定すれば回避できる.