可視化コンポーネントを使用してカスタム可視化を行う

このチュートリアルは、経験豊富な JavaScript デベロッパーを対象としており、関数のプログラミング技術をある程度理解していることを前提としています。

この例では、棒グラフから始めます。これは、週次販売に関する架空の情報を示す、Looker のネイティブなビジュアリゼーションです。

次に、可視化コンポーネントを使用して、前四半期に各ブランドの商品の傾向を示すカスタム ビジュアリゼーションを構築します。その結果、テーブル内にネストされた一連のスパークラインで構成された新しい種類のビジュアリゼーションが完成します。次の例をご覧ください。

この例では、カスタム ビジュアリゼーションの作成方法と、React アプリケーション内で Looker API を使用する際のベスト プラクティスをいくつか紹介します。

Looker コンポーネントを使用してカスタマイズされた可視化を行うには、設定が要件を満たしていることを確認してから、次の手順を行います。

  1. Explore でクエリを作成し、qid 値をコピーする
  2. データをカスタム ビジュアリゼーション コンポーネントに渡す
  3. CustomVis コンポーネントをビルドする
  4. 正規化されたデータを変換する
  5. 変換されたデータを CustomVis に挿入する
  6. カスタム ビジュアリゼーションを生成する

カスタム ビジュアリゼーションが埋め込みのアプリケーションや拡張機能を対象としている場合は、ビジュアリゼーション コンポーネントを使用してカスタム ビジュアリゼーションを構築するのが適切です。Looker インスタンス全体で Looker ユーザーがカスタム ビジュアリゼーションを使用できるようにするには、visualization のドキュメント ページの手順に従ってください。カスタム ビジュアリゼーションを開発して Looker Marketplace にアップロードする場合は、Looker Marketplace 用のカスタム ビジュアリゼーションの開発のドキュメントをご覧ください。

要件

手順を始める前に、以下の要素が必要です。

  • Looker インスタンスにアクセスできる必要があります。
  • 拡張フレームワークでビルドする場合でも、独自のスタンドアロン React アプリケーションでビルドする場合でも、Looker の API で認証を行い、Looker SDK オブジェクトにアクセスできることが重要です。詳細については、Looker API 認証または 拡張フレームワークをご覧ください。
  • Looker 可視化コンポーネントの NPM パッケージ@looker/components-data NPM パッケージがインストールされていることを確認します。可視化コンポーネント パッケージのインストールと使用については、README ドキュメント(GitHub および NPM)をご覧ください。

ステップ 1: Explore でクエリを作成してクエリ ID をコピーする

この例では、四半期全体でトラッキングしているブランドの架空の週情報を使用します。

Explore では、Looker のネイティブ ビジュアリゼーション タイプのいずれかを使用してクエリを実行し、データのグラフを作成できます。このグラフにはさまざまな情報が含まれていますが、各ブランドの商品の傾向を一目で把握することはできません。

シンプルなビジュアリゼーションのレンダリングの例と同様に、次のステップでは Explore の URL バーから qid 値をコピーします。この例では、qid4tQKzCBOwBGNWTskQyEpG8 になります。

ステップ 2: データをカスタム ビジュアリゼーション コンポーネントに渡す

まず、Explore の URL から取得した qid 値を Query コンポーネントに渡し、認証済みの SDK オブジェクトを DataProvider に渡します。

import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query } from '@looker/visualizations'

export const MyReactApp = () => {
  const { core40SDK } = useContext(ExtensionContext)

  return (
    <DataProvider sdk={core40SDK}>
      <Query query='4tQKzCBOwBGNWTskQyEpG8'></Query>
    </DataProvider>
  )
}

次に、Visualization コンポーネントを介して Looker のネイティブ ビジュアリゼーションをレンダリングするのではなく、CustomVis という独自のカスタム コンポーネントを構築します。

Query コンポーネントは、任意の React 要素を子として受け取ることができ、単に configdatafieldstotals の値をプロパティとして渡して、独自のビジュアリゼーション コンポーネントをレンダリングします。CustomVisQuery の子としてレンダリングするため、関連するすべてのデータをプロパティとして受け取ることができます。

import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query } from '@looker/visualizations'
import { CustomVis } from '../path/to/MyCustomVis'

export const MyReactApp = () => {
  const { core40SDK } = useContext(ExtensionContext)

  return (
    <DataProvider sdk={core40SDK}>
      <Query query='4tQKzCBOwBGNWTskQyEpG8'>
        <CustomVis />
      </Query>
    </DataProvider>
  )
}

ステップ 3: CustomVis コンポーネントをビルドする

