Skip to content

Instantly share code, notes, and snippets.

@Saigesp Saigesp/.block
Last active Oct 19, 2018

Embed
What would you like to do?
D3v4 calendar heatmap
0b0995536ec3e691cec6725783870a70

D3js Calendar

D3 implementation of heatmap calendar

See the demo and more charts from d3graphs repository

Features:

  • Object oriented approach
  • Responsive

Requires:

  • D3 v4+
  • d3-scale-chromatic

Default options:

{
    'margin': {'top': 30, 'right': 30, 'bottom': 10, 'left': 50},
    'key': 'key',
    'datefield': 'date',
    'dateformat': '%d-%m-%Y',
    'title': false,
    'source': false,
    'rectsize': 10,
    'colorScale': d3.interpolateRdBu,
    'emptycolor': '#EEE',
    'year': false,
    'mondaystart': false,
    'weekdayformat': '%a',
    'monthformat': '%b',
}
fecha precio
2017-09-25 59.48
2017-09-26 59.30125
2017-09-27 60.84125
2017-09-28 60.53667
2017-09-29 60.19
2017-09-30 58.57417
2017-10-01 57.135
2017-10-02 63.28083
2017-10-03 64.80458
2017-10-04 66.65792
2017-10-05 64.78167
2017-10-06 60.70958
2017-10-07 60.41
2017-10-08 60.9175
2017-10-09 69.06417
2017-10-10 71.16
2017-10-11 69.78958
2017-10-12 65.53167
2017-10-13 68.39125
2017-10-14 65.13333
2017-10-15 56.72583
2017-10-16 62.44292
2017-10-17 64.63542
2017-10-18 66.30875
2017-10-19 66.49458
2017-10-20 61.65792
2017-10-21 59.44542
2017-10-22 60.3825
2017-10-23 73.64
2017-10-24 68.1875
2017-10-25 67.38458
2017-10-26 66.77875
2017-10-27 62.90375
2017-10-28 57.53542
2017-10-29 56.57292
2017-10-30 58.91208
2017-10-31 64.1075
2017-11-01 56.04208
2017-11-02 63.52417
2017-11-03 65.31625
2017-11-04 59.22458
2017-11-05 48.95375
2017-11-06 61.88417
2017-11-07 64.36208
2017-11-08 63.40292
2017-11-09 63.63958
2017-11-10 64.79125
2017-11-11 61.90958
2017-11-12 58.10375
2017-11-13 59.16917
2017-11-14 64.09208
2017-11-15 71.22208
2017-11-16 73.51292
2017-11-17 72.23375
2017-11-18 65.1725
2017-11-19 65.405
2017-11-20 73.40333
2017-11-21 69.88917
2017-11-22 65.31417
2017-11-23 64.29042
2017-11-24 67.42208
2017-11-25 61.62542
2017-11-26 62.34375
2017-11-27 72.67542
2017-11-28 75.18958
2017-11-29 73.9125
2017-11-30 75.65917
2017-12-01 72.17583
2017-12-02 66.95875
2017-12-03 67.36833
2017-12-04 78.87833
2017-12-05 83.19542
2017-12-06 81.07625
2017-12-07 77.505
2017-12-08 72.20958
2017-12-09 73.88208
2017-12-10 55.31042
2017-12-11 59.01208
2017-12-12 75.75083
2017-12-13 75.16167
2017-12-14 67.87417
2017-12-15 66.775
2017-12-16 63.52875
2017-12-17 62.45208
2017-12-18 71.46417
2017-12-19 70.11125
2017-12-20 73.19958
2017-12-21 72.01292
2017-12-22 66.47875
2017-12-23 61.15083
2017-12-24 60.00125
2017-12-25 55.835
2017-12-26 53.99542
2017-12-27 50.42292
2017-12-28 56.53
2017-12-29 55.26917
2017-12-30 45.61958
2017-12-31 26.36833
2018-01-01 21.40917
2018-01-02 47.58958
2018-01-03 47.44417
2018-01-04 45.01625
2018-01-05 51.75917
2018-01-06 52.21
2018-01-07 53.41
2018-01-08 70.91375
2018-01-09 64.2925
2018-01-10 59.56417
2018-01-11 58.23625
2018-01-12 65.6725
2018-01-13 59.56375
2018-01-14 58.74042
2018-01-15 63.13917
2018-01-16 57.845
2018-01-17 59.56542
2018-01-18 63.2925
2018-01-19 60.84833
2018-01-20 52.7475
2018-01-21 50.31708
2018-01-22 59.61125
2018-01-23 63.42375
2018-01-24 61.78375
2018-01-25 58.71125
2018-01-26 55.76792
2018-01-27 54.22
2018-01-28 54.50208
2018-01-29 60.01542
2018-01-30 59.79792
2018-01-31 62.38958
2018-02-01 59.76083
2018-02-02 57.64917
2018-02-03 54.3125
2018-02-04 58.20583
2018-02-05 61.08042
2018-02-06 60.60708
2018-02-07 62.97958
2018-02-08 68.74792
2018-02-09 66.22833
2018-02-10 56.59042
2018-02-11 54.50417
2018-02-12 62.10125
2018-02-13 57.49333
2018-02-14 54.97167
2018-02-15 61.69458
2018-02-16 65.68958
2018-02-17 55.9175
2018-02-18 56.75583
2018-02-19 62.31458
2018-02-20 58.45958
2018-02-21 56.325
2018-02-22 59.84583
2018-02-23 64.05667
2018-02-24 63.97458
2018-02-25 62.61917
2018-02-26 65.68417
2018-02-27 70.86
2018-02-28 64.765
2018-03-01 58.13208
2018-03-02 57.7675
2018-03-03 52.30083
2018-03-04 50.90125
2018-03-05 58.33542
2018-03-06 57.21833
2018-03-07 58.5875
2018-03-08 55.74292
2018-03-09 51.05458
2018-03-10 38.96875
2018-03-11 16.96375
2018-03-12 47.78792
2018-03-13 57.36167
2018-03-14 46.88875
2018-03-15 43.40625
2018-03-16 54.22792
2018-03-17 53.3725
2018-03-18 47.78375
2018-03-19 53.49833
2018-03-20 47.10292
2018-03-21 54.03292
2018-03-22 61.23
2018-03-23 53.34208
2018-03-24 32.21417
2018-03-25 50.23708
2018-03-26 57.79708
2018-03-27 53.70417
2018-03-28 50.71125
2018-03-29 34.77042
2018-03-30 14.8975
2018-03-31 25.58375
2018-04-01 35.4025
2018-04-02 37.92917
2018-04-03 38.96125
2018-04-04 41.39542
2018-04-05 54.22125
2018-04-06 46.1925
2018-04-07 48.89417
2018-04-08 42.67458
2018-04-09 56.48667
2018-04-10 53.5325
2018-04-11 48.18708
2018-04-12 57.4625
2018-04-13 65.46375
2018-04-14 55.47958
2018-04-15 46.56292
2018-04-16 56.24625
2018-04-17 60.07125
2018-04-18 58.03417
2018-04-19 53.18958
2018-04-20 48.11875
2018-04-21 45.94083
2018-04-22 49.445
2018-04-23 57.49583
2018-04-24 55.43625
2018-04-25 52.76875
2018-04-26 55.2425
2018-04-27 55.77583
2018-04-28 50.20708
2018-04-29 35.62958
2018-04-30 50.26625
2018-05-01 47.72792
2018-05-02 52.38708
2018-05-03 49.42083
2018-05-04 49.57917
2018-05-05 52.075
2018-05-06 56.12042
2018-05-07 64.19292
2018-05-08 64.17167
2018-05-09 61.815
2018-05-10 61.26083
2018-05-11 64.7925
2018-05-12 49.4975
2018-05-13 44.61292
2018-05-14 59.58542
2018-05-15 57.5475
2018-05-16 58.90542
2018-05-17 61.21125
2018-05-18 64.30958
2018-05-19 66.25875
2018-05-20 65.64625
2018-05-21 69.52542
2018-05-22 68.92375
2018-05-23 70.91125
2018-05-24 69.99042
2018-05-25 70.28458
2018-05-26 66.88958
2018-05-27 66.5125
2018-05-28 68.2575
2018-05-29 70.7525
2018-05-30 70.32708
2018-05-31 68.77583
2018-06-01 68.60667
2018-06-02 67.04
2018-06-03 60.57167
2018-06-04 63.60875
2018-06-05 64.84167
2018-06-06 67.6225
2018-06-07 67.81875
2018-06-08 66.96792
2018-06-09 63.93542
2018-06-10 61.91542
2018-06-11 64.67
2018-06-12 60.95083
2018-06-13 59.58417
2018-06-14 63.56292
2018-06-15 62.70375
2018-06-16 58.19
2018-06-17 52.17292
2018-06-18 61.04458
2018-06-19 62.5425
2018-06-20 67.45125
2018-06-21 67.32
2018-06-22 65.67833
2018-06-23 62.29792
2018-06-24 64.36833
2018-06-25 68.03625
2018-06-26 69.31042
2018-06-27 68.78917
2018-06-28 68.39708
2018-06-29 68.28417
2018-06-30 64.07083
2018-07-01 61.4225
2018-07-02 67.68458
2018-07-03 67.75958
2018-07-04 67.20625
2018-07-05 66.38125
2018-07-06 67.32708
2018-07-07 65.185
2018-07-08 63.14542
2018-07-09 67.03292
2018-07-10 66.56083
2018-07-11 67.36292
2018-07-12 69.66208
2018-07-13 69.42292
2018-07-14 66.69833
2018-07-15 64.00042
2018-07-16 67.505
2018-07-17 68.68583
2018-07-18 69.4425
2018-07-19 69.98208
2018-07-20 68.16417
2018-07-21 66.66542
2018-07-22 64.61833
2018-07-23 69.26083
2018-07-24 70.68083
2018-07-25 70.78792
2018-07-26 72.17125
2018-07-27 70.59708
2018-07-28 68.72833
2018-07-29 64.63125
2018-07-30 71.86417
2018-07-31 72.11708
2018-08-01 68.66208
2018-08-02 71.08458
2018-08-03 73.98458
2018-08-04 70.46833
2018-08-05 69.67292
2018-08-06 73.94583
2018-08-07 70.325
2018-08-08 70.905
2018-08-09 68.51708
2018-08-10 68.85333
2018-08-11 68.47083
2018-08-12 67.23208
2018-08-13 70.18833
2018-08-14 68.16958
2018-08-15 66.16958
2018-08-16 71.34417
2018-08-17 67.19875
2018-08-18 63.16583
2018-08-19 63.73958
2018-08-20 69.09708
2018-08-21 72.55917
2018-08-22 75.0325
2018-08-23 72.9925
2018-08-24 70.49583
2018-08-25 68.60875
2018-08-26 70.59958
2018-08-27 72.65417
2018-08-28 73.905
2018-08-29 76.00208
2018-08-30 75.08208
2018-08-31 73.62542
2018-09-01 69.92958
2018-09-02 70.97917
2018-09-03 77.38542
2018-09-04 78.18417
2018-09-05 80.74
2018-09-06 76.53167
2018-09-07 74.82042
2018-09-08 75.0075
2018-09-09 76.85042
2018-09-10 79.18833
2018-09-11 77.33875
2018-09-12 80.61042
2018-09-13 79.97917
2018-09-14 79.01958
2018-09-15 75.77708
2018-09-16 75.70375
2018-09-17 78.055
2018-09-18 80.39458
2018-09-19 80.92042
2018-09-20 79.48125
2018-09-21 79.18917
2018-09-22 82.83583
2018-09-23 79.09667
2018-09-24 72.37
2018-09-25 71.14063
class Calendar{
constructor(selection, data, config = {}) {
let self = this;
this.selection = selection;
this.data = data;
// Graph configuration
this.cfg = {
'margin': {'top': 30, 'right': 30, 'bottom': 10, 'left': 50},
'key': 'key',
'datefield': 'date',
'dateformat': '%d-%m-%Y', // https://github.com/d3/d3-time-format/blob/master/README.md#locale_format
'title': false,
'source': false,
'rectsize': 10,
'colorScale': d3.interpolateRdBu,
'emptycolor': '#EEE',
'year': false,
'mondaystart': false,
'weekdayformat': '%a',
'monthformat': '%b',
};
Object.keys(config).forEach(function(key) {
if(config[key] instanceof Object && config[key] instanceof Array === false){
Object.keys(config[key]).forEach(function(sk) {
self.cfg[key][sk] = config[key][sk];
});
} else self.cfg[key] = config[key];
});
this.cfg.width = parseInt(this.selection.node().offsetWidth) - this.cfg.margin.left - this.cfg.margin.right,
this.cfg.height = parseInt(this.selection.node().offsetHeight)- this.cfg.margin.top - this.cfg.margin.bottom;
this.extentdates = d3.extent(this.data, function(d){ return d[self.cfg.datefield]});
this.year = self.cfg.year ? self.cfg.year : + self.extentdates[0].substr(0,4);
this.cfg.rectsize = this.cfg.width/53 < this.cfg.height/7 ? this.cfg.width/53 : this.cfg.height/7;
this.dayCalc = this.cfg.mondaystart ? function(d) { return (d.getDay() + 6) % 7; } : function(d) { return d.getDay(); }
this.weekCalc = this.cfg.mondaystart ? d3.timeFormat("%W") : d3.timeFormat("%U");
this.cScale = d3.scaleSequential(this.cfg.colorScale);
this.weekDay = d3.timeFormat(self.cfg.weekdayformat);
this.monthName = d3.timeFormat(self.cfg.monthformat);
this.initGraph();
}
initGraph() {
var self = this;
this.cScale.domain(d3.extent(this.data, function(d){ return +d[self.cfg.key]}).reverse())
this.svg = this.selection.append('svg')
.attr("class", "chart calendar")
.attr("viewBox", "0 0 "+(this.cfg.width + this.cfg.margin.left + this.cfg.margin.right)+" "+(this.cfg.height + this.cfg.margin.top + this.cfg.margin.bottom))
.attr("width", this.cfg.width + this.cfg.margin.left + this.cfg.margin.right)
.attr("height", this.cfg.height + this.cfg.margin.top + this.cfg.margin.bottom);
this.g = this.svg.append("g")
.attr("transform", "translate(" + (self.cfg.margin.left) + "," + (self.cfg.margin.top) + ")");
// TITLE
if(self.cfg.title){
this.svg.append('text')
.attr('class', 'title label')
.attr('text-anchor', 'middle')
.attr('transform', 'translate('+ (self.cfg.width/2) +',20)')
.text(self.cfg.title)
}
// SOURCE
if(self.cfg.source){
this.svg.append('text')
.attr('class', 'source label')
.attr('transform', 'translate('+ (self.cfg.margin.left) +','+(self.cfg.height + self.cfg.margin.top + self.cfg.margin.bottom - 5)+')')
.html(self.cfg.source)
}
this.rects = this.g.selectAll("rect")
.data(function(d) { return d3.timeDays(new Date(self.year, 0, 1), new Date(self.year + 1, 0, 1)); })
.enter().append("rect")
.attr("width", self.cfg.rectsize)
.attr("height", self.cfg.rectsize)
.attr("x", function(d) { return self.weekCalc(d) * self.cfg.rectsize; })
.attr("y", function(d) { return self.dayCalc(d) * self.cfg.rectsize; })
.attr("fill", self.cfg.emptycolor)
var nesteddata = d3.nest()
.key(function(d) { return d[self.cfg.datefield]; })
.rollup(function(d) { return +d[0][self.cfg.key]; })
.object(this.data);
this.rects.filter(function(d) { return d.yyyymmdd() in nesteddata; })
.attr("fill", function(d) { return self.cScale(nesteddata[d.yyyymmdd()]); })
.append("title")
.text(function(d) { return d.yyyymmdd() + ": " + nesteddata[d.yyyymmdd()]; });
self.drawLabels();
}
drawLabels() {
var self = this;
var j_start = this.cfg.mondaystart ? 1 : 0;
for(var j = j_start; j < j_start+7; j++){
self.g.append("text")
.attr("class", 'label')
.style("text-anchor", "end")
.attr("dy", self.cfg.rectsize * (j - j_start) + self.cfg.rectsize*0.7)
.attr("dx", "-1em")
.text(self.weekDay(new Date(2018, 0, j)));
}
for(var j = 0; j < 12; j++){
self.g.append("text")
.attr("class", 'label')
.style("text-anchor", "middle")
.attr("dy", -5)
.attr("dx", (j*self.cfg.rectsize*4.4) + (self.cfg.rectsize*2.2))
.text(self.monthName(new Date(2018, j, 1)));
}
}
}
Date.prototype.yyyymmdd = function(joinchar='-') {
var mm = this.getMonth() + 1;
var dd = this.getDate();
return [this.getFullYear(),
(mm>9 ? '' : '0') + mm,
(dd>9 ? '' : '0') + dd
].join(joinchar);
};
<html>
<head>
<meta charset="utf-8">
</head>
<style>
.chart .label {
font-size: 10px;
font-family: sans-serif;
}
</style>
<body>
<div id="calendar" style="width: 960px; height: 200px;"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="d3.calendar.js"></script>
<script>
d3.csv("accs_per_day.csv", function(error, data) {
if (error) throw error;
d3.timeFormatDefaultLocale({
"decimal": ".",
"thousands": ",",
"grouping": [3],
"currency": ["$", ""],
"dateTime": "%a %b %e %X %Y",
"date": "%m/%d/%Y",
"time": "%H:%M:%S",
"periods": ["AM", "PM"],
"days": ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
"shortDays": ["Dom", "Lun", "Mar", "Mie", "Jue", "Vie", "Sab"],
"months": ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
"shortMonths": ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"]
});
var chart = new Calendar(d3.select('#calendar'), data, {
'datefield': 'fecha',
'key': 'precio',
'year': 2018,
'mondaystart': true,
})
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.