Pythonの可視化パッケージ Seaborn の Lineplot の凡例でハマったこと

What is Seaborn?

Pythonの可視化パッケージにはいくつかありますが、そのうちの一つに Seaborn があります。
代表的なパッケージとしては以下があります。

それぞれにユニークな特徴がありますが、Seaborn は 比較的に低級なパッケージである Matplotlib のラッパーになっていて、簡単にキレイなVizをつくれるのがうれしいです。
また、最近のパッケージにはほとんど導入されていると思いますが、列指向の DataFrame を前提に作られているのが特に気に入っています。
BIツールのTableauも列指向のデータが前提ですね。
Seaborn の ドキュメントには列指向データを以下のよう簡潔で分かりやすく表現しています。

(seaborn.lineplot の API reference より抜粋)
Parameters:
data : DataFrame
Tidy (“long-form”) dataframe where each column is a variable and each row is an observation.

データサイエンティストは Tidy なデータは大好物ですが、Messy なデータはちょっと。。。という感じですよね。
ちなみにTidyデータの構造に関する資料は以下のペーパーが気に入ってます。
https://vita.had.co.nz/papers/tidy-data.pdf

Lineplot でハマった点

本題ですが、あるIoTデバイスの加速度データを周波数解析し、各周波数帯ごとのスペクトルパワーを可視化した際に、
その凡例が正しく表示されない挙動でハマりました。

前提条件は以下です。

  • Seaborn==0.9.0
  • 入力データフレーム (df_filter_bank)

f:id:gri-blog:20200123195817p:plain
(sampling_at: 時刻, Period: 波長(周波数の逆数で、単位はhour), PSD: スペクトルパワー)

上記条件下で以下のコードを実行しました。

df_plot = df_filter_bank.loc[df_filter_bank['Period'].isin([2.0, 4.0, 8.0, 16.0])]

fig, ax = plt.subplots(figsize=(20, 8))
sns.lineplot(data=df_plot, 
             x=df_plot.index, 
             y='PSD', 
             hue='Period',
             ax=ax)
fig.show()

上記コードを補足すると、波長が2, 4, 8, 16 時間のデータを抽出し、それらを個別にlineplot で描画するという単純なものです。
結果は以下のようになりました。

f:id:gri-blog:20200123200454p:plain

Vizの凡例(legend)に注目してください。プロットする波長は4つの組合せ(2, 4, 8, 16 時間)のはずなのに、
結果はなぜか(0, 5, 10, 15, 20)となっています。値が違うどころか、個数すら違います。

hue に指定したデータ型がNumeric が原因なのではと思い、文字列型にcastしたところ、以下のエラーが。。。

AttributeError: 'str' object has no attribute 'view'

解決した方法

以下のGithubのIssueに情報がありました。
github.com

詳細はIssueを読んで頂ければ分かると思いますが、数値型データをhueとして使う場合には、一旦Tex形式の文字列にするという割とトリッキーな方法が紹介されてました。
Matplotlib にはTex形式で数式記述ができる機能が公開されていて、それを利用しているんですね。
Writing mathematical expressions — Matplotlib 3.1.2 documentation


実際、実行コードを以下のようにすると正しい挙動になりました。

df_plot = df_filter_bank.loc[df_filter_bank['Period'].isin([2.0, 4.0, 8.0, 16.0])]
# 以下を追加
df_plot['Period'] = ["$%s$" % x for x in df_plot['Period']] 

fig, ax = plt.subplots(figsize=(20, 8))
sns.lineplot(data=df_plot, 
             x=df_plot.index, 
             y='PSD', 
             hue='Period',
             ax=ax) 
fig.show()

f:id:gri-blog:20200123212657p:plain

さいごに

Lineplot をつくる際には hue に数値型データを利用したいことは結構あると思います。Seaborn を上手く改修できるのが一番ですが、この記事ではちゃちゃっと改修するための方法を紹介しました。

Markdown使っていて、不満に思うこと

まだドキュメント作成にWord Excel 使ってるの

Markdown使っていて、不満に思うこと

文章を書く時、Markdownを使っている方は多いと思います。 私もあらゆるメモはMarkdown形式で記載することが多いのですが、さて、納品用ドキュメントを作成する際に困ることがあります。
* ヘッダー・フッターに共通の要素を入れたい
(ページ番号、copyright、ロゴ画像など)
* 改ページを任意の場所に入れたい
* 目次を自動生成したい

