Skip to content

Instantly share code, notes, and snippets.

@josgraha
Created January 10, 2017 21:41
Show Gist options
  • Save josgraha/fd5561637b8fc608cff76573269204ac to your computer and use it in GitHub Desktop.
Save josgraha/fd5561637b8fc608cff76573269204ac to your computer and use it in GitHub Desktop.
D3 visualization of daily revenue through calendar format looping through the months continuously
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Calendar Loop</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.js"></script>
<!-- <link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="style.css"> -->
</head>
<body>
<script>
// sample dataset
dataset=[{time:'1/1/16', revenue:738251},
{time:'1/2/16', revenue:176666},
{time:'1/3/16', revenue:981598},
{time:'1/4/16', revenue:940014},
{time:'1/5/16', revenue:155114},
{time:'1/6/16', revenue:92946},
{time:'1/7/16', revenue:164241},
{time:'1/8/16', revenue:74864},
{time:'1/9/16', revenue:816534},
{time:'1/10/16', revenue:402629},
{time:'1/11/16', revenue:208664},
{time:'1/12/16', revenue:943698},
{time:'1/13/16', revenue:331839},
{time:'1/14/16', revenue:955321},
{time:'1/15/16', revenue:292867},
{time:'1/16/16', revenue:933068},
{time:'1/17/16', revenue:145300},
{time:'1/18/16', revenue:764398},
{time:'1/19/16', revenue:930748},
{time:'1/20/16', revenue:28477},
{time:'1/21/16', revenue:385772},
{time:'1/22/16', revenue:943665},
{time:'1/23/16', revenue:52877},
{time:'1/24/16', revenue:865589},
{time:'1/25/16', revenue:81742},
{time:'1/26/16', revenue:921995},
{time:'1/27/16', revenue:794117},
{time:'1/28/16', revenue:665286},
{time:'1/29/16', revenue:158451},
{time:'1/30/16', revenue:875790},
{time:'1/31/16', revenue:517332},
{time:'2/1/16', revenue:377979},
{time:'2/2/16', revenue:255429},
{time:'2/3/16', revenue:22072},
{time:'2/4/16', revenue:774488},
{time:'2/5/16', revenue:596900},
{time:'2/6/16', revenue:656863},
{time:'2/7/16', revenue:764316},
{time:'2/8/16', revenue:744247},
{time:'2/9/16', revenue:914986},
{time:'2/10/16', revenue:608282},
{time:'2/11/16', revenue:117472},
{time:'2/12/16', revenue:672084},
{time:'2/13/16', revenue:238065},
{time:'2/14/16', revenue:949978},
{time:'2/15/16', revenue:190303},
{time:'2/16/16', revenue:873319},
{time:'2/17/16', revenue:721328},
{time:'2/18/16', revenue:377353},
{time:'2/19/16', revenue:793956},
{time:'2/20/16', revenue:209175},
{time:'2/21/16', revenue:979669},
{time:'2/22/16', revenue:963573},
{time:'2/23/16', revenue:715392},
{time:'2/24/16', revenue:941266},
{time:'2/25/16', revenue:433058},
{time:'2/26/16', revenue:770800},
{time:'2/27/16', revenue:810024},
{time:'2/28/16', revenue:616143},
{time:'2/29/16', revenue:623793},
{time:'3/1/16', revenue:22537},
{time:'3/2/16', revenue:865633},
{time:'3/3/16', revenue:524138},
{time:'3/4/16', revenue:612863},
{time:'3/5/16', revenue:967549},
{time:'3/6/16', revenue:949763},
{time:'3/7/16', revenue:482934},
{time:'3/8/16', revenue:561899},
{time:'3/9/16', revenue:666190},
{time:'3/10/16', revenue:974152},
{time:'3/11/16', revenue:736493},
{time:'3/12/16', revenue:20337},
{time:'3/13/16', revenue:104660},
{time:'3/14/16', revenue:831515},
{time:'3/15/16', revenue:951525},
{time:'3/16/16', revenue:340619},
{time:'3/17/16', revenue:556653},
{time:'3/18/16', revenue:311635},
{time:'3/19/16', revenue:555567},
{time:'3/20/16', revenue:152332},
{time:'3/21/16', revenue:154463},
{time:'3/22/16', revenue:753992},
{time:'3/23/16', revenue:681520},
{time:'3/24/16', revenue:697921},
{time:'3/25/16', revenue:481529},
{time:'3/26/16', revenue:94319},
{time:'3/27/16', revenue:718204},
{time:'3/28/16', revenue:498336},
{time:'3/29/16', revenue:991652},
{time:'3/30/16', revenue:66115},
{time:'3/31/16', revenue:940791},
{time:'4/1/16', revenue:446457},
{time:'4/2/16', revenue:390213},
{time:'4/3/16', revenue:975697},
{time:'4/4/16', revenue:307949},
{time:'4/5/16', revenue:205956},
{time:'4/6/16', revenue:616999},
{time:'4/7/16', revenue:863406},
{time:'4/8/16', revenue:417095},
{time:'4/9/16', revenue:598231},
{time:'4/10/16', revenue:941310},
{time:'4/11/16', revenue:623650},
{time:'4/12/16', revenue:94719},
{time:'4/13/16', revenue:846489},
{time:'4/14/16', revenue:991970},
{time:'4/15/16', revenue:110875},
{time:'4/16/16', revenue:221482},
{time:'4/17/16', revenue:979643},
{time:'4/18/16', revenue:602309},
{time:'4/19/16', revenue:285629},
{time:'4/20/16', revenue:228733},
{time:'4/21/16', revenue:71529},
{time:'4/22/16', revenue:291220},
{time:'4/23/16', revenue:274211},
{time:'4/24/16', revenue:528953},
{time:'4/25/16', revenue:452873},
{time:'4/26/16', revenue:894844},
{time:'4/27/16', revenue:127166},
{time:'4/28/16', revenue:331723},
{time:'4/29/16', revenue:211088},
{time:'4/30/16', revenue:568942},
{time:'5/1/16', revenue:626670},
{time:'5/2/16', revenue:458067},
{time:'5/3/16', revenue:928443},
{time:'5/4/16', revenue:241011},
{time:'5/5/16', revenue:487654},
{time:'5/6/16', revenue:586159},
{time:'5/7/16', revenue:262760},
{time:'5/8/16', revenue:67742},
{time:'5/9/16', revenue:741180},
{time:'5/10/16', revenue:754710},
{time:'5/11/16', revenue:913180},
{time:'5/12/16', revenue:481184},
{time:'5/13/16', revenue:337293},
{time:'5/14/16', revenue:958300},
{time:'5/15/16', revenue:260906},
{time:'5/16/16', revenue:656560},
{time:'5/17/16', revenue:151047},
{time:'5/18/16', revenue:780417},
{time:'5/19/16', revenue:824098},
{time:'5/20/16', revenue:101498},
{time:'5/21/16', revenue:175695},
{time:'5/22/16', revenue:374409},
{time:'5/23/16', revenue:927245},
{time:'5/24/16', revenue:936455},
{time:'5/25/16', revenue:193717},
{time:'5/26/16', revenue:937953},
{time:'5/27/16', revenue:66424},
{time:'5/28/16', revenue:519510},
{time:'5/29/16', revenue:490741},
{time:'5/30/16', revenue:902849},
{time:'5/31/16', revenue:216214},
{time:'6/1/16', revenue:936577},
{time:'6/2/16', revenue:231896},
{time:'6/3/16', revenue:581985},
{time:'6/4/16', revenue:531625},
{time:'6/5/16', revenue:562692},
{time:'6/6/16', revenue:549144},
{time:'6/7/16', revenue:575602},
{time:'6/8/16', revenue:473149},
{time:'6/9/16', revenue:529353},
{time:'6/10/16', revenue:82629},
{time:'6/11/16', revenue:303673},
{time:'6/12/16', revenue:597425},
{time:'6/13/16', revenue:417307},
{time:'6/14/16', revenue:384729},
{time:'6/15/16', revenue:377988},
{time:'6/16/16', revenue:785500},
{time:'6/17/16', revenue:393117},
{time:'6/18/16', revenue:90107},
{time:'6/19/16', revenue:325468},
{time:'6/20/16', revenue:872399},
{time:'6/21/16', revenue:70144},
{time:'6/22/16', revenue:613340},
{time:'6/23/16', revenue:395407},
{time:'6/24/16', revenue:569693},
{time:'6/25/16', revenue:39714},
{time:'6/26/16', revenue:949966},
{time:'6/27/16', revenue:360049},
{time:'6/28/16', revenue:872097},
{time:'6/29/16', revenue:911655},
{time:'6/30/16', revenue:476573},
{time:'7/1/16', revenue:534728},
{time:'7/2/16', revenue:902530},
{time:'7/3/16', revenue:975753},
{time:'7/4/16', revenue:410728},
{time:'7/5/16', revenue:296888},
{time:'7/6/16', revenue:322720},
{time:'7/7/16', revenue:400918},
{time:'7/8/16', revenue:828363},
{time:'7/9/16', revenue:990571},
{time:'7/10/16', revenue:455204},
{time:'7/11/16', revenue:504322},
{time:'7/12/16', revenue:931756},
{time:'7/13/16', revenue:827505},
{time:'7/14/16', revenue:949474},
{time:'7/15/16', revenue:268032},
{time:'7/16/16', revenue:932666},
{time:'7/17/16', revenue:668930},
{time:'7/18/16', revenue:215059},
{time:'7/19/16', revenue:842514},
{time:'7/20/16', revenue:922340},
{time:'7/21/16', revenue:800845},
{time:'7/22/16', revenue:936609},
{time:'7/23/16', revenue:956740},
{time:'7/24/16', revenue:887883},
{time:'7/25/16', revenue:778195},
{time:'7/26/16', revenue:385177},
{time:'7/27/16', revenue:210917},
{time:'7/28/16', revenue:132211},
{time:'7/29/16', revenue:368271},
{time:'7/30/16', revenue:244940},
{time:'7/31/16', revenue:486528},
{time:'8/1/16', revenue:679686},
{time:'8/2/16', revenue:718174},
{time:'8/3/16', revenue:332904},
{time:'8/4/16', revenue:756420},
{time:'8/5/16', revenue:615744},
{time:'8/6/16', revenue:786459},
{time:'8/7/16', revenue:455415},
{time:'8/8/16', revenue:581611},
{time:'8/9/16', revenue:457828},
{time:'8/10/16', revenue:451281},
{time:'8/11/16', revenue:680526},
{time:'8/12/16', revenue:28968},
{time:'8/13/16', revenue:985913},
{time:'8/14/16', revenue:82304},
{time:'8/15/16', revenue:735944},
{time:'8/16/16', revenue:491310},
{time:'8/17/16', revenue:710963},
{time:'8/18/16', revenue:899071},
{time:'8/19/16', revenue:569518},
{time:'8/20/16', revenue:994888},
{time:'8/21/16', revenue:931846},
{time:'8/22/16', revenue:686598},
{time:'8/23/16', revenue:717134},
{time:'8/24/16', revenue:992618},
{time:'8/25/16', revenue:384297},
{time:'8/26/16', revenue:435279},
{time:'8/27/16', revenue:652782},
{time:'8/28/16', revenue:304881},
{time:'8/29/16', revenue:829466},
{time:'8/30/16', revenue:509682},
{time:'8/31/16', revenue:425330},
{time:'9/1/16', revenue:475036},
{time:'9/2/16', revenue:543812},
{time:'9/3/16', revenue:687647},
{time:'9/4/16', revenue:448435},
{time:'9/5/16', revenue:879107},
{time:'9/6/16', revenue:542273},
{time:'9/7/16', revenue:750866},
{time:'9/8/16', revenue:766552},
{time:'9/9/16', revenue:498417},
{time:'9/10/16', revenue:767572},
{time:'9/11/16', revenue:159672},
{time:'9/12/16', revenue:598079},
{time:'9/13/16', revenue:856823},
{time:'9/14/16', revenue:599436},
{time:'9/15/16', revenue:178906},
{time:'9/16/16', revenue:180669},
{time:'9/17/16', revenue:633115},
{time:'9/18/16', revenue:29776},
{time:'9/19/16', revenue:34041},
{time:'9/20/16', revenue:224349},
{time:'9/21/16', revenue:812679},
{time:'9/22/16', revenue:414616},
{time:'9/23/16', revenue:631253},
{time:'9/24/16', revenue:939951},
{time:'9/25/16', revenue:612443},
{time:'9/26/16', revenue:925348},
{time:'9/27/16', revenue:243595},
{time:'9/28/16', revenue:314031},
{time:'9/29/16', revenue:880216},
{time:'9/30/16', revenue:59595},
{time:'10/1/16', revenue:153718},
{time:'10/2/16', revenue:708395},
{time:'10/3/16', revenue:203333},
{time:'10/4/16', revenue:568552},
{time:'10/5/16', revenue:934268},
{time:'10/6/16', revenue:524942},
{time:'10/7/16', revenue:964981},
{time:'10/8/16', revenue:51998},
{time:'10/9/16', revenue:481533},
{time:'10/10/16', revenue:488400},
{time:'10/11/16', revenue:992404},
{time:'10/12/16', revenue:668943},
{time:'10/13/16', revenue:341168},
{time:'10/14/16', revenue:925242},
{time:'10/15/16', revenue:567979},
{time:'10/16/16', revenue:637481},
{time:'10/17/16', revenue:81960},
{time:'10/18/16', revenue:652543},
{time:'10/19/16', revenue:736521},
{time:'10/20/16', revenue:27986},
{time:'10/21/16', revenue:184557},
{time:'10/22/16', revenue:509971},
{time:'10/23/16', revenue:712998},
{time:'10/24/16', revenue:436299},
{time:'10/25/16', revenue:266856},
{time:'10/26/16', revenue:787272},
{time:'10/27/16', revenue:867274},
{time:'10/28/16', revenue:613515},
{time:'10/29/16', revenue:431457},
{time:'10/30/16', revenue:243858},
{time:'10/31/16', revenue:457192},
{time:'11/1/16', revenue:78329},
{time:'11/2/16', revenue:826848},
{time:'11/3/16', revenue:841233},
{time:'11/4/16', revenue:30802},
{time:'11/5/16', revenue:754270},
{time:'11/6/16', revenue:658480},
{time:'11/7/16', revenue:530319},
{time:'11/8/16', revenue:163579},
{time:'11/9/16', revenue:790321},
{time:'11/10/16', revenue:448840},
{time:'11/11/16', revenue:501691},
{time:'11/12/16', revenue:664552},
{time:'11/13/16', revenue:22180},
{time:'11/14/16', revenue:895207},
{time:'11/15/16', revenue:163246},
{time:'11/16/16', revenue:608326},
{time:'11/17/16', revenue:484224},
{time:'11/18/16', revenue:51022},
{time:'11/19/16', revenue:953079},
{time:'11/20/16', revenue:277111},
{time:'11/21/16', revenue:146463},
{time:'11/22/16', revenue:667508},
{time:'11/23/16', revenue:166010},
{time:'11/24/16', revenue:971692},
{time:'11/25/16', revenue:702040},
{time:'11/26/16', revenue:991647},
{time:'11/27/16', revenue:440256},
{time:'11/28/16', revenue:445926},
{time:'11/29/16', revenue:311525},
{time:'11/30/16', revenue:792526},
{time:'12/1/16', revenue:162233},
{time:'12/2/16', revenue:984744},
{time:'12/3/16', revenue:732519},
{time:'12/4/16', revenue:394681},
{time:'12/5/16', revenue:795696},
{time:'12/6/16', revenue:557133},
{time:'12/7/16', revenue:425811},
{time:'12/8/16', revenue:795549},
{time:'12/9/16', revenue:274184},
{time:'12/10/16', revenue:799808},
{time:'12/11/16', revenue:661068},
{time:'12/12/16', revenue:882735},
{time:'12/13/16', revenue:766780},
{time:'12/14/16', revenue:176658},
{time:'12/15/16', revenue:404362},
{time:'12/16/16', revenue:232720},
{time:'12/17/16', revenue:54779},
{time:'12/18/16', revenue:654595},
{time:'12/19/16', revenue:176170},
{time:'12/20/16', revenue:675108},
{time:'12/21/16', revenue:647650},
{time:'12/22/16', revenue:377867},
{time:'12/23/16', revenue:768627},
{time:'12/24/16', revenue:44403},
{time:'12/25/16', revenue:622183},
{time:'12/26/16', revenue:966509},
{time:'12/27/16', revenue:940023},
{time:'12/28/16', revenue:47700},
{time:'12/29/16', revenue:295924},
{time:'12/30/16', revenue:232779},
{time:'12/31/16', revenue:85859}];
// set variables
var h = 600;
var w = 1000;
var padding=100;
var ds; //global variable
var datasets=[];
var minDate;
var maxDate;
var DaysOfTheWeek = ["Sunday", "Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
var buckets = 9;
var colors = ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"];
var calendar_box=[];
var days = [0, 1,2,3,4,5,6];
var weeks = [0,1,2,3,4,5];
var bucket_thresholds=[];
// create array for calendar boxes
for(j=0;j<weeks.length;j++){
for (i=0;i<days.length;i++){
index_num = i + (j*7);
calendar_box.push({Days:days[i], Weeks:weeks[j]});
}
};
// takes string from data input and converts it into a date
function getDate(d){
var strDate=new String(d);
var bracket_one = strDate.indexOf("/");
var month = strDate.substr(0,bracket_one)-1;
var Str1 = strDate.substr(bracket_one+1,strDate.length-bracket_one);
var bracket_two = Str1.indexOf("/");
var day = Str1.substr(0,bracket_two);
var Str2 = Str1.substr(bracket_two+1,2);
var year="20"+Str2;
return new Date (year, month,day);
};
function calendar_chart(ds_curr){
// clear out stuff for each iteration of the loop
d3.select("div").remove();
d3.select("svg").selectAll("rect").remove();
d3.select("svg").selectAll(".calendar-text").remove();
d3.select("svg").selectAll(".legend").remove();
// set min and maxes for scales
// use "+" to make sure value is evaluated as a number and not a string
var minDate = d3.min(ds_curr,function(d){return d['Date']});
var maxDate = d3.max(ds_curr,function(d){return d['Date']});
//var minRev = d3.min(ds_curr,function(d){return +d['Revenue']});
//var maxRev = d3.max(ds_curr,function(d){return +d['Revenue']});
var colorScale = d3.scaleQuantize()
.domain([global_minRev,global_maxRev])
.range(colors);
var bucket_thresholds=[];
var bucket_increment = (global_maxRev - global_minRev)/9;
for (i=0;i<colors.length;i++){
bucket_thresholds.push({Min:global_minRev+(i*bucket_increment), Max:global_minRev+(i+1)*bucket_increment});
};
var gridWidth= (w -padding*2)/7;
var gridHeight = (h - padding*2)/6;
//toolip
var tooltip = d3.select("body").append("div")
.attr("class","tooltip")
.style("opacity",0);
// clear out SVG for each iteration of the loop
d3.select("body").select("svg").remove();
var svg_obj = d3.select("body").append("svg")
.attr("width",w)
.attr("height",h)
.style("background","#E5E4E2");
var dayLabels = svg_obj.selectAll(".dayLabel")
.data(DaysOfTheWeek)
.enter().append("text")
.text(function (d) { return d; })
.attr("x", function (d, i) { return (i+1)*gridWidth - gridWidth/4 ; })
.attr("y", padding-5)
.style("text-align", "left")
.style("font-size",14);
var monthTitle = svg_obj
.append("text")
.text(d3.timeFormat('%B %Y')(minDate))
.attr("x",w/2)
.attr("y",padding/2)
.style("text-align", "center")
.attr("class","monthTitle")
.style("font-weight","bold");
// fill rectangles with data
var calendar = svg_obj.selectAll("rect")
.data(ds_curr)
.enter()
.append("rect")
.attr("x",function(d,i){
curr_day = d['Date'].getDay();
return curr_day*gridWidth + padding})
.attr("y",function(d,i){
// shift all the days of the month based on the day of the first of the month
shift = minDate.getDay();
curr_week = Math.floor((d['Date'].getDate()-minDate.getDate()+ shift)/7);
return h - padding - (6-curr_week)*gridHeight})
.attr("width",gridWidth)
.attr("height",gridHeight)
.attr("fill",function(d){
return colorScale(+d['revenue'])})
.style("stroke", "black")
.style("stroke-width", 1);
var textOnRect = svg_obj.selectAll(".calendar-text")
.data(ds_curr)
.enter()
.append("text")
.attr("x",function(d,i){
curr_day = d['Date'].getDay();
return curr_day*gridWidth + padding+(9*gridWidth/10)})
.attr("y",function(d,i){
// shift all the days of the month based on the day of the first of the month
shift = minDate.getDay();
curr_week = Math.floor((d['Date'].getDate()-minDate.getDate()+ shift)/7);
return h - padding - (6-curr_week)*gridHeight+(gridHeight/5)})
.attr("class","calendar-text")
.attr("fill","#696969")
.attr("font-size",10)
.text(function(d){return d['Date'].getDate();});
// add legend to the bottom of the chart
var legend = svg_obj.selectAll("legend")
.data(bucket_thresholds)
.enter();
legend.enter().append("g")
.attr("class", "legend");
legend.append("rect")
.attr("x", function(d, i) { return (gridWidth/2) * i+padding*3; })
.attr("y", h-gridHeight*6+padding*3.5)
.attr("width", gridWidth/2)
.attr("height", gridHeight / 2)
.style("fill", function(d, i) { return colors[i]; })
legend.append("text")
.text("Legend")
.attr("x", w/2)
.attr("y", h-gridHeight*6+padding*3.45)
.attr("font-size",12)
.attr("fill","#black")
.attr("text-align","center");
legend.append("text")
.html(function(d,i) {return d3.format("($,.0f")(d.Min)})
.attr("x", function(d, i) { return (gridWidth/2) * i+padding*3; })
.attr("y", h-gridHeight*6+padding*3.6)
.attr("font-size",10)
.attr("fill","#696969");
legend.append("text")
.html("-")
.attr("x", function(d, i) { return (gridWidth/2) * i+padding*3; })
.attr("y", h-gridHeight*6+padding*3.7)
.attr("font-size",10)
.attr("fill","#696969");
legend.append("text")
.html(function(d,i) {return d3.format("($,.0f")(d.Max)})
.attr("x", function(d, i) { return (gridWidth/2) * i+padding*3; })
.attr("y", h-gridHeight*6+padding*3.8)
.attr("font-size",10)
.attr("fill","#696969");
};
function data_chart(){
ds=dataset;
for (i=0;i<ds.length;i++){
// create new fields in the array as either calculations or date transfomration
ds[i]['Date']=getDate(ds[i]['time']);
};
// calculate min & max dates in the dataset and use a numberic index of year*12+month to iterate through the data
min_array_date=d3.min(ds,function(d){return d['Date']});
max_array_date=d3.max(ds,function(d){return d['Date']});
global_minRev = d3.min(ds,function(d){return +d['revenue']});
global_maxRev = d3.max(ds,function(d){return +d['revenue']});
min_array_i = min_array_date.getMonth() + (min_array_date.getFullYear()*12);
max_array_i = max_array_date.getMonth() + (max_array_date.getFullYear()*12);
//console.log(min_array_date);
//console.log(min_array_i);
//console.log(max_array_date);
//console.log(max_array_i);
for(i=min_array_i; i < max_array_i + 1; i++){
// use setTimeout to pause through each visual due to asynchronous nature of JavaScripts
// pause amount is 2.5 seconds
window.setTimeout(
(function (i){
return function() {
curr_year = Math.floor(i/12);
//console.log(curr_year);
curr_month = i % 12;
//console.log(curr_month);
var ds_curr=ds.filter(function(el){
return el['Date'].getMonth()== curr_month && el['Date'].getFullYear()==curr_year;
});
//console.log(ds_curr);
calendar_chart(ds_curr);
}
})(i)
,(i-min_array_i) * 2000)
};
};
// create the chart once (without this nothing happens for the first 12 seconds as setInterval executes after the delay time)
data_chart();
// create the chart in an infinite leeop after a delay of 12 seconds
window.setInterval(data_chart,24000);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment