読者です 読者をやめる 読者になる 読者になる

Gunosyデータ分析ブログ

Gunosyで働くデータエンジニアが知見を共有するブログです。

世界を代表する8人の旬なトップ機械学習研究者たち (2017年上半期版)

データ分析部の久保です。 最近行ったライブはAimerのAcoustic Live Tour 2017です。

早いもので2017年も3月になりましたが、機械学習分野は相変わらずとてもホットな分野です。 去年はAI、人工知能という言葉がディープラーニングとともにバズワードになり、その傾向は尚も続いています。

その流行の元となったのが機械学習なわけですが、今その最先端ではどういう人がどのような研究をしているのかをかなりざっくりと見ていきたいと思います。

調査方法は2013年に同様のことを行ったとき

qiita.com

と同じく、NIPSとICMLという機械学習の代表的国際会議の過去3年分を対象とし、1st authorの重要度をそれ以外の著者よりも重くしてスコアづけしました。具体的には複数人の著者がいる場合は1st authorを0.8として、残りの0.2を他の著者に分配、1人の場合は1としてあります。また1st authorと2nd authorの貢献度が同じと明記されている論文もありましたがそのあたりは考慮できていません。

結果5.0ポイント以上になった上位8人を今回は紹介したいと思います。 彼らの1st authorの論文も併記しておきます。

Prateek Jain (7.2ポイント)

https://www.microsoft.com/en-us/research/people/prajain/

  • マイクロソフトリサーチ(バンガロール, インド)の研究者
  • 専門は大規模非凸最適化および統計的学習理論
  • プライバシーやコンピュータビジョン、テキストマイニング、自然言語処理などへの応用も積極的
  • 主なNIPS, ICMLの論文
    • Alternating Minimization for Regression Problems with Vector-valued Outputs (NIPS 2015)
    • Predtron: A Family of Online Algorithms for General Prediction Problems (NIPS 2015)
    • Optimizing Non-decomposable Performance Measures: A Tale of Two Classes (ICML 2015)
    • Surrogate Functions for Maximizing Precision at the Top (ICML 2015) など

Ohad Shamir (6.8ポイント)

http://www.wisdom.weizmann.ac.il/~shamiro/

  • ワイツマン科学研究所(イスラエル)の研究者
  • 実用的な効率性と理論的な知見を組み合わせることを得意とする
  • 主なNIPS, ICMLの論文
    • Without-Replacement Sampling for Stochastic Gradient Methods: Convergence Results and Application to Distributed Optimization (NIPS 2016)
    • Convergence of Stochastic Gradient Descent for PCA (ICML 2016)
    • Fast Stochastic Algorithms for SVD and PCA: Convergence Properties and Convexity (ICML 2016) など

Cho-Jui Hsieh (6.0ポイント)

http://www.stat.ucdavis.edu/~chohsieh/rf/

  • カルフォルニア大学デービス校のアシスタント・プロフェッサー
  • 専門は大規模データに対する最適化
  • 主なNIPS, ICMLの論文
    • PASSCoDe: Parallel ASynchronous Stochastic dual Co-ordinate Descent (ICML 2015)
    • Fast Prediction for Large-Scale Kernel Machines (NIPS 2014)
    • QUIC & DIRTY: A Quadratic Approximation Approach for Dirty Statistical Models (NIPS 2014) など

Jacob Steinhardt (5.6ポイント)

http://cs.stanford.edu/~jsteinhardt/

  • スタンフォード大学の博士課程3年生(!)
  • 信頼できて人にとっても使いやすい機械学習とは何かを研究している
  • 安全なAIに関する長期的、また短期的なチャレンジについてのエッセイを書いていたりしている
  • 主なNIPS, ICMLの論文
    • Avoiding Imposters and Delinquents: Adversarial Crowdsourcing and Peer Prediction (NIPS 2016)
    • Unsupervised Risk Estimation Using Only Conditional Independence Structure (NIPS 2016)
    • Learning Fast-Mixing Models for Structured Prediction (ICML 2015) など

Jie Wang (5.0ポイント)

http://www-personal.umich.edu/~jwangumi/

  • ミシガン大学のアシスタント・プロフェッサー
  • 最適化全般、またバイオインフォマティクスや画像処理に対しても積極的
  • 主なNIPS, ICMLの論文
    • Multi-Layer Feature Reduction for Tree Structured Group Lasso via Hierarchical Projection (NIPS 2015)
    • Safe Screening for Multi-Task Feature Learning with Multiple Data Matrices (ICML 2015)
    • Two-Layer Feature Reduction for Sparse-Group Lasso via Decomposition of Convex Sets (NIPS 2014) など

Yining Wang (5.0ポイント)

http://yining-wang.com/

  • カーネギーメロン大学の博士課程3年生(!)
  • 行列補完や近似、部分空間クラスタリングに強み
  • 主なNIPS, ICMLの論文
    • Data Poisoning Attacks on Factorization-Based Collaborative Filtering (NIPS 2016)
    • Online and Differentially Private Tensor Decomposition (NIPS 2016)
    • A Theoretical Analysis of Noisy Sparse Subspace Clustering on Dimensionality-Reduced Data (ICML 2015) など

Elad Hazan (5.0ポイント)

https://www.cs.princeton.edu/~ehazan/

  • プリンストン大学の教授
  • 数理最適化やゲーム理論、計算複雑生など機械学習の中心的なところが研究分野
  • ちなみに2013年に同様の調査をしたときの唯一の連続ランクインで、当時はイスラエル工科大学の准教授であった
  • 主なNIPS, ICMLの論文
    • On Graduated Optimization for Stochastic Non-Convex Problems (ICML 2016)
    • Variance-Reduced and Projection-Free Stochastic Optimization (ICML 2016)
    • On Graduated Optimization for Stochastic Non-Convex Problems (ICML 2016) など

Kirthevasan Kandasamy (5.0ポイント)

http://www.cs.cmu.edu/~kkandasa/

  • カーネギーメロン大学の博士課程2年生(!!)
  • 統計とアルゴリズムの共通部分に興味があり、研究分野はバンディット問題、ベイズ最適化など
  • 主なNIPS, ICMLの論文
    • Gaussian Process Bandit Optimisation with Multi-fidelity Evaluations (NIPS 2016)
    • The Multi-fidelity Multi-armed Bandit (NIPS 2016)
    • Additive Approximations in High Dimensional Nonparametric Regression via the SALSA (ICML 2016) など

おまけ

今回のランキング方法でトップになる日本人(ぽい名前の)研究者は誰だろうと思いみてみると、東工大准教授の鈴木大慈先生(3.0ポイント)でした。

Taiji Suzuki's Homepage (鈴木大慈)

  • 主なNIPS, ICMLの論文
    • Minimax Optimal Alternating Minimization for Kernel Nonparametric Tensor Learning (NIPS 2016)
    • Convergence rate of Bayesian tensor estimator and its minimax optimality (ICML 2015)
    • Stochastic Dual Coordinate Ascent with Alternating Direction Method of Multipliers (ICML 2014)

また今回対象としたNIPSとICML以外にもAISTATS, WWW, WSDM, KDD, ICLR, IJCAI, COLTなど、さまざまな国際会議もあるので気になる人はチェックしてみてください。最近だとarxivにいきなり投稿されることも増えてきました。

所感

今回調査する前はやはり深層学習まわりの研究者が上位にくるのかなと思っていましたが、あまりそんなことはなく昔から着実に進んでいる研究分野の研究者が多かったという印象でした。

同じ年のNIPSやICMLに同時に1st authorとして複数論文を通している研究者もちらほらいるんですが、いったいどんな研究スタイルでそんなことできるのか個人的には気になるところです。。

機械学習はまだこれから発展していく研究分野であることは間違いないと思うので、これからも引き続き注目していきます。

Spark StreamingからAmazon Kinesis Analyticsへ移行する話

はじめに

こんにちは、データ分析部の森本です。主な業務は記事配信アルゴリズムの改善とログ基盤の整備です。
Gunosyでは、ユーザーへより良い記事を提供するためにアクセスログをストリーム処理し、集計結果を記事配信アルゴリズムに活用しています。 ストリームログ基盤にはSpark Streamingを利用していますが、現在Kinesis Analyticsへ移行中です。
この記事ではKinesis Analyticsへ移行する理由や運用上のTips等についてお話します。

Spark Streamingを利用したストリームログ基盤構成

f:id:moyomot:20170213185600p:plain

現在のストリームログ基盤はSpark Streamingで集計を行い、結果をRDSに保存しています。

なぜSpark StreamingからKinesis Analyticsへ移行するのか

サーバーコストと運用コストの削減を目的としています。

サーバーコストについて

Spark StreamingはEMR上で使用していますが、コストはそこそこします。 例えばサーバー(m4.xlarge)10台と考えると1ヶ月およそ約30万円になります。
($0.348 + $0.060) * ¥110 * 24hour * 30day * 10台
スポット価格で運用しサーバーコストを抑えてますが、スポット価格の変動に注意する必要があります。 これを複数クラスタ運用していたので、それなりのサーバーコストを要しました。 またクラスタの構築コスト、運用コストを考慮するとEMRが最適なため、自前でクラスタ環境の構築は行いませんでした。

運用コストについて

長期間運用していると、ノードがしばしば落ちます。
一時的に少数のノードが落ちてもクラスタ運用に問題ありませんが、長期的にはクラスタ状況をモニタリングし、問題が発生する前にノードの追加やクラスタの再構築が必要です。主観的に休日にクラスタ整備を行うことが多く、辛いです。(本当に土日よく落ちるのかノードダウンの統計情報を可視化してみたい)

Spark Streamingはこのアクセスログ集計でオーバースペックなところもありました。
そこで、新たな基盤となるKinesis Analyticsを利用したストリームログ基盤を紹介します。

Kinesis Analyticsを利用したストリームログ基盤構成

f:id:moyomot:20170213185624p:plain ログ集計はKinesis Analyticsが担当し、Kinesis FirefoseがElasticsearchに結果を保存しています。

  • Kinesis Analyticsとは
    ストリーミングデータをSQLクエリで集計できるサービスです。
    標準SQLを使用でき、容易に実装できました。
    集計はKinesis AnalyticsのSQLのみで完結するため、Sparkアプリケーションを実装するよりも開発コストを抑えることができました。 今回Kinesis AnalyticsのインプットにはKinesis Stream、アウトプットにはKinesis Firefoseを使用しています。

詳細はこちらの記事が詳しいです。

data.gunosy.io

運用してみて

現在Kinesis Analyticsを利用したログ基盤は運用テスト中で、大きな問題もなく順調に稼働しています。ここでは運用上のTipsについて紹介します。

マスターデータとJOINできる

Kinesis Analyticsは、SQLクエリ上で静的ファイル(リファレンスデータ)とJOINできます。
マスターデータ的なファイルをS3に保存しておき、必要なAPIを呼ぶことでKinesis Analytics上でJOINできます。 これは非常に便利な機能で重宝しています。
アクセスログ集計アプリケーションではマスターデータの更新を1日に1度Data Pipelineで行い、S3に再アップロードしています。 リファレンスデータの更新はLambdaで行っています。

AddApplicationReferenceDataSource - Amazon Kinesis Analytics

監視はDatadogとAWS Lambdaで実施

Gunosyの監視はCloudWatch、Datadog、Papertrail、PagerDutyなどの各種サービスを必要に応じて利用しています。 ログストリーム基盤の監視では最終的なアウトプット先であるElasticsearch Serviceに重きをおいて監視することにしました。 Elasticsearchの各種メトリックをDatadogでモニタリングし、アラート先をSlackに設定しています。 また直近N分間に登録されているドキュメント数もモニタリングしたいため、ドキュメント数をカウントし、Cloudwatchに登録するオリジナルスクリプトをLambdaで実装しています。(なんでも雑にLambdaで監視し、Slackに通知したくなってしまいます)

Kinesis Analyticsの対応しているリージョンが限定的

2017年2月現在、Kinesis Analyticsの対応しているリージョンは、アイルランド、オレゴン、バージニアのみとなっています。 調査したところ東京からレイテンシが最も小さいリージョンはオレゴンであったため、現在はオレゴンにKinesis Analytics環境を構築しています。 この場合、東京リージョンのログストリーム環境と比べて、オレゴンまでの転送分、遅延が多少発生します。(数秒レベル) 今回の用途では、そこまで遅延に対してシビアではないため利用に踏み切りましたが、利用用途に応じて検証する必要があります。

その他の所感

  • ElasticsearchのクエリはSQLと比較すると難しいが、中央値算出などいろいろな関数がサポートされているのが良い(なんでMySQLでは中央値を簡単に算出できないの…)
  • Kinesis streamのシャード数見積もり結構難しい、多めに見積もったほうが無難
  • Kinesis streamのシャード数分割も画面でできるようになって欲しい

