『Python実践データ分析100本ノック』がいい本だったので、紹介したい。
概要
この本は、実務で実際にありそうなシーンを対象として、どういったことを考え、そして分析していけばいいのかを取り扱ってる。
各項目は100本ノックという形で実際に手を動かしながら学べるようになっているので、テンポよく進められた。
具体的には、1章から10章まであり、各10本ずつノックが用意されている。
以下ではどんな内容があったのかを説明したい。
基礎編:データ加工
1、2章は基礎編で、データ加工について。
これらの章はけっこう重要で、実際にKaggleのコンペなんか見てみると分かるとおり、現実のデータってかなり汚い。
欠損値があったり、揺れがあったり。
あるいは、分析に適した形になっていなかったり。
そういう汚いデータを、実際に手を動かしながら整えていく経験をすることができる。
機械学習の入門書はいろいろ読んでるけど、概要の説明こそあれ、実例を使って試していくのはなかなか書かれていないので、勉強になった。
3〜5章では、機械学習を使って予測モデルを作っていく。
といっても、やるのは大部分がデータの整理。
結局、機械学習アルゴリズムを使う部分はライブラリを叩いてポンなんで、それまでが重要ということ。
第2部で紹介したケースはオーソドックスなものですが、思ったより機械学習モデルの構築までの道のりが長いと感じたのではないでしょうか。
実際の現場では、綺麗なデータが用意されていることはほとんどなく、多くのデータ加工とプレ分析が待ち構えています。
(『Python実践データ分析100本ノック』5章より引用)
実際に手を動かしてみると、このことを身をもって理解できると思う。
この本は面白いことに、6〜8章では最適化問題を扱ってる。
部品工場から組み立て工場への輸送などをテーマとして考えていて、(グラフ理論の)グラフを描くためのライブラリであるNetworkXを使ったり、(線形計画問題になるので)線形計画問題を解くためのライブラリであるPuLPを使ったりする。
あと、口コミが広がっていく様子をシミュレーションしたりも。
機械学習の本では見ない内容だったので、面白かった。
ちょっとだけ難点を挙げておくと、6〜8章を書いた人はおそらくPythonに不慣れで、コードがかなりPythonっぽくない。
まぁ、それをPythonっぽいコードにするのも、いい練習になるかもしれないけど。
発展編:画像処理、言語処理
9、10章では、発展的な内容ということで、画像処理と言語処理を扱ってる。
これもけっこう他の本とは違って、画像処理は深層学習を使うとかではなく、OpenCVを使っている。
それと、言語処理は形態素解析のライブラリであるMeCabを扱ってる。
これらのライブラリも使ったことがなかったので、いい勉強になった。
PythonやNumPy、Pandasの入門書ではない
一つ気をつけたいこととして、この本はPythonやNumPy、Pandasの入門書ではないということ。
Pythonの文法も知らないような人がこの本を見ても、さっぱり分からないので、そういう人はPythonのチュートリアルや入門書を読んでからの方がいい。
また、NumPyやPandasをほとんど知らない状態でこの本を読むのも、なかなか大変だと思う。
もちろん、使われてる関数を逐次調べれば読めると思うけど、あまりオススメできない・・・
むしろ、NumPyやPandasを扱ってる入門書とかを読んでみて、ある程度理解しても、実際にそれでデータ分析してみようとすると、途端に困ってしまうことが多いので、そういうタイミングで読むととてもいいと思う。
NumPyやPandasを扱ってるもので、自分がかなりマシだと思った本は、『Python Data Science Handbook』(邦題『Pythonデータサイエンスハンドブック』)。
(正直「これだ!」という本に出会ってない・・・PythonやNumPy、Pandas、あるいはMatplotlibのデザインがゴミまみれなのが原因な気もする)
これは原稿がGitHubで公開されてるので、GitHubでそのまま読むこともできるし、cloneしてきてJupyter Notebookで読むこともできる。
コードの質
もう一つ気をつけたいこととして、マシな方だとは思うけど、コードの質はそこまで高くない。
groupby()
の利用
これはこの本に限らないんだけど、まず思うのは、分かりにくいgroupby()
を使うんじゃなくて、pivot_table()
を使った方がいいということ。
ピボットテーブル作った方が、何をやってるのかが一発で分かる。
たとえば、ノック8でgroupby()
を使った次のようなコードがあった:
join_data.groupby('payment_month').sum()['price']
意味は「payment_month
列の各値ごとにテーブルを分割して、各テーブルの各列をsum()
で合計してから(これで1行のレコードになる)分割したテーブルを再結合して、price
列だけ抜き出す」というもの。
この「分割→処理→結合」という思考はややこしいし、コードもパッと見で何をやってるか分かりにくい。
ついでに言えば、
join_data.groupby('payment_month')['price'].sum()
とした方が余分な計算がされなくなるので早くなる。
(KaggleのNotebookで%time
を使って測ると、前者が約9ms、後者は約5msだった)
けど、そもそもこれはピボットテーブル使って
join_data.pivot_table(index='payment_month', values='price', aggfunc=sum)
と書いた方が分かりやすい。
意味は「payment_month
列の各値ごとにprice
列の値の合計の表を作る」と明快。
(%time
で時間を測ると、約11msでgroupby()
より少し遅いという問題はあった)
あるいは、ノック25だと、顧客と月ごとの利用回数をgroupby()
で次のように集計している:
uselog_months = uselog.groupby(["年月","customer_id"],as_index=False).count()
uselog_months.rename(columns={"log_id":"count"}, inplace=True)
del uselog_months["usedate"]
いや、パッと見でイミフでしょ。
pivot_table()
を使えばもっとシンプルになる:
uselog_months = uselog.pivot_table(index='customer_id', columns='年月', aggfunc='size', fill_value=0)
なお、groupby()
とは違う表になるけど、分析にはピボットテーブルの表の方が圧倒的に見やすいと思う。
もしgroupby()
と同じ表にしたいなら、melt()
を使うといい。
ちなみに、このあと顧客ごとの利用回数に関する集計をするんだけど、本だと
uselog_customer = uselog_months.groupby("customer_id").agg(["mean", "median", "max", "min" ])["count"]
uselog_customer = uselog_customer.reset_index(drop=False)
uselog_customer.head()
とまたgroupby()
を使ってるのに対し、ピボットテーブルにした自分のコードなら、
uselog_customer = pd.DataFrame({
'mean': uselog_months.mean(axis=1),
'median': uselog_months.median(axis=1),
'max': uselog_months.max(axis=1),
'min': uselog_months.min(axis=1),
}).reset_index()
と、各行について集計をすればいいだけなので、分かりやすい。
他にも、けっこうドッタンバッタンしてるコードがいるので、そういう部分はリファクタリングを考えながら進めるといいかも。
SettingWithCopyWarning
が出る
コードを試していると、SettingWithCopyWarning
という警告が出ることがあった。
けど、本では言及なし・・・(警告を抑制してる? それとも単に無視してる?)
これは何かというと、Pandasは列や行を抽出したときに、実体を返すかビューを返すかが曖昧なときがあり、そういった実体かビューかが曖昧なオブジェクトに対して実体を更新する処理をすると出る警告っぽい。
大抵は問題ないっぽいけど、どの実体が更新されるのか曖昧で、ちょっと危ないところがある。
実際に警告が出た一例は、ノック32。
customer_clustering['cluster'] = clusters.labels_
パッと見だとなんでこれでSettingWithCopyWarning
が出るのか分からないんだけど、原因はずっと前にあった:
features = ['mean', 'median', 'max', 'min', 'membership_period']
customer_clustering = customer[features]
...
customer_clustering['cluster'] = clusters.labels_
原因がずっと前の方にあるので、気付きにくい。。。
これは明示的にコピーしておくと警告が出なくなった:
customer_clustering = customer[features].copy()
あと、前述の通り、最適化問題を扱ってるところのコードはけっこうアレなので、頑張って直した方がいい。
Kaggleで試すときの注意事項
実際に手を動かしながら読んだ方がいいので、KaggleでNotebookを作って試すのがオススメ。
ただし、9章、10章に関しては、ちょっと注意が必要だった。
9章の注意点
まず、9章では画像を表示するのにcv2.imshow()
を使っているんだけど、これをKaggleのNotebooで実行すると、Notebookがリスタートする(^^;
この問題を解決するには、cv2.imshow()
の代わりにMatplotlibのimshow()
を使ってあげるといい:
(このとき、色をBGRからRGBに変換する必要がある)
img = cv2.imread('img01.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
fig, ax = plt.subplots(figsize=(15, 15))
ax.imshow(img_rgb);
あと、動画のキャプチャで、本ではcap.isOpened()
で終了判定をしてるけど、これ、動画の最後に来てもFalse
を返さないっぽい。
このせいで、動画の最後に来てるのにret, frame = cap.read()
が呼ばれ、これが無限待ちになって処理が終わらなくなる。。。
これは代わりに総フレーム数を使うようにするといい:
cap = cv2.VideoCapture('mov01.avi')
count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
for i in range(int(count)):
...
10章の注意点
10章の方は、MeCabのインストールがそれなりに大変。
以下のNotebookを参考にさせてもらった:
""" # インストールするときはこの行をコメントアウト
!mkdir mecab
%cd mecab
!apt install curl mecab libmecab-dev mecab-ipadic-utf8 file -y
!git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
%cd mecab-ipadic-neologd
!bin/install-mecab-ipadic-neologd -n -a -y --prefix /var/lib/mecab/dic/mecab-ipadic-neologd
!sed -i -e 's@^dicdir.*$@dicdir = /var/lib/mecab/dic/mecab-ipadic-neologd@' /etc/mecabrc
!pip install mecab-python3
%cd ../..
""";
これでインストールできるはず。
そんな感じで、問題がまったくないわけではないけど、むしろその問題の解決も含めていい勉強になると思うので、オススメ。
今日はここまで!