Rでk-meansクラスタリング

をした。

Pythonに比べると「ライブラリを引っ張ってくる必要がない」「クラスタリングしたあと、特に集計しなくても、クラスタ内包レコード数と各変数のクラスタ平均を出してくれる」あたりが便利だった。ただし上司の言によれば、データをPCのメモリに乗せる都合上、ビッグデータを扱いきれない場合があるとのこと。

流したコード

# ライブラリとデータマートの取り込み、内容確認
# read.csvの第一引数は使用するcsvの指定
# (header=TRUEは1行目にカラム名が入っている場合に使う)
library(dplyr)
df <- read.csv('Desktop/hoge.csv', header=TRUE)
head(df)

# 説明変数として使うカラムの番号とクラスタ数を指定してk-means実行、結果確認
# means(変数ごと平均)やsize(クラスタ内包数)をチェック
kmeans_result = kmeans(df[1:4], 3)
kmeans_result

# クラスタリングの結果(クラスタID)を
# もとのデータフレームに追加して変数に保存
# (その後のクラスタ分析のため)
df %>%
  dplyr::mutate(cluster = kmeans_result$cluster) -> cluster

# クラスタIDごとに結果の平均を算出、変数に保存
# (クラスタの特徴を可視化するため)
cluster %>%
  dplyr::group_by(cluster) %>%
  summarise_at(1:4, mean) -> cluster_mean

# 上記2種のデータフレームをcsvとして書き出し
write.csv(cluster, 'Desktop/cluster.csv', quote=FALSE, row.names=FALSE)
write.csv(cluster_mean, 'Desktop/cluster_mean.csv', quote=FALSE, row.names=FALSE)

長い(長い)

途中に添えたコメントアウトが全てなので特に説明することがない。今回は元になったデータがみんな大好きirisなので変数は少なかったけど、実際k-meansするならもっと変数が増えるだろうし、クラスタリングするからにはuserIDみたいなプライマリキーを持っておくはずなので、その辺りは適宜調整が必要。

思ったよりさっくりできたので満足した。

window関数が苦手だという話

「累計ならEXCELでやればいいでしょ」「GROUP BYでどうにかなるよね? ならない? なれよ」が口癖だった(わけではない)ので正直window関数にはなるべく触りたくなかった。わからないことはないけどいちいち調べないとダメな程度の知識量で、その「調べる」が面倒くさいのでやっぱり使わなかった。

とはいえ集計に必要なタイミングというのは往々にして訪れる。たとえば「その日までのユーザー累計購入額で分布を作って欲しい」とか。これを出すには一度「ユーザーごと、デイリーの累計購入額」を出すテーブル(サブクエリ)を作る必要があり、そのために長々クエリを組み立てていくのも面倒だしパフォーマンスも不安だというので、結局はwindow関数が近道ということに思い至る。

毎回毎回いやだーいやだーと思いながら調べるのもそれはそれで嫌なので、一度整理することにした。のが以下である。

そもそもwindow関数って何よ

「重複レコードを省かないグループ化」とは上司談である。「レコードをそのままに保ったグループ化」でも良いかもしれない。GROUP BYでまとめる場合は重複内容が1レコードにまとめられてしまうが、window関数のPARTITION BYでまとめる場合はレコードを元の件数のままで残してくれる。

それをすると何が便利か、というと、「その日の購入額を月平均購入額と比較したい場合」だとか、「累計を行いたい場合」だとかに対応できる。

覚えておいて損はない。やろう。

window関数の構成

といって文章で書けることは少ない。

SELECT
    SUM(target_column) OVER (PARTITION BY hoge ORDER BY piyo)
-- ①集計関数②OVER③PARTITION BY 仕切のカラム④ORDER BY 処理順のカラム

基本的にこの形。RANK()だと引数を取らずにPARTITION BYだけでよかったり、AVG()だと処理順は関係ないのでORDER BYが抜けたりする。

帰ってくるテーブルのレコード数が元のレコード数と同じため、ほぼレコード単位で処理の順番を指定できること、が大きい。「この人がこの日までに買った一番高い商品はなんだろう?」とかも、WHERE句で一回一回指定することなく一覧にできる。