Kinesis Analyticsのコスト

実際にそれなりのコストを抑えることに成功しましたが、具体的にどのくらい安くなったのかお伝えできないことが残念です。(すいません)
Kinesisの見積もり方法は下記リンクを参考にしてください。 また、オレゴンリージョンで環境を構築しているため、リージョンを越えるデータ転送料も発生しています。

料金 - Amazon Kinesis ストリーム | AWS

Amazon Kinesis Analytics 料金表 – アマゾン ウェブ サービス (AWS)

料金 - Amazon Kinesis Firehose | AWS

まとめ

  • コストの観点からアクセスログ集計はSparkStreamingからKinesis Analyticsに移行します
  • Kinesis Analyticsはフルマネージドで運用コストを抑えられます
  • Kinesis AnalyticsはSpark Streamingのような自由度はないため要件に応じた利用が求められます

ABテストの対象をいい感じに割り振る方法

こんにちは、データ分析部の石塚 (@ij_spitz) です。 最近聴いている曲は久保田利伸さんのLA・LA・LA LOVE SONGです。 ロンバケ最高でした、月曜9時はOLが街から消えるというのも納得です。

Gunosyではプロダクト改善のためにABテストを用いて意思決定を行っています。 今回はタイトルにもある通り、ABテストを実現させる上で必要となる対象の割り振り方法を、Gunosyで以前使っていた従来の手法と半年ほど前に新しく導入した手法の2つをご紹介します。 いい感じってなんだよと思われるかもしれませんが、従来の手法の課題を解決するようにいい感じに割り振る方法と理解していただければと思います。 それぞれの運用上で気づいたメリット・デメリットなども合わせてご紹介します。

従来の手法

以前はユーザIDを100で割った余りを使用していました。 例えば、全ユーザの1%でテストしたいという時には、100で割った余り0を対象にしてテストを実施し、100で割った余り1と比較するという仕組みです。

LPなどのABテストでは1回切りの訪問のため、ユーザが複数回訪問してきたときに同じテストが割り当たることは考慮しなくてもいいですが、グノシーは毎日のようにユーザが継続して利用するニュースアプリなので、テスト期間中は同じユーザが同じテストに割り当たることが必須条件となります。 次にこの手法のメリットとデメリットを見ていきます。

メリット

  • 集計がラク
    • 標準SQLには MOD という剰余を計算する関数があるので、集計する際にはログが格納されているのがBigQueryでもRedshiftでも簡単に行うことができます。
    • そのためログにユーザが割り当たっているABテストの情報を追加せずとも集計することができます。
  • 計算コストが気にならない
  • 管理しやすい
    • ユーザIDさえわかれば、どのユーザがどのABテストに割り当たっているかをすぐ確認できるので、管理しやすいです。
    • Gunosyではスプレッドシートを使ってテストを管理していました。

デメリット

  • テスト対象を選ぶのが面倒
    • この手法ではテスト対象と比較対象の2つを自分で選ぶ必要があります。
    • テストの数が2個や3個だと対象を選ぶのも苦ではないのですが、10個や20個となってくると1ユーザに複数のテストを割り当てることになり、各テストが均等に割り当てられているかの担保が難しく、時間の掛かる作業となってしまいます。
  • 以前のテストが影響してしまう
    • 前に実施していたABテストがバイアスとなって効いてくる場合があります。
    • あるテストを実施していた100で割った余りのユーザだけ数が少なくなっていたり、アクティビティの高いユーザ群になってしまい、誤った意思決定につながる可能性があります。

新しく導入した手法

現在使用している手法も余りを用いていますが、ハッシュ関数を利用したものになっています。 言葉で説明しても理解しづらいので、まずPythonのコードを載せます(Pythonのバージョンは3系を使用しています)。

import hashlib
ratio = 5
key = 'ab_test_key'
user_id = 4192481
mod = int(hashlib.sha1((key + str(user_id)).encode('utf-8')).hexdigest()[:15], 16) % 100
if mod < ratio:
    print('テスト対象')
elif mod >= 100 - ratio:
    print('比較対象')
