Visualizing Array as value of a Crossfilter dimension - d3.js

I'm working on a visualization tool for time series with multiple dimensions.
To simplify my case, each data-point has a dimension on type, clusterId and a set of months:
{
type: "green",
clusterId:42,
months:[1392185580000, 1394604780000, 1397279580000]
}, {
type: "red",
clusterId:43,
months:[1392185580000]
}
Now I would like to show the dates in a dc.barChart, which shows the months of all datasets as keys(bars), and the number of observations of each month as value of the bar.
In the given case, it would result in 3 bars, the first one with a height of 2, and the other with a height of 1.
How can I create this dimension and implement the grouping/reducing function?
You don't have to worry about filtering by this dimension, I already have a custom filter for this dimension. The only thing is displaying the bars on the barChart.

If i get this correctly, you need some code, that outputs: 1392185580000: 2, 1394604780000: 1, 1397279580000:1?
arr.forEach(function(d) {
d.months.forEach(function(month) {
if (!store.hasOwnProperty(month)) {
store[month]=0;
}
store[month]++;
});
});
demo fiddle

Related

How to show only limited number of records in box plot dc.js

I want to show the most recent 10 bins for box plot.
If a filter is applied to the bar chart or line chart, the box plot should show the most recent 10 records according to those filters.
I made dimension by date(ordinal). But I am unable to get the result.
I didn’t get how to do it with a fake group. I am new to dc.js.
The pic of scenario is attached. Let me know if anyone need more detail to help me.
in image i tried some solution by time scale.
You can do this with two fake groups, one to remove the empty box plots, and one to take the last N elements of the resulting data.
Removing empty box plots:
function remove_empty_array_bins(group) {
return {
all: function() {
return group.all().filter(d => d.value.length);
}
};
}
This just filters the bins, removing any where the .value array is of length 0.
Taking the last N elements:
function cap_group(group, N) {
return {
all: function() {
var all = group.all();
return all.slice(all.length - N);
}
};
}
This is essentially what the cap mixin does, except without creating a bin for "others" (which is somewhat tricky).
We fetch the data from the original group, see how long it is, and then slice that array from all.length - N to the end.
Chain these fake together when passing them to the chart:
chart
.group(cap_group(remove_empty_array_bins(closeGroup), 5))
I'm using 5 instead of 10 because I have a smaller data set to work with.
Demo fiddle.
This example uses a "real" time scale rather than ordinal dates. There are a few ways to do ordinal dates, but if your group is still sorted from low to high dates, this should still work.
If not, you'll have to edit your question to include an example of the code you are using to generate the ordinal date group.

dc.js Composite Graph - Plot New Line for Each Person

Good Evening Everyone,
I'm trying to take the data from a database full of hour reports (name, timestamp, hours worked, etc.) and create a plot using dc.js to visualize the data. I would like the timestamp to be on the x-axis, the sum of hours for the particular timestamp on the y-axis, and a new bar graph for each unique name all on the same chart.
It appears based on my objectives that using crossfilter.js the timestamp should be my 'dimension' and then the sum of hours should be my 'group'.
Question 1, how would I then use the dimension and group to further split the data based on the person's name and then create a bar graph to add to my composite graph? I would like for the crossfilter.js functionality to remain intact so that if I add a date range tool or some other user controllable filter, everything updates accordingly.
Question 2, my timestamps are in MySQL datetime format: YYYY-mm-dd HH:MM:SS so how would I go about dropping precision? For instance, if I want to combine all entries from the same day into one entry (day precision) or combine all entries in one month into a single entry (month precision).
Thanks in advance!
---- Added on 2017/01/28 16:06
To further clarify, I'm referencing the Crossfilter & DC APIs alongside the DC NASDAQ and Composite examples. The Composite example has shown me how to place multiple line/bar charts on a single graph. On the composite chart I've created, each of the bar charts I've added a dimension based off of the timestamps in the data-set. Now I'm trying to figure out how to define the groups for each. I want each bar chart to represent the total time worked per timestamp.
For example, I have five people in my database, so I want there to be five bar charts within the single composite chart. Today all five submitted reports saying they worked 8 hours, so now all five bar charts should show a mark at 01/28/2017 on the x-axis and 8 hours on the y-axis.
var parseDate = d3.time.format('%Y-%m-%d %H:%M:%S').parse;
data.forEach(function(d) {
d.timestamp = parseDate(d.timestamp);
});
var ndx = crossfilter(data);
var writtenDimension = ndx.dimension(function(d) {
return d.timestamp;
});
var hoursSumGroup = writtenDimension.group().reduceSum(function(d) {
return d.time_total;
});
var minDate = parseDate('2017-01-01 00:00:00');
var maxDate = parseDate('2017-01-31 23:59:59');
var mybarChart = dc.compositeChart("#my_chart");
mybarChart
.width(window.innerWidth)
.height(480)
.x(d3.time.scale().domain([minDate,maxDate]))
.brushOn(false)
.clipPadding(10)
.yAxisLabel("This is the Y Axis!")
.compose([
dc.barChart(mybarChart)
.dimension(writtenDimension)
.colors('red')
.group(hoursSumGroup, "Top Line")
]);
So based on what I have right now and the example I've provided, in the compose section I should have 5 charts because there are 5 people (obviously this needs to be dynamic in the end) and each of those charts should only show the timestamp: total_time data for that person.
At this point I don't know how to further breakup the group hoursSumGroup based on each person and this is where my Question #1 comes in and I need help figuring out.
Question #2 above is that I want to make sure that the code is both dynamic (more people can be handled without code change), when minDate and maxDate are later tied to user input fields, the charts update automatically (I assume through adjusting the dimension variable in some way), and if I add a names filter that if I unselect names that the chart will update by removing the data for that person.
A Question #3 that I'm now realizing I'll want to figure out is how to get the person's name to show up in the pointer tooltip (the title) along with timestamp and total_time values.
There are a number of ways to go about this, but I think the easiest thing to do is to create a custom reduction which reduces each person into a sub-bin.
First off, addressing question #2, you'll want to set up your dimension based on the time interval you're interested in. For instance, if you're looking at days:
var writtenDimension = ndx.dimension(function(d) {
return d3.time.hour(d.timestamp);
});
chart.xUnits(d3.time.hours);
This will cause each timestamp to be rounded down to the nearest hour, and tell the chart to calculate the bar width accordingly.
Next, here's a custom reduction (from the FAQ) which will create an object for each reduced value, with values for each person's name:
var hoursSumGroup = writtenDimension.group().reduce(
function(p, v) { // add
p[v.name] = (p[v.name] || 0) + d.time_total;
return p;
},
function(p, v) { // remove
p[v.name] -= d.time_total;
return p;
},
function() { // init
return {};
});
I did not go with the series example I mentioned in the comments, because I think composite keys can be difficult to deal with. That's another option, and I'll expand my answer if that's necessary.
Next, we can feed the composite line charts with value accessors that can fetch the value by name.
Assume we have an array names.
compositeChart.shareTitle(false);
compositeChart.compose(
names.map(function(name) {
return dc.lineChart(compositeChart)
.dimension(writtenDimension)
.colors('red')
.group(hoursSumGroup)
.valueAccessor(function(kv) {
return kv.value[name];
})
.title(function(kv) {
return name + ' ' + kv.key + ': ' + kv.value;
});
}));
Again, it wouldn't make sense to use bar charts here, because they would obscure each other.
If you filter a name elsewhere, it will cause the line for the name to drop to zero. Having the line disappear entirely would probably not be so simple.
The above shareTitle(false) ensures that the child charts will draw their own titles; the title functions just add the current name to those titles (which would usually just be key:value).

