この投稿では、ユーザーアクションを監視するための分析システムの開発に関する一連の記事を開きます。 最初の記事では、AndroidおよびAYOのモバイルアプリケーションから必要なデータを収集する方法について説明します。
package Birdy::Stat::Stalin;
Surfingbirdのアドバイザリーサービスです。 ユーザーを理解するほど、関連性の高い推奨事項が生成されます。
Googleアナリティクス、Flurry、Appsflyer-既存のアナリティクスを真っ先から塗りつぶすことができます。 DAU、MAU、DNU、ARPU、Kファクター、その他多数のインジケーターを表示する壮大な
ダッシュボードを作成できますが、これらはすべて洞窟の壁の影にすぎません。 ユーザーがアプリケーションを離れた理由、離脱を引き起こした正確な質問に答える分析システムはありません。ユーザーが離れたという事実を修正するだけです。 彼に別れのメールを書くこともできません)したがって、この質問や同様の質問に答えるためには、ユーザーに関するすべてのことを知る必要があると判断しました。 どのシーケンスで、どの間隔で、どの画面で、どのボタンを押したか。 振り向き、吐き出し、立ち去る前に彼が読んだ秒数と記事。 記事を読むときのヒストグラムとは何ですか。 各ピクセルに費やされた時間と、A / Bテストのバージョン。 ある時点で、スターリンが必要であることに気付きました。
まず、追跡されたイベントを送信するデータ構造について合意しました。 この構造は、Web、モバイル、および将来を見据えて同じです-バックエンドのデータベース(はい、たくさんあります)。
イベントは次の基本コンポーネントで構成されます
- アクション-ユーザーのアクション、彼がしたことの質問に答えます
- 画面-画面はどの画面で質問に答えます
- ContentType-コンテンツのタイプ。どのタイプのコンテンツがインタラクションであったかという質問に答えます
そしてまた:
- userToken-誰
- 時間-いつ
- clientVersion-バージョン
- contentID-コンテンツ識別子
- deviceID-デバイスの一意の識別子
- deviceType-デバイスタイプ
- 他の次元-絶対値、説明(エラーの場合)など
メジャーはイベントのカウントです。 デフォルトでは1に等しいが、時間分析の必要がない同じタイプのイベントの事前集計に使用できます。
これは、私たちが構築する基本セットです。
次に、測定値は、たとえば次のような特定の値のセットで表すことができます。
ディメンションの命名において、OLAPでのさらなる分析を容易にするために、同じ値を事前集計する機能も保護されていることに気付くかもしれません。 つまり データ収集レベルで横ばい-キューバのレベルで2レベルの階層に拡張できます。
たとえば、Androidのデータモデルを見ると、どのイベントも次のクラスとして表すことができます。
public ClassEvent (Action action, Screen screen, ContentType contentType, String contentID, String abTest1, String abTest2, String description, int count) { this.abTest1 = abTest1; this.abTest2 = abTest2;
アプリケーション自体ではどのように見えますか?
画面上のアクションはすべてイベントとして記録されます。
セッションの断片を確認する最も簡単な方法は、表形式です

セッションを開始し、サーフィンをクリックして、テキストエディターの5ページ目を読み込んで数秒間読んでから、人気のタブに切り替えて、iPhoneがAndroidよりも3倍高価な理由を読み始めました。 くそー、はい、それは昨夜でした、ところで、私はまだ理由を理解していませんでした)
同じデータは次のようになりますが、OLAPで処理した後は次のようになります。

