πŸ“Š Custom Charts - Waterfall Chart Example

Waterfall Chart

Waterfall chart is useful for visualizing how intermediate values contribute to a total, particularly when showing the cumulative effect of sequential positive or negative changes. Common applications include financial statements, P&L analysis, budget variances, and sales funnels.

Template Code
CustomChart {
  fields {
    field amount {
      type: 'measure'
    }
    field label {
      type: 'dimension'
    }
    field label_sorter {
      type: 'dimension'
    }
  }
  options {
    option begin_label {
      label: 'Begin Label'
      type: 'input'
      default_value: ''
    }
    option end_label {
      label: 'End Label'
      type: 'input'
      default_value: 'Total'
    }
    option bar_size {
      label: 'Bar Size'
      type: 'number-input'
      default_value: 50
    }
    option begin_end_color {
      label: 'Begin - End Color'
      type: 'color-picker'
      default_value: '#BDBDBD'
    }
    option begin_end_text {
      label: 'Begin - End Label Color'
      type: 'color-picker'
      default_value: 'black'
    }
    option positive {
      label: 'Positive Color'
      type: 'color-picker'
      default_value: '#58A65C'
    }
    option negative {
      label: 'Negative Color'
      type: 'color-picker'
      default_value: '#D85140'
    }
    option value_format {
      label: 'Format'
      type: 'input'
      default_value: '$,.0f'
    }
  }

  template: @vgl {
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "data": {
      "values": @{values}
    },
    "transform": [
      {"calculate": "datum['@{fields.amount.name}']", "as": "amount"},
      {"calculate": "datum['@{fields.label.name}']", "as": "label"},
      {"calculate": "datum['@{fields.label_sorter.name}']", "as": "label_sorter"},
      {
        "window": [{"op": "sum", "field": "amount", "as": "sum"}],
        "sort": [{"field": "label_sorter", "order": "ascending"}]
      },
      {
        "window": [{"op": "lead", "field": "label", "as": "lead"}],
        "sort": [{"field": "label_sorter", "order": "ascending"}]
      },
      {"calculate": "datum.lead === null ? '@{options.end_label.value}' : datum.lead", "as": "lead"},
      {"calculate": "datum.label === '@{options.end_label.value}' ? 0 : datum.sum - datum.amount", "as": "previous_sum"},
      {"calculate": "datum.label === '@{options.end_label.value}' ? datum.sum : datum.amount", "as": "amount"},
      {
        "calculate": "(datum.label !== '@{options.begin_label.value}' && datum.label !== '@{options.end_label.value}' && datum.amount > 0 ? '+' + format(datum.amount, '@{options.value_format.value}') : format(datum.amount, '@{options.value_format.value}'))", 
        "as": "text_amount"
      },
      {"calculate": "(datum.sum + datum.previous_sum) / 2", "as": "center"},
      {"calculate": "datum.sum < datum.previous_sum ? datum.sum : ''", "as": "sum_dec"},
      {"calculate": "datum.sum > datum.previous_sum ? datum.sum : ''", "as": "sum_inc"}
    ],
    "params": [
      {"name": "barSize", "value": @{options.bar_size.value}},
      {"name": "ruleOffset", "expr": "barSize/2"}
    ],
    "encoding": {
      "x": {
        "field": "label",
        "type": "ordinal",
        "sort": {"field": "label_sorter", "order": "ascending"},
        "axis": {
          "format": @{fields.label.format},
          "formatType": "holisticsFormat"
        } 
      }
    },
    "layer": [
      {
        "mark": {"type": "bar", "size": @{options.bar_size.value}},
        "params": [
          {
            "name": "normalPointSelection", 
            "select": {
              "type": "point",
              "toggle": "true",
              "clear": "mouseup" 
            }
          }
        ],
        "encoding": {
          "y": {"field": "previous_sum", "type": "quantitative", "format": @{fields.amount.format}, "formatType": "holisticsFormat"},
          "y2": {"field": "sum"},
          "color": {
            "condition": [
              {
                "test": "datum.label === '@{options.begin_label.value}'",
                "value": @{options.begin_end_color.value}
              },
              {"test": "datum.sum < datum.previous_sum", "value":  @{options.negative.value}}
            ],
            "value": @{options.positive.value}
          },
          "fillOpacity": {
            "condition": {"param": "normalPointSelection", "value": 1},
            "value": 0.3
          },
          "tooltip": [
            {"field": "label", "title": @{fields.label.name}, "format": @{fields.label.format}, "formatType": "holisticsFormat"},
            {"field": "amount", "title": @{fields.amount.name}, "format": @{options.value_format.value}},
            {"field": "sum", "title": "Total", "format": @{options.value_format.value}}
          ]
        }
      },
      {
        "transform": [
          {"filter": "datum.lead === '@{options.end_label.value}'"},
          {"calculate": "datum.sum / 2", "as": "center_end"}
        ],
        "encoding": {
          "x": {
            "field": "lead", 
            "sort": null,
            "axis": {
              "format": @{fields.label.format},
              "formatType": "holisticsFormat"
            } 
          },
          "tooltip": [
            {"field": "sum", "title": "Total", "format": @{options.value_format.value}}
          ]
        },
        "layer": [
          {
            "mark": {"type": "bar", "size": @{options.bar_size.value}},
            "encoding": {
              "y": {"field": "sum", "type": "quantitative"},
              "color": {"value": @{options.begin_end_color.value}}
            }
          },
          {
            "mark": {"type": "text", "fontWeight": "bold", "baseline": "middle"},
            "encoding": {
              "y": {"field": "center_end", "type": "quantitative"},
              "text": {"field": "sum", "format": @{options.value_format.value}},
              "color": {"value": @{options.begin_end_text.value}}
            }
          }
        ]
      },
      {
        "mark": {
          "type": "rule",
          "color": @{options.begin_end_color.value},
          "opacity": 1,
          "strokeWidth": 1,
          "xOffset": {"expr": "ruleOffset"},
          "x2Offset": {"expr": "-ruleOffset"}
        },
        "encoding": {
          "x2": {"field": "lead"},
          "y": {"field": "sum", "type": "quantitative"}
        }
      },
      {
        "mark": {"type": "text", "fontWeight": "bold", "baseline": "middle"},
        "encoding": {
          "y": {"field": "center", "type": "quantitative"},
          "text": {"field": "text_amount", "type": "nominal"},
          "color": {
            "condition": [
              {
                "test": "datum.label === '@{options.begin_label.value}' || datum.label === '@{options.end_label.value}'",
                "value": @{options.begin_end_text.value}
              }
            ],
            "value": "white"
          }
        }
      }
    ],
    "holisticsConfig": {
      "crossFilterSignals": ["normalPointSelection"],
    },
    "config": {
      "axis": {
        "title": null,
        "ticks": false,
        "labelPadding": 10,
        "labelFontSize": 11,
        "labelColor": "#858B9E",
        "labelAngle": 0,
        "labelOverlap": "parity",
        "labelLimit": 70,
        "gridDash": [8, 3],
        "gridColor": "#F4F6F8",
        "domainColor": "#bec1cb"
      },
      "axisY": {
        "domain": false
      },
      "view": {
        "opacity": 0
      },
      "font": "Inter"
    }
  };;
}

Required fields

Field type Description
Amount Measure The numeric value for each step. Can be positive or negative to show increases or decreases
Label Dimension The name or description of each step in your sequence
Label Sorter Dimension Control the order of steps in your waterfall chart

Chart options

  • Begin / End Column Label: Customize labels for the starting and ending columns (default: empty for start, β€œTotal” for end)
  • Begin / End Column Color: Set colors for the first and last columns to distinguish them from intermediate steps
  • Column width: Adjust the thickness of the bars for better visual balance
  • Positive Value Color: Color for columns showing increases
  • Negative Value color: Color for columns showing decreases
  • Value format: Customize how numbers are displayed (e.g., β€œ$,.0f” for currency)

Interactive features

  • Hover tooltips show detailed information for each step
  • Click to highlight specific columns
  • Cross-filtering support to connect with other charts
  • Auto-calculated running totals and intermediate values

Getting started with Custom Charts

Prerequisites: Custom Charts is available to customers from Standard Plan and above

1 Like