Bar-chart zoom confuses data filtering on DC chart - d3.js

I have quite a long period of observations (tornadoes) 800-2016 (about 2000 inputs). To visualize them on one bar chart I use mouseZoomable (true) function for DC.js but it starts to mess filtering: User can't properly select interval on the chart, it slips all the time and seems as asynchronized.
Sorry I am very new to D3 and DC.js and couldn't find an answer.
Here is my code:
var ndx = crossfilter(records);
var YearDim = ndx.dimension(function(d) { return d["Year"]; });
var numRecordsByYear = YearDim.group();
var minYear = YearDim.bottom(1)[0]["Year"];
var maxYear = YearDim.top(1)[0]["Year"];
var timeChart = dc.barChart("#time-chart");
timeChart
.width(600)
.height(140)
.margins({top: 10, right: 50, bottom: 40, left: 40})
.dimension(YearDim)
.group(numRecordsByYear)
.transitionDuration(500)
.mouseZoomable(true)
.x(d3.time.scale().domain([minYear, maxYear]))
.zoomOutRestrict([true])
.elasticY(true)
Will be very gratefull for helping or suggesting another way to visualize long and irregular time interval.

Related

How to make columns wide (not pencil-thin) on dc BarChart with time x-axis

I have a barchart set-up as follows:
function makeGraphs(recordsJson, factorText) {
// Clean data
var records = jQuery.parseJSON(recordsJson);
let parseDate = d3.timeParse("%Y-%m-%d");
records.forEach(function(d) {
d.date = parseDate(d.date);
d.factor = d.factor == true ? 1 : 0;
});
// Create a Crossfilter instance
var ndx = crossfilter(records);
// Define Dimensions
var dateDim = ndx.dimension(d => d.date);
// Group Data
var dateGroup = dateDim.group();
var factorGroup = dateDim.group().reduceSum(dc.pluck('factor'));
var all = ndx.groupAll();
// Define values (to be used in charts)
var minDate = dateDim.bottom(1)[0]["date"];
var maxDate = dateDim.top(1)[0]["date"];
// Chart
const timeChart = new dc.CompositeChart("#time_chart");
timeChart
.height(300)
.x(d3.scaleTime().domain([minDate,maxDate]))
.yAxisLabel("Number of Lines")
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
new dc.LineChart(timeChart)
.dimension(dateDim)
.colors('blue')
.group(dateGroup, "Total")
.curve(d3.curveLinear),
new dc.BarChart(timeChart)
.dimension(dateDim)
.colors('red')
.centerBar(true)
.gap(1)
.alwaysUseRounding(true)
.group(factorGroup, factorText)
.xUnits(d3.timeDays)
])
.brushOn(false)
.render();
The barchart is always displayed with pencil-thin columns, representing correctly the count of 'factor' items in that date. This occurs no matter the size of day range I apply.
I have tried .xUnits(d3.timeWeeks) to see if it can be made to display better, to no avail (actually, it still displays daily totals, suggesting I need to construct an aggregate function). However, I really need daily counts.
As advised by Gordon, a CompositeChart needs to have its .xUnit property defined in the main chart section, not under one of its sub-chart types:
timeChart
.height(300)
.x(d3.scaleTime().domain([minDate,maxDate]))
.yAxisLabel("Number of Lines")
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.xUnits(d3.timeDays)
.compose([
new dc.LineChart(timeChart)
.dimension(dateDim)
.colors('blue')
.group(dateGroup, "Total")
.curve(d3.curveLinear),
new dc.BarChart(timeChart)
.dimension(dateDim)
.colors('red')
.centerBar(true)
.gap(1)
.alwaysUseRounding(true)
.group(factorGroup, factorText)
])
.brushOn(false)
.render();
The bar-chart component then displays with a proper width.

DC.js - Custom ordering of ordinal scale line chart