注意点として、こうやって出す結果はもとのレコードと同じ数なので、集計関数としては扱ってもらえない。つまり同じ階層の他カラムを(重複削除のためとかで)GROUP BYに突っ込んだりしていると、これも入れなさいよと怒られる。はず(うろ覚えなので責任は取りません)。階層を変えるなりしないとダメらしい。

まとめこそすれ

やっぱり正直面倒くさいので、他の関数でできることなら他の関数でやったほうがいいと思う。

でもwindow関数は痒い所に手が届くというか、「質」を上げてくれる感じがする。これじゃなきゃだめ、という場合は少ないけど確実にあるし、使いこなせれば冗長なクエリが短縮できる場合もあるかもしれない。曖昧なことしか言えないのがまさに苦手であることの証明になってしまった。

R(dplyr)初学者の感想

R言語の勉強に手を出すことになった。Pythonと二足のわらじを履けば共倒れになることは分かりきっていて、Pythonを勉強し始めた際には「もうRにはしばらく縁がないんだろうな」と考えていた、矢先のことである。いきさつは省くが、要するに時間が空いていたのでということだった。PythonやTableauで作っていたサンキーダイアグラムが、Rなら(少なくともPythonよりは)手軽にきれいに作れるということを知ったということもある。

立場としてはアナリストにあたるので、まずは集計用のモジュールであるdplyr(でぃぷらいあー)から手を出している。SQLと比べて大きいと感じるのは三つで、

・パイプ( %>% ←これ)のおかげで処理の流れがわかりやすい

・選択関数( starts_with() とかそういうやつ)が非常に便利(たぶん)

・「集計対象一覧テーブルでのフィルタリング」が semi_join で明示的に行われている

のあたり。総じて「ぱっと見でわかりやすい」「操作をまとめて行える」というところ。

SQLをいじっているとどうしてもサブクエリやらビューやらが増えて、どの処理が最初に行われているのか見失いがちになってしまうのだけれども、Rはとにかく上から下へ流していくので、ここでグループ化したんだな、とか、ここでジョインしているんだな、といったことが見てわかるのがありがたい(本番環境といえるようなところでR集計を行っていないということを抜きにしても)。

あとkmeansとかライブラリ抜きで関数になってるのがすごいと思いました(小並感)。さすが分析用言語って感じ。機械学習は明日あたりからちょこちょこ始めていきたい。

Tableauでサンキーダイアグラムを作っていた日

気が付くと日記を書き忘れている。一昨日は体調不良だったが昨日はただのど忘れ。ちゃんとしたい。

といって今日はこれといったコードを書いていないので与太話になる。一応Tableauでサンキーダイアグラムを作ってみようとあくせくしていたのであるが、こちらも先人の明にただただ従っていただけであるのでいかんともしがたい。先人はこちらの方です。

Tableauでサンキーダイアグラムを作成する |Tableau Community

めちゃめちゃわかりやすかった、というか手取り足取りだった。データサンプルはこちらの記事の最下部に置いてくださっている、ということに気付くまでに一番時間がかかった気がする(節穴)。記事に従ってサンプルを動かして、自分で用意したデータでも試してみて、そのあと方法をExcelにまとめていたのが今日という一日だった。

話はそれるけれどもこの辺の(見分が狭いので「このへんの」としか言えないのだが)人たちはどうしてExcelに知見をまとめようとするんだろう。いやいやwordのほうがいいですよとは口が裂けても言えないのだが、なにか他に向いているツールがありそうなものだ。インデントが綺麗にできる程度しか利点が思いつかない。

目的意識

昔から、目的意識を持ったことがなかった。どちらかというと「やりたくないこと」に出会わないように出会わないように技術を身に付けたり、方向性を決めたり、という形で生きてきた。学生時代ごろからは折に触れそういった目的意識のなさを実感してきたのだが、今回は職場の同僚が「こういうものを作りたい」と言っているのを見かけて、やはりしみじみとしてしまった。

やりたいことがない。「これをしろ」と言われればその通りにしようと努力するが、「自分の将来のために好きなことを勉強していいよ」と言われると不安になる。今の職場はそういう(自主性に任せる)ところがあり、よいところだとは思っているけれど、主体性のない自分にも気付かされるので苦い気持ちになる。そんな感じの毎日。二十ちょっとにもなってこれはなあ。

