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

Gunosyデータ分析ブログ

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

【初心者向け】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ページにまとまっていてわかりやすいです。

Amazon AthenaをBigQueryと比較してみた

f:id:y-koid:20161201055559p:plain

こんにちは、データ分析部の阿部です。 作業中音楽は聞かない派ですが、ホワイトノイズを聞いていると集中できるという噂を聞いたことがあるので少し気になっています。

今回は、re:Invent2016で発表されたばかりのAthenaを紹介します。

Athenaとは

本日、AWSのre:Invent中で、RedshiftやEMRに続くビッグデータサービスとして、Athenaというサービスがリリースされました。

Athenaは、S3上のデータ(CSV, JSON, その他フラットファイル)に対して、インタラクティブにSQLを実行することができます。 RedshiftやEMRに比べて、クラスタの構築や運用を必要とせず、シンプルにクエリを実行できるというメリットがあります。 課金形態も、クエリ&読み込んだデータ量に応じて課金という点で、GoogleのBigQueryに近いサービスだと思えば良さそうです。

Redshiftなどのサービスとの違いや使い所は以下を見ると参考になるかと思います。 https://aws.amazon.com/jp/athena/faqs/

今回、GunosyでもAthenaを導入すべきかどうか検討すべく、早速パフォーマンスの検証をしてみました。

データの準備

データはS3に保存するだけで使えます。

Pricingのページを見てみると、GZIPでファイルを圧縮したり、列指向なストレージファイルフォーマットを使ったりすることで、読み込むデータ量を減らすことができる、と書いてあります。 料金 - Amazon Athena | AWS

今回は検証用に15億行のtable_aと、1.5億行のtable_bとtable_cをGZIP圧縮されたCSVと、Parquet*1の形式で準備しました。

また、Prestoベースということもあり、大きなファイル1つ、ではなく、ある程度のサイズで分割したほうがパフォーマンスは良くなるようです。 今回CSVの方はそれぞれ4000ファイルに分割しました。

※実際の運用としては、GunosyではKinesis Firehoseを利用しているので、ここからストリーミングデータをS3に保存し、Athenaからクエリを実行する、ということができるのではと考えています。

data.gunosy.io

テーブル作成

AthenaはWebコンソールが用意されており、Webコンソールでテーブルの作成ができます。

画面の見た目はHue*2とよく似ています。 f:id:y-abe-hep:20161127171639p:plain

データベースとテーブルの作成は画面上部のカタログマネージャをクリックして行います。

f:id:y-abe-hep:20161127171655p:plain

Step 1 ではデータベースの作成または既存のデータベースの選択とテーブルの作成をします。 Location of Input Data Set の項目では先ほどアップロードしたファイルがあるディレクトリのパスを指定します。

Step 2 ではデータフォーマット(CSV, JSONなど)を選びます。

Step 3 ではカラムの定義をします。

f:id:y-abe-hep:20161127190325p:plain

ここではカラム名と型を入力していきます。

最後に Step 4 ではパーティションの設定をします。 日毎や時間毎などに分割でき、こうすることでクエリ実行時に使用するデータを絞り込むことができます。 (クエリを実行するときに、うっかりパーティションの指定を忘れると、全てのデータが読み込まれるのでBigQuery破産ならぬAthena破産の可能性がありそうです。)

以上のことはクエリ(HiveQL)で行うこともできます。 下記はJSONの例です。シリアライザ・デシリアライザの指定は、FAQを見ると書いてあります。 よくある質問 - Amazon Athena | AWS

CREATE EXTERNAL TABLE IF NOT EXISTS tbl
(
   a string,
   b int
)
PARTITIONED BY ( partition_key string )
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' 
WITH SERDEPROPERTIES ('serialization.format' = '1')
LOCATION 's3://path/to/objects/';

パーティションを利用する場合は、 /YYYY/MM/DD/HH/ などの階層を、個別に追加します。

ALTER TABLE tbl ADD
IF NOT EXISTS PARTITION (partition_key='2016_MM_DD_HH')
LOCATION 's3://path/to/objects/2016/MM/DD/HH/';

速度測定

いくつかクエリを実行して速度を検証しました。 クエリはWebコンソールから実行することができます。

クエリサンプル1

まずはカラムの行数を調べるクエリです。