提出先によっては、書式を整えて提出したくなる場合があり、その際にMarkdownだといささか心もとないですね。

asciidocが良いみたい

プロジェクト内でドキュメントを作成する時は、複数の人間が同じ書類に変更を加えたり、レビューをしたりするので、当然テキストベースのマークアップ言語で記述して、Gitで管理するのが良いわけです。しかし、最後にWordにコピペする作業も、ドキュメント種が多くなると大変ですし、無駄な作業の感じがして辛いです。

でも、最初からWordで作ると、変更管理機能はあるものの、ファイル共有、レビュー、修正などにGitのメリットが活かせずスピード感が出ません。そんな時は、asciidocを使うことが多いです。記載もMarkdown同様に直感的ですし、ちょっとルールを覚える必要はありますが、前述の不満も解消できています。ドキュメント形式としては、HTMLまたはPDFにして収めることになります。(納品ファイル形式が定められている場合は、、素直に諦めます)

asciidocを使ってみよう

環境準備、記法などは、だいたい検索すれば出てくるので、ここでは割愛します。 個人的にいいなと思ったところを紹介しますね。 (わたしの環境は、macにasciidoctor/asciidoctor-pdf をインストールして使っています)

アイコンが簡単かわいい

文章の前に文字を記載するだけで、かわいいアイコンが出てきます

    NOTE: こんな情報アイコンが出てきます 。

f:id:gri-blog:20200110121201p:plain

    WARNING: 警告を書くこともできます

f:id:gri-blog:20200110121219p:plain

目次が作成できる

asciidocは、文章の構造から自動的に目次を生成してくれます。

以下のコードを、文章の頭に埋めるだけ。 * sectnums 番号を振る * toclevels サブタイトルの2階層目までを目次に採用する * toc-title 目次の名称を指定(デフォルトが英語のため)

    :toc:
    :sectnums:
    :toclevels: 2
    :toc-title: 目次

この文章のここまでの見出しを例にすると、以下のように生成されます

f:id:gri-blog:20200110121246p:plain

改ページを入れられる

改ページを入れるのも簡単です。

以下のように記載するだけで、文章1と文章2の間に改ページがを入れることができます

    文章1
    <<<
    文章2

もっと使いたおそう

こちらの記事では紹介しませんでしたが、asciidoctorでは、テーマをカスタマイズすることで 出力するPDFのレイアウト、フォント、文字サイズなどが指定できます。

もっと色々試して、実用的なものを紹介できればと思います。

角度データにおける平均方向と分散の算出方法

こんにちは!
新卒2年目分析官のMです。

皆さんは風向や波向などの角度で表現することができるデータ(以下角度データ)を扱った経験はありますか? 角度データは0°から360°の周期性を持つため、体重や身長などの線形データとは扱い方が異なってきます。

本記事では基本的な統計量として、角度データの平均方向と分散の算出方法についてご紹介します!
 
・角度データの平均方向
x1,x2,...,xnを線形観測値としたとき、
一般的な算術平均は(x1+x2+...+xn)/nで与えられます。
しかし角度データの場合、この計算方法では平均方向を算出することはできません。
 
例としてθ1=30°,θ2=330°を角度観測値とした場合で考えてみましょう。
この場合、下図の通り平均方向は0°ですが一般的な算術平均の算出方法だと
(30+330)/2=180°となってしまいます。

f:id:gri-blog:20200110112803p:plain
 
角度データの平均方向を算出するには、
まずはじめに角度観測値θi(i=1,2,...,n)を平面上の単位ベクトルvi=(cosθi,sinθi)に変換します。
つぎに各単位ベクトルviの和を取り、合成ベクトル(C,S)を算出します。
※CとSは以下のように定義されます。

f:id:gri-blog:20200110113106p:plain

この合成ベクトルの向きが平均方向となります。
先ほどの例だと、

f:id:gri-blog:20200110113119p:plain
 
となり、下図の通り合成ベクトルは(√3,0)となります。

f:id:gri-blog:20200110112834p:plain

よって平均方向は0°となります。
 
