Python3で色々する時の個人的Tips


#概要
VSCodeでPython3で機械学習とかデータマイニング的なことをする時に得られた知見をメモしていく。

読み辛いのは重々承知。

随時更新。


# Python3オンリーな場合の開発環境構築の個人的ベストプラクティス
[Python3 + venv + VSCode + macOS な開発環境の構築 - Qiita](http://qiita.com/KoheiKanagu/items/752c64aeeb154970d22e)

# 作図ライブラリ比較
[Jupyter-notebook の作図ライブラリ比較 - Qiita](http://qiita.com/deaikei/items/b71c1ca2c4c2a2c37526)

# インタラクティブな可視化
[ipywidgetsとBokeh使ってインタラクティブな可視化をする - Qiita](http://qiita.com/driller/items/0730325bf5c1cd689979)

VSCodeの[jupyter extension](https://marketplace.visualstudio.com/items?itemName=donjayamanne.jupyter)でipywidgetsをしようとすると、スクリプト周りが読めないようなので使えない。
[support for ipython/jupyter widgets · Issue #21 · DonJayamanne/vscodeJupyter](https://github.com/DonJayamanne/vscodeJupyter/issues/21)
大人しくブラウザのJupyterでやろう

## サンプル
https://github.com/bokeh/bokeh/blob/master/examples/howto/notebook_comms/Jupyter%20Interactors.ipynb

# numpy使ってたらpylintがhas no ~のエラーが出る時はVSCodeのsettings.jsonに
```json
    "python.linting.pylintArgs": [
        "--extension-pkg-whitelist=numpy"
    ]
```
[Pylint で no-member エラーを出なくする - Qiita](http://qiita.com/mkisono/items/ef1b1d0305e78ae0641a)


# k-meansした時に `ValueError: n_samples=1 should be >= n_clusters=3` なエラーが出る理由

必要となるデータは2次元である必要があるので、このブログではappendしていて非効率なので、`sample_data.iloc[:, 0:1]`のようにするといい。

これで抽出できるのは1列目であり、sample_data.iloc[:, 0]とした時と変わらないが、0:1とすることで2次元で表されることになるようで、上記のエラーは出なくなる

[機械学習・クラスタリングを理解するまで６日目 - ITの隊長のブログ](http://www.aipacommander.com/entry/2015/12/16/000005)


# `df = pd.DataFrame()` で `df.append(df2)` しても dfには追加されない。
`df = df.append(df2)` とすべし

[python - Appending to an empty data frame in Pandas? - Stack Overflow](https://stackoverflow.com/questions/16597265/appending-to-an-empty-data-frame-in-pandas)

# :の後ろに型名を書くおくと（Type Hints）、IntelliSenseしてくれるようになる

ただしType Hintsは単なるコメントと同義なようなので、合ってない型のオブジェクトを渡してもlinterは怒らないし、実行するまで型チェックはされない

[Pythonではじまる、型のある世界 - Qiita](http://qiita.com/icoxfog417/items/c17eb042f4735b7924a3)

# クラスメンバのスコープ
[Pythonのクラスメンバのスコープまとめ - Qiita](http://qiita.com/0xfffffff7/items/6ef16e79fe9886acb3f2)

# pandasのデータをいい感じに取り出したい時
[Python pandas データのイテレーションと関数適用、pipe - StatsFragments](http://sinhrks.hatenablog.com/entry/2015/06/18/221747)

# listをforeachするときにindexも欲しい時
[リストのインデックス (enumerate) - Python入門から応用までの学習サイト](http://www.python-izm.com/contents/application/index_and_value.shtml)

# dataframeの中身の型を変換したい時
[Pandas: Converting to numeric, creating NaNs when necessary](https://stackoverflow.com/a/33795876)

# pip-compileとpip-syncの組み合わせ良さげ
[pip関連ツールでPythonのパッケージ管理を楽にする - Qiita](http://qiita.com/kk6/items/90bc704c9d2a9edc580e)

# DataFrameの連結や結合した時の図解
シンプルに縦に結合したい時はappend、横に結合したい時はjoin

[Python pandas 図でみる データ連結 / 結合処理 - StatsFragments](http://sinhrks.hatenablog.com/entry/2015/01/28/073327)


# pandasのdataFrameをprintした時に量が多かったら省略されてしまうが、任意に設定すれば全表示とかできる
```py3
pd.set_option("display.max_rows", 10)
```

[pandasで表示が省略されるのを防ぐ - 仕事中の問題と解決メモ。](http://uyamazak.hatenablog.com/entry/2016/09/29/163534)

# 多次元のlistをソートする場合にはsortのkeyをlambda
[【Python】ソート#多次元リストのソート](http://qiita.com/fantm21/items/6df776d99356ef6d14d4#多次元リストのソート)

# データ構造別のソートの仕方
[Python sortのまとめ (リスト、辞書型、Series、DataFrame) - Qiita](http://qiita.com/shizuma/items/40f1fe4702608db40ac3)

# isortを使えばimportをいい感じにreformatしてくれます
[code-python-isort - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=freakypie.code-python-isort)

# %sql select * from hoge
とか書くだけていい感じにDataFrameとかにも突っ込めるjupyterのextention
[ipython-sql](https://github.com/catherinedevlin/ipython-sql)


# VSCodeでJupyterなpyをipynbに変換するツール作った - Qiita
[VSCodeでJupyterなpyをipynbに変換するツール作った - Qiita](http://qiita.com/KoheiKanagu/items/bd2560661f150cf532af)

# 前者のappendはIntelliSenseが働いてappendって出てくるが、後者のは出てこない

```py3
tttt = pd.DataFrame()
tttt.append(None)
tttt = df[["label"]]
tttt.append(None)
```
	
これは引数の型が分からないからなので、`assert isinstance` とかを使って`df[[“label”]]`の後に型を明示してあげれば、appendはIntelliSenseで出てくる

[IntelliSense に仕事してもらう Python の書き方 - アジョブジ星通信](http://azyobuzin.hatenablog.com/entry/2013/12/31/153502)

# grouperとても便利
[Python pandas アクセサ / Grouperで少し高度なグルーピング/集計 - StatsFragments](http://sinhrks.hatenablog.com/entry/2014/10/30/233606)

時系列データを1sec毎とか1日毎とかでグルーピングしたりもできる

# `.replace("hoge", "toHoge")` で置換できるのは対象の文字列に対してだけだが、
`.replace(".*", "+1", regex=True)` みたいに正規表現でもいける


# 機械学習の超初心者が、みんなが良いと言う記事を読んでまとめてみた - Qiita
[機械学習の超初心者が、みんなが良いと言う記事を読んでまとめてみた - Qiita](http://qiita.com/hi-ro-to/items/bae866695dfdd4a4b5b5)

# confusion matrixを出したくなったらこんな感じですぐできる
```py3
from sklearn.metrics import confusion_matrix

test_label_lb = []  # 正解ラベル
p_label = []  # 推定ラベル

cmx_data = confusion_matrix(y_true=test_label_lb, y_pred=p_label)
labels = ["A", "B", "C"]
df_cmx = pd.DataFrame(cmx_data, index=labels, columns=labels)
```
	
# [開発者がビッグデータ分析にPythonを使う時によくやる間違い | プログラミング | POSTD](http://postd.cc/top-mistakes-python-big-data-analytics/)

# jupyterで地下はともかく地図表示できる
[Folium](https://github.com/python-visualization/folium)

```py3
import folium
m = folium.Map(location=[33.763, -84.392], zoom_start=17)
folium.Marker(
    location=[33.763006, -84.392912],
    popup='World of Coca-Cola'
).add_to(m)
m
```

# やはりラムダ使う方がスマートか
[Python3におけるmap/filterの使い方 - どこかに向かうらしい話](http://hiroto1979.hatenablog.jp/entry/2016/02/24/020534)

# いい感じのplot例
[Python pandas プロット機能を使いこなす - StatsFragments](http://sinhrks.hatenablog.com/entry/2015/11/15/222543)

# mapやfilterで返してくるのはiteratorなので
list()などで中身出しちゃうとiteratorが進んでしまう

```py3
num_map = map(lambda n: n + 1, np.random.random(1000))
print(list(num_map)) # ここでは値でる

num_filter = filter(lambda n: n > 0.5, np.random.random(1000))
print(list(num_filter)) # ここでは値でる

print(list(num_map)) # ここではもうでない
print(list(num_filter)) # ここではもうでない
```

# dictのvalueがmaxなもののkeyを見つけたいなら1行でいける
```py3
max(dic, key=lambda i: dic[i])
```

# パスの操作はpathlibが簡単

[Python3.4以降ならos.pathはさっさと捨ててpathlibを使うべき](http://qiita.com/tag1216/items/29bf3b9c3c4b563ff0fe)

```py3
from pathlib import Path
LOG_DIR = "/Users/your_name/log"

Path(LOG_DIR).joinpath("log.json") #それか Path(LOG_DIR) / "log.json"
# PosixPath('/Users/your_name/log/log.json') となる

Path(LOG_DIR).joinpath("log.json").exists() 
# False
```

# multiprocessing使ってマルチプロセスで処理

[pythonでマルチコア並列処理をする方法](http://qiita.com/yotayokuto/items/23798d5fc75017d5a8b3)

rangeで適当に渡せばいいので簡単

# いろいろあるグラフ描画ツールのラッパーHoloViews

[Pythonの可視化ツールはHoloViewsが標準になるかもしれない](http://qiita.com/driller/items/53be86cea3c3201e7e0f)
[HoloViewsの基本的なグラフをワンライナーで](http://qiita.com/driller/items/c21200460548235fa1db)

# 進捗バーの表示
[Pythonで進捗バーを表示する（tqdm）](https://torina.top/detail/119/)

iterableのオブジェクトを渡せば秒速何iterate?で進捗しているのかも分かったりするのでいい目安になる

# matplotlibでsavefigしたらラベルがはみ出る時がある
[bbox_inches = "tight"とかそれ系のやつ](http://virsalus.hatenablog.com/entry/2015/01/19/120931)

フォントデカくしたりしたり、横長とか縦長グラフを作るとsavefigでラベルがはみ出ることがあるので`.savefig("test.png", bbox_inches="tight")`とすると綺麗に出る

# 実行時間を測りたい
[Jupyter Notebook > %timeit range(100) > 処理時間の計測 > %%timeit > 複数文の処理時間の計測](http://qiita.com/7of9/items/72df5443bde6bd3c4b0f)
[IPythonでコードの実行時間を測る話](http://ism1000ch.hatenablog.com/entry/2014/03/10/232932)

Jupyterでなら`%time func()`でfuncの実行時間が取れるが、割とブレる
`%timeit func()`にすると何回か実行して測ってくれる

VSCodeの[jupyter extension](https://marketplace.visualstudio.com/items?itemName=donjayamanne.jupyter)では`%%timeit`を認識しないので複数行を評価する場合はVSCodeのJupyterでは無理っぽい（まぁ関数にすればいい）

# そのDataFrameにNaNあるのか
[pandas DataFrame内にNaNありますか？](http://qiita.com/u1and0/items/fd2780813b690a40c197)
`df.isnull().values.any()`が覚えやすいし早いので良さげだが、型によっても変わってくるのでお試しあれ

# Pandasで高速に処理するためのtips
[Python pandas パフォーマンス維持のための 3 つの TIPS](http://sinhrks.hatenablog.com/entry/2015/07/11/223124)

# [pythonVSCode](https://github.com/DonJayamanne/pythonVSCode)でautocomplete遅い問題
[Slow auto complete speed for custom modules python #903](https://github.com/DonJayamanne/pythonVSCode/issues/903)
[Slow autocompletion/formatting #581](https://github.com/DonJayamanne/pythonVSCode/issues/581)

VSCodeのsettings.jsonに以下のように追記するとpreloadしてくれる。

```json
"python.autoComplete.preloadModules": [
    "pandas",
    "numpy",
    "matplotlib"
]
```

これにより、`pandas.DataFrame()`とかのsuggestは早くなってる気がするけど、型推論が必要な場面では変わらない気がする。
`assert isinstance`で明示してあげると早くなるけど、一々やってられん...

```py3
df = func_something()
df.sum() #ここではsumが出るの遅い

assert isinstance(df, pd.DataFrame)
df.sum() #ここではsumはすぐ出る
```

# JupyterでPoolでマルチプロセスな処理したらゾンビ大量発生問題
[IPythonでmultiprocessingを使ってたらゾンビ化するとき](http://qiita.com/cocuh/items/9777717f9873c7af6d2c)

```py3
#p = Pool()
p.terminate()
```

明示的に殺すか、

```py3
with Pool() as p:
    results = p.map(func, range(0, 100))
```

withを使う

# filterやmapを2重にしてlambdaの変数を参照するとpylintがE0602で怒る
`list_prefix`にあるprefixと一致するものを`list_ab`から見つける場合（この例はあまりうまい例じゃないけど...）

```py3
list_ab = ["aa_a", "aa_b", "ab_a", "ab_b", "ba_a", "ba_b"]
list_prefix = ["aa", "ab"]
```

```py3
print(list(
    filter(lambda a: True in map(lambda b: a.startswith(b), list_prefix),
            list_ab)
))  # ['aa_a', 'aa_b', 'ab_a', 'ab_b']
```
これだと`a`が[E0602](http://pylint-messages.wikidot.com/messages:e0602)で怒られる（が、pylintが怒るだけなので実行はできるし結果も期待通りに得られる）

```py3
from itertools import compress
print(list(
    compress(list_ab,
                [True in [a.startswith(b) for b in list_prefix] for a in list_ab]
                )
))  # ['aa_a', 'aa_b', 'ab_a', 'ab_b']
```

compressを使ってリスト内包表記で書くと良い。

# [【python】pythonでメモリ不足になったときにすること](http://hayataka2049.hatenablog.jp/entry/2017/02/09/190512)

要約すると

- multiprocessingをやめよう
- GCを上手く使おう
- numpy配列にしよう
- 32bitにしよう
- forで破壊的代入をしよう(遅いならcython)
- データ圧縮しよう（実用性微妙？）
- 物理的にメモリ増やそう

# [pickleの代わりにjoblibを使って永続化する](http://keisanbutsuriya.hateblo.jp/entry/2016/05/26/005048)

データ的に圧縮があんまり効かないので効果は薄いが、小さくはなってる
圧縮してるので書き出しまでの速度はpickeよりも当然遅い

compless=0にすると無圧縮なのでpickleで出した時とほぼ同じサイズになるが、dumpとloadにwith openとか書く必要ないのでjoblibの方が楽

```py3
import os
import pickle

import joblib
import numpy as np
import pandas as pd

dump_data = np.random.randn(10000000)

with open("dump_data.pkl", "wb") as f:
    pickle.dump(dump_data, f)

print(os.path.getsize("dump_data.pkl") / 1024 / 1024, "MB")
# 76.29409885406494 MB

joblib.dump(dump_data, "dump_data", compress=3)
print(os.path.getsize("dump_data") / 1024 / 1024, "MB")
# 73.5648946762085 MB

# joblib.load("dump_data") #読み込み
```

# seabornの作図例あれこれ

[Seabornの全メソッドを解説（その１：グラフ一覧）](http://own-search-and-study.xyz/2017/05/02/seabornの全メソッドを解説（その１：グラフ一覧）/)
[Python でデータ可視化 - カッコいいヒートマップを描こう](http://qiita.com/hik0107/items/67ad4cfbc9e84032fc6b)
[pythonで美しいグラフ描画 -seabornを使えばデータ分析と可視化が捗る その1](http://qiita.com/hik0107/items/3dc541158fceb3156ee0)

# pip-compileするときにegg_infoのエラーが出たら

matplotlibやseaborn関連で起きる場合が多い気がするが、egg_info云々とエラーが出てpip-compileができない場合がある。
その場合は `pip-compile --rebuild` するとうまくいくと思う
参考: https://github.com/jazzband/pip-tools/issues/586


# 別のディレクトリからimportしたい時

[Python 3でのファイルのimportのしかたまとめ](https://qiita.com/karadaharu/items/37403e6e82ae4417d1b3)

`__init__.py`を作って読むのがベストか


# [Visual Studio CodeのJupyter extensionからPlotlyを使う](http://garaemon.hatenadiary.jp/entry/2017/12/25/121916)

めちゃめちゃ便利

`HTML(html_code)`と`init_notebook_mode()`を同じCellで一気に実行すると表示されない。
なので、まず`HTML(html_code)`だけを実行してから、`init_notebook_mode()`を実行するとうまくいく（一旦表示できれば同じCellで一気に実行しても大丈夫）
JSのロードが非同期だから？