KLab-データ分析グループのblog

オープンソースのモバイル分析ツール「Countly」とは?(その1)

こんにちは。
寒い日が続きますね。

今日はモバイル分析ツール「Countly」を紹介します。

Countlyとは?
ここ2,3年、ゲームの分野ではスマートフォンで動作するアプリが普及する中、これまでのブラウザ上で動作するゲームと違って、サーバから得られるデータだけでは、ユーザの行動が把握・分析しにくくなってきました。最近は「グロースハック」なる言葉が言われるようになってきて、ユーザの行動データを細かく分析して、得られた知見を素早くシステムの改良に反映していくことがとても重要視されてきています。
そういったなか、スマートフォンアプリの中にSDKを組み込むことで、詳細なユーザの行動履歴を専用のサーバに送信して、データを可視化して閲覧できるようにするサービスが次々とリリースされるようになりました。

今回ご紹介する「Countly」もそうしたサービスの一つです。

countly_logo_color


「Countly」が類似サービスと、大きく違う点は、SaaS形態の有償サービスに加え、サーバ側アプリケーション、クライアントSDKがそれぞれオープンソースとして提供されていることです。(ライセンスがサーバとクライアントSDKで異なるため、実際のサービスで採用する場合は、事前にそれぞれのライセンス内容に留意して使ってください。)

こうしたサービスのクライアントSDKというのは、バイナリ形式で提供されることが一般的です。バイナリで提供されるとSDK内部の動作がわからないため、開発者としては、アプリ本体への影響が予測できないのが悩みの種でした。特にiOSの場合、クライアントSDK内部の実装内容によってはアプリの審査にも影響を与えかねません。ですので、クライアントSDKのソースコードが公開されていると、通信のタイミングなど、技術的に注意すべき点が明確になるので、とても安心できるというのは大きなメリットです。

Countlyを提供しているCountly社のWebサイトによると、このオープンソース版は「Community Edition」という名称で提供されており、有償版と比較すると、一部機能に制限があります

Community版で出来ること
ダッシュボード機能

countly_dashboard
登録したアプリ毎に、収集したデータのサマリーを一画面で概観することが出来ます。


ユニークユーザ数
 countly_uu
いわゆるDAU(Daily Active User)です。表示範囲を変更することで、表示期間の粒度も自動的に変更されます。たとえば表示範囲を1日(24時間)にすると、HAU(Hourly Active User)、1時間毎のユニークユーザ数が表示されます。

カスタムイベント
 countly_custom_event
 アプリ固有のイベントを記録できます。その際に、ユーザの属性などを付加して送信することが出来るので、属性別の絞り込み表示が出来ます。


その他の項目

上で紹介した項目の他に、
  • OSの種類・バージョン
  • アプリのバージョン
  • 端末の解像度
  •  ユーザの国・地域
  • 端末の機種
  • 通信キャリア
などが閲覧できます。

システム要件
Countlyのサーバは、下記の環境下で動作します。
  • node.js
  • mongoDB
  • imageMagick
システム構成
 
Countlyは、
  • 各種モバイル端末向けSDK
  • APIサーバー
  • フロントサーバー
で構成されています。これらの関連を図にすると、下のようになります。

スライド1
次回は、サーバプログラムのインストールについて説明します。


 

Amazon Redshiftで高速にINSERT + UPDATEを行なう

こんにちは。takada-at です。


最近 KLabデータ分析グループではAmazon Web Services(AWS)が提供するデータ分析に特化したデータベースであるAmazon Redshiftを導入し、活用しはじめています。
そこで、いくつか運用で学んだRedshiftのノウハウを書いていこうと思います。


今回はデータインポートの際のノウハウのひとつです。
MySQLには、データインポート時、LOAD DATA INFILE コマンドにREPLACEというキーワードをつけることで、データが存在しなければINSERT、データが存在すればUPDATEという動作を実現することができます。


RedshiftにはS3またはDynamoDBから高速にデータをインポートするCOPYコマンドが用意されていますが、このコマンドにはMySQLのREPLACE相当の機能はありません。


ただし、多少めんどうですが、Redshiftの公式ドキュメントでも紹介されている以下の方法で、REPLACE相当の機能を実現できます。


まず更新対象のテーブルに対しステージング用のテーブルを用意します。
例.
本番テーブル: salesdata
ステージングテーブル: salesdata_staging


以下の手順でコマンドを実行することで、高速にINSERT + UPDATEを実現できます。

1. salesdata_stagingのTRUNCATE
まずステージングテーブルを空にしておきます。 
 
2. salesdata_stagingへのCOPY
先にステージングテーブルにデータをインポートします。 

3. salesdataのUPDATE
UPDATE  salesdata
  SET col1=s.col1, col2=s.col2 
FROM
  salesdata_staging
WHERE
  salesdata.unique_id=salesdata_staging.unique_id  

ステージングテーブルのデータを使って本テーブルを更新します。  
 
4. salesdata_staging から salesdataへのINSERT
INSERT INTO salesdata
SELECT * FROM salesdata_staging
LEFT JOIN salesdata
  ON salesdata.unique_id=salesdata_staging.unique_id
WHERE
   salesdata.id IS NULL
 
salesdataに存在しないデータのみをINSERTします。


以上です。KLabデータ分析グループではpythonライブラリ内にこれを実現する関数を用意し、簡単に呼び出せるようにしています。
1から2ノードのRedshiftで使用して、数万件から数十万件程度のデータなら数十秒程度で完了します。


以下にpythonのサンプルスクリプトを掲載しておきます。


def upsert(db, table, stagingtable, joincolnames, updatecolnames, data, enable_update=True):
    u"""
    db -- dbコネクション
    table -- 更新対象
    stagingtable -- ステージングテーブル
    joincolnames -- ステージングテーブルとターゲットテーブルのJOINに使用するカラム。ユニークになる条件を指定すること
    updatecolnames -- アップデート対象のカラム
    data -- 取り込むデータ
    enable_update -- Falseの場合はUPDATEしない
    """
    joinconditions = ["{table}.{colname}={stagingtable}.{colname}".format(table=table, stagingtable=stagingtable, colname=colname) for colname in joincolnames]
    joincondition = " AND ".join(joinconditions)

    updateexprs = ["{colname}={stagingtable}.{colname}".format(table=table, stagingtable=stagingtable, colname=colname) for colname in updatecolnames]
    updateexpr  = ", ".join(updateexprs)

    updatestatement = "UPDATE {table} SET {updateexpr} FROM {stagingtable} WHERE {joincondition}"
    updatestatement = updatestatement.format(table=table, updateexpr=updateexpr, stagingtable=stagingtable, joincondition=joincondition)
    insertstatement = '''INSERT INTO {table} SELECT {stagingtable}.* FROM {stagingtable}
                         LEFT JOIN {table}
                         ON {joincondition}
                         WHERE {table}.{somecollumn} IS NULL
                      '''
    insertstatement = insertstatement.format(table=table, stagingtable=stagingtable, joincondition=joincondition, somecollumn=joincolnames[0])
    # ↑ データが存在するかどうかの判定をjoincolnames[0]がNULLかどうかで行なっているので、ちょっと注意。
    # nullableなカラムがjoincolnamesに入ってくることはおそらく無いだろうと仮定している

    # ステージのTRUNCATE
    db.execute("TRUNCATE {table}".format(table=stagingtable))
    # ステージにCOPY
    copytotable(db, stagingtable, data)
    if enable_update:
        # 重複するデータのupdate
        db.execute(updatestatement)

    # まだ存在しないデータのinsert
    db.execute(insertstatement)



第4回関西ソーシャルゲーム勉強会で発表してきました

こんにちは、ponpoko1968です。

去る5月18日、関西のソーシャルゲーム関係者有志一同で開催している「関西ソーシャルゲーム勉強会」で発表してきました。

この会も昨年6月に弊社大阪事務所で第1回を開催して以来、今回で4回目を迎えました。回を追うごとにご参加いただく方も増え、盛り上がってきています。

今回も盛りだくさんの内容で、Scala+play frameworkという前衛的な技術でサーバサイド構築をされているフリュー様のお話や、ネイティブアプリのブーストで工夫したことについて、社長の大原様自らが、生々しいお話を関西人ならではの率直さで話されたダンクハーツ様など、非常に内容の濃い、バラエティに富んだ勉強会となりました。


さて、今回の私の発表は「ソーシャルゲームのビジネスインテリジェンス」というお題で発表してきました。30分という長めの時間を頂いたので弊社のデータ分析の仕組みと、現在の課題、課題を解決するための技術の調査状況などを発表しました。
945164_269537789859151_87434415_n
249004_269537869859143_761288997_n
 
発表プレゼンは下記からご覧になれます。







最後になりましたが、今回も運営にご尽力いただいたスタッフの皆様、そして会場をご提供いただいたECCコンピュータ専門学校様、発表の機会を頂き、ありがとうございました。

私事で恐縮ですが、今回会場をご提供いただいたECCコンピュータ専門学校様は、筆者が以前短い期間ながら在籍していた職場で、こうして十数年ぶりに教壇に立つことができ、なおさら感慨深いものがありました。



今後も機会があれば日々のデータ分析で得られたノウハウを共有して、業界を盛り上げていきたいと思います。