Stacked area graph not rendering - javascript

I have a stacked area chart that reads from CSV. But it doesn't render the x axis and the graph correctly.
I have tried to change the x axis values, but didn't help.
below is the sample CSV file.
Currently the view shows the y axis of values, and the drug names on the right, but it doesn't show the actual stacked chart or the date x axis values.
So far return x(d.data.date) = returning NaN.
Thank you so much for your help!
date,drug,market_share
2016-01-01,insulin lispro,.01
2016-01-01,alogliptin,0.001323754341
2016-01-01,sitagliptin,.01
2016-01-01,canagliflozin,0.02842158621
2016-01-01,glimepiride,0.05668845799
2016-01-01,repaglinide,0.0005768749342
2016-01-01,insulin glargine,.01
2016-01-01,metformin,0.4972895171
2016-01-01,mifepristone,.02
2016-01-01,exenatide,.02
2016-01-01,bromocriptine,0.0002109506723
2016-01-01,pioglitazone,0.02324500184
2016-01-01,metformin hydrochloride,0.392074889
2016-02-01,saxagliptin,.02
2016-02-01,pioglitazone,0.02247815103
2016-02-01,exenatide,0.03
2016-02-01,repaglinide,0.0006204220565
2016-02-01,metformin hydrochloride,0.394153624
2016-02-01,sitagliptin,.08
2016-02-01,insulin lispro,.05
2016-02-01,canagliflozin,0.02907988245
2016-02-01,metformin,0.4933502396
2016-02-01,insulin glargine,.02
2016-02-01,bromocriptine,0.0002076549233
2016-02-01,mifepristone,.02
2016-02-01,alogliptin,0.001364306972
2016-02-01,glimepiride,0.05857620484
2016-03-01,canagliflozin,0.02908102306
2016-03-01,bromocriptine,0.000195238081
2016-03-01,metformin,0.4966376769
2016-03-01,alogliptin,0.00133532899
2016-03-01,insulin glargine,.03
2016-03-01,sitagliptin,.04
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<div id="div1"></div>
<div id="div2"></div>
</body>
<script src="https://d3js.org/d3.v4.js"></script>
<script>
var parseDate = d3.timeParse("%Y-%m-%d");
function type2(d, i, columns) {
d.date = parseDate(d.date);
return d;
}
function type(d, i, columns) {
d.date = parseDate(d.date);
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = d[columns[i]] / 100;
return d;
}
function drawGraph(error, data, selector, width, height) {
console.log("DATA "+selector+JSON.stringify(data));
console.log("COL "+selector+"---"+data.columns);
var svg = d3.select(selector).append("svg")
.attr("width", width)
.attr("height", height),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
var stack = d3.stack();
var area = d3.area()
.x(function (d, i) {
return x(d.data.date);
})
.y0(function (d) {
return y(d[0]);
})
.y1(function (d) {
return y(d[1]);
});
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var keys = data.columns.slice(1);
x.domain(d3.extent(data, function (d) {
return d.date;
}));
z.domain(keys);
stack.keys(keys);
console.log("Stacked Data "+ selector+"---" + JSON.stringify(stack(data)));
var layer = g.selectAll(".layer")
.data(stack(data))
.enter().append("g")
.attr("class", "layer");
layer.append("path")
.attr("class", "area")
.style("fill", function (d) {
return z(d.key);
})
.attr("d", area);
layer.filter(function (d) {
return d[d.length - 1][1] - d[d.length - 1][0] > 0.01;
})
.append("text")
.attr("x", width - 6)
.attr("y", function (d) {
return y((d[d.length - 1][0] + d[d.length - 1][1]) / 2);
})
.attr("dy", ".35em")
.style("font", "10px sans-serif")
.style("text-anchor", "end")
.text(function (d) {
return d.key;
});
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10, "%"));
}
d3.csv("market_shares.csv", type2, function (error, data) {
let stackedByDate = {}
let drugSet = new Set();
let defaultDrugMarketShareProp = {};
let newData = []
data.forEach(function (item, index) {
drugSet.add(item.drug);
stackedByDate[item.date] = {};
});
let drugNames = [...drugSet];
drugNames.forEach(function (item, index) {
defaultDrugMarketShareProp[item] = 0;
});
data.forEach(function (item, index) {
stackedByDate[item.date] = Object.assign({}, defaultDrugMarketShareProp);
});
data.forEach(function (item, index) {
stackedByDate[item.date][item.drug] = item.market_share;
});
Object.keys(stackedByDate).forEach(function (key) {
hash = {}
hash['date'] = key;
Object.keys(stackedByDate[key]).forEach(function (innerKey) {
hash[innerKey] = stackedByDate[key][innerKey]
});
newData.push(hash);
});
newData.columns = drugNames;
newData.columns.unshift('date');
drawGraph(error, newData, "#div2", 960, 500);
});
</script>

