💯

Barras apiladas normalizadas (porcentaje) con etiquetas dinámicas

Gráfico de barras apiladas normalizado (porcentaje) con etiquetas dinámicas. Basado en un ejemplo oficial de Vega-Lite y adaptado para Deneb en Power BI. Nota: la interactividad de cross-filter es limitada cuando se usa ‘aggregate’.

Vega-Lite normalized-stacked-bar intermedio ✓ Tooltip ✓ Context menu

Vista previa en vivo

Render con datos de muestra · Vega/Vega-Lite
Cargando vista previa…

Vista previa renderizada con el runtime de Deneb · MIT © Daniel Marsh-Patrick

🔽 Descargar .pbix 🔽 Descargar spec .json

🧩 Campos que espera

  1. __0__ Time Period número
  2. __1__ Category Code número
  3. __2__ Quantity número
  4. __3__ Classification número
  5. __4__ DisplayOptionsValue número medida

Arrastra tus campos a Values en este orden.

🎯 Técnica

Motor
Vega-Lite 5.19.0
Marks
bar, text
Power BI
pbiFormat

Cómo usarlo en Power BI

  1. Agrega el visual Deneb a tu reporte e ingresa a su editor.
  2. Arrastra tus campos a Values en este orden: Time Period , Category Code , Quantity , Classification , DisplayOptionsValue .
  3. Pega el spec (botón «Copiar spec» arriba) en el editor JSON de Deneb.
  4. Mapea tus columnas y ajusta a tu gusto. La interactividad nativa ya viene configurada.
