Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion range-ci-area-grouped/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ config = {
"xAxisTickFormat": ".0f",
"xAxisLabel": "x axis label",
"xDomain": [0, 160],
// either auto or a custom domain as an array e.g [0,100]
// either auto, auto-each or a custom domain as an array e.g [0,100]
"ciLegend": true,
"legendIntervalText": "Likely range (95% confidence interval)",
"legendEstimateText": "Estimated value",
Expand Down
49 changes: 22 additions & 27 deletions range-ci-area-grouped/data.csv
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
name,group,series,value,min,max
England,Group name 1,series 1,55.15726549,44.79967469,65.51485629
Wales,Group name 1,series 1,53,20,86
North East,Group name 2,series 1,38.29309225,21.04544788,55.54073662
North West,Group name 2,series 1,45.1373065,39.13579896,51.13881403
Yorkshire and The Humber,Group name 2,series 1,60,40,80
East Midlands,Group name 2,series 1,40.56442504,29.36591976,51.76293031
West Midlands,Group name 2,series 1,60.02733084,39.86949419,80.18516748
East,Group name 2,series 1,50.79985161,17.64884078,83.95086243
London,Group name 2,series 1,50.89339154,43.91069538,57.87608769
South West,Group name 2,series 1,29.68960375,9.379207492,50
South East,Group name 2,series 1,35.07397305,8.973142806,61.1748033
Another name,Group name 3,series 1,45.19066503,24.79415817,65.58717189
And another,Group name 3,series 1,43,33,53
England,Group name 1,series 2,110.1572655,99.79967469,120.5148563
Wales,Group name 1,series 2,120,90,150
North East,Group name 2,series 2,93.29309224,76.04544788,110.5407366
North West,Group name 2,series 2,100.1373065,94.13579896,106.138814
Yorkshire and The Humber,Group name 2,series 2,118.1506859,85.57469086,150.726681
East Midlands,Group name 2,series 2,95.56442503,84.36591976,106.7629303
West Midlands,Group name 2,series 2,115.0273308,94.86949419,135.1851675
East,Group name 2,series 2,124.4754312,110,138.9508624
London,Group name 2,series 2,105.8933915,98.91069538,112.8760877
South West,Group name 2,series 2,92.14356115,64.37920749,119.9079148
South East,Group name 2,series 2,90.07397306,63.97314281,116.1748033
Another name,Group name 3,series 2,100.190665,79.79415817,120.5871719
And another,Group name 3,series 2,97,93,101
All,Group name 1,all,53,20,86
Female,Sex,Straight or Heterosexual,14.29,13.59,14.98
Female,Sex,LGB+,50.15,44.21,56.08
Female,Sex,Gay or Lesbian,36.96,29.27,46.04
Female,Sex,Bisexual,63.18,52.58,73.78
Female,Sex,Other sexual orientation,46.04,30.88,65.56
Male,Sex,Straight or Heterosexual,28.2,27.09,29.3
Male,Sex,LGB+,67.13,59.95,74.31
Male,Sex,Gay or Lesbian,67.66,58.43,76.88
Male,Sex,Bisexual,71.48,57.32,88.04
Male,Sex,Other sexual orientation,55.65,35.67,82.62
16 to 24 years,Age on Census Day,Straight or Heterosexual,12.33,10.96,13.7
16 to 24 years,Age on Census Day,LGB+,28.72,22.84,35.67
25 to 34 years,Age on Census Day,Straight or Heterosexual,17.15,15.97,18.33
25 to 34 years,Age on Census Day,LGB+,48.4,41.01,55.8
35 to 44 years,Age on Census Day,Straight or Heterosexual,29.38,27.83,30.92
35 to 44 years,Age on Census Day,LGB+,84.36,71.74,96.98
45 to 54 years,Age on Census Day,Straight or Heterosexual,35.57,33.9,37.25
45 to 54 years,Age on Census Day,LGB+,95.48,79.38,111.58
55 to 64 years,Age on Census Day,Straight or Heterosexual,20.78,19.45,22.1
55 to 64 years,Age on Census Day,LGB+,70.48,54.1,90.18
65 years and over,Age on Census Day,Straight or Heterosexual,12.26,11.29,13.22
65 years and over,Age on Census Day,LGB+,41.79,25.33,64.82
130 changes: 77 additions & 53 deletions range-ci-area-grouped/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ let graphic = d3.select('#graphic');
let legend = d3.select('#legend');
let pymChild = null;
let graphicData, size, groups, xDomain, divs, svgs, charts;
const rectHeight = 16

function drawGraphic() {

Expand All @@ -16,28 +17,71 @@ function drawGraphic() {

groups = d3.groups(graphicData, (d) => d.group);

if (config.xDomain == 'auto') {
let min = 1000000;
let max = 0;
for (let i = 2; i < graphicData.columns.length; i++) {
min = d3.min([
min,
d3.min(graphicData, (d) => +d[graphicData.columns[i]])
]);
max = d3.max([
max,
d3.max(graphicData, (d) => +d[graphicData.columns[i]])
]);
}
xDomain = [min, max];
function computeDomain(data, columns) {
return d3.extent(data.flatMap(d => columns.filter(d => d != 'name' && d != 'group').map(col => +d[col])))
}


if (config.xDomain === 'auto') {
xDomain = computeDomain(graphicData, graphicData.columns)
} else if (config.xDomain === 'auto-each') {
xDomain = Object.fromEntries(
groups.map(([name, groupData]) => [name, computeDomain(groupData, graphicData.columns)
])
)
} else {
xDomain = config.xDomain;
}

//set up scales
const x = d3.scaleLinear().range([0, chartWidth]).domain(xDomain);

const xScales = config.xDomain === "auto-each" ? Object.fromEntries(
groups.map(([name]) => [
name,
d3.scaleLinear().range([0, chartWidth]).domain(xDomain[name]).nice()
])
) : {
global: d3.scaleLinear().range([0, chartWidth]).domain(xDomain).nice()
}

function getX(groupName) {
return config.xDomain === "auto-each"
? xScales[groupName]
: xScales.global;
}

const series = [...new Set(graphicData.map(d => d.series))]

const seriesByGroupAndName = {};

groups.forEach(([groupName, groupData]) => {
seriesByGroupAndName[groupName] = {};

d3.group(groupData, d => d.name).forEach((rows, name) => {
seriesByGroupAndName[groupName][name] =
[...new Set(rows.map(d => d.series))];
})
})

const clusterOffset = {};

Object.entries(seriesByGroupAndName).forEach(([groupName, names]) => {
clusterOffset[groupName] = {};

Object.entries(names).forEach(([name, seriesList]) => {
const spread = (seriesList.length - 1) * rectHeight;

clusterOffset[groupName][name] = d3.scalePoint().domain(seriesList).range([-spread / 2, spread / 2])
});
});

const yWithCluster = (d) => {
const baseY = groups.find(g => g[0] === d.group)[3](d.name);

return config.clustered ? baseY + clusterOffset[d.group][d.name](d.series) : baseY;
}

const colour = d3
.scaleOrdinal()
.range(config.colourPalette)
Expand All @@ -58,11 +102,6 @@ function drawGraphic() {
d[4] = d3.axisLeft(d[3]).tickSize(0).tickPadding(10);
});

//set up xAxis generator
let xAxis = d3.axisBottom(x)
.ticks(config.xAxisTicks[size])
.tickFormat(d3.format(config.xAxisTickFormat));

divs = graphic.selectAll('div.categoryLabels').data(groups).join('div');

if (groups.length > 1) { divs.append('p').attr('class', 'groupLabels').html((d) => d[0]) }
Expand All @@ -75,6 +114,8 @@ function drawGraphic() {
})

charts.each(function (d) {
if (config.xDomain === "auto-each") { let xAxis = d[6] }

d3.select(this)
.append('g')
.attr('class', 'y axis')
Expand Down Expand Up @@ -108,6 +149,14 @@ function drawGraphic() {
.attr('transform', (d) => 'translate(0,' + d[2] + ')')
.attr('class', 'x axis')
.each(function () {
const x = getX(d[0]);

const xAxis = d3.axisBottom(x)
.ticks(config.xAxisTicks[size])
.tickFormat(d3.format(config.xAxisTickFormat));



d3.select(this)
.call(xAxis.tickSize(-d[2]))
.selectAll('line')
Expand All @@ -119,23 +168,15 @@ function drawGraphic() {
});
});

const rectHeight = 16


charts
.selectAll("rect")
.data((d) => d[1])
.join("rect")
.attr("x", d => x(Number(d.min)))
.attr("y", (d, i) => {
const baseY = groups.filter((e) => e[0] == d.group)[0][3](d.name) - rectHeight / 2;
// if clustered is true, move series 0 up 10, series 1 down 10 only
if (config.clustered === true) {
if (d.series === series[0]) return baseY - 10;
if (d.series === series[1]) return baseY + 10;
}
return baseY;
})
.attr("width", d => Math.abs(x(Number(d.max)) - x(Number(d.min))))
.attr("x", d => getX(d.group)(+d.min))
.attr("y", d => yWithCluster(d) - rectHeight / 2)
.attr("width", d => Math.abs(getX(d.group)(Number(d.max)) - getX(d.group)(Number(d.min))))
.attr("height", rectHeight)
.attr("fill", d => colour(d.series))
.attr("opacity", 0.65)
Expand All @@ -145,29 +186,11 @@ function drawGraphic() {
.selectAll('rect.value')
.data((d) => d[1])
.join('rect')
.attr('x', (d) => x(d.value) - 5)
.attr('y', (d) => {
const baseY = groups.filter((f) => f[0] == d.group)[0][3](d.name) - 5;
// if clustered is true, move series 0 up 10, series 1 down 10 only
if (config.clustered === true) {
const series = [...new Set(graphicData.map(d => d.series))];
if (d.series === series[0]) return baseY - 10;
if (d.series === series[1]) return baseY + 10;
}
return baseY;
})
.attr('x', (d) => getX(d.group)(d.value) - 5)
.attr('y', d => yWithCluster(d) - 5)
.attr('width', 10)
.attr('height', 10)
.attr('transform', (d) => {
const baseY = groups.filter((f) => f[0] == d.group)[0][3](d.name);
let y = baseY;
if (config.clustered === true) {
const series = [...new Set(graphicData.map(d => d.series))];
if (d.series === series[0]) y = baseY - 10;
if (d.series === series[1]) y = baseY + 10;
}
return `rotate(45 ${x(d.value)} ${y})`;
})
.attr('transform', d => `rotate(45 ${getX(d.group)(d.value)} ${yWithCluster(d)})`)
.attr("fill", "white")
.attr("stroke-width", "2px")
.attr('stroke', d => colour(d.series))
Expand Down Expand Up @@ -210,7 +233,8 @@ function drawGraphic() {
.attr('class', (d, i) => config.useDiamonds && i == 0 ? 'legend--icon--diamond' : 'legend--icon--circle')
.style('background-color', function (d) {
return d[1];
});
})
.style('opacity',0.65);

legenditem
.append('div')
Expand Down