You correctly parsed the date strings. However, in this function...
Object.keys(stackedByDate).forEach(function (key) {
hash = {}
hash['date'] = key;//<----- HERE
Object.keys(stackedByDate[key]).forEach(function (innerKey) {
hash[innerKey] = stackedByDate[key][innerKey]
});
newData.push(hash);
});
... you're converting the date objects back to strings again.
The quickest solution is just:
hash['date'] = new Date(key);
Here is your code with that change only: https://bl.ocks.org/GerardoFurtado/e9538de82e96cc9e3efb5fc4c7b9b970/5ca6920405243c39b93d0245230b955c37c85a2c

Related

D3 js tooltip violin plot

I have made a violin plot in D3.js with the following code:
<script src="https://d3js.org/d3.v4.js"></script>`
<div id="power"></div>
<script>
var margin = {top: 120, right: 100, bottom: 80, left: 100},
width = 2600 - margin.left - margin.right,
height = 620 - margin.top - margin.bottom;
var svg = d3.select("#power")
.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 + ")");
// Read the data and compute summary statistics for each
d3.csv("static/csv/violinsummary.csv", function (data) {
// Show the X scale
var x = d3.scaleBand()
.range([0, width])
.domain(["2017-09", "2017-10", "2018-02", "2018-03"])
.paddingInner(0)
.paddingOuter(.5);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Show the Y scale
var y = d3.scaleLinear()
.domain([80, 105])
.range([height, 0]);
svg.append("g").call(d3.axisLeft(y));
// Features of density estimate
var kde = kernelDensityEstimator(kernelEpanechnikov(.2), y.ticks(50));
// Compute the binning for each group of the dataset
var sumstat = d3.nest()
.key(function (d) {
return d.DATE;
})
.rollup(function (d) { // For each key..
input = d.map(function (g) {
return g.Power;
});
density = kde(input); // And compute the binning on it.
return (density);
})
.entries(data);
var maxNum = 0;
for (i in sumstat) {
allBins = sumstat[i].value;
kdeValues = allBins.map(function (a) {
return a[1]
});
biggest = d3.max(kdeValues);
if (biggest > maxNum) {
maxNum = biggest
}
}
// The maximum width of a violin must be x.bandwidth = the width dedicated to a group
var xNum = d3.scaleLinear()
.range([0, x.bandwidth()])
.domain([-maxNum, maxNum]);
svg
.selectAll("myViolin")
.data(sumstat)
.enter() // So now we are working group per group
.append("g")
.attr("transform", function (d) {
return ("translate(" + x(d.key) + " ,0)")
}) // Translation on the right to be at the group position
.append("path")
.datum(function (d) {
return (d.value)
}) // So now we are working density per density
.style("opacity", .7)
.style("fill", "#317fc8")
.attr("d", d3.area()
.x0(function (d) {
return (xNum(-d[1]))
})
.x1(function (d) {
return (xNum(d[1]))
})
.y(function (d) {
return (y(d[0]))
})
.curve(d3.curveCatmullRom));
});
function kernelDensityEstimator(kernel, X) {
return function (V) {
return X.map(function (x) {
return [x, d3.mean(V, function (v) {
return kernel(x - v);
})];
});
}
}
function kernelEpanechnikov(k) {
return function (v) {
return Math.abs(v /= k) <= 1 ? 0.75 * (1 - v * v) / k : 0;
};
}
</script>
Data (violinsummary.csv):
Power,DATE
89.29,2017-09
89.9,2017-09
91.69,2017-09
89.23,2017-09
91.54,2017-09
88.49,2017-09
89.15,2017-09
90.85,2017-09
89.59,2017-09
93.38,2017-10
92.41,2017-10
90.65,2017-10
91.07,2017-10
90.13,2017-10
91.73,2017-10
91.09,2017-10
93.21,2017-10
91.62,2017-10
89.58,2017-10
90.59,2017-10
92.57,2017-10
89.99,2017-10
90.59,2017-10
88.12,2017-10
91.3,2017-10
89.59,2018-02
91.9,2018-02
87.83,2018-02
90.36,2018-02
91.38,2018-02
91.56,2018-02
91.89,2018-02
90.95,2018-02
90.15,2018-02
90.24,2018-02
94.04,2018-02
85.4,2018-02
88.47,2018-02
92.3,2018-02
92.46,2018-02
92.26,2018-02
88.78,2018-02
90.13,2018-03
89.95,2018-03
92.98,2018-03
91.94,2018-03
90.29,2018-03
91.2,2018-03
94.22,2018-03
90.71,2018-03
93.03,2018-03
91.89,2018-03
I am trying to make a tooltip for each violin that shows the median and mean upon hover. I cannot figure out how to make the tooltip show up.
I know I need to do something like this with mouseover and mouseout but I'm not sure...
var tooltip = d3.select('#power')
.append('div')
.attr('class', 'tooltip')
.style("opacity", 0);
Any tips/guidance would be very appreciated.
You can implement the tooltip functionality by following two steps.
Step 1:
Initialize the tooltip container which already you did I guess.
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
Step 2:
Change the visibility property of the tooltip in the mouseover, mouseout event of the element. In your case, it's myViolin
.on("mouseover", function() {
tooltip.style("display", null);
})
.on("mouseout", function() {
tooltip.style("display", "none");
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
Here is the implementation of tooltip jsFiddle
Hope it helps :)

D3 stacked area chart tsv to csv conversion problems

I am trying to convert this example from tsv to csv.
Currently this is what I have: https://jsfiddle.net/asb1926/sc5wdkLe/
The error I keep getting is this (on the jsfiddle it is line 46):
d3.v4.min.js:7 Uncaught TypeError: Cannot read property 'length' of undefined
at t (d3.v4.min.js:7)
at SVGPathElement.<anonymous> (line:61)
at SVGPathElement.<anonymous> (d3.v4.min.js:2)
at _t._l [as each] (d3.v4.min.js:4)
at _t.yl [as attr] (d3.v4.min.js:4)
at line:59
at Object.<anonymous> (d3.v4.min.js:7)
at _.call (d3.v4.min.js:4)
at XMLHttpRequest.e (d3.v4.min.js:7)
​The csv file I am currently using is this:
date,Detractors,Promoters,Passives
04/23/12,37,12,46
04/24/12,32,19,42
04/25/12,45,16,44
04/26/12,24,52,64
Your date strings have a different format. This is Bostock's dates:
"2015 Jun 15"
This is yours:
"04/23/12"
Therefore, you need a different parser:
var parseDate = d3.timeParse("%m/%d/%Y");
Besides that, your path d attribute is wrong. It should be simply:
.attr("d", area);
Here is your code with that changes:
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom;
var parseDate = d3.timeParse("%m/%d/%Y");
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
var stack = d3.stack();
var area = d3.area()
.x(function(d, i) {
return x(d.data.date);
})
.y0(function(d) {
return y(d[0]);
})
.y1(function(d) {
return y(d[1]);
});
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = d3.csvParse(d3.select("#csv").text(), type)
var keys = data.columns.slice(1);
x.domain(d3.extent(data, function(d) {
return d.date;
}));
z.domain(keys);
stack.keys(keys);
var layer = g.selectAll(".layer")
.data(stack(data))
.enter().append("g")
.attr("class", "layer");
layer.append("path")
.attr("class", "area")
.style("fill", function(d) {
return z(d.key);
})
.attr("d", area);
layer.filter(function(d) {
return d[d.length - 1][1] - d[d.length - 1][0] > 0.01;
})
.append("text")
.attr("x", width - 6)
.attr("y", function(d) {
return y((d[d.length - 1][0] + d[d.length - 1][1]) / 2);
})
.attr("dy", ".35em")
.style("font", "10px sans-serif")
.style("text-anchor", "end")
.text(function(d) {
return d.key;
});
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10, "%"));
function type(d, i, columns) {
d.date = parseDate(d.date);
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = d[columns[i]] / 100;
return d;
}
pre {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500" height="300"></svg>
<pre id="csv">date,Detractors,Promoters,Passives
04/23/12,37,12,46
04/24/12,32,19,42
04/25/12,45,16,44
04/26/12,24,52,64</pre>
PS: I'm using a <pre> element to store the CSV, because I cannot use a real CSV in the Stack snippet.

D3 Multi-Series Line Chart with ZOOM

Apologies for the newbie question here. I'm trying to reproduce this example https://bl.ocks.org/mbostock/3884955 with some data on online influencers (essentially drawing many lines) but with an scroll zoom in order to isolate parts of the data. Sounds simple enough, but I can't seem to get it to function!
< svg width = "960"
height = "500" > < /svg>
<script src="/ / d3js.org / d3.v4.min.js "></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 80, bottom: 30, left: 100},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform ", "translate(" + margin.left +
", " + margin.top + ")");
var zoom = d3.zoom()
.scaleExtent([1, 32])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var parseTime = d3.timeParse(" % d / % m / % Y ");
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.engagement); });
d3.csv("stack2.csv", type, function(error, data) {
if (error) throw error;
var influencers = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {date: d.date, engagement: d[id]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([d3.min(influencers, function(c) {
return d3.min(c.values, function(d) {
return d.engagement; }); }),
d3.max(influencers, function(c) {
return d3.max(c.values, function(d) {
return d.engagement; }); })
]);
z.domain(influencers.map(function(c) { return c.id; }));
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0, " + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 5)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Engagement");
var influ = g.selectAll(".influ")
.data(influencers)
.enter().append("g")
.attr("class", "influ");
influ.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", "lightgrey")
.on("mouseover", function(d, i) {
d3.select(this).transition()
.style("stroke", function(d) {
return z(d.id);
})
.style("stroke-width", 3)
console.log(d.id);
})
.on("mouseout", function(d) {
d3.select(this).transition()
.style("stroke", "lightgrey")
.style("stroke-width", 1)
})
influ.append("text")
.datum(function(d) {
return {
id: d.id,
value: d.values[d.values.length - 1]
};
})
.attr("transform", function(d) {
return "translate(" + x(d.value.date) + "," + y(d.value.engagement) + ")";
})
.attr("x", 3)
.attr("dy", ".35em")
.style("opacity", 0.7)
.style("font", "10px sans-serif")
.text(function(d) {
return d.id;
});
svg.call(zoom)
});
function type(d, _, columns) {
d.date = parseTime(d.date);
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
}
function zoomed() {
var t = d3.event.transform,
xt = t.rescaleX(x);
g.selectAll("path.line").attr("d", function(d) {
return xt(d.date);
});
g.select(".axis--x").call(xAxis.scale(xt));
}
< /script>
I think the problem is isolated to this part:
function zoomed() {
var t = d3.event.transform,
xt = t.rescaleX(x);
g.selectAll("path.line").attr("d", function(d) {
return xt(d.date);
});
g.select(".axis--x").call(xAxis.scale(xt));
}
The Axis zoom is fine, but i don't think I'm calling the data for the line graphs correctly. I think g.selectAll definitely selects the lines, as they disappear on scroll... so I am assuming that .attr("d", function(d) {return xt(d.date); is wrong. Anyone have any tips?
I use something along these lines in an interactive line chart D3 plot that I wrote a couple of days back. I've commented extensively, so it should be pretty self explanatory. This works really well and has been bug free.
function zoomed() {
lastEventTransform = d3.event.transform;
// Rescale axes using current zoom transform
gX.call(xAxis.scale(lastEventTransform.rescaleX(x)));
gY.call(yAxis.scale(lastEventTransform.rescaleY(y)));
// Create new scales that incorporate current zoom transform on original scale
var xt = lastEventTransform.rescaleX(x),
yt = lastEventTransform.rescaleY(y);
// Apply new scale to create new definition of d3.line method for path drawing of line plots
var line = d3.line()
.x(function(d) { return xt(d.x); })
.y(function(d) { return yt(d.y); });
// Update all line-plot elements with new line method that incorporates transform
innerSvg.selectAll(".line")
.attr("d", function(d) { return line(d.values); });
// Update any scatter points if you are also plotting them
innerSvg.selectAll(".dot")
.attr("cx", function(d) {return xt(d.x); })
.attr("cy", function(d) {return yt(d.y); });
}
Note: gX and gY are just the d3 g group elements that had the axes originally called on them during setup of the plot.
This Zoom function tricks the domain to change and then redraws the lines on the new domain. Its the best i can manage for now... but hardly elegant!
function zoomed() {
var t = d3.event.transform;
// var t = d3.event.scaleBy;
var xt = t.rescaleY(y);
domain = yAxis.scale().domain();
g.select(".axis--y").call(yAxis.scale(xt));
g.selectAll("path.line").attr("d", function(d) {
if ( d3.max(d.values.map(function(dd) { return dd.engagement } )) > domain[1] ) {
return null
}
else {
return line(d.values)
}
});
if (domain[1] > 50000000) {
max = 50000000
} else if ( domain[1] < 500 ) {
max = 500
} else {
max = domain[1]
}
y.domain([0, max]);
}
It zooms alright but it is prone to de-coupling the axis from what the lines are saying. If anyone has something better let me know!

How can I convert this D3 multi-series line chart x-axis to use sequential numbers instead of date values?

I am trying convert this chart I made, disregard the styles, using Highcharts, to this D3 multi-line chart.
This is the code for the d3 viz.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis--x path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 80, bottom: 30, left: 50},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.timeParse("%Y%m%d");
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.temperature); });
d3.tsv("data.tsv", type, function(error, data) {
if (error) throw error;
var cities = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {date: d.date, temperature: d[id]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(cities, function(c) { return d3.min(c.values, function(d) { return d.temperature; }); }),
d3.max(cities, function(c) { return d3.max(c.values, function(d) { return d.temperature; }); })
]);
z.domain(cities.map(function(c) { return c.id; }));
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Temperature, ºF");
var city = g.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return z(d.id); });
city.append("text")
.datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "10px sans-serif")
.text(function(d) { return d.id; });
});
function type(d, _, columns) {
d.date = parseTime(d.date);
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
}
</script>
I would like to keep the data in a tsv and just convert the x-axis from using dates to using the raw numbers seen in the Highchart series. So instead of city, date, and temperature it would be batter, game number, and slugging percentage. I would also like to make it so that the lines end, like in the Highchart example, once there is no corresponding value, and not just have the line dip down to 0. It would also be nice if I could keep the hover effect from the Highchart example.
Unfortunately, I have little idea in how to achieve this. I know that I have to use a different function than the parseTime function currently in the script but that's about as far as I've gotten.
Change your scale to:
var x = d3.scaleLinear().range([0, width])
And don't parse to a date:
function type(d, _, columns) {
d.date = +d.date;
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
}

Display Pie chart, Grouped and Stacked bar chart in one page

I want to display two charts in one page.The Pie chart gets displayed but the Grouped and Stacked bar chart is not displaying . I tried to change the Id name in but no luck :( . Will appreciate if anyone helps me with the code correction .
/* Display Matrix Chart ,Pie chart, Grouped and Stacked bar chart in one page */
<apex:page showHeader="false">
<apex:includeScript value="{!URLFOR($Resource.jquery1)}"/>
<apex:includeScript value="{!URLFOR($Resource.D3)}"/>
<apex:includeScript value="{!URLFOR($Resource.nvD3)}"/>
<div id="body" height="50%" width="100px" ></div> // Id for Pie chart
<div id="body1" height="30%" width="90px"></div> // Id for Stacked and Grouped bar chart
<script>
// Matrix Chart starts here
var drawChart = function(divId,matrixReportId) {
$.ajax('/services/data/v29.0/analytics/reports/'+matrixReportId,
{
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', 'Bearer {!$Api.Session_ID}');
},
success: function(response) {
console.log(response);
var chart = nv.models.multiBarChart();
var chartData = [];
document.getElementById(divId).innerHTML = '';
$.each(response.groupingsDown.groupings, function(di, de) {
var values = [];
chartData.push({"key":de.label, "values": values});
$.each(response.groupingsAcross.groupings, function(ai, ae) {
values.push({"x": ae.label, "y": response.factMap[de.key+"!"+ae.key].aggregates[0].value});
});
});
d3.select('#'+divId).datum(chartData).transition().duration(100).call(chart);
window.setTimeout(function(){
drawChart(divId,matrixReportId);
}, 5000);
}
}
);
};
$(document).ready(function(){
drawChart('chart','00O90000005SSHv');
});
// Pie Chart Starts here
var width =250 ,
height = 450,
radius = Math.min(width, height) / 2;
var data = [{"age":"<5","population":2704659},{"age":"14-17","population":2159981},
{"age":"14-17","population":2159981},{"age":"18-24","population":3853788},
{"age":"25-44","population":14106543},{"age":"45-64","population":8819342},
{"age":">65","population":612463}];
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc().outerRadius(radius - 10).innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
var svg = d3.select("#body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.age); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.age; });
// Grouped and Stacked Bar Chart starts here
var n = 2, // number of layers
m = 10, // number of samples per layer
stack = d3.layout.stack(),
layers = stack(d3.range(n).map(function() { return bumpLayer(m, .1); })),
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var margin = {top: 40, right: 10, bottom: 20, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([0, width], .08);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(0)
.tickPadding(6)
.orient("bottom");
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 + ")");
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0);
rect.transition()
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
if (this.value === "grouped") transitionGrouped();
else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d, i, j) { return x(d.x) + x.rangeBand() / n * j; })
.attr("width", x.rangeBand() / n)
.transition()
.attr("y", function(d) { return y(d.y); })
.attr("height", function(d) { return height - y(d.y); });
}
function transitionStacked() {
y.domain([0, yStackMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.transition()
.attr("x", function(d) { return x(d.x); })
.attr("width", x.rangeBand());
}
// Inspired by Lee Byron's test data generator.
function bumpLayer(n, o) {
function bump(a) {
var x = 1/(.1 + Math.random()),
y = 2*Math.random()-.5,
z = 10/(.1 + Math.random());
for (var i = 0; i < n; i++) {
var w = (i/n- y) * z;
a[i] += x * Math.exp(-w * w);
}
}
var a=[],i;
for (i = 0; i < n; ++i) a[i] = o + o * Math.random();
for (i = 0; i < 5; ++i) bump(a);
return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; });
}
</script>
<svg id="chart" height="50%" width="500px" ></svg> // Id for Matrix chart
</apex:page>
Thanks in advance
first, as we discussed here (d3 Donut chart does not render), you should position your
<div id="body1" height="30%" width="90px"></div>
above the script.
second, you are missing the # in your second svg-declaration to correctly select the div by its id, it has to be
var svg = d3.select("#body1").append("svg")
you could also think about naming the second svg differently (eg svg2) so you don't override your first svg-variable (in case you want to do something with it later).

Categories

Resources