しかし、ポイントではありません。 解決する必要のある次のタスクは、他の分析システムとの統合です(ところで、誰がどのくらい嘘をついているかを調べましたが、今はそうではありません)。
Androidでは、パッケージに50個をパックし、生成時に、クロスチェックのためにGoogleに分析を追加します。
public void newEvent(ClassEvent.Action action,ClassEvent.Screen screen,ClassEvent.ContentType contentType,String contentId) { registerEvent(new ClassEvent(action,screen,contentType,contentId)); } public void newEvent(ClassEvent.Action action,ClassEvent.Screen screen,ClassEvent.ContentType contentType,String contentId,String abTest1,String abTest2,String description, int count) { registerEvent(new ClassEvent(action,screen,contentType,contentId,abTest1,abTest2,description,count)); } public void registerEvent(ClassEvent event) { Tracker t = getTracker( SurfingbirdApplication.TrackerName.GLOBAL_TRACKER); t.setScreenName(event.screen.toString()); Map<String, String> hits = new HitBuilders.EventBuilder() .setCategory("event") .setAction(event.action.toString()) .setLabel(event.action.toString()) .build(); t.send(hits); if (TextUtils.equals("",event.userToken) || TextUtils.equals("null",event.userToken)) { String eventsString = "["; eventsString+=event.toString(); eventsString+="]"; events.clear(); aq.ajax(UtilsApi.eventsCallBackBasic(this, "some_method", eventsString)); } else { events.add(event); if (events.size()>50) { sendEvents(); } } } public void sendEvents() { if (events.size()>0) { String eventsString = "["; for (ClassEvent event: events) { if (!eventsString.equals("[")) eventsString+=","; eventsString+=event.toString(); } eventsString+="]"; events.clear(); aq.ajax(UtilsApi.eventsCallBack(this, "nop", eventsString)); } }
イベントの非常に限られた部分は基本認証で実行され、すぐに送信されます。残りはすべてパケットにパッケージ化され、蓄積されるか、プログラムの作業完了時に送信されます。
だから、「アンドロイドでイベントを投げる」ように見える
SurfingbirdApplication.getInstance().newEvent(ClassEvent.Action.install, ClassEvent.Screen.none, ClassEvent.ContentType.none, ""); SurfingbirdApplication.getInstance().newEvent(ClassEvent.Action.openPush, ClassEvent.Screen.page_parsed, ClassEvent.ContentType.siteShort,shortUrl); SurfingbirdApplication.getInstance().newEvent(ClassEvent.Action.registrationBegin_email, ClassEvent.Screen.start, ClassEvent.ContentType.none, "");
ayosでは、少し異なるロジックを試しました:
また、イベントはスタックに蓄積され、後続の要求で蓄積されます。蓄積されたメッセージの配列は、蒸気機関車によってそれにスタックされます。 50を超えるイベントが蓄積された場合、
nopシステムメソッドを使用してリクエストを強制します。 また、追跡されたイベントをできるだけ早く送信する必要がある場合は、nop要求を強制できます。
バックエンドで-イベントは真珠で書かれたモジュールに到達し、実際にエントリをテーブルに分解します。 しかし、これが彼の唯一の機能ではなく、データの整合性も制御します。 クライアントからスターリンに知られていないイベントが突然到着した場合、彼はそれを別のプレートに入れ、不整合の解消後(対応する列挙型に新しい値を追加した後など)後で処理します
注意、真珠のコード。 訓練されていない人々は、目の出血と早産を引き起こします package Birdy::Stat::Stalin; use constant { SUCCESS => 'success', FAILURE => 'failure', UNKNOWN => 'unknown', CONTENT_TYPE_NONE => 'none', }; sub track_events { my $params = shift; return unless ref $params eq 'ARRAY'; return unless @$params; my ($s_events, $f_events, $u_events) = ([],[],[]); foreach (@$params) { my $event = __PACKAGE__->new($_); $event->parse;
実装プロセス中に、いくつかの異常を発見しました。 たとえば、いくつかのイベントは遠い未来に、いくつかは過去に起こりました。 これらのユーザーはすべて、Android向けのスマートフォンの幸せな所有者だったと推測するのは簡単です。 しかし、一般的に-すべてが成功しました。 システムは定期的に統計を収集し、それを実現する時間はほとんどありません。
次の記事では、コンテンツの同化を分析する方法論、たわごととスティックからDWH / OLAPシステムを構築する方法、さらに送別通知書とこれがどのような馬鹿げた結果につながるかについてさらに詳しく説明する予定です。