俺のOneNote

俺のOneNote

データ分析が仕事な人のOneNote愛とか、分析小話とか。

Google Colaboratory からKaggle API を叩く

過去にQiitaに書いたことがありますが、
こちらにもメモとして再度検証、記録に残します。

Kaggle APIの公式リファレンスはこちら

github.com

Kaggle json のダウンロード

My Accountから、API Tokenを作成、kaggle.jsonをダウンロードします。

f:id:kopaprin:20200506105528j:plain

以下、google drive上に先ほどのjsonを格納

f:id:kopaprin:20200506105624j:plain

Colaboratory

以下、pipからkaggleモジュールをインポート

!pip install kaggle

google driveにマウント、必要があれば適当にカレントディレクトリを設定します。

import os
from google.colab import drive
drive.mount('/content/drive')
os.chdir("/content/drive/My Drive/hoge/hogehoge")

先ほど読み込んだjsonファイルからusernameとkeyを環境変数に読み込みます。

import os
import json
f = open("/content/drive/My Drive/kaggle.json", 'r')
json_data = json.load(f) 
os.environ['KAGGLE_USERNAME'] = json_data['username']
os.environ['KAGGLE_KEY'] = json_data['key']

以上で準備は終わりです。簡単でした。

api

上記処理で、あとはapiを叩くだけです。

現在開催されているコンペティションリストはこれ。

!kaggle competitions list

f:id:kopaprin:20200506111754p:plain

試しに現在開催されている以下コンペのデータをカレントディレクトリにダウンロードしてみます。

www.kaggle.com

Dataタブに表記されているapiを、colaboratoryにコピペするだけです。

f:id:kopaprin:20200506105600p:plain

!kaggle competitions download -c m5-forecasting-uncertainty

f:id:kopaprin:20200506111920p:plain

無事全ファイルダウンロードされていました。

また、この例ではデータはzip形式になっていますが、
pandas.read_csvはzipのまま読み込むことが可能です。

import pandas as pd
train = pd.read_csv("sales_train_validation.csv.zip")

print(train.shape)
display(train.head())

f:id:kopaprin:20200506112120p:plain

無事、ちゃんと読み込めていそうな感じでした。

appendix

過去から記事を漁るといろいろでてきますので、お好きな方法で実装すればよろしいかと思います。

【Pythonメモ】Google ColaboratoryでKaggle APIを使うおまじないコード&作法 - Qiita

Easy way to use Kaggle datasets in Google Colab | Data Science and Machine Learning

Google Collaboratory で kaggle を扱う

Google Colab上でKaggleのデータをロード、モデル訓練、提出の全てを行う - Qiita

Ternary plot をTableauで実現する

Ternary Plot、3次元プロット?をTableauで実装してみます。

en.wikipedia.org

Tearnary Plotは3次元の構成要素を正三角形内にプロットするVizです。

Tableau Public に Tearnary Plot のサンプルが多々 Publish されているので、
それを見ていただくとイメージがつかめます。
僕もこれらを参考に、以下設計していきます。

public.tableau.com

データ

e-statから市区町村の年齢3階級別人口を取得しました。

この3カテゴリの構成比データを2次元座標データに加工し、 Tableau上でTernary Plotとして描写するのが本記事のゴールです。

www.e-stat.go.jp

上記e-statでデータを加工し、以下のデータを取得します。

f:id:kopaprin:20200504233108p:plain

余計な情報はcsv上で処理し、Tableauへ渡します。

Tableau上での描写

Tableauに渡した段階のデータはこちらになります。

f:id:kopaprin:20200504234814p:plain

ここから、以下のとおり構成比のメジャーを作成します。

f:id:kopaprin:20200505124617p:plain

このデータをどう2次元座標データにするかという問題には、wikipediaに答えがあります。

en.wikipedia.org

以下計算式により、3次元のa,b,cデータをデカルト座標にプロットできます。*1

f:id:kopaprin:20200505125040p:plain

TableauでX, Yのメジャーを以下のとおり作成します。

/// X
(1/2) * 
(
(2*[B:15-64]+[C:65]) /
([A:-15]+[B:15-64]+[C:65])
)
///Y
(SQRT(3)/2) *
(
[C:65] /
([A:-15]+[B:15-64]+[C:65])
)

