I'm struggling to get a dc.js plot to work. The data object contains 2 value fields, and I need to designate val to scatterPlot and smooth to lineChart. Example below and jsfiddle here.
<div id="series-chart"></div>
<style>div{ display: inline; }</style>
<script>
var datum = [
{date: "2018-01-01T00:00:00Z", val: 0, smooth: 3, type: "set1"},
{date: "2018-01-02T00:00:00Z", val: 7, smooth: 4, type: "set1"},
{date: "2018-01-03T00:00:00Z", val: 1, smooth: 6, type: "set1"},
{date: "2018-01-04T00:00:00Z", val: 4, smooth: 8, type: "set1"},
{date: "2018-01-05T00:00:00Z", val: 8, smooth: 11, type: "set1"},
{date: "2018-01-06T00:00:00Z", val: 15, smooth: 14, type: "set1"},
{date: "2018-01-07T00:00:00Z", val: 12, smooth: 17, type: "set1"},
{date: "2018-01-08T00:00:00Z", val: 20, smooth: 20, type: "set1"},
{date: "2018-01-01T00:00:00Z", val: 15, smooth: 12, type: "set2"},
{date: "2018-01-02T00:00:00Z", val: 10, smooth: 11, type: "set2"},
{date: "2018-01-03T00:00:00Z", val: 14, smooth: 9, type: "set2"},
{date: "2018-01-04T00:00:00Z", val: 7, smooth: 7, type: "set2"},
{date: "2018-01-05T00:00:00Z", val: 12, smooth: 5, type: "set2"},
{date: "2018-01-06T00:00:00Z", val: 8, smooth: 4, type: "set2"},
{date: "2018-01-07T00:00:00Z", val: 8, smooth: 3, type: "set2"},
{date: "2018-01-08T00:00:00Z", val: 5, smooth: 2, type: "set2"}
];
var data = crossfilter(datum);
var typeSeriesDimension = data.dimension(function(d){ return [d.type, new Date(d.date)]; });
var totalGroupRaw = typeSeriesDimension.group().reduceSum(function(d){ return d.val; });
var totalGroupSmooth = typeSeriesDimension.group().reduceSum(function(d){ return d.smooth; });
var composite = dc.compositeChart("#series-chart");
composite
.width(400)
.height(400)
.x(d3.time.scale().domain([new Date("2018-01-01T00:00:00Z"), new Date("2018-01-08T00:00:00Z")]))
.dimension(typeSeriesDimension)
.compose([
dc.lineChart(composite)
.dimension(typeSeriesDimension)
.group(totalGroupSmooth)
// .seriesAccessor(function(d) {return d.key[0];})
.keyAccessor(function(d) {return d.key[1];})
.valueAccessor(function(d) {return d.value;}) ,
dc.scatterPlot(composite)
.dimension(typeSeriesDimension)
.group(totalGroupRaw)
// .seriesAccessor(function(d) {return d.key[0];})
.keyAccessor(function(d) {return d.key[1];})
.valueAccessor(function(d) {return d.value;})
])
.legend(dc.legend().x(100).y(10).itemHeight(20).gap(5));
dc.renderAll();
</script>
The categorical colour mapping doesn't work without seriesAccessor, and uncommented these lines cause script to fail with:
Uncaught TypeError: dc.lineChart(...).dimension(...).group(...).seriesAccessor is not a function
Also the lines are for some reason I can't establish plotting in the wrong order.
All round fail. Any assistance much appreciated!
That dimension makes sense for the scatter plot, but it doesn't make sense for the line chart.
A line chart expects to have simple scalar keys like dates. But the scatter plot expects to have 2D keys like [type, Date].
Luckily the composite chart doesn't really mind if its child charts use different dimensions.
So define a new time dimension and use that for the line chart's group:
var timeSeriesDimension = data.dimension(function(d) { return new Date(d.date);})
var totalGroupSmooth = timeSeriesDimension.group().reduceSum(function(d){ return d.smooth; });
And drop your key and value accessors from the line chart, because the default behavior is fine now:
//.keyAccessor(function(d) {return d.key[1];})
//.valueAccessor(function(d) {return d.value;}) ,
As for seriesAccessor, that's only used with dc.seriesChart, so I'm afraid it won't help you here. It's probably possible to combine a scatter plot with a series chart, because the series chart is just a specialization of the composite chart, but I haven't tried that - and I'd rather leave that for another question if you want to try that out.
Related
I'm trying to create a simple line-graph of points using a timeseries axis, but the chart doesn't render and my browser console shows the error:
c3.min.js Uncaught TypeError: c.forEach is not a function
Here's my javascript:
var chart = c3.generate({
bindto: '#chart',
data: {
json: [
{date: "2016-01-01", pageviews: 1},
{date: "2016-01-02", pageviews: 2},
{date: "2016-01-03", pageviews: 3},
{date: "2016-01-04", pageviews: 4},
{date: "2016-01-05", pageviews: 5}],
keys: {
x: 'date',
value: 'pageviews'
}
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d'
}
}
}
});
And here's a jsfiddle which uses the un-minified sources for D3 and C3 and gives the following browser console error: Uncaught TypeError: targetKeys.forEach is not a function
The data.keys.value is expecting an array, so simply wrap 'pageviews' in square brackets like so:
var chart = c3.generate({
bindto: '#chart',
data: {
json: [
{date: "2016-01-01", pageviews: 1},
{date: "2016-01-02", pageviews: 2},
{date: "2016-01-03", pageviews: 3},
{date: "2016-01-04", pageviews: 4},
{date: "2016-01-05", pageviews: 5}],
keys: {
x: 'date',
value: ['pageviews'] // needs to be an array
}
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d'
}
}
}
});
Working jsfiddle for completeness
I am facing a problem when trying to run a tutorial involving cross filter, d3, and dc. I am facing a problem similar to this. When I adjust the code to the proposed solutions. I still get an error
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/dc/1.7.0/dc.js"></script>
<script type="text/javascript" src="https://rawgithub.com/NickQiZhu/dc.js/master/web/js/crossfilter.js"></script>
</head>
<body style="background-color: #CBD0E7">
</body>
<div id="graphdiv"></div>
<div id="legenddiv"></div>
<div id="chart-line-hitsperday"></div>
<script type="text/javascript">
var data = [
{date: "2011-11-14T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:20:19Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:28:54Z", quantity: 1, total: 300, tip: 200, type: "visa"},
{date: "2011-11-14T16:30:43Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:48:46Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:53:41Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:54:06Z", quantity: 1, total: 100, tip: 0, type: "cash"},
{date: "2011-11-14T16:58:03Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:07:21Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:22:59Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:25:45Z", quantity: 2, total: 200, tip: 0, type: "cash"},
{date: "2011-11-14T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "visa"}
];
var instance = crossfilter(data);
var parseDate = d3.time.format("%m/%d/%Y").parse;
data.forEach(function(d) {
d.date = parseDate(d.date);
d.total = d.http_404 + d.http_302 + d.http_200;
});
print_filter("data");
var dateDim = instance.dimension(function(d) { return d.date; });
var hits = dateDim.group().reduceSum(function(d) { return d.total; });
var minDate = dateDim.bottom(1)[0].date;
var maxDate = dateDim.top(1)[0].date;
var hitslineChart = dc.lineChart("#chart-line-hitsperday"); // reference error number 1 is here.
hitslineChart
.width(500).height(200)
.dimension(dateDim)
.group(hits)
.x(d3.time.scale().domain([minDate,maxDate]));
dc.renderAll();
function print_filter(filter){
var f=eval(filter);
if (typeof(f.length) != "undefined") {}else{}
if (typeof(f.top) != "undefined") {f=f.top(Infinity);}else{}
if (typeof(f.dimension) != "undefined") {f=f.dimension(function(d) { return "";}).top(Infinity);}else{}
console.log(filter+"("+f.length+") = "+JSON.stringify(f).replace("[","[\n\t").replace(/}\,/g,"},\n\t").replace("]","\n]"));
}
</script>
</html>
and I get the following error:
"data(12) = [
\
{\"date\":null,\"quantity\":2,\"total\":null,\"tip\":0,\"type\":\"cash\"},
{\"date\":null,\"quantity\":1,\"total\":null,\"tip\":100,\"type\":\"visa\"}
]"
"error"
"ReferenceError: dc is not defined
at https://null.jsbin.com/runner:32:29"
As I keep adding dimensions to my data set, I encounter an issue of having my bar chart not displaying values of my "account" dimension.
Here is my current fiddle: https://jsfiddle.net/eugene_goldberg/yv3nnred/18/
var data = [
{date: "2015-03-25T12:10:00", resolver: "Group 1", escalation_pct: 20, bubble: 5,account: "Aon", region: "Americas", x: 1},
{date: "2015-03-25T12:15:00", resolver: "Group 2", escalation_pct: 10, bubble: 5,account: "Boeing", region: "AMEA", x: 1},
{date: "2015-03-25T12:25:00", resolver: "Group 3", escalation_pct: 50, bubble: 10,account: "Zurich", region: "Nordics", x: 1},
{date: "2015-03-25T12:40:00", resolver: "Group 4", escalation_pct: 30, bubble: 1,account: "Aon", region: "Americas", x: 1},
{date: "2015-03-25T12:35:00", resolver: "Group 5", escalation_pct: 5, bubble: 10,account: "Boeing", region: "Australia", x: 1},
{date: "2015-03-25T12:45:00", resolver: "Group 6", escalation_pct: 13, bubble: 1,account: "Zurich", region: "UK&I", x: 1}
];
var dimAccount = ndx.dimension(function(d) {return + d.account;});
var accountGroup = dimAccount.group().reduceSum(function(d) {return d.x});
var accountChart = dc.barChart("#account-chart");
accountChart.width(480)
.height(150)
.dimension(dimAccount)
.group(accountGroup)
.x(d3.scale.linear().domain([0, 10]))
.elasticY(true)
.xAxis().tickFormat();
It must be something very basic, I'm just not grasping it yet...
In my case, going with a row chart was what I needed.
A fully working fiddle is below:
https://jsfiddle.net/eugene_goldberg/yv3nnred/25/
var dimAccount = ndx.dimension(function(d) {return d.account;});
var accountGroup = dimAccount.group().reduceSum(function (d) {
return d.x;
});
var accountChart = dc.rowChart("#account-chart");
accountChart
.width(500)
.height(500)
.dimension(dimAccount)
.group(accountGroup);
A row chart works good. But another option is a selectmenu, because the height of the bar doesnt represent anything you could have a simple dropdown menu with the options.
(dc.selectMenu on 2.1 branch)
why my lineChart show the month in the xAxis instead of sunday?
Thanks for help =)
Here is my code:
var hitslineChart = dc.lineChart("#chart-line-hitsperday");
var data = [
{date: "12/27/2012", http_404: 2, http_200: 190, http_302: 100},
{date: "12/28/2012", http_404: 2, http_200: 10, http_302: 100},
{date: "12/29/2012", http_404: 1, http_200: 300, http_302: 200},
{date: "12/30/2012", http_404: 2, http_200: 90, http_302: 0},
{date: "12/31/2012", http_404: 2, http_200: 90, http_302: 0},
{date: "01/01/2013", http_404: 2, http_200: 90, http_302: 0},
{date: "01/02/2013", http_404: 1, http_200: 10, http_302: 1},
{date: "01/03/2013", http_404: 2, http_200: 90, http_302: 0},
{date: "01/04/2013", http_404: 2, http_200: 90, http_302: 0},
{date: "01/05/2013", http_404: 2, http_200: 90, http_302: 0},
{date: "01/06/2013", http_404: 2, http_200: 200, http_302: 1},
{date: "01/07/2013", http_404: 1, http_200: 200, http_302: 100}
];
var ndx = crossfilter(data);
var parseDate = d3.time.format("%m/%d/%Y").parse;
data.forEach(function(d) {
d.date = Date.parse(d.date);
d.total= d.http_404+d.http_200+d.http_302;
});
var dateDim = ndx.dimension(function(d) {return d.date;});
var hits = dateDim.group().reduceSum(function(d) {return d.total;});
var minDate = dateDim.bottom(1)[0].date;
var maxDate = dateDim.top(1)[0].date;
hitslineChart
.width(500).height(200)
.dimension(dateDim)
.group(hits)
.x(d3.time.scale().domain([minDate,maxDate]))
.xAxis().ticks(5)
.yAxisLabel("Hits per day");
dc.renderAll();![the result][1]
the result on the xAxis is : Thu27-Fri28-Sat29-Dec30-Mon31-2013-Wed02-Thu03-Fri04-Sat05-Jan06...
1) You will want to parse your incoming data using the date formatter:
d.date = Date.parse(d.date); \\ Old
d.date = parseDate.parse(d.date); \\ New
This will explicitly match the format of your input data.
2) If you want the data to be grouped by days, you will need to specify that in your dimension. The d3.time.day helper method will set the time to 00:00:00 on the data supplied. This makes the data group together like you want:
var dateDim = ndx.dimension(function(d) {return d.date;}); \\old
var dateDim = ndx.dimension(function(d) {return d3.time.day(d.date);}); \\ new
It's not clear whether you want a point per day (Jan 1, Jan 2, Jan 3, etc) or a point per day of the week (Sun, Mon, Tues). The d3.time.day method groups the date by the calendar day.
3) You can specify the tick format using the following method:
.xAxis().tickFormat(function(v) {return displayDate(v)});
Details about time formatting is available at https://github.com/mbostock/d3/wiki/Time-Formatting.
Here is a jsfiddle that adapts your code to display day of week on the x-axis:
http://jsfiddle.net/djmartin_umich/6V4Ey/
I hope this helps!
-DJ
I have just started working with crossfilter and d3.js ... I'm trying some snippets given in the API reference... I Have the following data
var payments = crossfilter([
{date: "2011-11-14T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:20:19Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:28:54Z", quantity: 1, total: 300, tip: 200, type: "visa"},
{date: "2011-11-14T16:30:43Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:48:46Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:53:41Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:54:06Z", quantity: 1, total: 100, tip: 0, type: "cash"},
{date: "2011-11-14T16:58:03Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:07:21Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:22:59Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:25:45Z", quantity: 2, total: 200, tip: 0, type: "cash"},
{date: "2011-11-14T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "visa"}
]);
I can create dimension via type as
var paymentsByTotal = payments.dimension(function(d) { return d.type; });
My question is that how can i filter an array of strings. I tried:
paymentsByTotal.filterRange(["cash","visa"]);
But I didn't get the expected result!
Any suggestions?
With the sourcecode in the master branch of Crossfilter.js, there is no provision for union of filters, you have to get the code from Jason Davies' union branch.
Then, you should be able to do paymentsByTotal.filter("cash","visa"); and get the desired output.
Appears that a filterFunction(function) has been added since the previous response, so that you can now do this with:
paymentsByTotal.filterFunction(function(d) { return d === "visa" || d === "cash" });
If you have an array of values, and you don't want to specify the explicit logic (i.e. d === "visa" ) for each item, then you can extend sai's filterFunction solution to check if the value is included in your array. Doing it this way just makes things easier if your array of items to filter is large or likely to change.
var items = ['Visa', 'Cash'];
paymentsByTotal.filterFunction(function(d) { return items.indexOf(d) > -1;});