全く関係ないけど今日はひとつ賢くなれたのでまたpythonのコードをメモしておきます。

半角区切りの標準入力をint型リストで格納

list = input().split(' ')
for i in range(len(list)):
    print(int(list[i]))

int型の計算がしたいときは、こういうふうにリストから「取り出す」時にキャストをかけていた。これが毎回面倒で、どうにかリストに「入れる」ときにint型で入れられねっかな~~と考えていたけどうまくやっている方法が見つかったので以下に。

list = [int(i) for i in input().split()]

要するに内包型のfor文で回せという……なるほどなーと思うしすっきりしていて見目がいい。

1 3 5

2 4 6

みたいな数字を

[[1, 3, 5], [2, 4, 6]]

みたいに多次元リストに入れてソートするときとか、初期段階でint型でリストに入れたかったので助かる(その場合は、上のコードをfor文でくるんでリストにappendしていけばいいんだろうと思う)。ネットは集合知である。ありがとうございます。

Tableauについて調べていた日

Tableau(タブロー)はBI(Business Intelligience)ツールの一つ。データを可視化する作業に長けていて、データセットからグラフを作ったり、地図上に分布を示したり、グルーピングして集計したり、といったことがドラッグ&ドロップを中心とした簡単な操作でできる。

また、csvを取り込むのみならず、RedshiftやBigQueryといった様々なデータベースにアクセスしてデータを読み込んで来ることが可能。そうして引っ張ってきた出どころの違うデータを加工し、組み合わせて集計することも容易である(らしい)。

スナップショット的にイメージを作る→レポートに役立てるだけでなく、インタラクティブに集計を行うことができるようだった。このインタラクティブというのが二通りの意味で、テーブルを都度読み込んで内容を更新してくれるので「ダッシュボード化できる」ということ、また気になる部分に操作を加えることで手軽に「ドリルダウンできる」ということである。つまり分析担当が最初の用意だけしておけば、誰もが気になったときに集計状況を見て、おかしいぞと思ったら深堀りができるらしい。仕事はどんどん自動化されていけば楽なので、世の中いいツールもあるものだなあと思った(感想)。

あと公式のデモ動画や有志の使ってみた動画を見た限り、UIがすっきりしていて綺麗だなと思った。ところどころ不便そうな部分は散見されたけれども、これは有志に知識がなかったからなのかもしれずなんとも……。

そろそろ14日の試用版をダウンロードして使ってみようかなと思う。練習用のデータセットは公式で用意してくれているようなので。今後案件で使うらしいので勉強しているわけだけれども、ちょっと触ってみたくなってきた。

PostgreSQLの日付指定の話

会社に新人さんが入ってきたので研修の様子を横目に眺めていた。データ分析主体の会社なので当然SQLから始めるのだけれども、初めてSQLに触れる人はまず確実にDATE()関数で躓いている。自分も躓いた。正しくは、BETWEEN句に日付だけをそのまま入れるおかげで、最後の日付の丸一日分がすっぽり抜け落ちてしまっているのである。

期間に正確にログを取るのであれば(これはpostgreSQLの話であるが)、

WHERE
    timestamp BETWEEN '2018-02-01' AND '2018-02-07'

ではなく、

WHERE
    timestamp BETWEEN '2018-02-01 00:00:00' AND '2018-02-07 23:59:59'

ないし

WHERE
    DATE(timestamp) BETWEEN '2018-02-01' AND '2018-02-07'

の形にしなければならないということ。過去にログを集計した際、間違っているほうのクエリでは、(この例の場合)期間尻に2018-02-07 00:00:00を指定しているのと同じ件数が返ってきたのでそういうことなのだと思う(この辺りは明言している記事が見つからなかったので言葉を濁しておく)。

日付を指定すればそれらしく返ってきそうなものだけど、そのへんはやはり厳しいらしい。研修担当の人が毎回これを教えているので、そろそろ研修資料に入れちゃってもいいんじゃないか、とは思うのだが、初学者にはむしろ一度躓いて調べてみてほしいという部分もあり複雑らしい。あしたはたぶん型変換ですっ転ぶんだろうなとslackの研修進捗報告を見ている。がんばれ。