Historical Population Pyramid
A population pyramid, also called an “age-sex-pyramid” or “mirror bar chart”, is a graphical illustration that shows the distribution of various age groups in a population.
By combining and linking the pyramid with a stacked area chart we are able to create a compact and interactive visualization covering a lot of historical date.
Related tutorials
Demo source
<!-- Styles -->
<style>
#chartdiv {
width: 100%;
height: 500px;
}
</style>
<!-- Resources -->
<script src="https://cdn.amcharts.com/lib/5/index.js"></script>
<script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
<script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
<!-- Chart code -->
<script>
am5.ready(function() {
// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("chartdiv");
// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([
am5themes_Animated.new(root)
]);
root.numberFormatter.setAll({
numberFormat: "#,###.#as",
bigNumberPrefixes: [
{ "number": 1e+3, "suffix": "M" }
]
});
// Create wrapper for charts
var container = root.container.children.push(am5.Container.new(root, {
width: am5.p100,
height: am5.p100,
layout: root.horizontalLayout
}));
// ==========================================
// Load data
// ==========================================
// Init data storage
var currentYear = new Date().getFullYear().toString();
var sourceData = [];
// Load pyramid data
am5.net.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-160/un_population_age_groups.csv").then(function(result) {
// Parse data
var data = am5.CSVParser.parse(result.response, {
skipEmpty: true
});
// Process data
var processor = am5.DataProcessor.new(root, {
dateFields: ["col3"],
dateFormat: "yyyy",
numericFields: ["col5", "col6", "col7"]
});
processor.processMany(data);
sourceData = data;
// Update current data
var currentData = getCurrentData();
pyramidYAxis.data.setAll(currentData);
pyramidSeriesMale.data.setAll(currentData);
pyramidSeriesFemale.data.setAll(currentData);
});
// Load population data
am5.net.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-160/un_population.csv").then(function(result) {
// Parse data
var data = am5.CSVParser.parse(result.response, {
skipEmpty: true
});
// Process data
var processor = am5.DataProcessor.new(root, {
dateFields: ["col3"],
dateFormat: "yyyy",
numericFields: ["col4", "col5", "col6"]
});
processor.processMany(data);
am5.array.each(data, function(item) {
if (new Date(item.col3).getFullYear() == currentYear) {
item.lineSettings = {
strokeDasharray: [3, 3],
strokeOpacity: 0.3,
fillOpacity: 0.3
};
}
});
popSeriesMale.data.setAll(data);
popSeriesFemale.data.setAll(data);
});
function getCurrentData() {
var currentData = [];
am5.array.each(sourceData, function(row, i) {
var year = new Date(row.col3).getFullYear();
if (year == currentYear) {
if (row.col6 > 0) {
row.col6 *= -1;
}
currentData.push(row);
}
});
currentData.sort(function(a, b) {
var a1 = Number(a.col4.replace(/[^0-9]+.*$/, ""));
var b1 = Number(b.col4.replace(/[^0-9]+.*$/, ""));
if (a1 > b1) {
return 1;
}
else if (a1 < b1) {
return -1;
}
return 0;
});
return currentData;
}
function updateData() {
var data = getCurrentData();
var pyramidData = pyramidSeriesMale.data.values;
if (data.length == 0) {
return;
}
am5.array.each(pyramidData, function(row, i) {
var row = JSON.parse(JSON.stringify(pyramidData[i]));
if (!data[i]) {
row.col5 = 0;
row.col6 = 0;
}
else {
row.col5 = data[i].col5;
row.col6 = data[i].col6;
}
pyramidSeriesMale.data.setIndex(i, row);
pyramidSeriesFemale.data.setIndex(i, row);
});
// Set title
pyramidTitle.set("text", currentYear + "");
}
// ==========================================
// Pyramid chart
// ==========================================
// Create chart
// https://www.amcharts.com/docs/v5/charts/xy-chart/
var pyramidChart = container.children.push(
am5xy.XYChart.new(root, {
width: am5.p50,
panX: false,
panY: false,
wheelX: "none",
wheelY: "none",
layout: root.verticalLayout
})
);
// Add titles
var pyramidSubtitle = pyramidChart.children.unshift(am5.Label.new(root, {
text: " ",
x: am5.p50,
centerX: am5.p50
}));
var pyramidTitle = pyramidChart.children.unshift(am5.Label.new(root, {
text: currentYear + "",
fontSize: 20,
x: am5.p50,
centerX: am5.p50
}));
// Add labels
var maleLabel = pyramidChart.plotContainer.children.push(am5.Label.new(root, {
text: "Males",
fontSize: 20,
x: am5.p100,
y: 5,
centerX: am5.p100,
dx: -5,
fill: pyramidChart.get("colors").getIndex(0),
background: am5.RoundedRectangle.new(root, {
fill: am5.color(0xffffff),
fillOpacity: 0.5
})
}));
var femaleLabel = pyramidChart.plotContainer.children.push(am5.Label.new(root, {
text: "Females",
fontSize: 20,
y: 5,
x: 5,
fill: pyramidChart.get("colors").getIndex(1),
background: am5.RoundedRectangle.new(root, {
fill: am5.color(0xffffff),
fillOpacity: 0.5
})
}));
// Create axes
// https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
var pyramidXAxis = pyramidChart.xAxes.push(
am5xy.ValueAxis.new(root, {
min: -20000,
max: 20000,
renderer: am5xy.AxisRendererX.new(root, {
minGridDistance: 50,
strokeOpacity: 0.1
}),
tooltip: am5.Tooltip.new(root, {})
})
);
var yRenderer = am5xy.AxisRendererY.new(root, {
minGridDistance: 10,
minorGridEnabled: true
})
var pyramidYAxis = pyramidChart.yAxes.push(
am5xy.CategoryAxis.new(root, {
categoryField: "col4",
renderer: yRenderer
})
);
yRenderer.grid.template.setAll({
location: 1
})
// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
var pyramidSeriesMale = pyramidChart.series.push(
am5xy.ColumnSeries.new(root, {
xAxis: pyramidXAxis,
yAxis: pyramidYAxis,
categoryYField: "col4",
valueXField: "col5",
clustered: false,
tooltip: am5.Tooltip.new(root, {
labelText: "{valueX}"
})
})
);
var pyramidSeriesFemale = pyramidChart.series.push(
am5xy.ColumnSeries.new(root, {
xAxis: pyramidXAxis,
yAxis: pyramidYAxis,
categoryYField: "col4",
valueXField: "col6",
clustered: false,
tooltip: am5.Tooltip.new(root, {
labelText: "{valueX}"
})
})
);
// Add cursor
// https://www.amcharts.com/docs/v5/charts/xy-chart/cursor/
var pyradmidCursor = pyramidChart.set("cursor", am5xy.XYCursor.new(root, {
xAxis: pyramidXAxis,
yAxis: pyramidYAxis
}));
pyradmidCursor.lineX.set("visible", false);
pyradmidCursor.lineY.set("visible", false);
// ==========================================
// Population chart
// ==========================================
// Create chart
// https://www.amcharts.com/docs/v5/charts/xy-chart/
var popChart = container.children.push(
am5xy.XYChart.new(root, {
width: am5.p50,
panX: false,
panY: false,
wheelX: "none",
wheelY: "none",
layout: root.verticalLayout
})
);
// Add titles
var popSubtitle = popChart.children.unshift(am5.Label.new(root, {
text: "(hover to see breakdown)",
x: am5.p50,
centerX: am5.p50
}));
var popTitle = popChart.children.unshift(am5.Label.new(root, {
text: "U.S. population",
fontSize: 20,
x: am5.p50,
centerX: am5.p50
}));
// Create axes
// https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
var popXAxis = popChart.xAxes.push(
am5xy.DateAxis.new(root, {
maxDeviation: 0.1,
groupData: false,
baseInterval: { timeUnit: "year", count: 1 },
renderer: am5xy.AxisRendererX.new(root, {
minGridDistance: 40
}),
tooltip: am5.Tooltip.new(root, {})
})
);
var popYAxis = popChart.yAxes.push(
am5xy.ValueAxis.new(root, {
maxDeviation: 0.1,
renderer: am5xy.AxisRendererY.new(root, {
opposite: true
})
})
);
// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
var popSeriesMale = popChart.series.push(
am5xy.LineSeries.new(root, {
minBulletDistance: 10,
xAxis: popXAxis,
yAxis: popYAxis,
valueYField: "col4",
valueXField: "col3",
stacked: true
})
);
popSeriesMale.strokes.template.setAll({
strokeWidth: 2,
templateField: "lineSettings"
});
popSeriesMale.fills.template.setAll({
visible: true,
fillOpacity: 0.5,
templateField: "lineSettings"
});
var popSeriesFemale = popChart.series.push(
am5xy.LineSeries.new(root, {
minBulletDistance: 10,
xAxis: popXAxis,
yAxis: popYAxis,
valueYField: "col5",
valueXField: "col3",
stacked: true,
tooltip: am5.Tooltip.new(root, {
pointerOrientation: "horizontal",
labelText: "[bold]U.S. population in {valueX.formatDate()}[/]\n[font-size: 20]Male: {col4}\nFemale: {col5}"
})
})
);
popSeriesFemale.strokes.template.setAll({
strokeWidth: 2,
templateField: "lineSettings"
});
popSeriesFemale.fills.template.setAll({
visible: true,
fillOpacity: 0.5,
templateField: "lineSettings"
});
// Add cursor
// https://www.amcharts.com/docs/v5/charts/xy-chart/cursor/
var popCursor = popChart.set("cursor", am5xy.XYCursor.new(root, {
xAxis: popXAxis,
yAxis: popYAxis,
}));
popCursor.lineY.set("visible", false);
popCursor.events.on("cursormoved", function(ev) {
var x = ev.target.getPrivate("positionX");
currentYear = popXAxis.positionToDate(x).getFullYear();
updateData();
});
// Make stuff animate on load
// https://www.amcharts.com/docs/v5/concepts/animations/
popSeriesMale.appear(1000, 100);
popChart.appear(1000, 100);
}); // end am5.ready()
</script>
<!-- HTML -->
<div id="chartdiv"></div>