Please forgive my english.
I am trying to create a line chart with ordinal scale and brushon function. I successfully set brushing on ordinal thanks to this method :
https://dc-js.github.io/dc.js/examples/brush-ordinal.html
And now I need to set the x-axis values order.
My data is like this :
[{"id_commune":4,"datation":"-30/-20","effectif":0.09,"commune":"Frejus","lieu_dit":"Les Aiguieres","departement":"83","pays":"FR","longitude":6.73703399,"latitude":43.433152,"quantite":1,"id_datation":1},
{"id_commune":4,"datation":"-20/-10","effectif":0.09,"commune":"Frejus","lieu_dit":"Les Aiguieres","departement":"83","pays":"FR","longitude":6.73703399,"latitude":43.433152,"quantite":1,"id_datation":2},
{"id_commune":4,"datation":"-10/1","effectif":0.09,"commune":"Frejus","lieu_dit":"Les Aiguieres","departement":"83","pays":"FR","longitude":6.73703399,"latitude":43.433152,"quantite":1,"id_datation":3},
{"id_commune":7,"datation":"20/30","effectif":0.33,"commune":"Nimes","lieu_dit":"Solignac","departement":"30","pays":"FR","longitude":4.36005399,"latitude":43.836699,"quantite":1,"id_datation":6},{"id_commune":6,"datation":"20\/30","effectif":0.6,"commune":"Muralto","lieu_dit":"Liverpool b","departement":"TI","pays":"CH","longitude":8.80560809,"latitude":46.1729618,"quantite":1,"id_datation":6},
{"id_commune":4,"datation":"20/30","effectif":0.09,"commune":"Frejus","lieu_dit":"Les Aiguieres","departement":"83","pays":"FR","longitude":6.73703399,"latitude":43.433152,"quantite":1,"id_datation":6},{"id_commune":1,"datation":"20/30","effectif":0.14,"commune":"Aislingen","lieu_dit":"NP","departement":"Lkr. Dillingen an der Donau BY","pays":"DE","longitude":10.4559987,"latitude":48.5065603,"quantite":1,"id_datation":6},]
My crossfilter dimension and group look like this :
var ndx = crossfilter(records)
var graphDim = ndx.dimension(function(d) {return d.datation});
var graphGroup = graphDim.group().reduceSum(function(d) {return d.effectif;});
And the chart :
lineChart
.width(950)
.height(350)
.margins({top: 10, right: 50, bottom: 35, left: 30})
.dimension(graphDim)
.keyAccessor(function(kv) { return graphGroup.ord2int(kv.key); })
.group(graphGroup)
.x(d3.scaleLinear().domain(linear_domain))
.xAxisLabel("Chronologie")
.yAxisLabel("Effectif")
.brushOn(true)
.renderArea(true)
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.elasticY(true)
.yAxis().ticks(4);
Right now, I get this :
The result I am trying to accomplish is the same chart but with x-axis ticks values ordered like this :
"-30/-20
-20/-10
-10/1
..."
Any help appreciated. Thank you!
Welcome to Stack Overflow. For future reference, it helps if you can include a reproducible example, for example a jsFiddle.
I understood what you were doing because I wrote the ordinal brushing example, but you didn't include all the code, so it wouldn't be clear to others.
Here is a fiddle with complete code, and here is the relevant code:
var graphDim = ndx.dimension(function(d) {return d.datation});
var graphGroup = graphDim.group().reduceSum(function(d) {return d.effectif;});
graphGroup = ordinal_to_linear_group(sort_group(graphGroup, function(a, b) {
return d3.descending(a.value, b.value);
}));
line = dc.lineChart('#line');
var linear_domain = [-0.5, data.length - 0.5];
line
.width(950)
.height(350)
.margins({top: 10, right: 50, bottom: 35, left: 30})
.dimension(graphDim)
.keyAccessor(function(kv) { return graphGroup.ord2int(kv.key); })
.group(graphGroup)
.x(d3.scaleLinear().domain(linear_domain))
.xAxisLabel("Chronologie")
.yAxisLabel("Effectif")
.brushOn(true)
.renderArea(true)
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.elasticY(true)
line.yAxis().ticks(4);
line.xAxis()
.tickValues(d3.range(data.length))
.tickFormat(function(d) { return graphGroup.int2ord(d); });
Here is the (bad) result:
As its name implies, sort_group() creates a fake group sorted by the ordering function. Right now it's sorting by value, from highest to lowest:
sort_group(graphGroup, function(a, b) {
return d3.descending(a.value, b.value);
})
It looks like you want to sort by the numeric value of the first part of each key. You can change the sort accordingly:
sort_group(graphGroup, function(a, b) {
return d3.ascending(+a.key.split('/')[0], +b.key.split('/')[0]);
})
This splits each key by /, then takes the first item ([0]), then converts it to a number (+), before using d3.ascending to specify sorting from low to high.
The result:
And a working version of the fiddle.

mouseZoomable does not work with elasticX on line chart

I have code of lineChart
as below,
var ndx = crossfilter(data),
dim = ndx.dimension(function(d) {return d.index;}),
profitGrp = dim.group().reduceSum(function(d) { return d.profit});
var profitChart = dc.lineChart("#profit-chart")
.width($("#profit-chart").parent().width())
.height(400)
.dimension(dim)
.mouseZoomable(true)
.x(d3.scale.linear()).xAxisPadding(0.25).elasticX(true)
.group(profitGrp, "Profit")
.renderHorizontalGridLines(true)
.elasticY(true)
.legend(dc.legend().x(400).y(10).itemHeight(13).gap(5))
.margins({left: 100, right: 40, top: 40, bottom: 40})
.brushOn(false);
But I can't mouse zoom in/out.
What should I do? I think it is because of elasticX(true)
Right, zoom is contradictory to elasticX(true)
One trick, if you want "elastic once" behavior, is:
chart.on('postRender', function(chart) {
chart.elasticX(false);
});
http://dc-js.github.io/dc.js/docs/html/dc.baseMixin.html#on
In my case Gordon answer didn´t work.
So there is another aproach, try to use "preRedraw" event:
chart.on('preRedraw', function(chart) {
chart.elasticX(false);
});