SELECT COUNT(*) FROM table_a
サービス 実行時間
Athena (csv.gz) 11秒
Athena (parquet) 7秒
BigQuery 10秒

Athena (csv.gz) と BigQuery では10秒程でした。 AthenaではParquetを使うことでパフォーマンスは更に改善し、7秒でした。

AthenaやBigQueryでは同時に複数のジョブを走らせたりもしてみましたが、他のジョブによる影響は無かったです。

クエリサンプル2

SELECT b, COUNT(a) FROM table_a GROUP BY b

カラムbのユニーク数は150です。

サービス 実行時間
Athena (csv.gz) 16秒
Athena (parquet) 20秒
BigQuery 10秒

Athenaでは20秒弱、BigQueryでは10秒で実行できました。 Parquetの方がパフォーマンスが良さそうと考えられますが、このクエリではcsv.gzの方が若干良い結果でした。

まとめ

今回発表されたAthenaはフルマネージドで超高速な分散処理サービスです。 運用が簡単でパフォーマンスも良く、導入する価値があるサービスだと思いました。 とくにAWSをメインに利用している場合、S3にログを保存している場合にはメリットが大きいのではないでしょうか。 あとは各種ライブラリや、弊社で重宝しているRe:dashなどのサービスへの対応が早く充実してくることに期待です。

*1:Parquetとは主にHadoopのエコシステムで利用される列指向なストレージファイルフォーマットです

*2:HadoopやHiveをWebUIで操作するためのツール

Amazon Kinesis AnalyticsとES/Kibana4でリアルタイムダッシュボード構築

こんにちは。開発・運用推進部の小出です。 猫にイヤホンを噛み切られること数回、最近のBGMはもっぱら環境音です。 「耳からうどんが出ているようにしか見えない」という噂のBluetoothイヤホンが気になっています。

今回は、Amazon Kinesis AnalyticsとElasticsearch/Kibana4を利用したリアルタイムダッシュボード構築についてです。

Amazon Kinesis Analytics とは

Amazon Kinesis Analytics(ストリーミングデータに対して標準 SQL クエリを最も簡単に実行)| AWS https://d0.awsstatic.com/amazonkinesis/KinesisAnalytics_LP_Digital%20Marketing.png Amazon Kinesis Analyticsは、ストリームデータをSQLで分析できるサービスです。 SourceとしてKinesis Streams/Firehose、クエリ結果のDestinationとしてKinesis Streams/Firehoseを選択できます。

さらにKinesis Firehoseは、Amazon Elasticsearch Serviceに出力でき(便利!)、Amazon Elasticsearch ServiceはKibana4も付いてくる(お得!)ので、今回はこれを利用してダッシュボードを構築してみます。

ダッシュボードを構築してみる

Gunosyでは、ログコレクタとしてfluentdを採用しているので、そこからKinesis Firehoseへログを送ります。

GitHub - awslabs/aws-fluent-plugin-kinesis: Fluent Plugin for Amazon Kinesis

f:id:y-koid:20161108104241p:plain

Source StreamとMapping

下記のようなJSON形式のログが送られているとします。

{"article_id": 12345, "user_id": 12345, "action": "click"}
{"article_id": 12345, "user_id": 12345, "action": "impression"}

Kinesis Analyticsのマネジメントコンソールにて、SourceとなるKinesis Firehoseを選択すると、 マッピングを指定することができます。

JSONの場合、下記のようにRaw Pathを指定し、カラム名とJSONのキーを対応させます。

f:id:y-koid:20161108110639p:plain

Query

Kinesis Analyticsでは、Window関数としてTumbling WindowSliding Windowが用意されています。今回はTumbling Windowを利用し、記事IDをベースに1分毎に集計を行います。

/* Destination Stream */
CREATE OR REPLACE STREAM "DESTINATION_SQL_STREAM" (
  TIME TIMESTAMP, ARTICLE_ID INTEGER, IMPRESSION INTEGER, CLICK INTEGER
);

/* Tumbling WindowでのGROUP BY (1分毎) */
CREATE OR REPLACE PUMP "STREAM_PUMP" AS
INSERT INTO "DESTINATION_SQL_STREAM"
SELECT STREAM
  ROWTIME AS TIME,
  ARTICLE_ID,
  SUM(CASE ACTION WHEN 'impression' THEN 1 ELSE 0 END) AS IMPRESSION,
  SUM(CASE ACTION WHEN 'click' THEN 1 ELSE 0 END) AS CLICK
FROM "SOURCE_SQL_STREAM_001"
GROUP BY
  ARTICLE_ID,
  FLOOR("SOURCE_SQL_STREAM_001".ROWTIME TO MINUTE);

クエリの結果は、下記のイメージのようになります。

TIME ARTICLE_ID IMPRESSION CLICK
2016-11-09 12:01:00.0 12345 5 0
2016-11-09 12:01:00.0 12346 10 2
2016-11-09 12:01:00.0 12347 8 1
2016-11-09 12:02:00.0 12345 7 2
2016-11-09 12:02:00.0 12346 10 3
2016-11-09 12:02:00.0 12347 5 0

Destination

クエリの結果を、Kinesis Firehose(&Elasticsearch Service)に向けると、あっという間にKibana4でダッシュボードができます。

例えば、Visualize -> Data Tableで、記事ID別のimpression/clickの表が作れます。

f:id:y-koid:20161110195505p:plain

ログデータを拡充する

Amazon Kinesis Analyticsでは、Reference Data Sourceとして、 S3に配置した静的ファイル(JSONまたはCSV)を参照することができます。

f:id:y-koid:20161108104308p:plain

以前のエントリにもありますが、Gunosyではユーザの性別・年齢といったデモグラフィック情報や、 興味・関心のある領域などを推定することで、記事・広告配信のアルゴリズムに活用しています。 今回は、これらのデータをCSVとしてS3に配置し、ログデータの拡充を行ってみます。

data.gunosy.io

Reference DataとMapping

下記のようなCSV(user_id, gender, segment_id)を用意し、S3に配置します。

101,female,1
102,male,2
103,female,3
...

先程のSource Streamの場合と異なり、今日現在のところ、Reference DataのMappingはマネジメントコンソールで行えず、CLIによる操作のみとなっています。 (もちろん、Source StreamのMappingをCLIで操作することは可能です)

$ aws kinesisanalytics add-application-reference-data-source \
  --application-name <my-application-name> \
  --current-application-version-id <version-id> \
  --reference-data-source '{
  "TableName": "REFERENCE_DATA_SOURCE",
  "S3ReferenceDataSource": {
    "BucketARN": "arn:aws:s3:::<my-bucket-name>",
    "FileKey": "mydata.csv",
    "ReferenceRoleARN": "arn:aws:iam::<account-id>:role/..."
  },
  "ReferenceSchema": {
    "RecordFormat": {
      "RecordFormatType": "CSV",
      "MappingParameters": {
        "CSVMappingParameters": {"RecordRowDelimiter": ",", "RecordColumnDelimiter": "\n"}
      }
    },
    "RecordEncoding": "UTF-8",
    "RecordColumns": [
      {"Name": "USER_ID", "Mapping": "0", "SqlType": "INTEGER"},
      {"Name": "GENDER",  "Mapping": "1", "SqlType": "VARCHAR(32)"},
      {"Name": "SEGMENT_ID", "Mapping": "2", "SqlType": "INTEGER"}
    ]
  }
}'

Query

上で設定したReference Dataは、"TableName"で指定した名前で呼び出すことができます。(今回の場合は"REFERENCE_DATA_SOURCE")

Souce StreamにユーザデータをJOINしたTemporary Streamを作成し、このTemporary Streamに対しTumbling WindowでのGROUP BYを行います

/* Destination Stream */
CREATE OR REPLACE STREAM "DESTINATION_SQL_STREAM" (
  TIME TIMESTAMP, GENDER VARCHAR(32), SEGMENT_ID INTEGER, ARTICLE_ID INTEGER, 
  IMPRESSION INTEGER, CLICK INTEGER
);

/* Temporary Stream */
CREATE OR REPLACE STREAM "TMP_SQL_STREAM" (
  GENDER VARCHAR(32), SEGMENT_ID INTEGER, ARTICLE_ID INTEGER
);

/* Tumbling WindowでのGROUP BY (1分毎) */
CREATE OR REPLACE PUMP "STREAM_PUMP" AS
INSERT INTO "DESTINATION_SQL_STREAM"
SELECT STREAM
  ROW_TIME AS TIME,
  GENDER, SEGMENT_ID, ARTICLE_ID,
  SUM(CASE ACTION WHEN 'impression' THEN 1 ELSE 0 END) AS IMPRESSION,
  SUM(CASE ACTION WHEN 'click' THEN 1 ELSE 0 END) AS CLICK
FROM "TMP_SQL_STREAM"
GROUP BY
  GENDER, SEGMENT_ID, ARTICLE_ID,
  FLOOR("TMP_SQL_STREAM".ROWTIME TO MINUTE);

/* REFERENCE DATAをJOINし、Temporary Streamへ */
CREATE OR REPLACE PUMP "TMP_PUMP" AS
INSERT INTO "TMP_SQL_STREAM"
SELECT STREAM
  R.GENDER, R.SEGMENT_ID, S.ARTICLE_ID, S.ACTION
FROM      "SOURCE_SQL_STREAM_001" S
LEFT JOIN "REFERENCE_DATA_SOURCE" R
  ON S.USER_ID = R.USER_ID;

クエリの結果は、下記のイメージのようになります。

TIME GENDER SEGMENT_ID ARTICLE_ID IMPRESSION CLICK
2016-11-09 12:01:00.0 male 1 12345 2 0
2016-11-09 12:01:00.0 female 3 12345 3 0
2016-11-09 12:01:00.0 male 1 12346 4 0
2016-11-09 12:01:00.0 male 2 12346 4 0
2016-11-09 12:01:00.0 female 4 12346 2 2

Destination

先程同様にクエリの結果を、Kinesis Firehose(&Elasticsearch Service)に向けると、あっという間にKibana4でダッシュボードができます。

Pie Chartにしたり、FilterをかけてStacked Area Chartにすると良いかもしれません。

まとめ

このように、S3上の静的データを参照しログデータを拡充することで、生ログだけではできなかったセグメント別等のリアルタイムなモニタリングが可能になります。

おまけ:AmazonES&Kibana4のダッシュボード共有

Amazon ES&Kibana4のアクセス制限に独自認証を使いたい…!という場合に、認証を求めるプロキシとリクエストに署名をするプロキシを組み合わせる、等の方法があります

github.com github.com

Re:dashで異なるData Sourceのクエリ結果をJOINできるようになったので試してみた - Query Results (Alpha)

こんにちは。グノシー開発部で部長をしている@cou_zです。最近はDJ RYOWのビートモクソモネェカラキキナ 2016 REMIXをよく聴いています。11/23のライブが楽しみですね。

Gunosyにおけるプロダクト改善は、データ可視化による現状把握から始まると考えています。Gunosyではデータ可視化にいくつかのツールを利用していますが、その中でも最近はRe:dashを用いることが多くなってきました。

先日、Re:dashを用いたリアルタイムKPI通知について紹介しました。 data.gunosy.io

今回は、Re:dashの新しい機能である Query Results (Alpha) Data Source を紹介します。 この機能により、複数Data Sourceのクエリ結果のJOINが可能になりました。アルバイトで分析を担当している松嶋も「ついに使えるようになったんですね!!redashが捗りそう」と期待の新機能です。

Re:dashのData Sourceとは

Re:dashを利用する基本的な流れは「接続先Data Sourceを登録 → クエリを書く → 結果をグラフで可視化 → グラフをダッシュボードに登録」となっています。 Data Sourceはクエリを書く接続先DBのことです。MySQLやBigQuery、Redshiftなどの他にも、Google SpreadsheetsやJSONを返すURLも指定することが出来ます。 下の図のようにたくさんのData Sourceに対応しています。 f:id:cou_z:20161030182920p:plain

Query Results (Alpha) Data Sourceとは

モチベーション

Re:dashを使っていて、異なるData Sourceの結果をJOINして可視化したいということがあります。グノシーでの例としては、人気記事の可視化があります。Redshiftのログをサマって記事ごとの閲覧数を集計し、MySQLにある記事テーブルとJOINし、記事のタイトルやURLと閲覧数をまとめて可視化したい場合です。