Multi line ordinal chart in dc.js

I am stuck on a problem here. Could be simple though but i am having a tough time figuring it out. I want to show multiple lines on a dc composite chart.
My data is like this:
{ Name: Mike, mark1: 26.9, mark2: 62.3 },
{ Name: John, mark1: 23.5, mark2: 60.3 },
{ Name: Firen, mark1: 24.3, mark2: 62.5 }
I need the name plotted against X axis and mark1 and mark2 plotted as lines against the Y axis. I found a fiddle here which uses a linear scale to achieve the same result. http://jsfiddle.net/anmolkoul/mzx6mnru/3/
But it uses a linear scale as the base dimension is numerical. My base dimension is a string and hence not working with the same code. I figured it is due to the scale definition that i am using. Here is the fiddle that i need help with: http://jsfiddle.net/anmolkoul/pjLoh1az/1/
I have currently defined my x axis as
.x(d3.scale.ordinal().domain(nameDimension))
.xUnits(dc.units.ordinal)
I think this is where it is going wrong. I have two supplementary questions as well:
Once this is done, how to assign different colors to the lines ( it should reflect in the legend as well)
I was taking a look at the dc.js series chart, what does this line of code do?
runDimension = ndx.dimension(function(d) {return [+d.Expt, +d.Run]; });
Does it pivot the two dimensions? or is it just a quicker way of creating two crossfilter dimensions.
Thank you for the help!`
You can get the ordinal values with :
nameDimension.top(Infinity).map(function(d) {return d.Name}))
which returns ["Mike", "John", "Firen"] , then use it for the ordinal domain.
But it's not necessary, it's calculated automatically :
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
For colors, you can use :
dc.lineChart(lineChart1).group(mark1Group,"Mark 1").colors("#FF0000")
Here is a fiddle with those modifications : http://jsfiddle.net/1exo25u9/

NVD3 X axis incorrect ordering (dates)

I'm trying to visualize 2 series but when I visualize them together, the dates don't go in sequential order anymore.
Here is the fiddle: http://jsfiddle.net/hohenheim/6R7mu/21/ Notice the weird x-axis.
Is there a way to fix the x axis on nvd3?
The data looks like this:
data1 = [{
"date": 1396828800,
"impressions": 49145385
}, {
"date": 1396915200,
"impressions": 46704447
} ....
The NVD3 "multiBarChart" uses an ordinal (category) scale, so it will only display the x-values you give it, in the order in which they are added to the scale. Because your two series only partially overlap on the x axis that's causing problems.
Unlike other NVD3 chart types, the multiBarChart doesn't give you the option of setting your own scale -- it needs to use an ordinal scale in order to generate even widths for the bars. However, you can set the x domain (the list of categories to use for each bar), by calling chart.xDomain(arrayOfXValues).
The array will need to be an ordered array of Date values that spans your data. In order to generate it, you'll need the d3 time intervals range functions. You might also need the d3.extent function to find your max and min values.

Setting jqplot minimum bar height

I want to set a minimum height on a stacked bar so that it always shows no matter how small the value.
Example:
If the stacked values are relatively close, the bars show no problem:
http://i41.tinypic.com/b88rc6.png
But if the values differ by a lot, then the smaller bar is not visible on the graph:
http://i40.tinypic.com/15rxwz6.png
I tried reading through the docs but didn't find any options for this. Help is appreciated!
Try to assing the smaller values to an other yaxis, so the graph will show 2 different yaxes, the first with a big interval, the second with an interval that permits to show correctly the smaller values (if you want to show the difference between the two kind of data, you could use min and max on the smaller yaxis).
axes: { [...], yaxes: { -big values- }, y2axes: { -small values-, min: -10, max: 10 }}
Don't know if that answer would be useful to you.

Resources