・角度データの分散
平均方向と同様、分散の算出方法も線形データの場合とは異なります。
例としてθ1=30°,θ2=330°とφ1=60°,φ2=300°を角度観測値とした場合で考えてみましょう。
θの平均方向とφの平均方向はともに0°となります。
しかし、同じ平均方向をとるθとφでもθの方が散らばり具合が小さく、
より平均方向にデータが集中しています。

f:id:gri-blog:20200110112851p:plain

この散らばり具合は合成ベクトルを基準化した
平均合成ベクトル(C',S')の長さR'(以下平均合成ベクトル長)により数値化することができます。
※C',S',R'は以下のように定義されます

f:id:gri-blog:20200110125841p:plain

R'は0から1までの値をとり、
1に近いほどデータの散らばり具合は小さく、
また、0に近いほどデータの散らばり具合は大きくなるといえます。
このことから、角度データの分散は1-R'で与えられます。

先ほどの例だと
θの平均合成ベクトル長は√3/2となり、
φの平均合成ベクトル長は1/2となるため、
θのほうが散らばり具合が小さいということになります。

f:id:gri-blog:20200110125630p:plain

・さいごに
角度データを扱う統計学は方向統計学や角度統計学と呼ばれており、
様々な角度データ用の確率分布や回帰モデル等が存在します。
興味を持たれた方はぜひ調べてみてください!

仕事における「脳」の使い方を考える

一時期流行った手と腕の組み方で自分の資質がわかるという診断があります。

参考)貴方の効き脳はどっち? 右脳派?左脳派?
http://www.izmic.jp/mame/2014/01/entry_1903/

これを仕事に当てはめてみると、右脳タイプは「感性を大事に仕事を進める」 顧客の要望を汲み取り、共感し、どちらかというと営業や企画の方がより求められる素質ではないでしょうか?

感覚的には「粘土づくり」に似ていると思います。 うっすらとしたイメージはあるのでそれに近づける仕事となります。

完成図はなく臨機応変・想像で作成していく「粘土づくり」

また、左脳タイプは「論理的に仕事を進める」 顧客の問題点を解消する具体案を提示し、決まったことをきっちりとさばくのが得意で、エンジニアや経理の方により求められる素質かと思います。

こちらも感覚的には「模型づくり」に似ていると思います。 設計図はあるのでそれに忠実に作成していく仕事となります。

f:id:gri-blog:20200110105649p:plain
設計図を元に間違いなく作成していくことが重要な「模型づくり」

ただ多くの記事でも言われるように、大事な点としては自分がどんな素質でも仕事においてバランス良く頭を使う必要があり、 バランスが悪いとうまく仕事が進まないことが多々あると思います。人間なので右脳と左脳両方使うのが大事です。 偏ってしまうと、それぞれ以下のような状況になります。

◆右脳寄り過ぎな仕事
  ・顧客要望に共感しすぎて本質がブレる
  ・自分の考えに固執してしまい、思い込みで進めてしまう

◆左脳寄り過ぎな仕事
  ・自分の知識が想像の限界で、柔軟な発想が出ない
  ・仕事を「作業」として進めてしまい、本来の目的を見失う

必要なのは左右の脳をそれぞれスイッチしながら使う。 「アイデアをロジカルに詰める」ことが重要だと思います。 これをまとめると以下のような感じになると思います。

・要望を聞き、素材を得て、周辺情報を集める(右脳・左脳)
        ↓
・集まった情報から本質を想像する(右脳)
        ↓
・そこから具体案を組み立てる(左脳)
        ↓
・案を顧客とディスカッションする(右脳)
        ↓
・最終案をまとめ、その内容でモック、ドラフトを作成する(左脳)
        ↓
・できたものの見直し、改善する(右脳)
        ↓
・顧客へ報告し、更なる改善を議論し、顧客の要望を引き出す(右脳・左脳)

そう、先程の「模型と粘土」の例でたとえるのならば、右脳と左脳を全力で活用するあの世界大会「GBWC」がイメージに近いと思います。

https://bandai-hobby.net/GBWC/japan/

既製品に足りない点を自分の頭で考え作り出す、そんなこの大会で求められる資質同様に 自分の独りよがりでもなく、設計図通りでもない「想像の上を行く創造」が毎回仕事でもでできればなぁと 思いながら自身も仕事をしていますが、なかなかうまくできないことも多々あります。