あとはX, Yのメジャーを行列に配置し、 詳細に市町村名を配置、適当に都道府県等のディメンションで色分け、pointの大きさを人口にしてみます。

X,Yの軸の範囲は0~1に固定しておきましょう。 また、レポートの背景色・罫線などの情報は削除しておきます。

f:id:kopaprin:20200505140436p:plain

別にダッシュボードを作成、 背景に上記wikipediaで公開されているTernary Plotの画像を適当に拝借しましょう。

commons.wikimedia.org

設置した背景の上に作成したレポートを浮動オブジェクトで合わせます。

f:id:kopaprin:20200505140923p:plain

これで完成、全国市区町村の年齢3階級別構成比を2次元のTernary Plotで表現できました。

f:id:kopaprin:20200505141110g:plain

Public Garrary上の動かせるデータはこちらです。

public.tableau.com

appendix

正直、市区町村年齢構成の大勢はそこまで変わりませんので、
DataViz作品としては微妙でしたね。

もう少し時系列データで遷移を見たりすると面白いかもしれません。

3次元かつ、それぞれの軸が百分率で表現できる指標にすると、こちらのVizで表現できます。
機会があったら他の例もつくってみたいと思います。

*1:線形代数がダメなので、表現間違ってたらすみません。勉強しておきます

商品購買のアソシエーションルールをネットワークで可視化する

アソシエーション分析の概略

アソシエーション分析は、商品の何と何が一緒に買われやすいのか?を示すための分析手法です。
「アソシエーション分析」とか「アソシエーションルール」とかをググると大量の記事が出てきますので、詳細はそちらの記事を参考にしてください。*1

以下、本記事中で重要になる点だけ記載します。

支持度(support)
総購買ユーザーNのうち、商品Aと商品Bを同時に買っている人の割合です。

f:id:kopaprin:20200504011925p:plain

信頼度(confidence)
商品Aを買った人のうち、商品Bを購入した人の割合です。

f:id:kopaprin:20200504011950p:plain

リフト(lift)

商品Aを買った人のうち商品Bを購入する人の割合(信頼度)が、
そもそも総購買ユーザーNにおける商品Bの購入率より高いか低いか、を表します。

f:id:kopaprin:20200504012034p:plain

一般的には、ある程度の支持度がある購入パターンのみで線引きし、リフト値を見て商品ごとの連関強弱を計ります。

今回の目的は、アソシエーションルールをネットワークで可視化し、商品ごとの連関を分かりやすく表現できるか検証します。
ネットワークは、Pythonのnetworkxを利用し、Tableauで描写することにします。

SQLによるデータ抽出

自身のお仕事にも活かせるよう購買データを使います。
postgresqlが提供してくれているDVDレンタルのサンプルデータを使用することにします。 ただし、バスケット(同レシート内購買)単位での分析は難しいので、ユーザー単位で集計することとします。

サンプルデータとER図はここで確認できます。

www.postgresqltutorial.com

f:id:kopaprin:20200502112927p:plain

これを自身のDB環境に入れます。

以下、SQLで上記指標に必要なA∩B, A, B, Nを集計します。*2