グノシーでは、こういう場合はAWSのData Pipelineを利用して、ログの集計前にデイリーでMySQLからRedshiftにデータをコピーしています。AWSのマネジメントコンソールをポチポチするだけで、簡単にデータをコピーすることが出来るので便利ですが、アドホックにクエリを書いている時にわざわざデータのコピーをするのは面倒ですし、コピーするテーブルが増えると管理も煩雑になります。Big QueryなどのAWS以外のサービスを利用している場合は、Data Pipelineのみだとデータのコピーが出来ないので、更に面倒です。

そんな時、Re:dashのみで異なるData Sourceの結果をJOIN出来れば最高ですよね。 2週間ほど前に、Re:dashのファウンダーのArikさんとチャットしている中で、こんな機能が欲しいと伝えてみたら、

It's a planned feature, and I still need to do some research to understand how much effort is involved. If there is a minimal version I can push out this week, I'll do that. Otherwise it will take longer (we're in the middle of upgrading the hosted version to version 0.12).

と「今週リリースいけたらいく」と返信がきました。

待つこと数日。このチャットのことを忘れかけていた先週金曜日にArikさんからαバージョンがリリースされたとチャットが来ました…!

The support for running queries on top of query results is finally deployed.

使い方

使い方は非常に簡単です。Query Results (Alpha)というData Sourceを追加すると、FROM内で他のクエリの結果を query_<id> で使用したクエリを書くことが出来るようになります。インメモリのSQLiteを使用しているため、 query_<id> の結果が大きい場合は、メモリ不足で失敗することがあるとのことです。

使ってみた

さっそく使用してみます。まずはじめに、結果をJOINしたいクエリを2個用意しておきます。

1つ目は、Redshfitから記事閲覧ログをサマって、記事ごとの閲覧数を取得するクエリです。ここではクリック数上位5記事のみを取得することにしました。 f:id:cou_z:20161103164723p:plain f:id:cou_z:20161103164733p:plain

2つ目は、MySQLから記事一覧を取得クエリです。ここでは直近3日間の記事のみを取得することにしました。 f:id:cou_z:20161103164132p:plain f:id:cou_z:20161103164041p:plain

次に、これらのクエリの結果をJOINするために、Query Results (Alpha)というData Sourceの設定をします。Data SourceのNameは、そのままQuery Resultsにしました。 f:id:cou_z:20161031233135p:plain

New Queryページにいくと、Data Sourceで追加したQuery Resultsが指定可能になっています。 f:id:cou_z:20161031233203p:plain

クエリを書いて実行してみます。( query_1は前日の記事の閲覧ログをサマるクエリで、query_2は記事一覧を返すクエリです。) f:id:cou_z:20161101002738p:plain

query_1とquery_2の結果がJOINされました。クエリ実行時に、 query_<id> で指定しているクエリも実行されるため、最新の結果を取得出来るみたいです。 f:id:cou_z:20161031235427p:plain

最後に

Re:dashに追加されたQuery Results (Alpha) Data Sourceを使用して、クエリの結果をJOINする方法を紹介しました。データ可視化がまた簡単になりました。 Arikさんはいつもチャットの返信も新機能開発も速くてすごいです。

データ分析について学んだこと by サマーインターンシップ

こんにちは。データ分析部インターン生の荻原です。 最近はThe Script - Superheroesをよく聞いています。 本記事では、私と同じデータ分析部所属の大原が9/20・21の2日間開催されたエンジニア職向けサマーインターンシップに参加したので、 その模様及びそこから得た学びを共有したいと思います!

Gunosy Summer Internship 2016とは

Gunosy Summer Internship 2016とは、Gunosyが夏休みの期間に実施した短期インターンシップであり、 総合職向け「経営企画・戦略コース」、エンジニア職向け「テクノロジーコース」がそれぞれ開催されました。

f:id:ogiogi93:20161013163038p:plain Gunosy SummerInternship 2016

総合職向けインターンシップは計5回・約120名ほどの学生が参加し、多くの新規ビジネス案が創造されたようです。 またこのインターンシップに参加することで、役員や社員から直接ビジネスや技術、働き方に関する知識を学ぶことができるだけではなく、 「報酬2万円」「新卒採用フローの大幅スキップ(優秀者のみ)」「優勝賞金10万円(エンジニア職向けのみ)」など様々な特典があります。 何かとお金がかかる大学生にとって、非常にありがたい特典ですね。私はこの報酬で新しいMacbook Proを買いたいなと思っていました。

エンジニア職向けサマーインターンシップの様子

それでは、エンジニア職向けサマーインターンシップの様子を紹介します。 スケジュールは以下の表の通りです。

時間 内容
1日目 9:30 オフィス集合
9:30-10:30 インターンシップに関する説明
10:30-11:30 Gunosyに関する講義
11:30-13:00 昼食、休憩
13:00-14:00 Gunosyの開発組織・技術に関する講義
14:20-18:00 ハッカソン
18:00-20:00 懇親会
2日目 9:00-16:45 ハッカソン再開
17:00-18:00 ハッカソン結果発表、総評

Gunosyに関する講義

まず初めに、Gunosy創業者の1人でCEOの福島から「会社Gunosyについて」そして「グノシーの開発秘話」などに関する講義が行われました。 f:id:ogiogi93:20161014134552j:plain システム開発だけではなくビジネスや起業に興味を持っている学生にとっても、非常に有意義かつ貴重な講義であったと思います。 講義の中で特に

  • 最新技術を好む技術者であるほど新たな技術・考えを試してみたくなることがあり、不要な機能を追加→ユーザーの満足度を低下させてしまうことがある
  • ユーザーの視点になってサービスを考えていくかが最重要→ユーザーの求めているものは行動ログなどのデータの中にある

といったことがとても印象に残りました。「なぜGunosyはデータ分析に注力しているのか」 を知れたと同時に、Gunosyのビジネスにデータ分析は欠かせない重要な要素であることを再認識できました。

Gunosyの開発組織・技術に関する講義

六本木のおしゃれなレストランで美味しい肉料理を堪能した後、開発本部執行役員の松本からGunosyの開発組織・技術に関する講義が行われました。 開発組織がいかに開発に挑み、どのような技術がサービス改善につながっているかを実体験の内容を交えた講義であったため、日々サービスを開発・改善・運用していくために大切な「マインドセット」、そして常に最新技術を取り入れていくための具体的な仕組みについて深く学ぶことができました。

ハッカソン

講義終了後、ついにメインイベントであるハッカソンが始まりました!
テーマは「実際のグノシーの行動ログデータによるクリック予測モデルの構築
「ユーザー情報」「記事情報」「記事インプ・クリック情報」などのデータから、あるユーザーがある記事をクリックするかしないかを予測していきます。 また評価方法も社員による評価ではなく、Kaggleのような実データをもとにした予測精度によって算出されます。 徹底したデータドリブンな文化であるGunosyならではの内容ですね!

インターン生1人1人にAWSのインスタンス、そしてベンチマークスクリプトも用意されており、リアルタイムでスコアが反映されていきます。 つまり今自分が何位なのかが明確に分かり、常に高いモチベーションで作業を進めていくことができる環境でした。

インターン生はグループに分かれ、各個人黙々と作業を進めていきます。 f:id:ogiogi93:20161014134813j:plain f:id:ogiogi93:20161014134925j:plain 多くのGunosy社員もメンターとして参加し、インターン生に混じって課題に挑戦していました。 社員もてこずる程、非常に難易度の高い課題... f:id:ogiogi93:20161014134718j:plain f:id:ogiogi93:20161014134954j:plain

そして、2日間におよぶハッカソンが終了し、結果発表に向けて社員が集計コマンドを実施 f:id:ogiogi93:20161014135031j:plain 多くの社員に見守られながら、ついに結果発表です! f:id:ogiogi93:20161014135204j:plain f:id:ogiogi93:20161014135226j:plain 結果、スコア上位3名の方々が入賞しました。入賞者が特に際立っていた点は

  • 手堅くデータを可視化、データの特徴を理解していること
  • 場合の数と確率など、シンプルな手法を取り入れていること
  • 制限時間をしっかりと把握、問題が生じた際も臨機応変に対応していること

などが挙げられると思いました。社員のスコアよりも高いインターン参加者も多く、非常に白熱したハッカソンだったと思います!! f:id:ogiogi93:20161014135241j:plain f:id:ogiogi93:20161014135314j:plain

インターンシップでの気づき(荻原)

私自身は制限時間内に処理が終わらない実装を行ってしまい、不甲斐ない結果となってしまいました。 しかしこのインターンシップを通じて多くの学び・気づきを得ることができたと思っています。それは

  • データ・機械学習アルゴリズム理解の重要性
  • 「切り捨てるもの」を明確にすること
  • 時には作業方針をダイナミックに変更する勇気の大切さ

です。

データ・機械学習アルゴリズム理解の重要性

今回のハッカソンは作業時間約12時間ほどと非常に短かったことから、データの可視化作業を怠ってしまい、自身の経験・勘から いきなり予測モデルの構築作業を始めてしまいました。結果、あまり高い予測精度を出すことはできませんでした。

成績上位者の話を聞くと、やはりデータを可視化することでパターンを見つけ、しっかりと予測モデルに落とし込んでいました。 自身の経験・勘による仮説は基本的に「間違っている」という姿勢で、しっかりと実データから仮説の根拠・パターンを考察することの 大切さを再認識しました。

また限られた時間では様々な手法を試すことは困難です。そのため各手法の特徴・仕組み、具体的に「どのようなデータ・状況の 時に有効であるか」をしっかりと理解し、適切に選択していくことが重要であることも再認識しました。

「切り捨てるもの」を明確にすること

今回のハッカソンではユーザーの行動ログデータの他に記事データなど様々なデータが提供されており、私は「提供されているデータは余ることなく使い切りたい」 とありとあらゆる特徴量を生成しようとしてしまいました。その結果、実験をする時間がほぼなくなりました。 「多種多様かつ大量のデータを用いると良いモデルが構築できる」と思いがちですが、多種多様であるほど特徴量生成・データの理解に時間がかかりますし、大量であるほど学習に時間がかかります。与えられた時間内でやりきるためには、「切り捨てるもの」を明確にすることが大切だと学びました。

状況に応じて作業方針をダイナミックに変更する決断力の大切さ

終了残り2時間ほど前になっても精度が平均以下であり、焦りを感じていました。この時、機械学習による予測モデルの修正ではなくルールベースによる修正を実施しようと一瞬思いましたが、「予測に大きく効く特徴量がまだあるのでは !?」と考えてしまい、作業方針を変えませんでした。 今考えてみるとルールベースによる修正を行った方が良かったと後悔しています。状況に応じて作業方針をダイナミックに変更する決断力が不足していました。

インターンシップでの気づき(大原)

大原です。私も今回のインターンシップに参加したので、私が感じたこと・気づいたことを書きたいと思います。

機械学習のモデルに対する理解と経験が必要

今回のインターンにおいて、私も含め多くの人がxx回帰やRandomForestなどの機械学習モデルを使ったと思います。 scikit-learnを使えば、ほとんどソースコードを変えずに別の予測モデルも構築できるので、特徴量だけ作って私もいろいろなモデルに投げていました。Logistic回帰は精度が悪い。SVMやRandomForest等は全データを突っ込むと学習に時間がかかりすぎる。xgboostも試そうとしていたのですが、与えられた環境ではうまく動かず、時間もないため諦めました。これはどれもモデルの特徴への理解・経験が浅すぎることが原因でした。 結果的には最後に、データを解析した結果をもとにしたルールベースの予測に変えましたが、解析に割ける時間も足りず、結果は全然よくありませんでした。 予測モデルについて理解を深めたいという気持ちがものすごく強まりました。

評価手法について理解すること

最初は評価手法について特に考えず、1つ1つの記事をクリックするかしないかの精度を良くすればいいと思っていました。 間違いではないのですが、評価手法を考えると単純な方法でスコアを大幅に上げられます。 記事配信をする際には、何かの数字を上げるために記事を配信すると思うので、それを考えると当然だったのですが、評価手法について何も考えずにいると、ずっとスコアが上がらないまま終わるところでした。

また他にも、泥臭くても良いから成果を出すことの重要さ等、実際のサービスではどういう考えが必要とされるかについて深く考えさせられました。 参加して得られたもの・考えさせられたこと・気づいたことが多く、非常に有意義なインターンでした。ご飯も全部美味しかったです。

終わりに

今回のエンジニア職向けインターンシップは約20 名の学生が参加し、非常に充実した日々を過ごすことができたと思います。 Gunosyではアルバイト・長期インターン生を随時募集しており、2018年度新卒採用も開始しました。Gunosyに少しでも興味を持たれた方はぜひ応募してみてください!

gunosy.co.jp

www.wantedly.com

www.wantedly.com