ただ意識することは大切だと思うので、上記を頭の片隅に置いて日々の仕事を行うのが良いと思います。

面倒なスキーマ定義を自動で行いRedshiftのテーブルを楽に作成する方法

どうも、アナリティクス&デベロップメント部のTです。

最近数十~100GBほどのcsvデータを分析するという場面に遭遇しました。

通常ならGCPのBigQueryかな、ってとこなのですが、 今回はクライアントさんがAWSしか使用していなかったので、 代わりにRedshiftを使うことにしました。

***

さて、Redshiftに限らず、RDBで新しいテーブルを作成する際は、CREATE TABLEで最初にスキーマカラム名、データ型など)を定義しなくてはいけません。

これ、カラムが少ないデータだったらまだいいのですが、10列、20列、それ以上... とあるデータに対して、 各カラムの名前・型云々を一個一個て入力していくのって、超絶面倒です。

しかも一度テーブルを作成してしまったら、あとから細かい修正を加えるのも手間がかかります。

***

そこで本記事のお話ですが、AWS GlueというサービスをRedshiftと組み合わせて使えば、面倒なスキーマ作成・編集をかなり楽に行うことができます。

ポイントとしては以下の2つです。

  • Glueでcsvファイルをパースすることで、スキーマを自動で定義できる(データカタログの作成)
  • Glueで定義したスキーマをもとにRedshiftから直接csvファイルを読み込むことで、データを確認しつつ並行してスキーマ編集もできる

解説

続きを読む

PrestoでUNIX時間の範囲指定をしたらハマった話

最近になってPrestoというSQLに近いクエリで操作できるデータ分散処理基盤を扱うようになりましたが、 今まで書いてきたSQLクエリと同じ感覚でデータ抽出しようとしていたら痛い目に遭いました。

本記事ではその時の模様をお話をします。

Take Home Message

  • UNIX時間のカラムで抽出範囲指定をするときは、TD_TIME_RANGE関数を使うのがよい
    • Presto側で設定したPartitionを有効利用できるため
    • TIMESTAMPをUNIX時間に変換してから数値比較に持ち込むにしても、型に注意が必要
    • 公式ドキュメントが参考になる

状況

課題

とあるテーブルから、2019年7月(日本時間)の一か月間分のログの行数を数えたいと思いました。

しかしそのテーブルlog_tableでは、時刻カラムtimeUNIX時間でデータが入っていました。

さて、どう抽出すればよいでしょう?

正解例

SELECT COUNT(*) FROM log_table
WHERE TD_TIME_RANGE("time", '2019-07-01 00:00:00', '2019-07-02 00:00:00', 'Asia/Tokyo')
)

PrestoにはTD_TIME_RANGE関数が用意されていて、これを使うとタイムゾーン指定も含めて簡単に書けます。


アンチパターン

なにをやってしもたみや?

ところがどっこい、とある港区のデータ分析会社で働くT君は、 以下のようなクエリを書いて処理を行っていました。

SELECT COUNT(*) FROM log_table
WHERE time BETWEEN TO_UNIXTIME(TIMESTAMP '2019-07-01 00:00:00 Asia/Tokyo') 
                             AND TO_UNIXTIME(TIMESTAMP '2019-07-02 00:00:00 Asia/Tokyo')

いったんTO_UNIXTIME関数でTIMESTAMPをUNIX時間に直して、BETWEEN句により範囲指定を行おうとしたわけです。

しかし、こうしてしまったがゆえに、待てど暮らせど結果が返ってこず、とんだ待ちぼうけを食らってしまいました。

さて、何が悪かったのでしょう?

やってはいけない理由

まずは前提として、UNIX時間の範囲指定を行うときは、必ず整数型同士の比較を行わなくてはいけません。

さもないと、Presto側で用意したPartitionをうまく利用してくれず、必要以上にスキャンを走らせることになります。

しかし、ドキュメントTO_UNIXTIMEの仕様を見てみると、以下のようにありました:

to_unixtime(timestamp) → double

Returns timestamp as a UNIX timestamp.

はい、もうわかりましたね。

気が付かないうちに、INTEGERとDOUBLEで比較を行っていたのです。

     ・・・

