JQL: mixpanel で複雑な分析機能を使い始める – その1 –

2022.09.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

MixpanelにはJQL (JavaScript クエリ言語) という生のイベントデータを分析するJavaScript プログラムを作成する機能が備わっています。

※ JavaScriptベースのクエリ言語をゼロから設計した

標準レポートでは実行できない複雑な分析を行いたい場合に最適だと言われています。

JQL で実行したいことのいくつかの例:

3週間前にアカウントを作成し、少なくとも 1 人の友人を招待し、最初の1週間に4つのメッセージを送信した各ユーザーの distinct_id を取得します

サインアップ ファネルからドロップアウトした後、ユーザーが次にどこに行くかを把握する

先月解約したユーザーと、解約前に何があったかを特定する

JQLでは、コードを記述する必要があります。 主なユーザーは開発者とデータサイエンティストですが、クエリを保存し、他のユーザーが使用できるようにレポートを作成することは可能とのことです。

使ってみる

Mixpanel AppsメニューのJQLをクリックします。

このページではJQLの新規作成、作成したJQLの一覧表示などが確認できます。

作成

+ NEWをクリックするといくつかのサンプルを含むGet startedのダイアログが起動します。

Build your own をクリックすると、ある程度一からJQLを作成していくことができます。

デフォルトの記述は以下。

fromからtoの日付で、発生したイベントごとにカウントするクエリになっています。

function main() {
  return Events({
    from_date: '2022-09-27',
    to_date:   '2022-09-28'
  })
  .groupBy(["name"], mixpanel.reducer.count());
}

※ JQLはmain() functionを必ず定義しないといけないようです。

Run Queryをクリックすると、結果が表示されます。

Save & Run Queryをクリックすると、エディタに記述したクエリを保存できます。

エディタですが、何か入力するとJQLで使えるコードが補完されるようになっていました。

Eventを分析する方法

JQLの作成ではmain()関数を実行する必要がありますが、まずはその中で

を呼び出して分析するコレクションを選択する必要があります。

1. コレクションの取得

今回はEventを対象に進めてみようと思います。

Events()関数は、発生したイベントを返しますが、

  • from_date
  • to_date

が必須パラメーターとなっていて、発生イベントを期間指定して取得します。

例)

function main() {
  return Events({
    from_date: "2022-09-21",
    to_date: "2022-09-28"
  })
}

オプションパラメーターとして、

  • event_selectors

がありますが、これは

[{event: "your event name", selector: "Segmentation expression", label: "selected event description"}, ...]

というフォーマットを使用してこのリストに含まれるイベントのみにプレフィルターをかけるために使用されます。

Filter()変換を使用するよりも、Eventクエリ内でイベントをフィルタリングする方が高速になります。

例)

全てのサインアップイベントと、yahooかgmailのアドレスでの購入イベントの取得

event_selectors: [
        {event: 'signup', label: 'Signup'},
        {event: 'purchase', selector: '"yahoo" in properties["$email"]',
            label: 'Purchase (Yahoo)'},
        {event: 'purchase', selector: '"gmail" in properties["$email"]',
            label: 'Purchase (Gmail)'}
    ]

labelを使用することで異なるセレクタで類似のイベントを区別することができます。

Events()から返されるイベントオブジェクトは、以下の属性を持ちます。

  • name
    • イベント名
  • distinct_id
    • このイベントに関連付けられたユーザーのdistinct_id
  • labels
    • event_selectorsで設定してlabel
  • time
    • イベントのタイムスタンプ
  • sampling_factor
    • このイベントがサンプリングされた割合です(Mixpanelのサンプリング機能を使用している場合)。
    • 0.05は、このイベントが5%でサンプリングされることを意味します。これは、サンプリングされたイベントの正確なカウントを得るために重要です。サンプリング機能を使用していない場合、これは常に1.0になります
  • properties
    • イベントのすべてのプロパティを含むオブジェクト
{
    "name": "page_open",
    "distinct_id": "9851481147289",
    "labels": [
      "PageOpen(Mac OS X)"
    ],
    "time": 1663672463000,
    "sampling_factor": 1,
    "dataset": "$mixpanel",
    "properties": {
      "$browser": "Chrome",
      "$browser_version": 104,
      ~~~~~ ~~~~~~~
    }
  },