else:
    print('どちらでもない')
  • 割り当てアルゴリズムは以下のようになっています。
    • ABテストごとに固有な文字列とユーザIDを連結
    • 連結した文字列をハッシュ化して16進数の文字列に変換
    • ハッシュ化した16進数の文字列の先頭15文字を取得する
      • 先頭15文字を取得しているのはRedshiftで16進数を10進数に変換するときに、文字列が長すぎて桁あふれが起きてしまったため
    • 取得した15文字を10進数に直す
    • 最後に取得した数値の100で割った余りがテスト割合より小さければテスト対象として割り当てる
    • 100からテスト割合を引いた値以上ならば比較対象として割り当てる

メリット

  • テスト対象を選ぶのがラク
    • 対象は自動で選ばれるのでかなりラクになりました。
    • ただ1つ注意するポイントとしては、他のテストがテスト対象と比較対象に同じ割合で含まれている点です。同じ割合で含まれていれば比較する際には問題ないという仮定を置いているのですが、どうしてもこの2つのテストは被らせたくないという条件があれば、ABテストごとに設定する固有な文字列を同じにすると被らなくなります。
  • 以前のテストが影響がなくなる
    • この手法を用いると対象となるユーザはテストごとに変わるので、ずっと同じ対象でテストをし続けるといったことはなくなります。

デメリット

  • 管理が複雑になる
    • 以前のようにスプレッドシートだけで管理することはできなくなりましたが、テスト対象と比較対象をデータベースで管理しています。
    • また、ABテストの拡大・停止のためのツールを作成しているので、管理コストはかなり抑えられていると思います。
  • 集計が複雑になる
    • 以前のようにSQLだけで集計を完結させることは難しくなりましたが、割り当たっているABテストの情報をログに付与したり、テーブルで管理することによって集計も手軽に実施することができます。
    • ヒューマンエラーや手間を考えると集計は自動化することをおすすめします。

まとめ

今回新しく導入した手法で、テスト対象を選ぶ手間だったり、以前のテストが影響してしまうといった従来の手法による課題は解決することができました。 ただし、割り当てがブラックボックス化してしまい、本当に上手く行っているのかがわかりづらくなっているという欠点もあります。 そういったところをきちんと検証したいという場合には、A/AテストやGroup Validationといった仕組みを用いると誤った意思決定を減らすことができるので、興味があれば下記のリンクを参照してみてください。

さくっとトレンド抽出: Pythonのstatsmodelsで時系列分析入門

久しぶりの投稿になってしまいましたが、ニュースパス(現在CM放映中!!)開発部の大曽根です。 作業中はGrover Washington Jr のWinelightを聴くと元気が出ます。参加ミュージシャンが素晴らしいですね。

なぜ時系列分析をするのか

数値を非常に重視している弊社では、数値を知るためのツールとしてRedashやChartioおよびSlackへの通知を活用しています。現在の数値を理解する上では、長期のトレンド(指標が下がっているのか、上がっているのか)を知ることが重要です。しかし、日々変化するデータ(特に売上やKPIと言われる指標)は、ばらつきも大きく、変化を適切に捉えることが難しいこともあります。

特にSlackなどへの通知を行っていると、日々の変化に囚われがちです。例えば、弊社ではニュースを扱っていますが、サッカーや競馬のニュースであれば土日に、技術系のニュースであれば平日に偏る傾向があります。日々の数値の浮き沈みに注目していると全体的なトレンドの変化を見逃す可能性もあります。そこで、時系列分析を行うことで変化を適切に捉えたり、予測をある程度正確にすることができます。 今回は周期的なデータとトレンド成分を分割して表現できる季節調整モデルについて紹介します (1週間であれば移動平均などでもある程度のノイズ除去は可能)。

季節調整

季節調整データをざっくり説明すると、時系列のデータを 観測値 = トレンド成分 + 季節成分 + ノイズ成分 で説明するモデルです。

f:id:dr_paradi:20170201104931p:plain

※詳しい説明は北川源四郎 『時系列解析入門』の12章を参考にいただけると幸いです。

弊社の提供するアプリもですが、人間の生活に密着している以上は、人間の生活リズムに影響を受けます。大きく分けて「月要因」、「曜日要因」、「時間要因」がありますが、今回は曜日に注目してサンプルを実装しようかと思います。

実演

本来ならサービスのデータを使いたいのですが、当然使えないので、東京電力さんのデータを使って実装したいと思います。 まずいつものpandas+jupyter+matplotlibで生データを可視化してみます。

data.gunosy.io

import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

# csvファイルの読み込み
row_data = pd.read_csv('tokyo2016.csv', index_col=0)