Ver el spec Vega-Lite completo
spec.vega-lite.json
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "usermeta": {
    "information": {
      "uuid": "36599ca9-ab84-492b-ad10-3ab310179e16",
      "generated": "2024-08-08T04:25:18.675Z",
      "previewImageBase64PNG": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
      "name": "Normalized (Percentage) Stacked Bar Chart With Dynamic Labels",
      "description": "Normalized (Percentage) Stacked Bar Chart With Dynamic Labels: Based on an official Vega-Lite example and adapted for use in Deneb within Power BI. Note: Cross-filtering interactivity is limited when using 'aggregate'. Author: Cristobal Salcedo Beltran. Contact: contacto@csalcedodatabi.com.",
      "author": "Cristobal Salcedo Beltran"
    },
    "deneb": {
      "build": "1.7.0.0",
      "metaVersion": 1,
      "provider": "vegaLite",
      "providerVersion": "5.19.0"
    },
    "interactivity": {
      "tooltip": true,
      "contextMenu": true,
      "selection": false,
      "selectionMode": "simple",
      "highlight": false,
      "dataPointLimit": 50
    },
    "config": "{\n  // View configuration: remove default stroke for cleaner appearance\n  \"view\": {\n    \"stroke\": \"transparent\"\n  },\n  // Y-axis configuration\n  \"axisY\": {\n    \"grid\": false, // Disable grid lines for a cleaner look\n    \"offset\": 3, // Offset to position the axis labels slightly away from the axis\n    \"labelAngle\": 0, // Angle of the y-axis labels (horizontal)\n    \"labelFontSize\": 14, // Font size of y-axis labels\n    \"titleFontSize\": 18 // Font size of the y-axis title\n  },\n  // X-axis configuration\n  \"axisX\": {\n    \"grid\": false, // Disable grid lines for a cleaner look\n    \"labelAngle\": 0, // Angle of the x-axis labels (horizontal)\n    \"labelFontSize\": 14, // Font size of x-axis labels\n    \"tickCount\": 3, // Number of ticks on the x-axis for simplicity\n    \"titleFontSize\": 18 // Font size of the x-axis title\n  },\n  // Legend configuration\n  \"legend\": {\n    \"title\": \"\", // No title for the legend\n    \"symbolSize\":  {\"expr\": \"DisplayValueMode==4?0:280\"}, // Size of the symbols in the legend\n    \"disable\": false, // Ensure legend is enabled\n    \"labelFontSize\": {\"expr\": \"DisplayValueMode==4?0:14\"}, // Font size of legend labels\n    \"labelFontWeight\": \"bold\", // Bold font for legend labels\n    \"columnPadding\": 80, // Padding between legend columns\n    \"labelColor\": {\n      \"expr\": \"datum.label == 'Male' ? '#116e9d' : '#9d2993'\" // Conditional color for labels\n    },\n    \"orient\": \"right\", // Position the legend at the top\n    \"labelAlign\": \"left\" // Align legend text to the left of the symbol\n  }\n}",
    "dataset": [
      {
        "key": "__0__",
        "name": "Time Period",
        "description": "The specific year or time frame the data represents.",
        "kind": "column",
        "type": "numeric"
      },
      {
        "key": "__1__",
        "name": "Category Code",
        "description": "A numeric code representing different categories such as gender, region, or type.",
        "kind": "column",
        "type": "numeric"
      },
      {
        "key": "__2__",
        "name": "Quantity",
        "description": "The numerical count or measurement of items in a particular group or category.",
        "kind": "column",
        "type": "numeric"
      },
      {
        "key": "__3__",
        "name": "Classification",
        "description": "The grouping or classification such as age range, income bracket, or type.",
        "kind": "column",
        "type": "numeric"
      },
      {
        "key": "__4__",
        "name": "DisplayOptionsValue",
        "description": "Parameter to select the mode of value display in the chart: 1 for absolute values, 2 for percentage values, 3 for both values, and 4 full values",
        "kind": "measure",
        "type": "numeric"
      }
    ]
  },
  "description": "Normalized (Percentage) Stacked Bar Chart With Dynamic Labels: Based on an official Vega-Lite example and adapted for use in Deneb within Power BI. Note: Cross-filtering interactivity is limited when using 'aggregate'. Author: Cristobal Salcedo Beltran. Contact: contacto@csalcedodatabi.com.",
  "params": [
    {
      "name": "DisplayValueMode",
      "expr": "pluck(data('dataset'),'DisplayOptionsValue')[0]"
    }
  ],
  "data": {
    "name": "dataset"
  },
  "transform": [
    {
      "filter": "datum['__0__'] == 2000"
    },
    {
      "calculate": "datum['__1__'] == 2 ? 'Female' : 'Male'",
      "as": "gender"
    }
  ],
  "encoding": {
    "x": {
      "aggregate": "sum",
      "field": "__2__",
      "stack": "normalize",
      "title": "Percentage of Population"
    },
    "y": {
      "field": "__3__",
      "title": "Age Group"
    },
    "color": {
      "field": "gender",
      "scale": {
        "range": [
          "#9d2993",
          "#116e9d"
        ]
      }
    }
  },
  "layer": [
    {
      "mark": {
        "type": "bar",
        "opacity": 1,
        "stroke": "white",
        "strokeWidth": 0.5
      }
    },
    {
      "mark": {
        "type": "text",
        "fontSize": 14
      },
      "transform": [
        {
          "window": [
            {
              "field": "__2__",
              "op": "sum",
              "as": "TotalPopulationPerAgeGroup"
            }
          ],
          "frame": [
            null,
            null
          ],
          "groupby": [
            "__3__"
          ]
        },
        {
          "window": [
            {
              "field": "__2__",
              "op": "sum",
              "as": "TotalPopulationPerGenderAge"
            }
          ],
          "frame": [
            null,
            null
          ],
          "groupby": [
            "gender",
            "__3__",
            "TotalPopulationPerAgeGroup",
            "__1__"
          ]
        },
        {
          "calculate": "pbiFormat(datum.TotalPopulationPerGenderAge/datum.TotalPopulationPerAgeGroup,'0%')",
          "as": "percentageOfPopulation"
        },
        {
          "calculate": "pbiFormat(datum.TotalPopulationPerGenderAge,'#,0,,.#M')",
          "as": "formattedTotalPopulation"
        },
        {
          "calculate": "datum.gender + '_' + datum.formattedTotalPopulation +  ' | ' +  datum.percentageOfPopulation ",
          "as": "labelTextFirst"
        },
        {
          "calculate": "DisplayValueMode==4? (datum['__3__']=='0'?datum.gender + ' | ' + datum.formattedTotalPopulation +  ' | ' +  datum.percentageOfPopulation: datum.gender + '_' + datum.formattedTotalPopulation +  ' | ' +  datum.percentageOfPopulation ): datum.labelTextFirst",
          "as": "labelText"
        }
      ],
      "encoding": {
        "color": {
          "value": "white"
        },
        "text": {
          "value": {
            "expr": "DisplayValueMode==1? split(split(datum.labelText,'_')[1],'|')[0]: DisplayValueMode==2? split(datum.labelText,'|')[1]: DisplayValueMode==3? split(datum.labelText,'_')[1]: datum['__3__']=='0'? datum.labelText: split(datum.labelText,'_')[1]"
          }
        },
        "x": {
          "aggregate": "sum",
          "field": "__2__",
          "type": "quantitative",
          "stack": "normalize",
          "bandPosition": 0.55
        },
        "detail": {
          "field": "labelText"
        }
      }
    }
  ],
  "title": {
    "text": "Age and Gender Distribution of Population in 2000",
    "fontSize": 25,
    "fontWeight": "bold",
    "fontStyle": "arial"
  }
}
¡Contáctame!