Edited at

「Python Pandas もくもく勉強会 in 新潟 #2」まとめ


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時間ほどの開催がよいのではないか

以上