df = row_data.reset_index()
df['date'] = pd.to_datetime(df['date'], format='%Y/%m/%d')
df['power'] = df['power'].values.astype(int)
daily_data = df.groupby('date')['power'].sum().reset_index() #元データが1時間ごとなので日毎のデータにする
daily_data = daily_data.set_index(['date'])
daily_data.plot()

すると下記のようなデータが得られます。1年分のデータを可視化する人間の目でもなんとなく傾向は掴めますが、細かい範囲ではデータが波打っており正確なトレンドを把握することは難しいかと思います。

f:id:dr_paradi:20170201013929p:plain

そこで季節調整モデルの出番です。今回は pythonのstatsmodelsというライブラリを用いてデータをトレンド成分 + 季節成分に分けていきます。 StatsModels: Statistics in Python — statsmodels documentation

追記: Pythonのversionは3.5.1、statsmodelsのバージョンは0.8.0rc1を使用しています。

ソースは下記の通りです。

import statsmodels.api as sm
res = sm.tsa.seasonal_decompose(daily_data.values, freq=7)
res.plot()

freq には周期を入力します。今回はdailyのデータということで1週間を周期として設定します。 出力は以下のようになります。グラフの上から 観測データ(生データ)トレンド成分季節成分残差になります。 それぞれの値の関係として

観測データ(生データ) = トレンド成分 + 季節成分 + 残差 が成り立ちます。

f:id:dr_paradi:20170201014033p:plain

季節成分を除いたトレンドを見てみます。

trend = res.trend
trend = pd.DataFrame({'trend': trend, 'date':daily_data.index})
trend['date'] = pd.to_datetime(trend['date'], format='%Y-%m-%d')
trend = trend.set_index(['date'])
trend = trend.plot()

f:id:dr_paradi:20170201010642p:plain

生データと比較すると滑らかになっているのがわかるかと思います。 5月前半のゴールデンウィークなどの大型連休で凹んでいるので、事前に大きな休日などの変化要因を除くことでよりトレンドを正確に把握できるかもしれません。

また、消費電力外気温と適温との差 と考えることもできるので消費電力から気温のトレンドがより分かるようになります。今回は単年でのトレンドを追っていますが、年度ごとに比較することで年ごとの傾向がよりわかりやすくなるでしょう。

次に曜日成分を見てみます。

seasonal_data = res.seasonal
seasonal = pd.DataFrame({'seasonal': seasonal_data, 'date':daily_data.index})
seasonal['date'] = pd.to_datetime(seasonal['date'], format='%Y-%m-%d')
seasonal['weekday'] = seasonal['date'].dt.dayofweek
seasonal[0:7]

f:id:dr_paradi:20170201011320p:plain

weekdayの0は月曜日なので、土日に消費電力は大きく下がることがわかります。おそらく家庭での消費電力よりも工場やオフィスでの消費電力が多いことが影響していそうです。また、月曜日の消費電力が少なく出ていますが、こちらは月曜日に祝日が多いことも影響してそうです。事前にデータを除去したり、平滑化することでトレンド抽出がより正確になる可能性もあります。

以上のように、ただデータを見るだけではなく、季節成分とトレンド成分を分離することで、トレンドの把握が容易になるだけではなく、いろいろな知見を得ることができます。

おまけ: 時間別に見てみる

上記では、1日単位での変動を分析しましたが、時間帯によっても季節変動の差がでるので時間帯別でも同様にトレンド成分を分解してみてみます。

df = row_data.reset_index()
morning_power = df[df['time'] == '8:00'].reset_index()
morning_power['date'] = pd.to_datetime(morning_power['date'], format='%Y/%m/%d')
morning_power['power'] = morning_power['power'].values.astype(int)
morning_data = morning_power.groupby('date')['power'].sum().reset_index()
morning_data = morning_data.set_index(['date'])
res = sm.tsa.seasonal_decompose(morning_data.values, freq=7)
res.plot()

朝8時台のみ抽出したデータ f:id:dr_paradi:20170131013747p:plain

昼の12時台のみ抽出したデータ f:id:dr_paradi:20170131013738p:plain

非常に当たり前の結論になりますが、 トレンド成分を見ると、朝は冬のほうが消費電力が高く、昼は夏の方が消費電力が高いことがわかります。(生データで見てもわかるといえばわかりますが…) 時間帯別に別にトレンドを見ることで当たり前の結論も可視化で明確にすることができます。