dc.js - How set brush (with specific width) to barChart on load

On load i see this, all data set without filters:
Picture: what i have
I want to see this, brush(selector) picked last 21 day already on load:
Picture: what i want
Solved! click for answer
Here i saw similar question, but not works for me
dc.js - is it possible to show the user grab handles upon page load
var lineChart = dc.lineChart(".chart-line-graph");
var dateRangeChart = dc.barChart('.chart-date-range');
var hourDim = ndx.dimension(function(d) {return d.hour;});
var valueByHour = hourDim.group().reduceSum(function(d) {return d.value;});
var minDate = hourDim.bottom(1)[0].date;
var maxDate = hourDim.top(1)[0].date;
lineChart
.renderArea(true)
.width(1360)
.height(350)
.transitionDuration(200)
.margins({top: 30, right: 50, bottom: 45, left: 80})
.dimension(hourDim)
.mouseZoomable(true)
.rangeChart(dateRangeChart)
.brushOn(false)
.x(d3.time.scale().domain([minDate,maxDate]))
.elasticY(true)
.group(value)
.renderHorizontalGridLines(true);
var dayDim = ndx.dimension(function (d) {return d.day;});
var groupByDayDim = moveDays.group();
dateRangeChart
.width(1360)
.height(35)
.margins({top: 0, right: 50, bottom: 20, left: 80})
.dimension(moveDays)
.group(volumeByMonthGroup)
.centerBar(false)
.gap(1)
.x(d3.time.scale().domain([minDate,maxDate]))
.round(d3.time.week.round)
.alwaysUseRounding(true)
.xUnits(d3.time.days);
dateRangeChart
.yAxis().ticks(0);
If i add this:
lineChart
.filter(dc.filters.RangedFilter(d3.time.day.offset(xrange[1], -21), d3.time.day.offset(xrange[1], 1)))
Then barChart are filtered, but not selected
I solved my first problem!
Just added filter after all code (after renderAll()) and then call redrawAll()
Screenshot, how it looks like for now, on load charts

Error with .compose using dc.js

I am trying to create a composite of 2 line charts with dc.js.
But I get this error everytime:
Uncaught TypeError: timeChart.width(...).height(...).x(...).elasticY(...).margins(...).dimension(...).compose is not a function
it is a time series where I want to plot netCommercialPosition and netCommercialPosition as two seperate line. It works when I stack them but not when i want to use .compose.
I have followed several examples such as:
Dual Y axis line chart in dc.js
http://dc-js.github.io/dc.js/examples/series.html
So hopefully I use the .compose element correctly
my data set is a json with the following structure:
[{"CFTC_Commodity_Code": 1, "Net_Commmercial_Position": -113520, "Report_Date_as_MM_DD_YYYY": "14/07/2015", "Net_Fund_Position": -12246, "Price": 583.5, "Net_ Commmercial_Position": 3877, " },{…}]
here is my code:
d3.json("/donorschoose/COT", function (error, dataset1){
var ymdFormat = d3.time.format("%d/%m/%Y");
dataset1.forEach(function(p) {
p.Report_Date_as_MM_DD_YYYY = ymdFormat.parse(p.Report_Date_as_MM_DD_YYYY);
});
var COTProjects = dataset1;
var ndx = crossfilter(COTProjects);
var all = ndx.groupAll();
FilterDimension = ndx.dimension(function (d) {
return d.CFTC_Commodity_Code;
});
var dateDim = ndx.dimension(function(d) { return d.Report_Date_as_MM_DD_YYYY; });
var Prices = dateDim.group().reduceSum(function(d) {return d.Price; });
var netFundPosition = dateDim.group().reduceSum(function(d) {return d.Net_Fund_Position; });
var netCommercialPosition = dateDim.group().reduceSum(function(d) {return d.Net_Commmercial_Position; });
var minDate = dateDim.bottom(1)[0]["Report_Date_as_MM_DD_YYYY"];
var maxDate = dateDim.top(1)[0]["Report_Date_as_MM_DD_YYYY"];
var timeChart = dc.lineChart("#time-chart");
actualValuesChart = dc.lineChart(timeChart)
.group(netFundPosition)
normValuesChart = dc.lineChart(timeChart)
.group(netCommercialPosition)
timeChart
.width(650)
.height(260)
.x(d3.time.scale().domain([minDate, maxDate]))
.elasticY(true)
.margins({top: 0, right: 5, bottom: 20, left: 40})
.dimension(dateDim)
.compose([actualValuesChart,normValuesChart])
.transitionDuration(500)
.yAxis().ticks(4);
.brushOn(false)
FilterDimension.filter(1)
dc.renderAll();
});
Any help appreciated
thanks in advance
I found the solution, my problem was that I was trying to call compose() from a lineChart object instead of a compositeChart object.
dc.js-s doc

Resources