2. 絞り込み

JQL フレームワークは、コレクションと変換という2つの概念を中心に展開しています。コレクションは、分析を実行して複雑な質問に答えるために他のコレクションに変換できる値のリストです。

変換の1つの例はfilter()です。これは、コレクションを必要な値に絞り込むために使用されます。

filter(function(item) {}) で実装していきます。

※ コレクションの各要素に対して、指定された関数を呼び出してフィルタリングします。関数が真と評価された場合、その要素を保持します。そうでなければ、その要素を破棄します。

特定のイベントだけに絞り込みたい場合は、

function main() {
    return Events({
      from_date: '2022-09-01',
      to_date:   '2022-09-28'
    }).filter(
      function(event) {
        return event.name == "<<イベント名>>"
      }  
    )
  ;
}

と書きます。

event.properties.$browser == "Chrome" のように、イベントオブジェクトに含まれるプロパティも絞り込む対象として指定できます。

3. カウント

絞り込んだイベントをカウントしたい場合、reduce()関数を使って入力されたコレクションを単一の値に変換します。

reduce([reducer function(s)])で実装していきます。

[reducer function(s)]は、 2つの引数を受け取ります。1つは以前の結果の配列(アキュムレータ)、もう1つは削減する項目の配列です。最初の引数であるアキュムレータは、初期値のセットをリデューサ関数の多数の呼び出しに分解するために使用されます。JQLは階層的な方法で削減を実行し、入力のいくつかのサブセットが最初にアキュムレータ値に削減され、その後複数のアキュムレータ値が結合されます。入力の順番は保証されていません。

function(accumulators, items) {
  // Combine previously-aggregated "accumulators"
  // with new "items" and return the result.
}

単純なカウントは一般的なユースケースであるため、組み込みのmixpanel.reducer.count()をreduce関数に渡します。

function main() {
  return Events({
    from_date: '2022-09-01',
      to_date:   '2022-09-28'
  })
  .reduce(mixpanel.reducer.count());
}

このほか、Mixpanelではさまざまな組み込みずみのreducer関数が用意されているので、以下を参照してみてください。

組み込みのリデューサー関数

単純なカウントではなく、より複雑なことをするのであれば、function(accumulators, items) {}を実装していくことになります。

ドキュメントに記載されていた例として、イベントの最も新しいタイムスタンプと最も古いタイムスタンプを返すには、

と言った感じで柔軟に実装していけます。

試してみた結果は以下。

4. グループ化する

何種類かのカテゴリがあって、どのカテゴリの記事がどれくらい参照されたか知りたいんだが? と言った質問があったときにはグループ化を行うかと思います。

MixpanelではgroupBy()関数を使用してグループ化することができます。

groupBy([keys], [reducer function(s)])で実装していきます。

指定したキーのリストに従ってコレクションをグループ化し、 指定したreducer関数を各グループに適用します。

グループに分割した後、groupBy()に渡された reduce 関数が各グループで個別に呼び出されます。

例)

function main() {
  return Events({
    from_date: '2022-09-01',
      to_date:   '2022-09-28'
  })
  .filter(function(event) { return event.name == "purchase" })
  .groupBy(["properties.plan"], mixpanel.reducer.count());
}

上記の例の順序としては、planというプロパティでグループ化してから、イベントの数を数えます。

実行後は以下のような結果が返されます。

[
  {
    "value": 254,
    "key": [
      "Basic"
    ]
  },
  {
    "value": 105,
    "key": [
      "Premium"
    ]
  }
]

イベントの取得、絞り込み、カウント、グループ化といった分析に使用する基本的な方法をJQLで試してみました。

今回はドキュメントを見ながら単純な例を実行してみましたが、もちろんもっと複雑な分析もJQLで実装することができます。

と言った例がドキュメントにも記載されているので、気になる方は読んでみましょう。

また、別記事にて手元で実際に取れるデータを使用し、上記例に似た感じの分析をJQLで取得できないかは試していく予定です。

参考