使用可视化组件构建自定义可视化图表

本教程面向经验丰富的 JavaScript 开发者,并假定您对函数式编程技术有一定的了解。

在此示例中,我们从一个与某些品牌的假设季度销售信息相关的查询开始。首先,我们会针对特定品牌过滤查询,然后按销售季度透视结果。有关示例,请参阅下表。

按品牌划分的订单数查询的结果,其中包含“创建订单数量”维度的数据。

然后,我们将使用可视化组件来构建自定义可视化图表,以显示每个品牌的商品在过去一个季度的趋势。这将产生一种新的可视化效果,由嵌套在表中的一系列火花谱线图组成,如下所示:

自定义的可视化图表,其中显示了一个表格,每个品牌各占一行,嵌入的迷你图表则显示了每行中各季度的订单。

除了向您展示如何创建自定义可视化图表,此示例还演示了一些在 React 应用中使用 Looker API 的最佳实践。

如需使用 Looker 组件构建自定义可视化图表,请确保您的设置符合要求,然后执行以下步骤:

  1. 在“探索”中构建查询并复制 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 软件包。如需了解如何安装和使用可视化组件软件包,请参阅 GitHubNPM 中的 README 文档。

第 1 步:在“探索”中构建查询并复制查询 ID

在本例中,我们使用一段时间内跟踪的品牌的假设性季度销售信息。

我们会透视这些数据,因为 Looker 内置了对查询结果进行分组的方法。在探索中,我们可以运行查询并使用 Looker 的原生可视化类型之一来创建数据图表。该图表提供了大量信息,但难以一目了然地分析每个品牌的商品的趋势:

通过查询按品牌划分的订单数生成的图表,其中包含“创建订单数量”维度的数据透视。

呈现简单可视化图表的示例一样,下一步是从“探索”的网址栏中复制 qid 值。在本示例中,qid 值为 Uijcav7pCA4MZY2MompsPZ,但该值特定于我们的测试实例;您的值会有所不同。

第 2 步:将数据传递到自定义可视化组件

首先,将通过探索网址获取的 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='Uijcav7pCA4MZY2MompsPZ'></Query>
    </DataProvider>
  )
}

接下来,我们将构建我们自己的自定义组件,名为 CustomVis,而不是通过 Visualization 组件呈现原生 Looker 可视化图表。

Query 组件可以接受任何 React 元素作为子级,并且只会将 configdatafieldstotals 值作为属性进行传递,以呈现您自己的可视化组件。我们会将 CustomVis 呈现为 Query 的子项,以便它可以接收所有相关数据作为属性。

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='Uijcav7pCA4MZY2MompsPZ'>
        <CustomVis />
      </Query>
    </DataProvider>
  )
}

第 3 步:构建 CustomVis 组件

接下来,构建 CustomVis 组件。继承自 Query 组件的属性包括 configfieldsdatapivotstotals

  • config 描述了图表中呈现数据的所有方式,例如火花谱线图中线条的粗细或散点图中点的大小和形状。
  • fields 会存储有关从查询返回的测量值和维度值的其他元数据,例如,应该如何设置这些值的格式,或要为每个轴添加标签。
  • data 是从查询返回的键值对响应。
  • pivots 描述透视查询所依据的维度。
  • totals 引用 Looker 的行总数,以用于基于表格的可视化图表。

我们可以通过插入 Table 组件,将这些未经修改的属性传递到表格可视化图表中。

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

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

这让我们可以了解直接从 SDK 返回的数据。在呈现的响应中,每个品牌都有一个对应的行,其结果按季度分组或透视。

第 4 步:转换标准化数据

为了转换此透视数据以使用嵌套火花谱线图呈现,我们需要隔离所有测量值,并将它们传递给子图表。在下图中,单行的相关数据突出显示,以说明我们将通过子可视化图表折叠和呈现的数据:

数据结果图表,其中突出显示了第二行中的订单数量。

我们将为此创建一个自定义转换。以下是特定于此场景的示例;您需要相应地解析自己的数据。


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

