How to filter measures in crossfilter? - dc.js

I would like to make measures and have one measure.
So now I would like to filter this measure by other dimensions. like:
var regionDimLocation1:any = regionALocation1.dimension(function (d:any) {
return d.region;
How to combine dimensions?
var volume = ndx1.groupAll().reduceSum(function(d:any) {return d.size;});
I dont find any example how to display filtered measures on the web.
Does anyone have an example?
Thanks

Related

Is there a way to filter a dimension based on the value of another field?

I'm building a data dashboard for a project and I want to be able to compare data from two distinct groups in the same dataset.
My dataset looks like this:
Number,Name,Gender,Race,Height,Publisher,Alignment,Weight,Superpower,Strength,Costume Colour
1,A-Bomb,Male,Metahuman,203,Marvel Comics,Good,441,Superhuman Strength,10,None
2,Abin Sur,Male,Alien,185,DC Comics,Good,90,Cosmic Power,40,Green
3,Abomination,Male,Metahuman,203,Marvel Comics,Bad,441,Superhuman Strength,10,None
4,Abraxas,Male,Cosmic Entity,1000,Marvel Comics,Bad,1000,Reality Warping,40,Green
5,Absorbing Man,Male,Metahuman,193,Marvel Comics,Bad,122,Matter Duplication,5,None
6,Adam Strange,Male,Human,185,DC Comics,Good,88,None,0,Red
I want to create two separate selectMenus which list the character names, but with each of the two menus filtered on Publisher name.
So one drop down will have all the characters associated with Marvel Comics, and the other will have all the characters associated with DC Comics.
Once this is set up, the idea is that the dashboard can then show a set of graphs which work as a comparison between the two characters that have been selected - so I don't want the entire dataset to be split out, I just want the selection to be filtered by Publisher.
I've been through dozens of different Stack Overflow threads about similar stuff but still struggling. I've created the dimensions for character name and character publisher but I'm getting really lost trying to use one to filter the other.
This is what I've got so far (ignore the costume color stuff, that's for something further down the line) - the data in 'heroes-information.csv' is in the same format I shared above.
// Bring in data from csv files
Promise.all([d3.csv("../data/heroes-information.csv"), d3.csv("../data/costume-colors.csv")])
.then(function(data) {
// Tidy data before use
data.forEach(function(d) {
d.Height = +d.Height;
d.Weight = +d.Weight;
d.Strength = +d.Strength;
});
// Bring in Heroes data
var ndx = crossfilter(data[0]);
// Bring in costume color data
var ndxcol = crossfilter(data[1]);
// Create colorScale to dynamically color pie chat slices
var colorScale = d3.scaleOrdinal()
.domain(data[1].map(row => row.Name))
.range(data[1].map(row => row.RGB));
// Define chart type
var dccomicsSelector = dc.selectMenu('#dccomics-selector');
// Define chart dimension
var character = ndx.dimension(dc.pluck('Name'));
var characterPublisher = ndx.dimension(dc.pluck('Publisher'));
var dccomicsCharacters = character.group();
var dccomicsPublisher = characterPublisher.group();
dccomicsSelector
.dimension(character)
.group(dccomicsCharacters);
dc.renderAll();
});
I'm probably missing something really obvious but I'm fairly new to DC.js and Crossfilter so a bit lost in the weeds with this one, any help would be much appreciated!

Get only non-filtered data from dc.js chart (dimension / group)

So this is a question regarding a rather specific problem. As I know from Gordon, main contributor of dc.js, there is no support for elasticY(true) function for logarithmic scales.
So, after knowing this, I tried to implement my own solution, by building a workaround, inside dc.js's renderlet event. This event is always triggered by a click of the user onto the barchart. What I wanted to do is this:
let groupSize = this.getGroupSize(fakeGroup, this.yValue);
let maximum = group.top(1)[0].value;
let minimum = group.top(groupSize)[groupSize-1].value;
console.log(minimum, maximum);
chart.y(d3.scale.log().domain([minimum, maximum])
.range(this.height, 0)
.nice()
.clamp(true));
I thought, that at this point the "fakeGroup" (which is just group.top(50)) contains only the data points that are NOT filtered out after the user clicked somewhere. However, this group always contains all data points that are in the top 50 and doesn't change on filter events.
What I really wanted is get all data points that are NOT filtered out, to get a new maximum and minimum for the yScale and rescale the yAxis accordingly by calling chart.y(...) again.
Is there any way to get only data rows that are still in the chart and not filtered out. I also tried using remove_empty_bins(group) but didn't have any luck with that. Somewhere is always all() or top() missing, even after giving remove_empty_bins both functions.
This is how i solved it:
I made a function called rescale(), which looks like this:
rescale(chart, group, fakeGroup) {
let groupSize = this.getGroupSize(fakeGroup, this.yValue);
let minTop = group.top(groupSize)[groupSize-1].value;
let minimum = minTop > 0 ? minTop : 0.0001;
let maximum = group.top(1)[0].value;
chart.y(d3.scale.log().domain([minimum, maximum])
.range(this.height, 0)
.nice()
.clamp(true));}
I think the parameters are pretty self-explanatory, I just get my chart, the whole group as set by dimension.group.reduceSum and a fake group I created, which contains the top 50 elements, to reduce bar count of my chart.
The rescale() method is called in the event listener
chart.on('preRedraw', (chart) => {
this.rescale(chart, group, fakeGroup);
}
So what I do is re-defining (re-setting min and max values regarding filtered data) the charts yAxis everytime the chart gets redrawn, which happens to also be every time one of my charts is filtered. So now, the scale always fits the filtered data the chart contains after filtering another chart.

How to generate line chart with discrete value, not time serial, as x-axis

Sorry for this silly question, but I am lost and hope any other expert can help me.
I need to draw a bar chart, but the x axis is NOT time series, it is discrete values.
var ndx = crossfilter( self.getStatus( contracts ));
var skuDim = ndx.dimension( function(d){
return d.sku;
});
var skuDimCount = skuDim.group().reduceCount();
var chartLineContractSku= dc.barChart("#chart-line-contract-sku");
chartLineContractSku
.width(500)
.height(200)
.dimension(skuDim)
.group(skuDimCount)
.x(d3.scale.linear().domain(skus))
.legend(dc.legend());
the skus is:
array of sku:
["PAR-ND-SRX1-SPCNPC", "SVC-ND-M10i", "SVC-CP-SRX3400", "SVC-ND-SRX3400", "SVC-ND-SRX3-IOC", "SVC-CP-SRX3-IOC", "SVC-CP-SRX3-NPC", "SVC-3-ND-SRX3-IOC", "SVC-CP-SRX3-SPC", "SVC-ND-SRX3-SPC"]
which is used to group dimension the input data.
But the output graph is always empty.
Can anyone tell me how to fix this? And how to generate the barChart with discrete value? Also, another question is how to make the label vertical? as we have quite a lot skus.
Thanks
Gordon is correct in saying that you should use ordinal scale to get your desired result. Replace your d3.scale.linear code with
x(d3.scale.ordinal().domain(skus))
Look at this as an example
http://bost.ocks.org/mike/bar/3/

Extracting a selection of countries from JSON file

I'm using this great example: Countries By Area
However I'm wanting to modify this code for my own use and project only a selection of chosen countries.
I've managed to read the JSON file into an array, and the code is now looking through that array but I don't see any way on rendering a country (or countries) if a criteria is met.
For example, if I want to simply render the country, who has an ID of 533, I don't see any way of attaching a condition.
Can anyone shed any light on how I may be able to do this.
Have edited my original question here as I've managed to do it, but I'm sure there's a more elegant way of achieving it:
Original code was:
var svg = d3.select("#map").selectAll("svg")
.data(topojson.feature(world, world.objects.countries).features)
Which I've changed to:
var svg = chartDetails.plotArea.selectAll("svg")
.data(
function(d){
a = topojson.feature(tempWorld, tempWorld.objects.countries).features
var returnobject =[]
$.each(a, function (i, v) { if (v.id == 826) { returnobject.push(v) } });
return returnobject
})
826 refers to United Kingdom.
If your array looks like this:
var a = [];
a.push({id:1, name:'Argentina'});
a.push({id:2, name:'Bahamas'});
Then you might need to iterate it to find the key(id) you want.
Take a look at these solutions:

How to show "missing" rows in a rowChart using crossfilter and dc.js?

I'm using code similar to that in the dc.js annotated example:
var ndx = crossfilter(data);
...
var dayName=["0.Sun","1.Mon","2.Tue","3.Wed","4.Thu","5.Fri","6.Sat"];
var dayOfWeek = ndx.dimension(function (d) {
var day = d.dd.getDay();
return dayName[day];
});
var dayOfWeekGroup = dayOfWeek.group();
var dayOfWeekChart = dc.rowChart("#day-of-week-chart");
dayOfWeekChart.width(180)
.height(180)
.group(dayOfWeekGroup)
.label(function(d){return d.key.substr(2);})
.dimension(dayOfWeek);
The issue I've got is that only days of the week present in the data are displayed in my rowChart, and there's no guarantee every day will be represented in all of my data sets.
This is desirable behaviour for many types of categories, but it's a bit disconcerting to omit them for short and well-known lists like day and month names and I'd rather an empty row was included instead.
For a barChart, I can use .xUnits(dc.units.ordinal) and something like .x(d3.scale.ordinal.domain(dayName)).
Is there some way to do the same thing for a rowChart so that all days of the week are displayed, whether present in data or not?
From my understanding of the crossfilter library, I need to do this at the chart level, and the dimension is OK as is. I've been digging around in the dc.js 1.6.0 api reference, and the d3 scales documentation but haven't had any luck finding what I'm looking for.
Solution
Based on #Gordon's answer, I've added the following function:
function ordinal_groups(keys, group) {
return {
all: function () {
var values = {};
group.all().forEach(function(d, i) {
values[d.key] = d.value;
});
var g = [];
keys.forEach(function(key) {
g.push({key: key,
value: values[key] || 0});
});
return g;
}
};
}
Calling this as follows will fill in any missing rows with 0s:
.group(ordinal_groups(dayNames, dayOfWeekGroup))
Actually, I think you are better off making sure that the groups exist before passing them off to dc.js.
One way to do this is the "fake group" pattern described here:
https://github.com/dc-js/dc.js/wiki/FAQ#filter-the-data-before-its-charted
This way you can make sure the extra entries are created every time the data changes.
Are you saying that you tried adding the extra entries to the ordinal domain and they still weren't represented in the row chart, whereas this did work for bar charts? That sounds like a bug to me. Specifically, it looks like support for ordinal domains needs to be added to the row chart.

Resources