d3 dynamic data in bar graph - javascript

i am using the d3 for making the bar graph . From example i see that they use the tsv file for the data . In my case i have the dynamic data . Here is the code and i want to use the var data1 to make the graph
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<div id="bar"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
var $window = $(window);
var wWidth = $window.width();
var wHeight = $window.height();
var margin = {top: 10, right: 0, bottom: 30, left: 40},
width = 300 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatPercent);
//var data = [[1,1],[2,3],[3,2],[4,5],[5,4]];
var svg = d3.select("#bar").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data1 = [
{letter:"A",frequency:"58.13"},
{letter:"B",frequency:"53.98"},
{letter:"C",frequency:"67.00"},
{letter:"D",frequency:"89.70"},
{letter:"E",frequency:"99.00"}
];
d3.tsv("data.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); });
});
function type(d) {
d.frequency = +d.frequency;
return d;
}
</script>
data.tsv
letter frequency
A .08167
B .01492
C .02780
D .04253
E .12702
F .02288
G .02022
H .06094
I .06973
J .00153

Change the variable name,
data1 To data
And remove these line
1. d3.tsv("data.tsv", type, function(error, data) {
2. });
See DEMO Here

Related

Plot bar chart using D3 JS

I am plotting a bar chart in D3.js (Version 3). Which has two axis, one is receive_data and another one is responses. I have a JSON file where I stored the data. JSON format looks like,
[{"receive_date":"2013-11-04","responses":"2"}]
In my JSON, I have two responses values for the same date 2013-11-04 .
Like,
[{"receive_date":"2013-11-04","responses":"2"},{"receive_date":"2013-11-04","responses":"8668"}
This is the JSON Source :- https://api.myjson.com/bins/gdpu7
So, when I am plotting the graph, it is not taking the sum of the values for the same receive_date instead it is showing two times. I want it to show the sum of responses. responses should be (8668+2) for the receive_date 2013-11-04
I also found it that by using reduce we can do this. I tried to use d3.json.reduce . But it is showing error d3.json.reduce is not a function.
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width + (2 * margin.left) + margin.right) //set width
.attr("height", height + margin.top + margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
d3.json("https://api.myjson.com/bins/gdpu7", function(error, data) {
x.domain(data.map(function(d) {
return d.receive_date
}));
y.domain([0, d3.max(data, function(d) {
return d.responses
})]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + x(d.receive_date) + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.responses);
})
.attr("x", function(d, i) {
return x.rangeBand() + (margin.left / 2);
})
.attr("height", function(d) {
return height - y(d.responses);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand() + margin.left)
.attr("y", function(d) {
return y(d.responses) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.responses;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("responses");
});
function type(d) {
d.receive_date = +d.receive_date; // coerce to number
return d;
}
#chart rect {
fill: #4aaeea;
}
#chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
body {
background: #1a1a1a;
color: #eaeaea;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="chart"></div>
JSfiddle :- https://jsfiddle.net/bL9940at/
The relevant part:
var array1 = data; //input
var array2 = [];
var last_d;
array1.reduce(function (accumulator, currentValue, i) {
var r = Number(currentValue.responses),
d = currentValue.receive_date;
if (d == last_d) r += accumulator;
array2[i] = {
receive_date: d,
responses: r
};
last_d = d;
return accumulator + Number(currentValue.responses);
}, 0);
data = array2; //output
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width + (2 * margin.left) + margin.right) //set width
.attr("height", height + margin.top + margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
d3.json("https://api.myjson.com/bins/gdpu7", function(error, data) {
//create new arrays
var array1 = data; //input
var array2 = [];
var last_d;
array1.reduce(function (accumulator, currentValue, i) {
var r = Number(currentValue.responses),
d = currentValue.receive_date;
if (d == last_d) r += accumulator;
array2[i] = {
receive_date: d,
responses: r
};
last_d = d;
return accumulator + Number(currentValue.responses);
}, 0);
data = array2; //output
x.domain(data.map(function(d) {
return d.receive_date;
}));
y.domain([0, d3.max(data, function(d) {
return d.responses;
})*1.1]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + x(d.receive_date) + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.responses);
})
.attr("x", function(d, i) {
return x.rangeBand() + (margin.left / 2);
})
.attr("height", function(d) {
return height - y(d.responses);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand() + margin.left)
.attr("y", function(d) {
return y(d.responses) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.responses;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("responses");
});
function type(d) {
d.receive_date = +d.receive_date; // coerce to number
return d;
}
#chart rect {
fill: #4aaeea;
}
#chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
body {
background: #1a1a1a;
color: #eaeaea;
padding: 10px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://code.jquery.com/jquery-git.js"></script>
<div id="chart"></div>
</body>
</html>

error updating my barchart using d3

I came through this problem just when I though I had d3 undercontrol... I can't find out why my code is crashing when I update my barChart using an empty array of data...
here is my code:
<!DOCTYPE html>
<html>
<head>
<title>Simple tables in D3</title>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<style type="text/css">
.chart rect {
fill: steelblue;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.axis {
font: 10px sans-serif;
}
</style>
</head>
<body>
<svg class="chart">
</svg>
<script>
var scanCounters = JSON.parse("[{\"scan\":\"111\",\"repetition\":3},{\"scan\":\"222\",\"repetition\":2},{\"scan\":\"333\",\"repetition\":4},{\"scan\":\"123\",\"repetition\":2},{\"scan\":\"456\",\"repetition\":1},{\"scan\":\"789\",\"repetition\":1}]");
var scanCounters2 = JSON.parse("[{\"scan\":\"111\",\"repetition\":8},{\"scan\":\"222\",\"repetition\":6},{\"scan\":\"333\",\"repetition\":5},{\"scan\":\"123\",\"repetition\":3}]");
var scanCounters3 = JSON.parse("[]");
var columns = ["scan", "repetition"];
var margin = {top: 20, right: 30, bottom: 30, left: 40};
var width = 960;
var height = 500;
var innerWidth = width - margin.left - margin.right;
var innerHeight = height - margin.top - margin.bottom;
var bcScan = barChart(600, 400, "chart");
bcScan.update(scanCounters);
setTimeout(function(){
bcScan.update(scanCounters2);
}, 1000);
setTimeout(function(){
bcScan.update(scanCounters3);
}, 2000);
function barChart(w, h, node) {
var bC = {};
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("." + node)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
bC.update = function(data) {
x.domain(data.map(function (d) {
return d[Object.keys(d)[0]];
}));
y.domain([0, d3.max(data, function (d) {
return d[Object.keys(d)[1]];
})]);
svg.select(".x.axis")
.transition()
.duration(300).call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
svg.select(".y.axis")
.transition()
.duration(300)
.call(yAxis);
var bars = svg.selectAll(".bar").data(data, function (d) {
return d[Object.keys(d)[0]];
});
bars.exit()
.transition()
.duration(300)
.attr("y", y(0))
.attr("height", height - y(0))
.style('fill-opacity', 1e-6)
.remove();
bars.enter()
.append("rect")
.attr("class", "bar")
.attr("y", y(0))
.attr("height", height - y(0));
var trans = bars.transition().duration(300).attr("x", function (d) {
return x(d[Object.keys(d)[0]]);
});
trans.attr("width", x.rangeBand())
.attr("y", function (d) {
return y(d[Object.keys(d)[1]]);
})
.attr("height", function (d) {
return height - y(d[Object.keys(d)[1]]);
});
};
return bC;
}
</script>
</body>
</html>
The problem is when I call bcScan.update(scanCounters3); where scanCounters3 is an empty array.
This is the error message:
Error: Invalid value for attribute height="NaN"
Error: Invalid value for attribute y="NaN"
QUESTON: Could someone help me to understand why it is crashing?
thanks
#EthanJewett, is correct, if you look at your y.domain() on each iteration you get:
[0, 4]
[0, 8]
[0, NaN]
So on your last iteration, your exit transition is doing math on an NaN:
.attr("y", y(0))
.attr("height", height - y(0))
I would just drop those lines altogether and make the exit transition:
bars.exit()
.transition()
.duration(300)
.style('fill-opacity', 1e-6)
.remove();

Passing a JSON object/String to D3 instead of JSON file

I am trying to create charts using D3 so that we can analyze city based data. Since the data is coming from backend I don't want to use JSON file and instead create a JSON object and pass it to D3 for rendering the graphs. I am attaching the code that I am working on below this. Any help on this will be really helpful.
<!DOCTYPE html>
<html>
<%# page language="java" import="java.util.*, java.lang.*"%>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var stringTry = '${city}';
var city = [];
var trial;
while(!(stringTry.indexOf('\,')== -1)){
trial = stringTry.substring(0,stringTry.indexOf('\,'));
if(trial.indexOf('\[')==0){
trial = trial.substring(1,trial.length);
}else{
trial = trial;
}
city.push(trial);
stringTry = stringTry.substring((stringTry.indexOf('\,')+1),stringTry.length);
}
trial = stringTry.substring(0,stringTry.indexOf('\]'));
city.push(trial);
var stringTry2 = '${frequency}';
var frequency = [];
var trial2;
while(!(stringTry2.indexOf('\,')== -1)){
trial2 = stringTry2.substring(0,stringTry2.indexOf('\,'));
if(trial2.indexOf('\[')==0){
trial2 = trial2.substring(1,trial.length);
}else{
trial2 = trial2;
}
frequency.push(trial2);
stringTry2 = stringTry2.substring((stringTry2.indexOf('\,')+1),stringTry2.length);
}
trial2 = stringTry2.substring(0,stringTry2.indexOf('\]'));
frequency.push(trial2);
var test = []
test.push("\{\"city\"\:"+city[0]);
for (var i=1;i<(city.length-1);i++){
test.push(city[i]);
}
test.push(city[city.length-1]+"\}");
var test2 = [];
test2.push("\{\"frequency\"\:"+frequency[0]);
for (var j=1;j<(frequency.length-1);j++){
test2.push(frequency[j]);
}
test2.push(frequency[frequency.length-1]+"\}");
var dataArray = [];
data.push(test);
data.push(test2);
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//d3.json('${file}', type, function(error, data) {
// if (error) throw error;
x.domain(dataArray.map(function(d) { return d.city; }));
y.domain([0, d3.max(dataArray, function(d) { return d.frequency; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(dataArray)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.city); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); });
//});
function type(d) {
d.frequency = +d.frequency;
return d;
}
</script>**strong text**

D3: Drawing a histogram + heatmap

d3 noob here.
I'd like to draw something like this:
(http://bl.ocks.org/mbostock/3883195)
But with a heat aspect, sort of like this:
Code (from above link):
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.area {
fill: steelblue;
}
</style>
<body>
<center>
<div>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var area = d3.svg.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.close); });
var svg = d3.select("div").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.tsv("data.tsv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
// .attr("style", "stroke: #001")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
});
</script>
</div>
</center>
</body>
Perhaps changing the area variable in someway? I could add another column to the data, say, color. But how would I apply the color to the "vertical line" of each day?
Thoughts? Thanks in advance!

d3.time.scale() returning NAN

I am trying to follow this example:http://bl.ocks.org/mbostock/3883245
However, when trying to replicate to my needs values for x scale being returned in NAN. Values for y scale returns fine. Kindly help me to find out what I a doing wrong.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.key); })
.y(function(d) { return y(d.values.total); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("combine.csv", function(error, data) {
data.forEach(function(d) {
d.week = parseDate(d.week);
});
data= d3.nest()
.key(function(d) { return d.week; })
.rollup(function(d) { return {"total": d.length , hours: d3.sum(d, function(g) {return g.hours; })};})
.entries(data);
x.domain(d3.extent(data, function(d) { console.log(d.key);return d.key; }));
console.log(d3.extent(data, function(d) { return d.key; }));
y.domain(d3.extent(data, function(d) { console.log( d.values.total);return d.values.total; }));
console.log(data);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
</script>
</head>
You need to use actual Date objects, not just the string. So you can update the following lines to fix it:
var line = d3.svg.line()
.x(function (d) {
return x(new Date(d.key));
});
And
x.domain(d3.extent(data, function (d) {
return new Date(d.key);
}));
I created a fiddle simulating your data: http://jsfiddle.net/LRY7w/
Alternatively, you could add the week property to the object you're returning from the rollup. Here's a fiddle and here's the revised rollup function:
.rollup(function (d) {
return {
week: d[0].week,
"total": d.length,
hours: d3.sum(d, function (g) {
return g.hours;
})
};
})
Have to use d[0] since d is the nested rows, but since you're keying off of the week, it's the same in each object of the array, so using the first works just fine. And then instead of using new Date(d.key), you can just use d.values.week.

Categories

Resources