まとめ

  • サービスを開発、改善するにあたってのKPIは多くの要因によって変化することが多いので正確に評価するのはなかなか大変です
  • 今回紹介したPythonのstatsmodelを用いて季節変動などの周期的なノイズを除去することでトレンドの把握がしやすくなります
  • 短期的な変動に左右されずにサービスの状態を把握してサービス改善につなげましょう

今後

ノイズ除去後のデータで変化検知とか異常値検知の自動化もしたいですね。

【初心者向け】Jupyter+Pandas+matplotlibを使ったデータ分析入門

こんにちは、データ分析部でバイトをしている子田(id:woody_kawagoe)です。

ニュースパスのログを集計して分析するといった業務を行っています。Gunosyで分析に利用しているツールとしては主にJupyter, Pandas, matplotlibがあります。 この組み合わせは非常に相性が良く、研究でも役立つと思います。 そこで今回のブログではデータ分析に役立つtipsや学んだことをまとめます。

Jupyter

jupyter.org

ブラウザ上で利用できる開発環境です。 対話型で、作成したスクリプトと出力結果の対応関係が非常に見やすいです。 スクリプトでprint文をかかなくても最終行に変数おけば表示してくれます。 またgithub上にJupyterで作成できるipynbファイルを置くと他のユーザからでもJupyterと同様のUIで確認出来ます。 そのためipynbファイルをそのまま解析結果の資料として職場が学校で報告することが可能です。

これは僕がJupyterで作成したコードです。 データ分析ではなく自然言語処理100本ノックの第1章の解答ですが、出力結果と合わせて見ることができて見やすいかと思います。

github.com

またvimを使う人にとっても親切なことに、Jupyter上でvimが使える拡張機能が公開されています。 以下のリンクを参考に設定してみてください。

qiita.com

Pandas

Python Data Analysis Library — pandas: Python Data Analysis Library

pythonで利用できるデータ分析用ライブラリです。 表形式のデータをSQLやRのように操作し、高速で処理できて便利です。

Pandasで扱うデータ型としてDataFrameとSeriesがあります。 DataFrameは複数の列を持ち、Seriesは1列のみのデータ型です。 DataFrameから1列を抽出した場合も勝手にSeriesになります。 この2つの型で使用できるメソッドが異なる場合があるので注意が必要です。

以下、pythonの対話モードでの実行例です。

>>> import pandas as pd
>>> df = pd.DataFrame(np.random.randn(6,4), index=dates, columns=list('ABCD'))
>>> df
'''
          A         B         C         D
0  0.563617  2.232149  0.246438 -1.582011
1 -0.865735 -0.092077 -1.117475 -0.832390
2  0.464084 -0.713113 -0.859806  1.683599
3 -0.569110 -0.343353 -0.054545  0.380017
4  0.232089  1.577586 -0.550634  1.990078
5  0.436252 -0.293231 -0.033918  0.738073
'''
>>> type(df)
'''
<class 'pandas.core.frame.DataFrame'>
'''
>>> df.A
'''
0    0.563617
1   -0.865735
2    0.464084
3   -0.569110
4    0.232089
5    0.436252
'''
>>> type(df.A)
'''
<class 'pandas.core.series.Series'>
'''

例えばvalue_counts()というユニークな値の個数をカウントするというメソッドがあります。 よく利用されるメソッドですが、Seriesのみでしか使えません。

df['E'] = ['one', 'one','two','three','four','three']
>>> df
'''
          A         B         C         D      E
0  0.563617  2.232149  0.246438 -1.582011    one
1 -0.865735 -0.092077 -1.117475 -0.832390    one
2  0.464084 -0.713113 -0.859806  1.683599    two
3 -0.569110 -0.343353 -0.054545  0.380017  three
4  0.232089  1.577586 -0.550634  1.990078   four
5  0.436252 -0.293231 -0.033918  0.738073  three
'''
>>> df.E.value_counts()
'''
three    2
one      2
four     1
two      1
Name: E, dtype: int64
'''
>>> df.value_counts()
'''
(中略)
AttributeError: 'DataFrame' object has no attribute 'value_counts'
'''

よく使うメソッドとしてgroupby()があります。これはSQLのGROUPBY文に似ています。 ユニークにしたいカラム(複数でも可)を指定し、その後に集計関数(sum(),max()など)をつけると他カラムの集計結果を返します。

>>> df.groupby('E').sum()
'''
              A         B         C         D
E                                            
four   0.232089  1.577586 -0.550634  1.990078
one   -0.302118  2.140072 -0.871037 -2.414401
three -0.132858 -0.636584 -0.088463  1.118090
two    0.464084 -0.713113 -0.859806  1.683599
'''
>>> df.groupby('E').max()
'''
              A         B         C         D
E                                            
four   0.232089  1.577586 -0.550634  1.990078
one    0.563617  2.232149  0.246438 -0.832390
three  0.436252 -0.293231 -0.033918  0.738073
two    0.464084 -0.713113 -0.859806  1.683599
'''

またDataFrameは自分で中身を作らなくてもread_csv()でCSVファイルを読み込んだり、 read_sql_query()を使ってSQLをDBに送って作成することもできます。

matplotlab

matplotlib: python plotting — Matplotlib 1.5.3 documentation

pythonのデータ可視化ライブラリです。 PandasのDataFrameを元にプロットができ、出力したグラフはそのままJupyter上に表示されます。 最低plot()をDataFrame型かSeries型につければグラフの表示が出来ます。

以下、Jupyter上での実装例です。

import pandas as pd
import numpy as np 
import matplotlib.pyplot as plt
%pylab inline --no-import-all

ts = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000))
ts = ts.cumsum() # 各要素に直前のデータと合計した値を格納する、つまり累計する
ts.plot()

f:id:woody_kawagoe:20161220152909p:plain

スタイルをggplotにしておく良い感じの見た目になります。

plt.style.use('ggplot') 
ts.plot()

f:id:woody_kawagoe:20161220152917p:plain

plot内のオプションを変更することでグラフの設定を変更できます。 試しにヒストグラムを表示してみます。

ts.plot(kind='hist')

f:id:woody_kawagoe:20161220153645p:plain

データ分析の基本的な流れ

データ分析の基本的な流れは以下のような形になります。

1. SQLを飛ばしてデータを取得

2. Pandasでガチャガチャいじる

3. matplotlibでグラフ出力

f:id:woody_kawagoe:20161226110148p:plain

それぞれの処理について詳しく説明します。

1.SQLを飛ばしてデータを取得

まずPandasのread_sql_query()を使ってDBから大まかなデータを集計してDataFrameを作ります。 Pandasをほとんど使わなくてもSQLで十分データを整形できるというパターンもあるかもしれませんが、 一回クエリを送って取得したデータを繰り返し使うということが多いです。 理由としては何度もクエリを送ると時間がかかってしまったり、 DBに負荷を与えてしまう可能性があるためです。 余談ですが僕は上司に「重いクエリ投げたら(そのクエリを)すぐ殺すから安心して」と言われて一瞬ビビったことがあります。 SQLとPandasのどちらに力をいれるかはケースバイケースですが、 データサイズが大きい程クエリの回数は少なくすべきかと思います。 解析対象がCSVファイルの場合はread_csv()を使ってDataFrameを作ります。

2.Pandasでガチャガチャいじる

先ほどのDataFrameを様々な方法で整形します。 場合によってはそのまま表を結果として表示することもあります。 グラフ出力したい場合もここで中身を確認しつつ解析するのが良いと思います。

またSeries型をそのままJupyter上で出力するとテキストのみの表示になってしまうのですが、 pd.DataFrame(<Series型の変数>)と無理矢理DataFrameに変えて出力すると表形式で表示されて見やすいです。

3.matplotlibでグラフ出力

整形したDataFrameをグラフにします。 ただ全体をプロットしてもいいですが、特徴的な部分をplt.xlim()plt.ylim()で範囲指定した方が見やすい場合もあります。 他にもpltのメソッドで色々指定ができるので見やすく整形しましょう。

参考資料

Pythonによるデータ分析入門 https://www.amazon.co.jp/Pythonによるデータ分析入門-―NumPy、pandasを使ったデータ処理-Wes-McKinney/dp/4873116554www.amazon.co.jp

今回紹介したツールについて詳しく解説している本です。Pandasについて解説している書籍は少ないようで、Pandasを学びたい人に役立つ一冊となりそうです。

10 Minutes to pandas(英語) 10 Minutes to pandas — pandas 0.19.2 documentation

Pandasの公式サイトにある解説ページです。たぶん10分じゃ終わらないと思いますが、役立つ機能が1ページにまとまっていてわかりやすいです。