// we assign this value to a constant to ensure that fields and data
// objects remain in sync.
const NESTED_DATA_KEY = 'orderCount'

const nestSparklines = (data) => {
  return data.reduce((acc, d) => {
    // the first entry is the dimension (brand name), and the rest of the rows are the
    // quarterly sales information we want to pass to the Sparkline.
    const [parentDimension, ...measurePairs] = Object.entries(d)

    // `nonPivotedData` represents a single data row.
    // e.g. [{entry: 1, orderCount: 10}, {entry: 2, orderCount: 15}, ...etc]
    const nonPivotedData: SDKRecord[] = measurePairs.map(([_, value], i) => {
      return { entry: i, [NESTED_DATA_KEY]: value }
    })

    // now for each row in the table we render a Sparkline using the `nonPivotedData`
    // that we built above.
    // E.G. [{products.brand: 'adidas', orderCount: <Sparkline />}]
    return [
      ...acc,
      {
        [parentDimension[0]]: parentDimension[1],
        [NESTED_DATA_KEY]: () => (
          <Sparkline
            height={75}
            data={nonPivotedData}
            fields={{
              measures: [{ name: NESTED_DATA_KEY }],
              dimensions: [],
            }}
          />
        ),
      },
    ]
  }, [])
}

该函数的创建步骤如下:

  1. 对数据集进行缩减,以便将品牌名称与每行的季度订单数据隔离开来。
  2. 更新每一行,以添加维度和一个已呈现的 React 组件(可表示表格中每一行的值)。

第 5 步:将转换后的数据插入 CustomVis

现在,使用新的函数转换数据,并将输出分配给名为 nestedData 的新变量:


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

  return (
    <Table
      fields={{
        measures: [{ name: NESTED_DATA_KEY, label: 'Orders Count By Quarter' }],
        dimensions: fields.dimensions,
        pivots: [],
      }}
      config={config}
      data={nestedData}
      pivots={pivots}
    />
  )
}

第 6 步:生成自定义可视化图表

插入转换的数据并配置图表后,可视化图表将如以下表格示例所示,其中每行均有一个火花谱线图:

自定义的可视化图表,其中显示了一个表格,每个品牌各占一行,嵌入的迷你图表则显示了每行中各季度的订单。

呈现上述可视化效果所需的完整代码如下所示:


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

// we assign this value to a constant to ensure that fields and data
// objects remain in sync.
const NESTED_DATA_KEY = 'orderCount'
const ROW_HEIGHT = 75

const nestSparklines = data => {
  return data.reduce((acc, d) => {
    // the first entry is the dimension (brand name), and the rest of the rows are the
    // quarterly sales information we want to pass to the Sparkline.
    const [parentDimension, ...measurePairs] = Object.entries(d)

    // `nonPivotedData` represents a single data row.
    // e.g. [{entry: 1, orderCount: 10}, {entry: 2, orderCount: 15}, ...etc]
    const nonPivotedData = measurePairs.map(([_, value], i) => {
      return { entry: i, [NESTED_DATA_KEY]: value }
    })

    // now for each row in the table we render a Sparkline using the `nonPivotedData`
    // that we built above.
    // E.G. [{products.brand: 'adidas', orderCount: <Sparkline />}]
    return [
      ...acc,
      {
        [parentDimension[0]]: parentDimension[1],
        [NESTED_DATA_KEY]: () => (
          <Sparkline
            height={ROW_HEIGHT}
            data={nonPivotedData}
            fields={{
              measures: [{ name: NESTED_DATA_KEY }],
              dimensions: [],
            }}
          />
        ),
      },
    ]
  }, [])
}

const CustomVis = ({ fields, data, pivots, config }) => {
  const nestedData = nestSparklines(data)

  return (
    <Table
      config={config}
      height={500}
      fields={{
        measures: [{ name: NESTED_DATA_KEY, label: 'Orders Count By Quarter' }],
        dimensions: fields.dimensions,
        pivots: [],
      }}
      data={nestedData}
      pivots={pivots}
      defaultRowHeight={ROW_HEIGHT}
    />
  )
}

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

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

后续步骤