一応、

CAST(TO_UNIXTIME(TIMESTAMP '2019-07-02 00:00:00 Asia/Tokyo' AS INTEGER)

のように整数型に変換すれば問題なく実行できます。

ただ、せっかくTD_TIME_RANGE関数という文明の利器(?)があるのですから、恩恵にすがりましょう。


おわりに

先ほどのアンチパターンクエリですが、実行してみると以下のようなWARNINGが出ていたので問題に気付き、リンク先のページを見て解決できました。

** WARNING: time index filtering is not set on
  - hoge_db.log_table
** This query could be very slow as a result.
** Please see https://docs.treasuredata.com/articles/presto-performance-tuning#leveraging-time-based-partitioning

Prestoは時刻操作関連の関数が通常のSQLと比べて独自なものが多く、 知らないといろいろと余計な苦労するな、と感じました。

皆様もPrestoを使うときはお気を付けを!

ForecastFlow を Tableau Prep から使う方法 - 予測編

ForecastFlow の予測を Tableau Prep から使用する方法を紹介します.

執筆時点の ForecastFlow Python パッケージのバージョンは 0.0.2 1.0.5 です.

ForecastFlow でモデルを訓練する

いつもどおりにモデルを訓練してください.

2019年12月3日以前のモデルは Prep に対応していないので再度訓練をお願いします.

TabPy サーバーの用意

Python3.7 以上をインストールしてから forecastflow, tabpy をインストールしてください.

pip install forecastflow tabpy

次に ForecastFlow TabPy Examples から config.toml を入手してください.

GitHub - GeeResearch/ForecastFlow_TabPy_Examples

コンソールを開いて config.toml の存在するディレクトリで以下のようにして TabPy を実行してください.

tabpy --config config.toml

スクリプトの用意

スクリプトの取得は2019/12/16に新しくスニペットから取得する方法が追加されました. スニペットから取得した場合はメールアドレスと ID が自動的に埋まるため少しだけ楽になります.

f:id:gri-blog:20191216164506p:plain
図の部分を編集

スニペットからスクリプトを取得する

プロジェクト -> データ -> モデルの順に選択して予測に使用するモデルのページを表示してください.

左側のメニューから Python アイコンをクリックするとコードスニペットが表示されます. コピーして prediction_example.py などの名前で保存してください.

正しいパスワードを埋めてください. 必要に応じてデータ名, 予測名を編集してください.

f:id:gri-blog:20191216171414p:plain
モデルページからスクリプトを取得

GitHub からスクリプトを取得する(これまでの方法)

prediction_example.py を ForecastFlow TabPy Examples から入手してください.

prediction_example.py はそのままでは利用できません. email, password, project_id, model_id を編集する必要があります.

email と password は ForecastFlow へのログインに使うものを入力してください.

project_id と model_id は ForecastFlow でモデル一覧を表示してから Actions にある <> をクリックすると表示されます.

f:id:gri-blog:20191204090747p:plain
ID を表示するには Actions の <> をクリック

data_name を編集して Prep からアップロードされる予測データの名前を変えることができます.

prediction_name を編集して生成される予測モデルの名前を変えることができます.

Tableau Prep から利用する

まず Tableau Prep で予測に使いたいデータを読み込みます.

次に + をクリックしてスクリプトの追加を選択します.

f:id:gri-blog:20191204085543p:plain
スクリプトの追加を選択

  • 接続タイプ Tableau Python Server
  • 接続
    • サーバー: TabPyサーバーのIPアドレスを指定. TabPy サーバーと Tableau Prep が同じマシンならば localhost
    • ポート: 9004
    • その他は空欄で OK
  • prediction_example.py を参照
  • 関数名に ff_predict を指定

f:id:gri-blog:20191204085630p:plain
接続タイプ, サーバー, 関数名を指定して, prediction_example.py を開く

この状態でフローを実行すると Tableau Prep から ForecastFlow にデータが渡されて予測が行われます.

出力はプライマリーキーと予測値を列に持つので, プライマリーキーを使って元のデータと結合して使うことが多いと思います.

f:id:gri-blog:20191115101528p:plain
典型的な利用例

更新履歴

2019/12/16 ForecastFlow 1.0.5

gri.jp