次に、CustomVis コンポーネントをビルドします。Query コンポーネントから継承したプロパティは、configfieldsdatatotals です。

  • config では、スパークラインの線の太さや散布図の点のサイズや形状など、データをグラフに表示する際のすべての方法を記述します。
  • fields には、クエリから返されたメジャーとディメンションの値に関する追加のメタデータ(値の形式や、各軸に何ラベルを付けるかなど)が保存されます。
  • data は、クエリから返された Key-Value レスポンスです。
  • totals は、テーブルベースのビジュアリゼーションで使用する Looker の行合計を参照します。

これらの変更されていないプロパティをテーブルの可視化に渡すには、Table コンポーネントを挿入します。

import React from 'react'
import { Table } from '@looker/visualizations'

export const CustomVis = ({ config, fields, data }) => {
  return <Table config={config} data={data} fields={fields} />
}

これにより、SDK から直接返されたデータを把握できます。レンダリングされたレスポンスでは、すべてのブランドの行が毎週表示されます。

ステップ 4: 正規化されたデータを変換する

このデータから時系列の傾向情報を取得するには、ブランド名で行をグループ化する必要があります。週やブランドごとに新しい行を作成するのではなく、ブランド名ごとに 1 行を使用します。その行内に、日付と注文数のリストをネストします。

このような値をグループ化するカスタム変換を作成します。以下に、このシナリオに固有の例を示します。これに従って、独自のデータを解析する必要があります。

import React from 'react'
import { Table } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  return <Table config={config} data={data} fields={fields} />
}

次の手順では、この関数を作成します。

  1. まず、参照 products.brand_namebrandKey という名前の変数に代入します。データ レスポンスでこの系列がどのようにラベル付けされ、情報をグループ化するかを指定します。
  2. 次に、レスポンスをマッピングし、products.brand_name に割り当てられた値のみを返すことで、ブランド名の一意のリストを作成します。これらは Set コンストラクタに渡されます。JavaScript では、Set は値が 1 回だけ出現するリストで、一意の値のリストを自動的に作成します。
  3. その後、一意のブランドの Set を配列に変換して、map などのリスト変換ユーティリティを利用します。
  4. 一意のブランドごとに、元のデータ レスポンスを、そのブランド名に関連する時系列の行のみにフィルタします。
  5. 最後に、ブランド名ごとに、値を orders.count オブジェクト キーの下にネストします。前述したように、ブランド名が文字列値に設定され、orders.count はそのブランドに関連するすべての値のリストに設定されます。

ステップ 5: 変換されたデータを CustomVis に挿入する。

次に、新しい関数を使用してデータを変換し、その出力を nestedData という新しい変数に代入します。

import React from 'react'
import { Table } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  const nestedData = transformData(data)

  return <Table config={config} data={data} fields={fields} />
}

次に、nestedData 配列をループして、ネストされた値を標準のスパークライン チャートに渡します。これは、Looker の可視化コンポーネント ライブラリによって提供されます。

import React from 'react'
import { Table, Sparkline } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  const nestedData = transformData(data)

  const nestedSparklines = nestedData.map(d => {
    return {
      ...d,
      'orders.count': () => (
        <Sparkline
          data={d['orders.count']}
          config={config}
          fields={fields}
          height={75}
        />
      ),
    }
  })

  return <Table config={config} data={nestedSparklines} fields={fields} />
}

上記のコードの内容は次のとおりです。

  • データをもう一度マッピングして、各行に新しいスパークラインを作成します。
  • orders.count キー(そのブランドに属する一連の日付とカウント)の下にネストされている値が、Sparklinedata プロパティに渡されます。
  • すべてのグラフのレンダリングに必要な config オブジェクトと fields オブジェクトも渡されます。高さも 75 ピクセルに設定されており、表の各行に合わせて快適にレイアウトできます。
  • 最後に、ネストされたグラフ オブジェクトが Table グラフに渡されます。

ステップ 6: カスタム ビジュアリゼーションを生成する

変換されたデータを挿入してグラフを構成すると、各行のスパークライン チャートごとに表が表示されます。

上記のビジュアリゼーションをレンダリングするために必要なコード全体は次のとおりです。

import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query, Table, Sparkline } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  const nestedData = transformData(data)

  const nestedSparklines = nestedData.map(d => {
    return {
      ...d,
      'orders.count': () => (
        <Sparkline
          data={d['orders.count']}
          config={config}
          fields={fields}
          height={75}
        />
      ),
    }
  })

  return <Table config={config} data={nestedSparklines} fields={fields} />
}

export const MyReactApp = () => {
  const { core40SDK } = useContext(ExtensionContext)

  return (
    <DataProvider sdk={core40SDK}>
      <Query query='4tQKzCBOwBGNWTskQyEpG8'>
        <CustomVis />
      </Query>
    </DataProvider>
  )
}

次のステップ