Working With Filters in Crossfilter - d3.js

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;});

Related

Aggregation to bucket consecutive records that have the same values

I am trying to build a Gantt chart based on a boolean field from a time series data. My records in an ES index looks like this:
{date: '2022-01-01', value: true}
{date: '2022-01-02', value: true}
{date: '2022-01-03', value: true}
{date: '2022-01-04', value: false}
{date: '2022-01-05', value: true}
{date: '2022-01-06', value: false}
{date: '2022-01-07', value: true}
{date: '2022-01-08', value: true}
{date: '2022-01-09', value: false}
{date: '2022-01-10', value: false}
{date: '2022-01-11', value: false}
{date: '2022-01-12', value: true}
{date: '2022-01-13', value: false}
{date: '2022-01-14', value: true}
{date: '2022-01-15', value: true}
{date: '2022-01-16', value: false}
{date: '2022-01-17', value: false}
{date: '2022-01-18', value: false}
{date: '2022-01-19', value: true}
{date: '2022-01-20', value: true}
I need to retrieve the start and end dates of each date range that starts with true and ends with true. As soon as false is encountered, the bucket has to be stopped. A new bucket has to be created when the next true is encountered.
The output would be something like this:
{start: '2022-01-01', end: '2022-01-03'}
{start: '2022-01-05', end: '2022-01-05'}
{start: '2022-01-07', end: '2022-01-08'}
{start: '2022-01-12', end: '2022-01-12'}
{start: '2022-01-14', end: '2022-01-15'}
{start: '2022-01-19', end: '2022-01-20'}
I'll then use these date ranges to plot a Gantt chart like this:
I looked at various bucket aggregations in ES but looks like none of them solve this type of problem. Am I overlooking? Is this possible with Elasticsearch aggregation?

Multiple layers with dc.js series charts

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.

c3js: Uncaught TypeError: c.forEach is not a function

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

ReferenceError: dc is not defined

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"

Trying to create a grouped and stacked bar chart but having some trouble

I'm trying to create a grouped and stacked bar chart but having a little trouble understanding how to do this with c3js. This tutorial is great and shows me a nicely grouped chart.
http://blog.trifork.com/2014/07/29/creating-charts-with-c3-js/
I'm looking for something similar but to have those stacked 2 data sets per bar. My data looks like:
columns: [
['x', 'data1', 'data2'],
['Jan', {data1: 20, data2: 80}, {data1: 80, data2: 20}, {data1: 20, data2: 80}]
['Feb', {data1: 20, data2: 80}, {data1: 80, data2: 20}, {data1: 20, data2: 80}]
['Mar', {data1: 20, data2: 80}, {data1: 80, data2: 20}, {data1: 20, data2: 80}]
]
I would like the chart to have groups of Jan, Feb, Mar. Then have 3 stacked bars labeled within each group. Any ideas if this is possible and how might it be possible, if at all.
Thanks!
Are you wanting something like this? -->
http://jsfiddle.net/3r39gknt/1/
If so, the data1 and data2 fields will need renamed per the example. If not, draw/scan a picture/sketch of what you'd expect - groups in c3 are the data elements involved in each stack, but I've interpreted rightly or wrongly your use of month 'groups' as being different points on the x-axis.
var chart = c3.generate({
data: {
columns: [ // randomish data
['data1', 30, 200, 200],
['data2', 130, 100, 100],
['data3', 230, 220, 200],
['data4', 230, 220, 200],
['data5', 230, 200, 210],
['data6', 230, 190, 200]
],
type: 'bar',
groups: [
['data1', 'data2'],
['data3', 'data4'],
['data5', 'data6'],
],
order: null,
colors: {
data1: '#f00',
data2: '#00f',
data3: '#f33',
data4: '#33f',
data5: '#f66',
data6: '#66f'
}
},
bar: {
width: {
ratio: 0.5 // this makes bar width 50% of length between ticks
}
},
axis: {
x: {
type: 'category',
categories: ['january', 'february', 'march']
}
}
});

Resources