Skip to content

Instantly share code, notes, and snippets.

@XavierGimenez
Last active February 17, 2023 03:11
Show Gist options
  • Save XavierGimenez/82ed6db55ade843c51ee062852b5313e to your computer and use it in GitHub Desktop.
Save XavierGimenez/82ed6db55ade843c51ee062852b5313e to your computer and use it in GitHub Desktop.
Sankey diagram with vega.js
<html>
<head>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vega@4"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@3"></script>
<style>
body {
font-family: sans-serif;
}
</style>
</head>
<body>
<div id="vis" style="height: 500px;"></div>
</body>
<script type="text/javascript">
vegaEmbed('#vis', 'vega-spec.json', {defaultStyle: true});
</script>
</html>
{
"$schema": "https://vega.github.io/schema/vega/v3.0.json",
"height": 2000,
"width": 700,
"data": [
{
"name": "rawData",
"values": [{
"organizationReference.legalName": "Autolinee Caivano",
"municipality": "Picerno",
"sum_amount": 251830
},
{
"organizationReference.legalName": "P&C Consorzio Stabile arl",
"municipality": "Pietragalla",
"sum_amount": 90856
},
{
"organizationReference.legalName": "Pepice Nicola",
"municipality": "San Fele",
"sum_amount": 119040
},
{
"organizationReference.legalName": "F.lli Martoccia",
"municipality": "Pietragalla",
"sum_amount": 213877
},
{
"organizationReference.legalName": "Daraio Pasquale",
"municipality": "Baragiano",
"sum_amount": 56298
},
{
"organizationReference.legalName": "Inno srls",
"municipality": "Ruvo del Monte",
"sum_amount": 6393
},
{
"organizationReference.legalName": "3N Costruzioni",
"municipality": "Pietrapertosa",
"sum_amount": 83291
},
{
"organizationReference.legalName": "Esteja Soc. Coop",
"municipality": "Tolve",
"sum_amount": 527485
},
{
"organizationReference.legalName": "C.C.D. Costruzioni e manutenzioni srl",
"municipality": "Baragiano",
"sum_amount": 239217
},
{
"organizationReference.legalName": "Costruzioni Messina Soc. Coop.",
"municipality": "Balvano",
"sum_amount": 70799
},
{
"organizationReference.legalName": "Tecno Edil di Varallo Salvatore",
"municipality": "Marsico Nuovo",
"sum_amount": 504209
},
{
"organizationReference.legalName": "NC Edil Pitturazioni di Nicola Cerverizzo",
"municipality": "Baragiano",
"sum_amount": 270017
},
{
"organizationReference.legalName": "Trasporto 2000 Soc. Coop. onlus",
"municipality": "Baragiano",
"sum_amount": 151482
},
{
"organizationReference.legalName": "Sud Costruzione srl dei F.lli De Carlo",
"municipality": "Campomaggiore",
"sum_amount": 92843
},
{
"organizationReference.legalName": "Presenza e Realtà nel territorio Soc. Coop. Onlus",
"municipality": "Muro Lucano",
"sum_amount": 702825
},
{
"organizationReference.legalName": "STCV srl",
"municipality": "Muro Lucano",
"sum_amount": 98000
},
{
"organizationReference.legalName": "Slem srl",
"municipality": "Avigliano",
"sum_amount": 515366
},
{
"organizationReference.legalName": "Cosvim Sooc. Cop.",
"municipality": "Castelmezzano",
"sum_amount": 416674
},
{
"organizationReference.legalName": "C.A.E.T. Soc. Coop. Arl",
"municipality": "Picerno",
"sum_amount": 198109
},
{
"organizationReference.legalName": "La Cantina srl",
"municipality": "Picerno",
"sum_amount": 268374
},
{
"organizationReference.legalName": "Duino Alberto Giuseppe",
"municipality": "Balvano",
"sum_amount": 162455
},
{
"organizationReference.legalName": "Deserta",
"municipality": "Marsico Nuovo",
"sum_amount": 433966
},
{
"organizationReference.legalName": "Dafne società cooperativa",
"municipality": "Pietragalla",
"sum_amount": 208275
},
{
"organizationReference.legalName": "Enrico Picone",
"municipality": "Marsico Nuovo",
"sum_amount": 81467
},
{
"organizationReference.legalName": "Ati Giannotti costruzioni srl - Guida Impianti",
"municipality": "Marsico Nuovo",
"sum_amount": 175417
},
{
"organizationReference.legalName": "Claps Costruzioni",
"municipality": "Brindisi Montagna",
"sum_amount": 137778
},
{
"organizationReference.legalName": "Ambiente srls",
"municipality": "Pignola",
"sum_amount": 62177
},
{
"organizationReference.legalName": "Nasce un Sorriso Soc. Cop. Sociale",
"municipality": "Pietragalla",
"sum_amount": 51734
},
{
"organizationReference.legalName": "TanDem di Antonella Potenza & C. snc",
"municipality": "Baragiano",
"sum_amount": 160083
},
{
"organizationReference.legalName": "Deserta",
"municipality": "Filiano",
"sum_amount": 119600
},
{
"organizationReference.legalName": "L.D.A. srl",
"municipality": "San Fele",
"sum_amount": 149440
},
{
"organizationReference.legalName": "Mancusimmobiliare srl",
"municipality": "Pignola",
"sum_amount": 1163149
},
{
"organizationReference.legalName": "Bagnuolo Donato & figli sas",
"municipality": "Balvano",
"sum_amount": 55078
},
{
"organizationReference.legalName": "Tennis Tecnica",
"municipality": "Tolve",
"sum_amount": 393718
},
{
"organizationReference.legalName": "Ecoriciclo srl",
"municipality": "Balvano",
"sum_amount": 100262
},
{
"organizationReference.legalName": "ABC Sport srl",
"municipality": "Vaglio Basilicata",
"sum_amount": 101475
},
{
"organizationReference.legalName": "Turlione srl",
"municipality": "Brindisi Montagna",
"sum_amount": 346798
},
{
"organizationReference.legalName": "3A servizi globali srl",
"municipality": "San Fele",
"sum_amount": 86988
},
{
"organizationReference.legalName": "Edil Bradanica Calcestruzzi srl unip.",
"municipality": "Balvano",
"sum_amount": 387157
},
{
"organizationReference.legalName": "Ciliberti Luigi",
"municipality": "Pietrapertosa",
"sum_amount": 12125
},
{
"organizationReference.legalName": "GESTIONE SERVIZI AMBIENTALI Soc. Coop.",
"municipality": "Ruvo del Monte",
"sum_amount": 163914
},
{
"organizationReference.legalName": "Pacella Pietro Impresa Costruzioni",
"municipality": "Muro Lucano",
"sum_amount": 202653
},
{
"organizationReference.legalName": "Deserta",
"municipality": "Baragiano",
"sum_amount": 273750
},
{
"organizationReference.legalName": "Silvano srl",
"municipality": "Pignola",
"sum_amount": 334304
},
{
"organizationReference.legalName": "2C Costruzioni srl",
"municipality": "Picerno",
"sum_amount": 50842
},
{
"organizationReference.legalName": "Catena Saverio",
"municipality": "Balvano",
"sum_amount": 147310
},
{
"organizationReference.legalName": "Deserta",
"municipality": "Ruvo del Monte",
"sum_amount": 180306
},
{
"organizationReference.legalName": "Andreani Tributi srl",
"municipality": "Avigliano",
"sum_amount": 325500
},
{
"organizationReference.legalName": "Mastroberti srl",
"municipality": "San Fele",
"sum_amount": 62000
},
{
"organizationReference.legalName": "Sieltec snc di Cuomo Antonio & Luigi",
"municipality": "Muro Lucano",
"sum_amount": 735920
},
{
"organizationReference.legalName": "Radano Impianti",
"municipality": "Balvano",
"sum_amount": 80576
},
{
"organizationReference.legalName": "ATI Erremme sas",
"municipality": "Baragiano",
"sum_amount": 144320
},
{
"organizationReference.legalName": "CS Cooperazione e Solidarietà",
"municipality": "Cancellara",
"sum_amount": 163043
},
{
"organizationReference.legalName": "GLC sas",
"municipality": "Ruvo del Monte",
"sum_amount": 26961
},
{
"organizationReference.legalName": "Co.Pro.Im. Sport srl",
"municipality": "San Chirico Nuovo",
"sum_amount": 87789
},
{
"organizationReference.legalName": "Ventre Michele & C. snc",
"municipality": "Marsico Nuovo",
"sum_amount": 118498
},
{
"organizationReference.legalName": "D.C. Services srl",
"municipality": "Pietragalla",
"sum_amount": 148462
},
{
"organizationReference.legalName": "Deserta",
"municipality": "San Fele",
"sum_amount": 94737
},
{
"organizationReference.legalName": "Consorzio Stabile Oscar Scarl",
"municipality": "Pietrapertosa",
"sum_amount": 606965
},
{
"organizationReference.legalName": "Autolinee Liscio srl",
"municipality": "Muro Lucano",
"sum_amount": 60437
},
{
"organizationReference.legalName": "Nasce un Sorriso Soc. Cop. Sociale",
"municipality": "Picerno",
"sum_amount": 115384
},
{
"organizationReference.legalName": "GLM ristorazione srl",
"municipality": "Pietrapertosa",
"sum_amount": 104016
},
{
"organizationReference.legalName": "Consorzio Stabile Oscar Scarl",
"municipality": "Tolve",
"sum_amount": 500319
},
{
"organizationReference.legalName": "Tangreda Nicola",
"municipality": "Picerno",
"sum_amount": 57115
},
{
"organizationReference.legalName": "Isolarte Costruzioni di Nicola Ruggiero",
"municipality": "Ruvo del Monte",
"sum_amount": 72344
},
{
"organizationReference.legalName": "Pevit srl",
"municipality": "Vaglio Basilicata",
"sum_amount": 162940
},
{
"organizationReference.legalName": "Promozione 80 Coop. Soc. arl",
"municipality": "Avigliano",
"sum_amount": 140000
},
{
"organizationReference.legalName": "Costruzioni Messina Soc. Coop.",
"municipality": "San Fele",
"sum_amount": 93181
},
{
"organizationReference.legalName": "Sabia Leonardo $ C. sas",
"municipality": "Brindisi Montagna",
"sum_amount": 98585
},
{
"organizationReference.legalName": "Pace Rocco Costruzioni srl",
"municipality": "Trivigno",
"sum_amount": 80421
},
{
"organizationReference.legalName": "Velluzzi Prospero",
"municipality": "San Fele",
"sum_amount": 94737
},
{
"organizationReference.legalName": "Eredi Galasso Vincenzo sas",
"municipality": "Albano di Lucania",
"sum_amount": 368944
},
{
"organizationReference.legalName": "Deserta",
"municipality": "Tolve",
"sum_amount": 35000
},
{
"organizationReference.legalName": "Turlione srl",
"municipality": "Baragiano",
"sum_amount": 150883
},
{
"organizationReference.legalName": "Selettra SpA",
"municipality": "Marsico Nuovo",
"sum_amount": 80469
},
{
"organizationReference.legalName": "Edil Bradanica Calcestruzzi srl unip.",
"municipality": "Tolve",
"sum_amount": 638108
}
],
"transform": [
{
"type": "formula",
"expr": "datum['organizationReference.legalName']",
"as": "stk1"
},
{
"type": "formula",
"expr": "datum.municipality",
"as": "stk2"
},
{
"type": "formula",
"expr": "datum.sum_amount",
"as": "size"
}],
"transform": [
{
"type": "formula",
"expr": "datum['organizationReference.legalName']",
"as": "stk1"
},
{
"type": "formula",
"expr": "datum.municipality",
"as": "stk2"
},
{
"type": "formula",
"expr": "datum.sum_amount",
"as": "size"
}
]
},
{
"name": "nodes",
"source": "rawData",
"transform": [
{
"type": "filter",
"expr": "!groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2"
},
{
"type": "formula",
"expr": "datum.stk1+datum.stk2",
"as": "key"
},
{
"type": "fold",
"fields": [
"stk1",
"stk2"
],
"as": [
"stack",
"grpId"
]
},
{
"type": "formula",
"expr": "datum.stack == 'stk1' ? datum.stk1+' '+datum.stk2 : datum.stk2+' '+datum.stk1",
"as": "sortField"
},
{
"type": "stack",
"groupby": [
"stack"
],
"sort": {
"field": "sortField",
"order": "descending"
},
"field": "size"
},
{
"type": "formula",
"expr": "(datum.y0+datum.y1)/2",
"as": "yc"
}
]
},
{
"name": "groups",
"source": "nodes",
"transform": [
{
"type": "aggregate",
"groupby": [
"stack",
"grpId"
],
"fields": [
"size"
],
"ops": [
"sum"
],
"as": [
"total"
]
},
{
"type": "stack",
"groupby": [
"stack"
],
"sort": {
"field": "grpId",
"order": "descending"
},
"field": "total"
},
{
"type": "formula",
"expr": "scale('y', datum.y0)",
"as": "scaledY0"
},
{
"type": "formula",
"expr": "scale('y', datum.y1)",
"as": "scaledY1"
},
{
"type": "formula",
"expr": "datum.stack == 'stk1'",
"as": "rightLabel"
},
{
"type": "formula",
"expr": "datum.total/domain('y')[1]",
"as": "percentage"
}
]
},
{
"name": "destinationNodes",
"source": "nodes",
"transform": [
{
"type": "filter",
"expr": "datum.stack == 'stk2'"
}
]
},
{
"name": "edges",
"source": "nodes",
"transform": [
{
"type": "filter",
"expr": "datum.stack == 'stk1'"
},
{
"type": "lookup",
"from": "destinationNodes",
"key": "key",
"fields": [
"key"
],
"as": [
"target"
]
},
{
"type": "linkpath",
"orient": "horizontal",
"shape": "diagonal",
"sourceY": {
"expr": "scale('y', datum.yc)"
},
"sourceX": {
"expr": "scale('x', 'stk1') + bandwidth('x')"
},
"targetY": {
"expr": "scale('y', datum.target.yc)"
},
"targetX": {
"expr": "scale('x', 'stk2')"
}
},
{
"type": "formula",
"expr": "range('y')[0]-scale('y', datum.size)",
"as": "strokeWidth"
},
{
"type": "formula",
"expr": "datum.size/domain('y')[1]",
"as": "percentage"
}
]
}
],
"scales": [
{
"name": "x",
"type": "band",
"range": "width",
"domain": [
"stk1",
"stk2"
],
"paddingOuter": 0.05,
"paddingInner": 0.95
},
{
"name": "y",
"type": "linear",
"range": "height",
"domain": {
"data": "nodes",
"field": "y1"
}
},
{
"name": "stackNames",
"type": "ordinal",
"range": [
"Organization",
"Municipality"
],
"domain": [
"stk1",
"stk2"
]
}
],
"axes": [
{
"orient": "bottom",
"scale": "x",
"domain" : false,
"ticks" : false,
"labelPadding" : 20,
"encode": {
"labels": {
"update": {
"text": {
"scale": "stackNames",
"field": "value",
"fontWeight" : "bold",
"fontSize" : 14
}
}
}
}
},
{
"orient": "top",
"scale": "x",
"domain" : false,
"ticks" : false,
"labelPadding" : 20,
"encode": {
"labels": {
"update": {
"text": {
"scale": "stackNames",
"field": "value",
"fontWeight" : "bold",
"fontSize" : 14
}
}
}
}
},
{
"orient": "left",
"scale": "y",
"labels" : false,
"domain" : false,
"ticks" : false
}
],
"marks": [
{
"type": "rect",
"from": {
"data": "nodes"
},
"encode": {
"enter": {
"stroke": {
"value": "#000"
},
"strokeWidth": {
"value": 1
},
"width": {
"scale": "x",
"band": 1
},
"x": {
"scale": "x",
"field": "stack"
},
"y": {
"field": "y0",
"scale": "y"
},
"y2": {
"field": "y1",
"scale": "y"
}
}
}
},
{
"type": "path",
"name": "edgeMark",
"from": {
"data": "edges"
},
"clip": true,
"encode": {
"update": {
"stroke": [
{
"test": "groupSelector && groupSelector.stack=='stk1'",
"value": "blue"
},
{
"value": "grey"
}
],
"strokeWidth": {
"field": "strokeWidth"
},
"path": {
"field": "path"
},
"strokeOpacity": {
"signal": "!groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3"
},
"zindex": {
"signal": "!groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0"
},
"tooltip": {
"signal": "datum.stk1 + ' → ' + datum.stk2 + ' ' + format(datum.size, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'"
}
},
"hover": {
"strokeOpacity": {
"value": 1
}
}
}
},
{
"type": "rect",
"name": "groupMark",
"from": {
"data": "groups"
},
"encode": {
"enter": {
"fill": {
"value": "grey"
},
"width": {
"scale": "x",
"band": 1
}
},
"update": {
"x": {
"scale": "x",
"field": "stack"
},
"y": {
"field": "scaledY0"
},
"y2": {
"field": "scaledY1"
},
"fillOpacity": {
"value": 0.6
},
"tooltip": {
"signal": "datum.grpId + ' ' + format(datum.total, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'"
}
},
"hover": {
"fillOpacity": {
"value": 1
}
}
}
},
{
"type": "text",
"from": {
"data": "groups"
},
"interactive": false,
"encode": {
"update": {
"x": {
"signal": "scale('x', datum.stack) + (datum.rightLabel ? bandwidth('x') + 8 : -8)"
},
"yc": {
"signal": "(datum.scaledY0 + datum.scaledY1)/2"
},
"align": {
"signal": "datum.rightLabel ? 'left' : 'right'"
},
"baseline": {
"value": "middle"
},
"fontWeight": {
"value": "bold"
},
"text": {
"signal": "abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : ''"
}
}
}
},
{
"type": "group",
"data": [
{
"name": "dataForShowAll",
"values": [
{}
],
"transform": [
{
"type": "filter",
"expr": "groupSelector"
}
]
}
],
"encode": {
"enter": {
"xc": {
"signal": "width/2"
},
"y": {
"value": 30
},
"width": {
"value": 80
},
"height": {
"value": 30
}
}
},
"marks": [
{
"type": "group",
"name": "groupReset",
"from": {
"data": "dataForShowAll"
},
"encode": {
"enter": {
"cornerRadius": {
"value": 6
},
"fill": {
"value": "#f5f5f5"
},
"stroke": {
"value": "#c1c1c1"
},
"strokeWidth": {
"value": 2
},
"height": {
"field": {
"group": "height"
}
},
"width": {
"field": {
"group": "width"
}
}
},
"update": {
"opacity": {
"value": 1
}
},
"hover": {
"opacity": {
"value": 0.7
}
}
},
"marks": [
{
"type": "text",
"interactive": false,
"encode": {
"enter": {
"xc": {
"field": {
"group": "width"
},
"mult": 0.5
},
"yc": {
"field": {
"group": "height"
},
"mult": 0.5,
"offset": 2
},
"align": {
"value": "center"
},
"baseline": {
"value": "middle"
},
"fontWeight": {
"value": "bold"
},
"text": {
"value": "Show All"
}
}
}
}
]
}
]
}
],
"signals": [
{
"name": "groupHover",
"value": {},
"on": [
{
"events": "@groupMark:mouseover",
"update": "{stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}"
},
{
"events": "mouseout",
"update": "{}"
}
]
},
{
"name": "groupSelector",
"value": false,
"on": [
{
"events": "@groupMark:click!",
"update": "{stack:datum.stack, stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}"
},
{
"events": [
{
"type": "click",
"markname": "groupReset"
},
{
"type": "dblclick"
}
],
"update": "false"
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment