1. Python Pandas もくもく勉強会 in 新潟 #2
2019/08/04 に「Python Pandas もくもく勉強会 in 新潟 #2」を開催しました。本記事は、その時に行われた質問やアドバイス等を、勉強会後に、自分なりにまとめたものです。Pandas の理解を深める一助となれば幸いです。内容について間違いや問題等ありましたらご連絡お願いします。
(参考1) 本勉強会の開催案内 → https://connpass.com/event/139933/
(参考2) 次回勉強会の開催案内 → https://connpass.com/event/142121/
1.1. pandas では string(文字列) は dtype='object' になる
data = ['a']
pd.Series(data)
data.dtype
# 0 a
# dtype: object
1.2. Series で s[index] としたとき戻り値が Series になるときとならない時がある
Series に重複する index がある場合は、 s[index] とすると Series が戻る。
data = [1, 2, 3]
s = pd.Series(data, index=['a', 'b', 'a'])
s
# a 1
# b 2
# a 3
# dtype: int64
s['a']
# a 1
# a 3
# dtype: int64
しかし、 index に重複がない場合 Series ではなく値が戻る。
s['b']
# 2
Series で s[['b']] のようにすれば、値ではなく必ず Series として戻るので、 index に重複があってもなくても常に Series で戻るので処理を統一できて便利。
s[['b']]
# b 2
# dtype: int64
1.3. ディクショナリで渡す時は、順序を保持したい場合は OrderedDict を使用すれば良い?
勉強会後に調べたところ、 pd.DataFrame.from_dict 関数に OrderedDict を渡せば良いことがわかりました。
pd.concat を使用してもできます。アドバンスドな内容なため深入りはしません。
https://stackoverflow.com/questions/13653030/how-do-i-pass-a-list-of-series-to-a-pandas-dataframe
1.4. DataFrame を辞書で作成する場合、辞書のそれぞれのバリューのリストの長さは同じでなければならない。そうでなければエラーとなる
data1 = [1, 2, 3]
data2 = [10, 20]
df = pd.DataFrame({'A': data1, 'B': data2})
# ValueError: arrays must all be same length
1.5. DataFrame を Series の辞書で作成する場合、辞書のバリューの Series の長さは異なってもエラーにはならない。Series のマージが行われ、カラムで元の Series に存在しないindex の値には欠損値が設定される
data1 = ['10', '20', '30']
data2 = ['100', '300']
s1 = pd.Series(data1, index=[1, 2, 3])
s2 = pd.Series(data2, index=[1, 3])
df = pd.DataFrame({'A': s1, 'B':s2})
df
# A B
# 1 10 100
# 2 20 NaN
# 3 30 300
1.6. name 属性 は何に使われる?
勉強会後に調べたところ、 一つの Series を DataFrame に変換すると name 属性がカラム名として登録されるようです。
(参考URL) https://stackoverflow.com/questions/13653030/how-do-i-pass-a-list-of-series-to-a-pandas-dataframe
data = ['a', 'b', 'c']
s = pd.Series(data, name='Apple')
df = pd.DataFrame(s)
df.columns
# Index(['Apple'], dtype='object')
ただし、 二つの Series をリストとして渡すと以下のようになってしまいます.
data1 = ['a', 'b', 'c']
data2 = ['d', 'e']
s1 = pd.Series(data1, name='Apple')
s2 = pd.Series(data2, name='Orange')
df = pd.DataFrame([s1, s2])
df
# 0 1 2
# Apple a b c
# Orange d e NaN
このような場合は、 pd.concat を使用すると Series を縦に結合することができます。
pd.concat([s1, s2], axis=1)
# Apple Orange
# 0 a d
# 1 b e
# 2 c NaN
1.7. DataFrame のコンストラクタ の dtype は全体に適用される、指定しなれば自動で決まる
data1 = [0.5, 1.5, 2.5]
data2 = [10.1, 20.5, 30.9]
s1 = pd.Series(data1, name='x')
s2 = pd.Series(data2, name='y')
df = pd.DataFrame({s1.name: s1, s2.name: s2}, dtype=int)
df
# x y
# 0 0 10
# 1 1 20
# 2 2 30
1.8. DataFrame の columns の dtype の変換には astype 関数を使用するとできる
astype 関数の引数に辞書を使うと カラムごとにデータを変換できる
data1 = [0.5, 1.5, 2.5]
data2 = [10, 20, 30]
s1 = pd.Series(data1, name='x')
s2 = pd.Series(data2, name='y')
df = pd.DataFrame({s1.name: s1, s2.name: s2}, dtype=int)
df2 = df.astype({s1.name: int, s2.name: float})
df2.dtypes #注意 DataFrame なので dtype dtypes
# x int64
# y float64
# dtype: object
astype 関数の引数が一つの dtype の場合 DataFrame 全体に適用される
df3 = df.astype(str)
df3.dtypes
# x object
# y object
# dtype: object
1.9. df.loc[行ラベル] は Series なので注意
data1 = [0.5, 1.5, 2.5]
data2 = [10, 20, 30]
s1 = pd.Series(data1, name='x')
s2 = pd.Series(data2, name='y')
df = pd.DataFrame({s1.name: s1, s2.name: s2}, dtype=int)
df.loc[0]
# x 0
# y 10
# Name: 0, dtype: int64
Series ではなく DataFrame が戻って欲しい場合は以下のように df.loc[[行ラベル]] のように書く
df.loc[[0]]
# x y
# 0 0 10
2. その他 TIPS
- エクセルで作成した csv のエンコーディングは sjis
- csv にマルチバイト文字がある場合も適切に扱われるように見える
- pandas が遅い時がある、同じ操作で numpy の方が早い時がある
- 今後、DataFrame でSeriesを追加する時遅い時の時間計測を行えると良い
3. その他(勉強会の反省点等)
- 説明は qiita、gist、github などで絵を入れられると良い
- 開催について、午前か午後どちらかに分けた方が良い
- データサイエンスの人と達が使う言葉を知れると良い
- 機械学習の前のデータ、オープンデータがある、新潟市のものが使えるか?
- 開催日について平日の夜とかが良い
- 勉強時間当ててフィードバックするビジネスとかできそう?
- Prototype Cafe 8/7 は空いてない
- 平日2時間ほどの開催がよいのではないか
以上