with item as (
    select
        inventory_id
        , title
        , name 
    from

--- 中略------------------------------------------

    inner join(
        select
            tran.title
            , count (distinct customer_id) as "B"
        from
            tran
        group by
            tran.title
    ) as target_table
    on fromto.target_item = target_table.title
    
    ;

※正直汚いSQLです。すみません。
 気になる方は以下GitHubをご確認いただければと思います。

github.com

こんな感じのテーブルに仕上がりました。

f:id:kopaprin:20200504010456p:plain

ここから、support, confidence, liftの算出とネットワーク座標の生成はPythonに渡します。

Python・networkxによる node座標データの作成

ここは、先般のブログどおり、netowrokxでTableauに取り込むテーブルデータを準備します。

kopaprin.hatenadiary.jp

まず、support , confidence , lift を計算します。 さらにAandBの出現UUが少ない組み合わせは除外してしまいましょう。

import pandas as pd
edge_df = pd.read_csv("output.csv")
# 以下、support , confidence , liftを計算します。
edge_df["support"] = edge_df["AandB"] / edge_df["N"]
edge_df["confidence"] = edge_df["AandB"] / edge_df["A"]
edge_df["lift"] = edge_df["confidence"] / ( edge_df["B"] / edge_df["N"] )
# supportが低い組み合わせを除外します。
edge_df = edge_df[edge_df["AandB"]>3]
display(edge_df)
print(edge_df.shape)

本来はconfidenceとliftの両方を考察すべきですが、
一旦liftだけをweightとしてみました。

edge_df_lift = edge_df[["source_item","target_item","confidence"]]
col_name = ["source", "target","weight"]
edge_df_lift.columns = col_name


import networkx as nx

def make_network(df):
  G = nx.from_pandas_edgelist(df,edge_attr=True)
  pos = nx.spring_layout(G)
  edges = G.edges()
  weights = [G[u][v]['weight'] for u,v in edges]
  nx.draw(G, pos, edges=edges, width=weights, node_size=10, node_color="c",with_labels=False)

  node = []
  x = []
  y = []
  for k,v in pos.items():
    node.append(k)
    x.append(v[0])
    y.append(v[1])

  node_df=pd.DataFrame({
      "item":node,
      "X":x,
      "Y":y
  })

  return node_df 

node_df = make_network(edge_df_lift)

・・・一応netowrokxでの画像出力していますが、完全に意味不明な感じになっています。

f:id:kopaprin:20200504020725p:plain

結果が見える戦いをするのはつらいですが、Tableau化まで以前と同じ手順で進めます。

result_df_1 = pd.merge(edge_df_lift,node_df,how="left",left_on="source",right_on="item")
result_df_2 = pd.merge(edge_df_lift,node_df,how="left",left_on="target",right_on="item")
result_df = pd.concat([result_df_1, result_df_2])
result_df["edge_name"] = result_df["source"] + "_" + result_df["target"]

Tableau上での可視化

手順は上にあげた過去記事とおりですので省略します。

なんとなく、lift(weight)が明らかに高い組み合わせもありますが、全体のネットワークの傾向としては解釈が難しいですね。

networkx側でもう少し手を加えるべきなのでしょうか・・・?
そもそも購買データをネットワークグラフにするのが適切ではない・・・?
あるいはサンプルデータだからランダマイズされてるとか・・・?

技術・知識的な未熟さが原因か、そもそもデータの話かは検証する余地がありそうですが、 面白い試みだったかなぁと思います。

*1:Albertの記事が大変分かりやすかったです。

*2:ここではユニークユーザー数ベースの集計値です。もしかしたら単純なトランザクションのほうがよかったかも?

【随時更新】誰にも教えたくないBIダッシュボードデザインに便利なサイト集を晒す

デザイン・分析アイデア

public.tableau.com

powerbi.microsoft.com

d3js.org

www.storytellingwithdata.com

datavizproject.com

data-viz-lab.com

From data to Viz | Find the graphic you need

デザイン素材

icooon-mono.com

free-line-design.com

kage-design.com

unsplash.com

www.pakutaso.com

www.irasutoya.com

stories.freepik.com

配色ツール

https://color.adobe.com/ja/create/color-wheel/color.adobe.com

coolors.co

brandpalettes.com

データ

www.kaggle.com

https://archive.ics.uci.edu/ml/index.phparchive.ics.uci.edu

www.data.go.jp

www.e-stat.go.jp

appendix

気になったものなど、随時更新していきます。

ネットワークグラフをつくるためのTableauとPython

Tableauでネットワークグラフを可視化しようとすると、座標データをどう用意するかが一番ネックになると思います。
現状、Tableau内でネットワークグラフを自動生成する機能は無さそうです。

ので、この辺の処理はPythonを噛ませると楽にできるので、ちゃんとできるか試行します。

networkxによる処理

そもそもnetworkxを使って完結しちゃえばいいじゃん、という話ではありますが、
TableauはじめBIプラットフォーム上で表現できれば色々使用用途が高いです。

まずはリファレンスに従い、乱数を固定しておいたほうが良さそうです。

networkx.github.io

import random
random.seed(246) 
import numpy
numpy.random.seed(4812)

pandas.DataFrameで以下のようなサンプルデータを用意。

import pandas as pd
edge_df = pd.DataFrame({
    "source":["A","B","B","C","A","C","C","A","B","F","E","A","H"],
    "target":["B","C","D","D","E","E","F","G","G","G","H","I","I"],
    "weight":[0.6,0.1,0.2,0.9,0.8,0.7,0.5,0.3,0.8,0.1,0.7,0.4,0.8]
})

networkxを使用して、任意のアルゴリズムによるノードの座標を取得しておきます。

import networkx as nx
G = nx.from_pandas_edgelist(edge_df,edge_attr=True)
pos = nx.spring_layout(G)

この座標情報をテーブル化してTableauに読ませることになります。

なお、普通に描写するとこんな感じ。

edges = G.edges()
weights = [G[u][v]['weight'] for u,v in edges]
nx.draw(G, pos, edges=edges, width=weights, node_size=400, node_color="c",with_labels=True,font_weight="bold")

f:id:kopaprin:20200501153406p:plain

エッジのweight以外も属性をつけることが可能です。
その場合、上記の例ではinput_dfに別カラムをつけ、edge_attr=TrueをしておけばOK。

これと同じネットワークグラフをTableauで描写するのが目的です。 posはdict型なので、テーブル化しておきます。

print(type(pos))
print(pos)
>>> <class 'dict'>
>>> {'A': array([ 0.05494571, -0.43277963]), 'B': array([0.38390294, 0.05184413]), 'C': array([-0.08115845,  0.52158239]), 'D': array([-0.08585107,  0.92455717]), 'E': array([-0.38630969, -0.13289213]), 'F': array([0.5411944 , 0.85869962]), 'G': array([ 0.75986786, -0.13710168]), 'H': array([-0.73922077, -0.65390988]), 'I': array([-0.44737093, -1.        ])}
import pandas as pd
node = []
x = []
y = []
for k,v in nodePos.items():
  node.append(k)
  x.append(v[0])
  y.append(v[1])

node_df=pd.DataFrame({
    "item":node,
    "X":x,
    "Y":y
})

f:id:kopaprin:20200501154446p:plain

作成したノードの座標情報データnode_dfと、
最初につくったエッジの情報データedge_dfをTableauへ送り込む用に加工します。

result_df_1 = pd.merge(edge_df,node_df,how="left",left_on="source",right_on="item")
result_df_2 = pd.merge(edge_df,node_df,how="left",left_on="target",right_on="item")
result_df = pd.concat([result_df_1, result_df_2])
result_df["edge_name"] = result_df["source"] + "_" + result_df["target"]

edge_dfを縦積みにして、source,targetそれぞれのX座標,Y座標をjoinします。
おまけにfrom-toを表すedge_nameをつけておきます。

f:id:kopaprin:20200501173837p:plain

よーし、準備OK!
result_dfをTableauに持っていくぞー。

Tableauによる表現

まずはX,Y座標によるscatter plotにして二重軸にします。
X,Y座標のメジャーを「合計」にしないように注意。
「平均」とか「最大」とかを選びましょう。

f:id:kopaprin:20200501175501p:plain

1つめの軸はディメンションのedge_nameを詳細にドラッグ(itemよりも上の階層)してマークの種類は「線」、
edgeの太さや色はweightでお好きに加工。
edgeの完成です。

f:id:kopaprin:20200501175817p:plain

2つめの軸のマークは「点」、詳細はitemだけでOKで、適当にラベルとか大きさを調整します。 これでnodeの完成。

f:id:kopaprin:20200501175941p:plain

あとは二重軸を同期して完成です~

f:id:kopaprin:20200501180039p:plain

無事、networkxと同じ描写ができましたね。
BIに載せることができると、表示範囲のフィルタリング等がしやすいので重宝しそうです。
データも最低限source, targetの情報さえあれば描写できちゃいますしね。

Python の progress bar いろいろ

プログレスバーなんてtqdm一択じゃね?

という結論で落ち着く話ではありそうですが、
なんとなく、「もっとライブラリの種類あるのかなぁ?」と思って調べてみました。

環境はgoogle colabです。

tqdm

たぶん誰もが使ったことがあるおなじみtqdm

github.com

from tqdm import tqdm
for i in tqdm(range(100)):
  time.sleep(0.1)

f:id:kopaprin:20200430181607g:plain

※たまにこんな感じで改行されるケースがあり、イラっとします。
 改行コードの関係みたいですけど。

f:id:kopaprin:20200430165459p:plain

stackoverflowにて解決済みではありますが、jupyterでは tqdm.notebookで回避することができるようです。

stackoverflow.com

from tqdm.notebook import tqdm
for i in tqdm(range(100)):
  time.sleep(0.1)

f:id:kopaprin:20200430181725g:plain

ちょっとデザイン変わっちゃいますね。

fastprogress

fastprogressっていうライブラリもあるみたいです。

github.com

from fastprogress.fastprogress import  progress_bar
for i in progress_bar(range(100)):
  time.sleep(0.1)

f:id:kopaprin:20200430181957g:plain

tqdm.notebookとほとんど一緒な件。 目盛り線がオシャレポイントか。

progressbar2

progressbar2 。

github.com

デフォルトはいたってシンプルですな。

import progressbar
for i in progressbar.progressbar(range(100)):
  time.sleep(0.1)

f:id:kopaprin:20200430182117g:plain

結論

好きなのを使えばよろしいいかな、と思います。
見た目もいろいろ設定できそうだし。

おまけ

tqdm_gui を使ってみたかったけどcolab環境だとうまくいかず。
 きっとテクニックがあるんだろうな。

f:id:kopaprin:20200430164707p:plain

stackoverflow.com

IFTTTでGoogleスプレッドシートに接続、Tableau Publicで可視化、自動更新する

タイトルのとおり、今回のアーキテクチャはこんな感じです。

f:id:kopaprin:20200501000429p:plain

超シンプル!

自分のandroidの電源プラグ抜き差しの情報を IFTTT 経由で Googleスプレッドシート に流し、
さらに Tableau Public の Googleスプレッドシートコネクタで情報取得、Publicギャラリー上で更新・可視化できるようにするものです。

なぜ充電状況なのか

便利なIFTTTのAppletsがあったから(後述)。
メインの目的はTableau PublicのGoogleスプレッドシートの接続確認です。
無料でデータ自動更新できるの、めちゃくちゃいいよね、という話。

IFTTT

たぶん3年ぶりぐらいに使いました。
Power Automateが間違いないんですが、お手軽なのはこっちかな、と思ったので。 久々の訪問、UIもだいぶ変わっていて(?*1)ワクワクします。

ifttt.com

f:id:kopaprin:20200501001252j:plain

とりあえずandroidにアプリをインストール。

play.google.com

で、使うのは定型フォーマットがあるこのApplets f:id:kopaprin:20200501001621p:plain

Plug in と Unpluggedをそれぞれポチる。

f:id:kopaprin:20200501001713p:plain

Google Driveの認証とかをくぐりますが、基本は定型のまま何もいじらなくて大丈夫なはずです。 Googleスプレッドシートも用意不要(もしない場合、勝手に作ってくれるので優秀)

2つのAppletが無事Connectedになれば準備完了。 f:id:kopaprin:20200501001958p:plain

(Appletが走るまで数分まつ)

で、電源抜き差しすると・・・

f:id:kopaprin:20200501002223p:plain

完璧!
ここまで約10分、IFTTTまだまだオワコンじゃないぞ!

Tableau

今回の主目的はIFTTTではなくこちらです。 Tableau Public の Googleスプレッドシートコネクタが運用できそうか試します。

public.tableau.com

Tableau Public 開いて さっきのGoogleスプレッドシートを選択。

f:id:kopaprin:20200501002530p:plain

適当にいろいろ認証くぐると無事にデータ接続できました。

f:id:kopaprin:20200501002816p:plain

※なぜかデータソースのテーブルは更新されないなど、挙動がイマイチ理解できていない部分もあります。今後いろいろ調べる。

普通にPublishしてもちゃんとGoogleスプレッドシートに接続されて、更新されてます。
やったぜ!

Vizは気が向いたらちゃんとデザインするかもしれませんがとりあえずこれでいいやー。

Tableau Public & Google スプレッドシートはかなり利用価値が高そうでした。

*1:覚えてないだけかも