Docs / Build Workflow

Visualization — bar

Cuándo usar bar

Bar es la elección correcta cuando la pregunta es "¿cómo se comparan estas categorías?". El eje de categoría lista cosas discretas — productos, regiones, channels, statuses — y el eje de valor mide una o más cantidades numéricas para cada una. El mismo renderer cubre cuatro shapes comunes:

  • Single-series — una barra por categoría, un measure. El default.
  • Grouped — múltiples barras por categoría, una por measure. Ve por ella cuando los measures están en la misma escala (revenue y refunds, por ejemplo).
  • Stacked — barras apiladas una arriba de otra; la altura total es la suma de todos los measures. Usa chart.stack: normal.
  • Percent-stacked — cada categoría reescalada a 100%, con las barras mostrando la parte de cada componente. Usa chart.stack: percent.
  • Dual-axis — dos measures en escalas distintas compartiendo un chart, uno bindeado a un eje y izquierdo y el otro a uno derecho. Setea axis: right en la segunda series.
  • Combo (bar + line) — barras para un measure más una línea sobre las mismas categorías para otro (por ejemplo un overlay de porcentaje acumulado). Setea type: line en la series de línea; normalmente con axis: right y chart.y2_axis.

Usa line en su lugar cuando el eje x está ordenado por tiempo y la pregunta es sobre trend. Usa pie cuando hay muy pocas categorías (≤ 6) y la pregunta es puramente sobre composición. Usa grid cuando la audiencia necesita los números reales al lado de los nombres de categoría en vez de una comparación visual.

Mapping

Dos campos de mapping manejan el chart:

  • mapping.x — requerido. El campo categórico. Cada valor distinto se vuelve un tick en el eje de categoría. Usa un field string para labels limpios; los fields numéricos o de fecha se formatean con el default de la plataforma a menos que overrides vía format.
  • mapping.series — requerido. Array. Una entry por measure a plotear. Cada entry tiene que declarar un field; lo demás es opcional.
mapping:
  x: category
  series:
    - field: revenue

Usa una entry para un chart simple, dos o más para grouped o stacked, y el axis: right opcional en una series para ponerla en el eje y secundario.

Opciones por series:

  • field — requerido. El field numérico del resultado de la query que maneja la altura de barra para esta series.
  • label — string. Se muestra en la legend y el tooltip. Default al nombre del field.
  • label_from_field — string. Trae el label de legend desde un field en los datos en vez de declararlo en YAML.
  • color — color hex para esta series. Default al próximo slot en la paleta de la plataforma.
  • axis"left" (default) o "right". Right pone la series en el eje y secundario.
  • type"bar" (default) o "line". line dibuja el measure como línea sobre las mismas categorías (combo chart). Las series line nunca se apilan. No combinable con chart.orientation: horizontal — la validación rechaza esa combinación.
# two measures on the same scale (grouped)
series:
  - field: revenue
    label: Revenue
  - field: refunds
    label: Refunds

# two measures on different scales (dual-axis)
series:
  - field: revenue
    label: Revenue
  - field: order_count
    label: Orders
    axis: right

# one measure with explicit color
series:
  - field: revenue
    label: Revenue
    color: "#6c47ff"

Shortcuts de chart

Keys top-level del bloque chart. El bloque chart es tipado y cerrado — cualquier cosa no listada en esta página se rechaza en tiempo de validación.

  • chart.orientation"vertical" (default) o "horizontal". Elige horizontal cuando los labels de categoría son largos, o cuando la audiencia lee top-to-bottom (rankings, leaderboards).
  • chart.stack"normal" (sum stacking) o "percent" (reescalar cada categoría a 100% y switchear los labels del eje de valor a porcentajes). Omitirlo para grouped bars.
  • chart.show_value_labels — boolean. Prende el value label para cada series. El contenido del label es el valor de la barra, formateado por format; estilizalo con chart.value_label abajo.
  • chart.cross_filter — boolean, default true. Setea a false para charts que siempre tienen que mostrar la vista sin filtrar (ej. un headline de "total revenue").
  • chart.height — altura en pixels del container de la viz. Seteala explícita cuando el chart necesita más espacio vertical — por ejemplo, una bar horizontal con muchas categorías.

Value labels

chart.value_label es un objeto aplicado a cada series cuando chart.show_value_labels está prendido:

  • position — placement del label relativo a la barra. Valores comunes: "top", "inside", "insideTop", "insideBottom", "outside".
  • rotate — grados, entre -90 y 90. Útil cuando el label es más ancho que la barra.
  • color, font_size, font_weight.
  • formatter — template string (sin callbacks). Útil para agregar un prefix o suffix de unidad (ej. "{c}M").
  • distance, align ("left" | "center" | "right"), vertical_align ("top" | "middle" | "bottom"), clip.
# compact label inside the bar with white text
chart:
  show_value_labels: true
  value_label:
    position: inside
    color: "#fff"
    font_size: 11

