I have to create a visualisation with D3 like in the figure bellow, time-series, stacked bars:
The dataset comes from a consultant in this format, nested arrays of json objects:
[{
"period": "6/2017",
"data": [
{
"partner": "TR",
"val": 5201888581
},
{
"partner": "CH",
"val": 8509470105
},
{
"partner": "RU",
"val": 10677690328
},
{
"partner": "GB",
"val": 16086915825
},
{
"partner": "US",
"val": 17817589838
},
{
"partner": "CN",
"val": 26120939253
},
{
"partner": "TOTAL",
"val": 145385348496
}
]
},
{
"period": "7/2017",
"data": [
{
"partner": "TR",
"val": 4832746886
},
{
"partner": "CH",
"val": 8194483975
},
{
"partner": "RU",
"val": 10082530447
},
{
"partner": "US",
"val": 15251181551
},
{
"partner": "GB",
"val": 15515343080
},
{
"partner": "CN",
"val": 27480148190
},
{
"partner": "TOTAL",
"val": 142118881451
}
]
},
{
"period": "8/2017",
"data": [
{
"partner": "TR",
"val": 4827335758
},
{
"partner": "CH",
"val": 7087004314
},
{
"partner": "RU",
"val": 10372167568
},
{
"partner": "GB",
"val": 14555013893
},
{
"partner": "US",
"val": 16838219917
},
{
"partner": "CN",
"val": 27876046083
},
{
"partner": "TOTAL",
"val": 143363806063
}
]
}
...
]
More precisely, each object contains the period and an sub-array of objects, country code and value:
{
period,
data: [{country,value},{country,value},{country,value}...]
}
The data arrays contain different number of objects (country records), from 3 to 10 and different countries (they can be any of the UN countries).
I tried to use the standard layouts in D3 (v5) such as "stack" but, as you see, my data does not match the required data format in d3 layout, i.e. tabular data like:
time field1 field2 field3
t1 v11 v12 v13
t2 v21 v22 v23
...
I don't know how to manage this with my data, please help. Tx.
LE: I tried also with chart.js but failed due to similar issues.
As the number of countries varies, you could create your own function to append the countries' rect elements. The example use d3.selection.each to call the new function, and within this function, a new data array is created with the y offsets for each rect.
let data = [{
"period": "6/2017",
"data": [
{
"partner": "TR",
"val": 5201888581
},
{
"partner": "CH",
"val": 8509470105
},
{
"partner": "RU",
"val": 10677690328
},
{
"partner": "GB",
"val": 16086915825
},
{
"partner": "US",
"val": 17817589838
},
{
"partner": "CN",
"val": 26120939253
},
{
"partner": "TOTAL",
"val": 145385348496
}
]
},
{
"period": "7/2017",
"data": [
{
"partner": "TR",
"val": 4832746886
},
{
"partner": "CH",
"val": 8194483975
},
{
"partner": "RU",
"val": 10082530447
},
{
"partner": "US",
"val": 15251181551
},
{
"partner": "GB",
"val": 15515343080
},
{
"partner": "CN",
"val": 27480148190
},
{
"partner": "TOTAL",
"val": 142118881451
}
]
},
{
"period": "8/2017",
"data": [
{
"partner": "TR",
"val": 4827335758
},
{
"partner": "CH",
"val": 7087004314
},
{
"partner": "RU",
"val": 10372167568
},
{
"partner": "GB",
"val": 14555013893
},
{
"partner": "US",
"val": 16838219917
},
{
"partner": "CN",
"val": 27876046083
},
{
"partner": "TOTAL",
"val": 143363806063
}
]
}]
let width = 500
let height = 500
let margin = 50
let x = d3.scaleBand()
.domain(["6/2017","7/2017","8/2017"])
.range([0, width])
.padding(0.3)
let y = d3.scaleLinear()
.domain([0, 145385348496])
.range([height, 0])
var svg = d3.select("body").append("svg")
.attr("width", width + margin + margin)
.attr("height", height + margin + margin)
var g = svg.append("g")
.attr("transform", "translate(" + margin + ", " + margin + ")")
var periods = g.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("class", "period")
.attr("transform", function(d){
return "translate(" + x(d.period) + ", " + 0 + ")"
})
.each(appendCountries)
function appendCountries(period, i){
let countries = []
let offset = 0
let colour = d3.scaleOrdinal(d3.schemeAccent);
period.data.forEach(function(c){
if (c.partner != "TOTAL") { //assuming that total should not be included
offset = offset + c.val
let obj = {}
obj.y = offset
obj.val = c.val
obj.partner = c.partner
countries.push(obj)
}
})
let rects = d3.select(this).selectAll("rect")
.data(countries)
.enter()
.append("rect")
.attr("width", x.bandwidth)
.attr("x", 0)
.attr("y", d => y(d.y))
.attr("height", d => height - y(d.val))
.style("fill", d => colour(d.partner))
.style("stroke", "white")
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>