ごはんと飲み物は紙一重

あんまり更新できてないです

matplotlibのsubplotsでグラフをいい感じにjupyterに出力したい

いつも可視化のときに位置に拘ってしまう悪い癖が働いてしまい・・・備忘録代わりに。

jupyter notebookを使ってデータ分析しているとmatplotlibで可視化するのは日常茶飯事だと思うのですが、

あるフォルダから全てのcsvファイルを読み込み!!

そのデータをすべてヒストグラムにしたい!!

それもいい感じに2列n行の形で!!

とはみなさん思いませんか?そのために結局色々悩んだので、悩んでる方の参考になればと。
(本当はseabornとかもっと方法が色々あるとは思うのですが・・・)

私が触ったのは、

あるフォルダから全てのcsvファイルを読み込み!!os.walk

そのデータをすべてヒストグラムにしたい!!plt.hist

それもいい感じに2列n行の形で!!内包表記で配置位置のリストを生成

な感じです。今回はmatplotlib関連が主題なのでos.walk内包表記で配置位置のリストを生成あたりのお話をしようかと。

os.walk をつかったファイル参照

import osして使用するメソッドで、使用するときにフォルダの位置を指定することで、3つの返り値を取得することができ、

をrtを含むrt以降のフォルダすべてで検索した結果を返してくれます。シェル的にいうls -Rみたいな感じですね。

たとえばls -R

main/    test.txt

./main: main.py

と出た場合、

os.walkすると

./
["main"]
["test.txt", ".DS_Store"](隠しファイルまで探します)
./main
[]
["main.py"]

のように返ってきます。このファイルのリストを使ってfor文を回せばうまい具合にcsvの入ったフォルダを参照させることができます。詳しく条件をつけて検索したい場合とかはimport reとかして正規表現とかを使ったりすると幸せになれる???

2列n行でsubplotsする

一番話したかったところですね。とりあえず形にはしてるので、もっといい方法があればコメントしていただけると。 どういうことをするかというと、

for rt, dirs, fnames in os.walk(csvがあるフォルダ):
  fig, ax = plt.subplots(ncols=2, nrows = len(fnames)//2)
  row = [_%(len(fnames)//2) for _ in range(len(fnames))]
  col = [_//(len(fnames)//2) for _ in range(len(fnames))]
  for i, fname in enumerate(fnames):
    # なんやかんや前処理
    ax[row[i], col[i]].hist(ヒストグラムにするデータ)
    # その他出力オプション

subplotsで配置する位置を先にリストで作ってしまう方法を取りました。
こうすれば検索したファイル数を問わず2列n行でsubplotすることができると思います。スマートに汎用性高いプログラムにしたくなってしまって時間が取られてしまうの非常に辛い。

もっとこれよりもスマートに見た目でわかりやすいコードとかあれば更新しようかと思います。 (そこまでめんどくさいことしなくてもcellわけて一つずつplt.plotすればよk)

追記 11/17(金)

上記のように配置場所をリストにせずともうまく出力する方法が優秀な大学の先輩から提案されたのでこちらも参考にしていただけると。

for root, dirs, fnames in os.walk("./data/"):
    col = 2
    row = len(fnames)//2    
    fig = plt.figure(figsize=(16, 10))
    
    for i, fname in enumerate(fnames):
        fig.add_subplot(row, col, i+1)
        df = pd.read_csv(root + fname, header=None)
        plt.hist(df)

こちらはfigに対してadd_subplotを使用している形ですね。うまく出力行と列がfor文で回っててこちらの方がしっくりきてます(個人談)。これならdfにかぎらず画像もできそう。

f:id:ST_ha1cyon:20171117113434p:plain

それ以外にもpandasのdfから直接histを出してしまう方法もあり、こちらはグラフ毎に色を自動で変えてくれたり気を利かせてくれるのでとてもいい感じに可視化してくれますし、コードもかなりスッキリしていていい感じですね。一つ一つ読み込むのではなくcsvを一度にまとめてDataFrameに入れているので、データ分析をするときにはこちらを使用するほうが幸せになれそうです。

for root, dirs, fnames in os.walk("./data/"):
    row = 2
    col = len(fnames)
    
    datasets = [np.loadtxt(root + fname, delimiter=",") for fname in fnames]
    dataframe = pd.DataFrame(datasets)
    dataframe.T.plot(kind="hist", subplots=True, layout=(col, row), sharex=False, legend=False, bins=20, figsize=(16,10))

f:id:ST_ha1cyon:20171117114210p:plain