# label above each vertical bar with currency formatter
chart:
  show_value_labels: true
  value_label:
    position: top
    formatter: "${c}"
    distance: 4

# rotated labels for narrow bars
chart:
  show_value_labels: true
  value_label:
    rotate: -90
    position: insideBottom

Legend & tooltip

chart.legend:

  • show — boolean. Setea a false cuando el chart tiene una sola series y la legend es redundante.
  • position — shortcut: "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right".
  • orient"horizontal" | "vertical". Usalo para overridear el orient derivado de position.
  • top / bottom / left / right — número de pixels o string de porcentaje para posicionamiento directo.
  • text_style — estilo de texto: color, font_style, font_weight, font_family, font_size, line_height.
  • item_width, item_height, item_gap — tamaños en pixels para los swatches coloreados y gaps.

chart.tooltip:

  • show — boolean.
  • trigger"item" (una barra a la vez, default para single-series), "axis" (group-wise; preferido cuando hay múltiples series para que hover las compare), o "none".
  • confine — boolean. Mantiene el tooltip dentro de los bounds del chart.
  • formatter — template string. Usa placeholders como {a} (series), {b} (categoría), {c} (valor).
  • background_color, border_color, border_width.
  • padding — número único o array de 2 a 4 números (top/right/bottom/left).
  • text_style — mismo shape que en la legend.
  • axis_pointer.type"line" | "shadow" | "none" | "cross". Elección común para bar: "shadow".

Ejes

chart.x_axis, chart.y_axis y chart.y2_axis comparten el mismo shape para bloques de eje de valor (y2_axis configura el eje derecho cuando una series usa axis: right; con un extra en x_axis):

  • name — título del eje.
  • name_location"start" | "middle" | "center" | "end".
  • name_gap — pixels entre la línea del eje y el nombre.
  • min, max — límites numéricos del eje. Usa y2_axis.max: 1 para fijar una línea de porcentaje acumulado a un dominio 0–100% cuando los valores están en 0–1.
  • axis_label.show — boolean.
  • axis_label.rotate — grados, -90 a 90. Usalo para evitar que nombres largos de categoría se solapen.
  • axis_label.interval — entero ≥ 0 (saltear cada N labels) o "auto".
  • axis_label.color, axis_label.font_size, axis_label.font_weight.
  • axis_label.formatter — template string.
  • axis_label.max_chars — entero ≥ 1. Trunca los labels con una elipsis.
  • x_axis.visible_window — entero ≥ 1. Restringe la cantidad visible de categorías y habilita un range slider horizontal; útil cuando hay muchas categorías.
# long category names with rotation and ellipsis
chart:
  x_axis:
    axis_label:
      rotate: -30
      max_chars: 14

# named y-axis with a percent formatter
chart:
  y_axis:
    name: Margin
    name_gap: 28
    axis_label:
      formatter: "{value}%"

# data-zoom slider for many categories
chart:
  x_axis:
    visible_window: 12

format

El format de número es field-keyed en el top level del YAML de la viz. Los patterns por field ganan sobre el pattern root. Patterns comunes para un bar chart:

format:
  revenue: "$#,##0a"       # abbreviated currency: $1.2M, $340K
  order_count: "#,##0"     # integer with grouping
  margin_pct: "#,##0.00%"  # percentage
  avg_ticket: "$#,##0.00"  # full currency with cents

El format aplica a value labels, tooltips y axis tick labels de valor. La gramática completa de patterns vive en el overview de viz-types.

Comportamiento de cross-filter

Dentro de un dashboard, clickear una barra agrega un "pill" que estrecha cada otra viz de la página a la categoría clickeada. Mecanismo completo en Cross-filtering. Específicos de bar:

  • La categoría de la barra clickeada se vuelve el valor de cross-filter.
  • Para hacer un field cross-filterable, declara un parámetro con su nombre en la primera firma de source del archivo de model propio de este bar.
  • Deshabilita por viz con chart.cross_filter: false. Útil para charts "headline" que siempre tienen que mostrar totales.
  • Para resaltar una barra específica desde un valor externo, usa el bloque top-level emphasis:
    emphasis:
      field: category
      value_from_param: highlight_category
      bar_color: "#6c47ff"
    La barra cuyo category iguala al valor de runtime de highlight_category se colorea con bar_color.

Ejemplos trabajados

Comparación simple:

id: ec_revenue_by_category_bar
title: Revenue by Category
query: "models/ec_revenue.malloy::by_category"
type: bar
mapping:
  x: category
  series:
    - field: revenue
      label: Revenue
chart:
  height: 320
  show_value_labels: true
  value_label:
    position: top
  x_axis:
    axis_label:
      rotate: -30
      max_chars: 14
  y_axis:
    name: Revenue
format:
  revenue: "$#,##0.00"
published: true

Grouped (dos measures, misma escala):

