Skip to content

Instantly share code, notes, and snippets.

@cpudney
Last active August 29, 2015 14:16
Show Gist options
  • Save cpudney/6d5ef950071e60b98122 to your computer and use it in GitHub Desktop.
Save cpudney/6d5ef950071e60b98122 to your computer and use it in GitHub Desktop.
Easter Sunday visualization

Easter Sunday Visualization

Easter Sunday is the Sunday following the first full moon after March 21. A few years ago I published this histogram of Easter Sunday dates. A friend then asked whether it was possible to visualize Easter Sunday dates from one year to the next. I've finally gotten around to producing such a visualization.

The time-series chart shows 1000 years of Easter Sunday dates. Use the mini-chart to focus the top chart on a specific range of years.

This chart allows us to see repeating patterns in the sequence of Easter Sunday dates. The patterns don't always repeat exactly but the human visual system is good at spotting similar patterns. For example, I was quickly able to match the sequence of Easter Sunday dates starting in 1943 and 2038 (April 25, 21, 17, 13, ...). The sequences are identical for almost 30 years. If you spot any others please let us know.

Implementing this Easter Sundays chart was really just an excuse for me to experiment with the NVD3 library, a collection of reusable charts built atop the excellent d3.js library. The beauty of NVD3 is that it allows you to create fairly complex, interactive charts with only a few lines of code.

Happy Easter!

This work is licensed under a Creative Commons Attribution 4.0 International License

var WIDTH = 1000;
var HEIGHT = 500;
var MARGIN = {top: 20, right: 20, bottom: 50, left: 80};
nv.addGraph(function () {
// This year.
var yyyy = new Date().getFullYear();
// Create focus line chart.
var chart = nv.models.lineWithFocusChart()
.width(WIDTH)
.height(HEIGHT)
.margin(MARGIN)
.showLegend(false)
.brushExtent([yyyy - 25, yyyy + 25]);
// Format y-axes.
var dateFormat = d3.time.format("%B %d");
chart.yAxis.axisLabel("Date").tickFormat(function (d) {
return dateFormat(new Date(d))
});
chart.y2Axis.tickValues([0, 1])
.tickFormat(function (d) {
return dateFormat(new Date(d))
});
chart.xAxis.axisLabel("Year");
// Tooltip.
chart.tooltipContent(function (key, x, y, e, graph) {
return '<h3>' + key + '</h3>' + '<p>' + y + ', ' + x + '</p>'
});
// Plot chart.
d3.select('#chart svg')
.datum(getEasterSundayData())
.transition().duration(500)
.call(chart)
.style({'width': WIDTH + 'px', 'height': HEIGHT + 'px'});
// Redraw chart to fit resized window.
nv.utils.windowResize(chart.update);
return chart;
});
const FIRST_YEAR = 1500;
const LAST_YEAR = 2500;
const SECONDS_PER_DAY = 86400000;
// Get the Easter Sunday data.
//
function getEasterSundayData() {
var data = [];
for (var year = FIRST_YEAR;
year <= LAST_YEAR;
year++) {
// Calculate Easter for year.
var date = easterSunday(year);
date.setYear(FIRST_YEAR);
data.push({x: year, y: date});
}
return [
{
values: data,
key: "Easter Sunday"
}
];
}
// Determines the date of Easter Sunday for a given year.
// See: http://javascript.about.com/library/bleaster1.htm
//
// year: the year to calculate.
//
function easterSunday(year) {
var a = year % 19;
var b = Math.floor(year / 100);
var c = year % 100;
var h = (19 * a + b - Math.floor(b / 4) - Math.floor((b - Math.floor((b + 8) / 25) + 1) / 3) + 15) % 30;
var k = (32 + 2 * (b % 4) + 2 * Math.floor(c / 4) - h - c % 4) % 7;
var m = Math.floor((a + 11 * h + 22 * k) / 451);
var month = Math.floor((h + k - 7 * m + 114) / 31) - 1;
var day = ((h + k - 7 * m + 114) % 31) + 1;
return new Date(year, month, day)
}
// Calculates the day of the year for a given date.
// date: the date to calculate.
//
function dayOfYear(date) {
return Math.ceil((date - new Date(date.getFullYear(), 0, 1)) / SECONDS_PER_DAY);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.min.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.7.0/nv.d3.min.js" charset="utf-8"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.7.0/nv.d3.min.css" rel="stylesheet" type="text/css">
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Easter Sunday</h1>
<span id="chart" class='with-3d-shadow with-transitions'>
<svg></svg>
</span>
<p><a target="_blank" rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License"
style="border-width:0"
src="https://i.creativecommons.org/l/by/4.0/80x15.png"/></a><br/><span
xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/InteractiveResource"
property="dct:title" rel="dct:type"><em>Easter Sunday Visualization</em></span> by <a target="_blank"
xmlns:cc="http://creativecommons.org/ns#" href="http://vislives.com" property="cc:attributionName"
rel="cc:attributionURL">Chris Pudney</a> is licensed under a <a rel="license" target="_blank"
href="http://creativecommons.org/licenses/by/4.0/">Creative
Commons Attribution 4.0 International License</a>.</p>
<script src="chart.js"></script>
</body>
</html>
text {
font: 12px sans-serif;
}
svg {
display: block;
}
body {
font: 75% Verdana, Helvetica, Arial, sans-serif;
background: White;
color: Black;
margin: 1em;
padding: 1em;
}
h1, h2, h3, h4, h5, h6 {
clear: left;
font: 100% Verdana, Helvetica, Arial, sans-serif;
margin: 0;
padding-left: 0.5em;
}
h1 {
font-size: 150%;
}
h2 {
font-size: 130%;
}
h3 {
font-size: 120%;
padding-left: 1.0em;
}
h4 {
font-size: 110%;
padding-left: 1.5em;
}
p {
text-align: justify;
line-height: 1.5em;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment