Visualizações personalizadas em painéis de IA/BI

Importante

Esse recurso está em Visualização Pública.

As visualizações personalizadas permitem personalizar gráficos em painéis de IA/BI além dos tipos de visualização internos. As visualizações personalizadas usam a biblioteca Vega-Lite para renderizar gráficos de uma especificação JSON.

Criar uma visualização personalizada

  1. Selecione um conjunto de dados.
  2. No painel de configuração de visualização, selecione Custom Viz na seção Visualização Avançada .
  3. Na seção Campos , adicione os campos que você deseja usar. Cada campo tem um Nome exclusivo. Faça referência aos campos em sua especificação do Vega-Lite usando estes nomes.
  4. Insira sua especificação JSON do Vega-Lite no editor de especificação do Vega-Lite.

Exemplo: gráfico em camadas com uma média móvel

Este exemplo cria um gráfico em camadas que representa pontos de dados brutos de temperatura com uma sobreposição de linha de média móvel, usando dados meteorológicos dos conjuntos de dados de exemplo do Databricks.

Exemplo de gráfico de visualização personalizado.

  1. Crie um conjunto de dados com a seguinte consulta:

    SELECT date, temperature AS temp_max
    FROM samples.accuweather.historical_hourly_imperial
    WHERE city_name = 'singapore'
    ORDER BY date
    
  2. No painel de configuração de visualização, em Avançado, selecione Viz Personalizado.

  3. Selecione o conjunto de dados que você criou na etapa anterior.

  4. Na seção Campos , adicione um campo para a coluna de data e defina seu Nome como date.

  5. Adicione um campo para a coluna de temperatura e defina seu Nome como temp_max.

  6. Copie a especificação a seguir no editor de especificação do Vega-Lite. Se o eixo x estiver cortado, consulte Fazer um gráfico ser redimensionado automaticamente.

    Especificação JSON
    {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "width": "container",
      "height": "container",
      "config": {
        "autosize": { "type": "fit", "contains": "padding" }
      },
      "data": { "name": "databricks_query" },
      "transform": [
        {
          "window": [{ "field": "temp_max", "op": "mean", "as": "rolling_mean" }],
          "frame": [-15, 15]
        }
      ],
      "encoding": {
        "x": { "field": "date", "type": "temporal", "title": "Date" },
        "y": {
          "type": "quantitative",
          "scale": { "zero": false },
          "axis": { "title": "Max temperature and rolling mean" }
        }
      },
      "layer": [
        {
          "mark": { "type": "point", "opacity": 0.3 },
          "encoding": { "y": { "field": "temp_max", "title": "Max temperature" } }
        },
        {
          "mark": { "type": "line", "color": "red", "size": 3 },
          "encoding": { "y": { "field": "rolling_mean", "title": "Rolling mean of max temperature" } }
        }
      ]
    }
    

Colunas de conjunto de dados de referência

Há duas maneiras de referenciar colunas em uma especificação de Vega-Lite:

  • Use "field": "{columnName}". O exemplo a seguir atribui a xField coluna ao eixo x:

    "encoding": {
      "x": { "field": "xField", "type": "quantitative" }
    }
    
  • Em expressões, use datum["{columnName}"] ou datum.{columnName}. O exemplo a seguir define uma nova coluna x a partir das colunas r e angle:

    { "calculate": "datum.r * cos(datum.angle)", "as": "x" }
    

Para obter mais informações, consulte datum na documentação de expressões do Vega.

Herdar o tema do painel

As visualizações personalizadas se adaptam automaticamente ao tema do painel, incluindo o modo claro e escuro. Os seguintes elementos de gráfico herdam valores de tema sem alterações em sua especificação:

  • As fontes dos eixos, da legenda, do título e do cabeçalho correspondem às fontes configuradas no painel.
  • As linhas de grade do eixo correspondem à cor das linhas de grade do painel no modo ativo.
  • O plano de fundo do gráfico é transparente sobre o plano de fundo temático do widget.

As configurações definidas no bloco de config sua especificação têm precedência sobre os padrões herdados.

Referenciar valores de tema em expressões

Para estilizar marcas com valores sensíveis ao tema, faça referência aos seguintes sinais dentro de uma expressão do Vega-Lite ({ "expr": "..." }):

Sinal Descrição
colors Tokens de cores pré-resolvidos para o modo ativo. Use-os para valores comuns, como colors.textPrimary.default, colors.gridColore colors.markHighlightColor. Não indexe-os com [mode]; eles já estão resolvidos.
mode O modo de cor ativo, seja 'light' ou 'dark'. Use-o para indexar dashboardTheme campos que fornecem variantes por modo.
dashboardTheme O tema completo configurado pelo proprietário do painel, incluindo fontes (resolvedFontSettings), a paleta categórica (visualizationColors) e as cores por modo (gridLineColor). Campos com variantes por modo exigem índice [mode].

Os sinais colors e dashboardTheme são independentes. colors fornece tokens de conveniência resolvidos para o modo ativo, enquanto dashboardTheme expõe o tema completo configurado pelo proprietário. Use colors primeiro e dashboardTheme para fontes, a paleta completa ou qualquer valor que colors não ofereça.

Os exemplos a seguir mostram como referenciar valores de tema:

  • Use a fonte do corpo do painel para uma marca de texto:

    { "expr": "dashboardTheme.resolvedFontSettings.fieldValue.fontFamily" }
    
  • Use a cor do título do painel para o modo ativo:

    { "expr": "dashboardTheme.resolvedFontSettings.fieldTitle.fontColor[mode]" }
    
  • Use uma cor da paleta categórica do painel:

    { "expr": "dashboardTheme.visualizationColors[0]" }
    

Observação

Como os tokens colors já estão resolvidos para o modo ativo, não os indexe com [mode]. Use colors.markHighlightColor, não colors.markHighlightColor[mode]. Os campos em dashboardTheme que fornecem variantes por modo, como dashboardTheme.gridLineColor[mode], exigem o índice [mode].

Filtrar outros widgets ao selecionar

Uma visualização personalizada pode atuar como uma fonte de filtro cruzado: quando um usuário clica em uma marca, a seleção filtra os outros widgets no painel. Para habilitar isso, adicione um parâmetro de seleção de ponto com o nome databricks_mark_selectionreservado. O renderizador detecta esse nome e conecta a seleção ao estado de filtro cruzado do painel.

"params": [
  {
    "name": "databricks_mark_selection",
    "select": { "type": "point", "fields": ["categoryName"] }
  }
]

Os seguintes requisitos são aplicados:

  • O parâmetro name deve ser exatamente databricks_mark_selection. Qualquer outro nome é tratado como um parâmetro regular e não conduz a filtragem cruzada.
  • select.type deve ser point. As seleções de intervalo (pincel) não são compatíveis como fontes de filtragem cruzada.
  • select.fields deve listar os nomes de campo definidos na seção Campos da configuração do widget, não os nomes de coluna brutos.
  • Listar somente campos de dimensão (agrupamento) em select.fields. Medidas agregadas, como SUM(...) ou AVG(...), não podem conduzir um filtro cruzado.
  • Para selecionar em vários campos, liste-os juntos: "fields": ["categoryName", "regionName"].

Realçar as marcas selecionadas

Para realçar as marcas selecionadas, use as condições stroke e strokeWidth em uma marca baseada em preenchimento (como bar, arc ou rect) e mantenha a codificação color vinculada ao seu campo. Use { "expr": "colors.markHighlightColor" } para o traço para que o realce permaneça legível nos modos claro e escuro.

O exemplo a seguir filtra o restante do painel por categoryName ao clicar em uma barra. As barras selecionadas recebem um traço temático e as barras não selecionadas escurecem enquanto mantêm a codificação de cores.

Especificação JSON
{
  "$schema": "https://vega.github.io/schema/vega-lite/v6.json",
  "data": { "name": "databricks_query" },
  "width": "container",
  "height": "container",
  "config": { "autosize": { "type": "fit", "contains": "padding" } },
  "params": [
    {
      "name": "databricks_mark_selection",
      "select": { "type": "point", "fields": ["categoryName"] }
    }
  ],
  "mark": { "type": "bar", "stroke": null },
  "encoding": {
    "x": { "field": "categoryName", "type": "nominal" },
    "y": { "field": "salesValue", "type": "quantitative" },
    "color": { "field": "categoryName", "type": "nominal" },
    "fillOpacity": {
      "condition": { "param": "databricks_mark_selection", "value": 1 },
      "value": 0.3
    },
    "stroke": {
      "condition": {
        "param": "databricks_mark_selection",
        "empty": false,
        "value": { "expr": "colors.markHighlightColor" }
      },
      "value": null
    },
    "strokeWidth": {
      "condition": { "param": "databricks_mark_selection", "empty": false, "value": 2 },
      "value": 0
    }
  }
}

Redimensionar um gráfico automaticamente

Para fazer com que um gráfico se redimensione para se ajustar ao seu contêiner, adicione as seguintes configurações no nível superior da sua especificação:

"width": "container",
"height": "container",
"config": {
  "autosize": {
    "type": "fit",
    "contains": "padding"
  }
}

Especificações de gráfico de exemplo

As especificações a seguir mostram gráficos que não estão disponíveis como tipos de visualização internos. Para obter mais exemplos, consulte as galerias de exemploVega-Lite.

Gráfico de bala

Exemplo de gráfico de marcadores.

Defina categoryField, currentField, paceField e targetField na seção Campos.

Especificação JSON
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "width": "container",
  "height": "container",
  "data": { "name": "databricks_query" },
  "config": {
    "autosize": { "type": "fit", "contains": "padding" }
  },
  "transform": [
    {
      "fold": ["targetField", "paceField", "currentField"],
      "as": ["measure_name", "measure_value"]
    },
    {
      "calculate": "toNumber(datum.measure_value)",
      "as": "measure_value"
    },
    {
      "calculate": "{ \"targetField\": \"Target\", \"paceField\": \"Pace\", \"currentField\": \"Current\" }[datum.measure_name]",
      "as": "measure_label"
    },
    {
      "calculate": "indexof([\"Target\", \"Pace\", \"Current\"], datum.measure_label)",
      "as": "measure_order"
    }
  ],
  "layer": [
    {
      "mark": "bar",
      "params": [
        {
          "name": "legend_click",
          "select": { "type": "point", "fields": ["measure_label"] },
          "bind": "legend"
        }
      ],
      "encoding": {
        "color": { "field": "measure_label" },
        "opacity": { "value": 0 }
      }
    },
    {
      "transform": [{ "filter": { "param": "legend_click" } }],
      "layer": [
        {
          "layer": [
            {
              "mark": { "type": "bar", "tooltip": true },
              "encoding": { "color": { "field": "measure_label", "legend": null } },
              "transform": [{ "filter": { "field": "measure_label", "oneOf": ["Pace"] } }]
            },
            {
              "mark": { "type": "bar", "height": 7, "tooltip": true },
              "encoding": { "color": { "field": "measure_label", "legend": null } },
              "transform": [{ "filter": { "field": "measure_label", "oneOf": ["Current"] } }]
            },
            {
              "mark": { "type": "tick", "tooltip": true, "thickness": 3 },
              "encoding": { "color": { "field": "measure_label", "legend": null } },
              "transform": [{ "filter": { "field": "measure_label", "oneOf": ["Target"] } }]
            }
          ],
          "encoding": {
            "x": {
              "field": "measure_value",
              "type": "quantitative",
              "stack": null,
              "title": "Value",
              "axis": { "orient": "bottom" }
            },
            "color": {
              "scale": {
                "domain": ["Target", "Pace", "Current"],
                "range": ["#000000", "#bcbcbc", "#A66BBF"]
              }
            },
            "order": {
              "field": "measure_order",
              "type": "quantitative",
              "sort": "descending"
            }
          }
        }
      ],
      "encoding": {
        "y": {
          "field": "categoryField",
          "type": "ordinal",
          "title": "Category",
          "axis": { "labelOverlap": true }
        },
        "tooltip": [
          { "field": "categoryField", "type": "nominal", "title": "Category" },
          { "field": "currentField", "type": "quantitative", "title": "Current" },
          { "field": "paceField", "type": "quantitative", "title": "Pace" },
          { "field": "targetField", "type": "quantitative", "title": "Target" }
        ]
      }
    }
  ]
}

Medidor

Exemplo de gráfico gauge.

Definir $valueField e $totalField na seção Campos .

Especificação JSON
{
  "$schema": "https://vega.github.io/schema/vega-lite/v6.json",
  "width": "container",
  "height": "container",
  "data": { "name": "databricks_query" },
  "config": {
    "concat": { "spacing": 0 },
    "autosize": { "type": "fit", "contains": "padding" }
  },
  "params": [
    { "name": "ring_max", "expr": "min(width, height) / 2 - 16" },
    { "name": "ring_width", "expr": "max(12, (min(width, height) / 2) * 0.12)" },
    { "name": "ring_gap", "expr": "max(4, (min(width, height) / 2) * 0.03)" },
    { "name": "label_color", "value": "#000000" },
    { "name": "ring_background_opacity", "value": 0.3 },
    { "name": "ring0_percent", "value": 100 },
    { "name": "ring0_outer", "expr": "ring_max + 2" },
    { "name": "ring0_inner", "expr": "ring_max + 1" },
    { "name": "ring1_outer", "expr": "ring0_inner - ring_gap" },
    { "name": "ring1_inner", "expr": "ring1_outer - ring_width" },
    { "name": "ring1_middle", "expr": "(ring1_outer + ring1_inner) / 2" },
    { "name": "arc_size", "expr": "220" }
  ],
  "transform": [
    { "as": "ratio", "calculate": "datum['$valueField'] / datum['$totalField']" },
    { "as": "_arc_start_degrees", "calculate": "360 - ( arc_size / 2 )" },
    { "as": "_arc_end_degrees", "calculate": "0 + ( arc_size / 2 )" },
    { "as": "_arc_start_radians", "calculate": "2 * 3.14 * ( datum['_arc_start_degrees'] - 360 ) / 360" },
    { "as": "_arc_end_radians", "calculate": "2 * 3.14 * datum['_arc_end_degrees'] / 360" },
    { "as": "_arc_total_radians", "calculate": "datum['_arc_end_radians'] - datum['_arc_start_radians']" },
    { "as": "_ring_start_radians", "calculate": "datum['_arc_start_radians']" },
    {
      "as": "_ring_end_radians",
      "calculate": "datum['_arc_start_radians'] + ( datum['_arc_total_radians'] * datum['ratio'] )"
    }
  ],
  "layer": [
    {
      "mark": {
        "type": "arc",
        "color": "lightgrey",
        "theta": { "expr": "datum['_arc_start_radians']" },
        "radius": { "expr": "ring1_outer" },
        "theta2": { "expr": "datum['_arc_end_radians']" },
        "radius2": { "expr": "ring1_inner" },
        "cornerRadius": 10
      }
    },
    {
      "name": "RING",
      "mark": {
        "type": "arc",
        "theta": { "expr": "datum['_ring_start_radians']" },
        "radius": { "expr": "ring1_outer" },
        "theta2": { "expr": "datum['_ring_end_radians']" },
        "radius2": { "expr": "ring1_inner" },
        "cornerRadius": 10
      },
      "encoding": {
        "color": {
          "value": "#307E31",
          "condition": [
            { "test": "datum['ratio'] < 0.33", "value": "#880808" },
            { "test": "datum['ratio'] < 0.66", "value": "#E49B0F" }
          ]
        }
      }
    },
    {
      "mark": { "type": "text", "fontSize": 40 },
      "encoding": {
        "text": { "field": "$valueField" },
        "color": {
          "value": "#307E31",
          "condition": [
            { "test": "datum['ratio'] < 0.33", "value": "#880808" },
            { "test": "datum['ratio'] < 0.66", "value": "#E49B0F" }
          ]
        }
      }
    }
  ]
}

Gráfico de radar

Exemplo de gráfico de radar.

Definir $key e $value na seção Campos .

Especificação JSON
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "width": "container",
  "height": "container",
  "config": {
    "autosize": { "type": "fit", "contains": "padding" }
  },
  "data": { "name": "databricks_query" },
  "transform": [
    { "window": [{ "op": "row_number", "as": "category" }] },
    { "calculate": "datum.category - 1", "as": "category" },
    {
      "joinaggregate": [
        { "op": "count", "as": "numCategories" },
        { "op": "max", "field": "$value", "as": "maxValue" }
      ]
    },
    { "calculate": "2 * PI * datum.category / datum.numCategories", "as": "angle" },
    { "calculate": "100 * datum['$value'] / datum.maxValue", "as": "r" },
    { "calculate": "datum.r * cos(datum.angle)", "as": "x" },
    { "calculate": "datum.r * sin(datum.angle)", "as": "y" },
    { "calculate": "110 * cos(datum.angle)", "as": "label_x" },
    { "calculate": "110 * sin(datum.angle)", "as": "label_y" }
  ],
  "layer": [
    {
      "transform": [
        { "joinaggregate": [{ "op": "count", "as": "numCategories" }] },
        { "aggregate": [{ "op": "max", "field": "numCategories", "as": "numCategories" }] },
        { "calculate": "[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]", "as": "cats" },
        { "flatten": ["cats"], "as": ["cat"] },
        { "filter": "datum.cat <= datum.numCategories" },
        { "calculate": "2 * PI * datum.cat / datum.numCategories", "as": "angle" },
        { "calculate": "100 * cos(datum.angle)", "as": "x" },
        { "calculate": "100 * sin(datum.angle)", "as": "y" }
      ],
      "mark": { "type": "line", "color": "#ddd", "strokeWidth": 1 },
      "encoding": {
        "x": { "field": "x", "type": "quantitative", "scale": { "domain": [-120, 120] }, "axis": null },
        "y": { "field": "y", "type": "quantitative", "scale": { "domain": [-120, 120] }, "axis": null },
        "order": { "field": "cat" }
      }
    },
    {
      "transform": [
        { "joinaggregate": [{ "op": "count", "as": "numCategories" }] },
        { "aggregate": [{ "op": "max", "field": "numCategories", "as": "numCategories" }] },
        { "calculate": "[20,40,60,80,100]", "as": "levels" },
        { "flatten": ["levels"], "as": ["level"] },
        { "calculate": "[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]", "as": "cats" },
        { "flatten": ["cats"], "as": ["cat"] },
        { "filter": "datum.cat <= datum.numCategories" },
        { "calculate": "2 * PI * datum.cat / datum.numCategories", "as": "angle" },
        { "calculate": "datum.level", "as": "r" },
        { "calculate": "datum.r * cos(datum.angle)", "as": "x" },
        { "calculate": "datum.r * sin(datum.angle)", "as": "y" }
      ],
      "mark": { "type": "line", "color": "#ddd", "strokeWidth": 1 },
      "encoding": {
        "x": { "field": "x", "type": "quantitative" },
        "y": { "field": "y", "type": "quantitative" },
        "detail": { "field": "level" },
        "order": { "field": "cat" }
      }
    },
    {
      "mark": { "type": "line", "color": "#9467bd", "strokeWidth": 2, "interpolate": "linear-closed" },
      "encoding": {
        "x": { "field": "x", "type": "quantitative" },
        "y": { "field": "y", "type": "quantitative" },
        "order": { "field": "category" }
      }
    },
    {
      "mark": { "type": "point", "filled": true, "size": 50, "color": "#9467bd" },
      "encoding": {
        "x": { "field": "x", "type": "quantitative" },
        "y": { "field": "y", "type": "quantitative" }
      }
    },
    {
      "mark": { "type": "text", "fontSize": 14, "fontWeight": "bold" },
      "encoding": {
        "x": { "field": "label_x", "type": "quantitative" },
        "y": { "field": "label_y", "type": "quantitative" },
        "text": { "field": "$key", "type": "nominal" }
      }
    }
  ],
  "view": { "stroke": null }
}

Gráfico radial

Exemplo de gráfico radial.

Definir $valueField e $colorField na seção Campos .