id: ec_revenue_vs_refunds_bar
title: Revenue vs Refunds
query: "models/ec_revenue.malloy::revenue_vs_refunds"
type: bar
mapping:
  x: category
  series:
    - field: revenue
      label: Revenue
      color: "#6c47ff"
    - field: refunds
      label: Refunds
      color: "#fb7185"
chart:
  legend:
    show: true
    position: top
  tooltip:
    trigger: axis
    axis_pointer:
      type: shadow
format:
  revenue: "$#,##0.00"
  refunds: "$#,##0.00"
published: true

Composición percent-stacked:

id: ec_channel_mix_bar
title: Channel Mix per Category
query: "models/ec_revenue.malloy::channel_mix"
type: bar
mapping:
  x: category
  series:
    - field: rev_direct
      label: Direct
    - field: rev_organic
      label: Organic
    - field: rev_paid
      label: Paid
chart:
  stack: percent
  show_value_labels: true
  value_label:
    position: inside
    color: "#fff"
  legend:
    show: true
    position: top
format:
  rev_direct: "#,##0.0%"
  rev_organic: "#,##0.0%"
  rev_paid: "#,##0.0%"
published: true

Pareto (barras ordenadas + línea de porcentaje acumulado en el eje derecho). La query devuelve el conteo de cada categoría ordenado descendente más una columna de porcentaje acumulado (valores 0–1); mapea el conteo como barras y el porcentaje acumulado como series type: line en axis: right. El model acompañante:

##! experimental.parameters

# models/ec_returns.malloy — returned items per category plus running share.
source: ec_returns() is ecommerce.sql("""
  WITH by_category AS (
    SELECT p.category AS category, COUNT(*) AS return_count
    FROM `bigquery-public-data.thelook_ecommerce.order_items` oi
    JOIN `bigquery-public-data.thelook_ecommerce.products` p ON oi.product_id = p.id
    WHERE oi.status = 'Returned'
    GROUP BY 1
  )
  SELECT
    category,
    return_count,
    SUM(return_count) OVER (ORDER BY return_count DESC, category)
      / SUM(return_count) OVER () AS cumulative_pct
  FROM by_category
  ORDER BY return_count DESC
""") extend {
  view: by_category_pareto is { select: * }
}
id: ec_returns_pareto
title: Returns by category — Pareto
query: "models/ec_returns.malloy::by_category_pareto"
type: bar
mapping:
  x: category
  series:
    - field: return_count
      label: Returns
    - field: cumulative_pct
      label: Cumulative %
      type: line
      axis: right
      color: "#6c47ff"
chart:
  y2_axis:
    max: 1
  legend:
    show: true
format:
  return_count: "#,##0"
  cumulative_pct: "#,##0%"
published: true

Dual-axis (revenue vs orders):

id: ec_revenue_orders_bar
title: Revenue and Orders
query: "models/ec_revenue.malloy::by_month"
type: bar
mapping:
  x: order_month
  series:
    - field: revenue
      label: Revenue
    - field: order_count
      label: Orders
      axis: right
chart:
  legend:
    show: true
    position: top
  y_axis:
    name: Revenue
format:
  revenue: "$#,##0"
  order_count: "#,##0"
published: true

Ranking horizontal con muchas categorías:

id: ec_top_brands_bar
title: Top Brands by Revenue
query: "models/ec_revenue.malloy::by_brand_desc"
type: bar
mapping:
  x: brand
  series:
    - field: revenue
      label: Revenue
chart:
  orientation: horizontal
  height: 540
  show_value_labels: true
  value_label:
    position: right
  x_axis:
    visible_window: 25
    axis_label:
      max_chars: 18
  y_axis:
    name: Revenue
format:
  revenue: "$#,##0a"
published: true

Nota: los bloques de eje mantienen sus roles lógicos bajo orientación horizontal — x_axis sigue siendo el eje de categoría (así que max_chars y visible_window se quedan bajo x_axis), y el renderer intercambia los lados de dibujo por ti. value_label.position: right coloca los labels pasado el extremo de la barra en barras horizontales.

Notas de diseño

  • Los stacks de porcentaje se leen como composición cuando cada serie en mapping.series[] es parte del mismo todo — mantén las measures en una sola escala.
  • Los charts dual-axis necesitan espacio para dos escalas — setea name_gap explícito en ambos ejes, reduce categorías, o divide en dos charts.
  • Para labels de categoría largos, agrega chart.x_axis.axis_label.rotate: -30 y max_chars: 14, o cambia a chart.orientation: horizontal (las barras horizontales van solo con series de barras — los overlays de línea estilo Pareto quedan verticales).
  • Para value labels en barras chicas, setea chart.value_label.clip: false, mueve la posición a "top" / "outside", o reduce font_size.
  • Leyenda: position: bottom cuando compite con el chart; show: false con una sola serie.
  • Para muchas categorías, habilita un slider con chart.x_axis.visible_window, u ordena y limita a top-N en la query Malloy.
  • El color es por serie — para resaltar una barra específica, usa el bloque top-level emphasis (mira arriba).