Especificação JSON
{
  "$schema": "https://vega.github.io/schema/vega-lite/v6.json",
  "width": "container",
  "height": "container",
  "config": {
    "autosize": { "type": "fit", "contains": "padding" }
  },
  "data": { "name": "databricks_query" },
  "transform": [
    {
      "aggregate": [{ "op": "sum", "field": "$valueField", "as": "total" }],
      "groupby": ["$colorField"]
    },
    {
      "window": [{ "op": "rank", "as": "rank" }],
      "sort": [{ "field": "total", "order": "descending" }]
    }
  ],
  "layer": [
    {
      "mark": { "type": "arc", "innerRadius": 20, "stroke": "#fff" }
    }
  ],
  "encoding": {
    "theta": {
      "field": "total",
      "type": "quantitative",
      "scale": { "type": "sqrt" },
      "stack": true,
      "sort": "descending"
    },
    "radius": { "field": "total", "scale": { "type": "sqrt", "zero": true } },
    "color": {
      "field": "$colorField",
      "type": "nominal",
      "title": "Sub-Category",
      "sort": { "field": "total", "order": "descending" },
      "legend": { "orient": "right" }
    },
    "tooltip": [
      { "field": "$colorField", "type": "nominal", "title": "Sub-Category" },
      { "field": "total", "type": "quantitative", "title": "Sales" }
    ]
  },
  "view": { "stroke": null }
}

Gráfico de explosão solar

Exemplo de gráfico de explosão solar.

Definir outerGroupField, innerGroupFielde sizeField na seção Campos .

Especificação JSON
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "width": "container",
  "height": "container",
  "data": { "name": "databricks_query" },
  "config": {
    "autosize": { "type": "fit", "contains": "padding" }
  },
  "transform": [
    { "calculate": "datum['outerGroupField']", "as": "OUTSIDE" },
    { "calculate": "datum['innerGroupField']", "as": "INSIDE" },
    { "calculate": "datum.OUTSIDE + '-' + datum.INSIDE", "as": "OUT_IN" },
    { "calculate": "toNumber(datum['sizeField'])", "as": "SIZE" }
  ],
  "resolve": {
    "scale": { "color": "independent" },
    "legend": { "color": "independent" }
  },
  "layer": [
    {
      "mark": {
        "type": "arc",
        "tooltip": true,
        "innerRadius": { "expr": "min(width, height)/9" },
        "outerRadius": { "expr": "min(width, height)/3" }
      },
      "encoding": {
        "theta": { "field": "SIZE", "type": "quantitative", "stack": true },
        "color": {
          "field": "OUT_IN",
          "type": "ordinal",
          "sort": "ascending",
          "title": "Inner Grouping",
          "scale": {
            "range": [
              "#1DF9B9",
              "#1DE5B9",
              "#1DD1B9",
              "#1DBDB9",
              "#1DA9B9",
              "#3DF23B",
              "#3DDA3B",
              "#3DC23B",
              "#3DAA3B",
              "#3D923B"
            ]
          }
        },
        "order": { "field": "OUT_IN", "sort": "ascending" },
        "tooltip": [
          { "field": "OUTSIDE", "type": "nominal", "title": "Outer Grouping" },
          { "field": "INSIDE", "type": "nominal", "title": "Inner Grouping" },
          { "field": "SIZE", "type": "quantitative", "title": "Count" }
        ]
      }
    },
    {
      "transform": [
        {
          "aggregate": [{ "op": "sum", "field": "SIZE", "as": "total_users" }],
          "groupby": ["OUTSIDE"]
        }
      ],
      "mark": {
        "type": "arc",
        "tooltip": true,
        "innerRadius": { "expr": "min(width, height)/3" }
      },
      "encoding": {
        "theta": {
          "field": "total_users",
          "type": "quantitative",
          "stack": true,
          "sort": "ascending",
          "title": "Users Count"
        },
        "color": {
          "field": "OUTSIDE",
          "type": "ordinal",
          "sort": "ascending",
          "title": "Outer Grouping",
          "scale": { "range": ["#1DD1B9", "#3DC23B"] }
        },
        "order": { "field": "OUTSIDE", "sort": "ascending" },
        "tooltip": [
          { "field": "OUTSIDE", "type": "nominal", "title": "Outer Grouping" },
          { "field": "total_users", "type": "quantitative", "title": "Count" }
        ]
      }
    }
  ]
}

Limitações

  • Não há suporte para gráficos de mapa de árvore. Vega-Lite não dá suporte a mapas de árvore.
  • No momento, não há suporte para imagens em visualizações vega personalizadas.