I have data in the form {date, testscript, testcase, duration, clickcount}
I want to filter on testscript and date then get a count.
Like a distinct from testscript and date together:
X-Axis ManualTest: it should be 5, not 10 because there are always two records with the same date and testscript
X-Axis ErrorTest: it should be 2
How do I achieve this?
As mentioned above, you should probably use a helper library like Reductio to do this. In Reductio, you use exception aggregation:
var clickcountGroup = reductio()
.exception('date')
.exceptionCount(true)(testcaseDim.group());
testcaseRow
.margins({ top: 5, left: 10, right: 10, bottom: 20 })
.dimension(testcaseDim)
.group(clickcountGroup)
.valueAccessor(function(d){ return d.value.exceptionCount; })
.elasticX(true);
https://jsfiddle.net/esjewett/4r9t3ozr/
Related
I have few bar charts in my dashboard where i'm displaying labels on top of the bar. My users requesting to have thousand seperators for the labels.
The reason is the numbers are like over 7 digits and more for each bar.
Is there any workaround to achieve this functionality?
Here is my code:
Chart
.width(1700)
.height(200)
.margins({top: 5, left: 40, right: 20, bottom: 30})
.transitionDuration(1000)
.dimension(dateDim)
//.formatNumber(d3.format(","))
.group(dateGroup)
.renderLabel(true)
.brushOn(true)
.elasticY(true)
//.centerBar(true)
//.x(d3.time.scale().domain([minDate,maxDate]))
.yAxisLabel("Trades per day")
.xAxisLabel("Days")
.ordinalColors(['#215979'])
.x(d3.time.scale().domain([minDate, d3.time.day.offset(maxDate, 1)]))
.yAxis().tickFormat(d3.format('s'));
Chart.xUnits(function(){return 30;});
Chart
.on("postRedraw", function(chart, filter){
window.globalActiveFilters.SelectedTradeCount=chart.filters().join(",")
});
The labels on top of the bars are controlled by .label().
In the case of bar charts, the default behavior is overridden to use the total Y value of the stacked bar:
_chart.label(function (d) {
return dc.utils.printSingleValue(d.y0 + d.y);
}, false);
(source link)
You can specify your own label accessor which uses d3.format as suggested by #REEE, supplying it the total stacked value:
.label(d => d3.format(',')(d.y0 + d.y1))
Example fiddle.
Use d3-format, you can find the relevant docs here: https://github.com/d3/d3-format
Example use case (as shown in link above):
d3.format(",")(20000)
-> "20,000"
d3.format(",")(200000000)
-> "200,000,000"
I have data for every date from Jan 2018 and I am creating a stacked line chart out of that data. Every weekend the count of my data is zero, so every weekend it shows a dip in my graph (as data reaches to zero). I want to avoid that dip. I have a Date column as well as a Day column. The Day column has values from 1 to 7 representing each day of week (1 is Monday and 7 is Sunday). Can I modify my x axis or graph to show only weekdays data?
Fiddle
var data = [
{ Type: 'T', Date: "2018-01-01", DocCount: 10, Day: 1},
{ Type: 'E', Date: "2018-01-01", DocCount: 10, Day: 1},
...
]
chart
.height(350)
.width(450)
.margins({top: 10, right: 10, bottom: 5, left: 35})
.dimension(dateDim)
.group(tGroup)
.stack(eGroup, "E")
.valueAccessor( function(d) {
return d.value.count;
})
.transitionDuration(500)
.brushOn(false)
.elasticY(true)
.x(d3.time.scale().domain([minDateTime, maxDateTime]))
.xAxis().ticks(10).tickFormat(d3.format("s"));
A time scale is always going to be very literal about how it maps dates to x coordinates. It has no notion of "skipping dates".
Instead, I would suggest using an ordinal scale for this purpose. With an ordinal scale, you decide exactly what the input and output values will be. dc.js will also help you out by automatically determining the input (domain) values.
Tell the chart to use an ordinal scale like this:
chart
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
Remove any empty dates like this. remove_empty_bins is from the FAQ but I modified it to look at the count element.
function remove_empty_bins(source_group) {
return {
all:function () {
return source_group.all().filter(function(d) {
//return Math.abs(d.value) > 0.00001; // if using floating-point numbers
return d.value.count !== 0; // if integers only
});
}
};
}
var nz_tGroup = remove_empty_bins(tGroup),
nz_eGroup = remove_empty_bins(eGroup);
chart
.group(nz_tGroup)
.stack(nz_eGroup, "E")
Only question is, what if there is a weekday that happens not to have any data? Do you still want that to drop to zero? In that case I think you'd probably have to modify the filter in remove_empty_bins above.
Fork of your fiddle.
I'm trying to create a histogram using dc.js to display post counts aggregated by month. I've set up the crossfilter dimension and group to aggregate the data correctly but I can't get the widths of the resulting chart to fill the correct widths on the x axis.
My (simplified) code looks like this:
var ndx = crossfilter(items)
var dateDimension = ndx.dimension(d => d.date)
// group by month
var overviewGroup = dateDimension.group(d => {
if (d) {
return new Date(d.getUTCFullYear(), d.getUTCMonth())
}
})
var minMonth = new Date(dateDimension.bottom(1)[0].date.getUTCFullYear(), dateDimension.bottom(1)[0].date.getUTCMonth())
var maxMonth = new Date(dateDimension.top(1)[0].date.getUTCFullYear(), dateDimension.top(1)[0].date.getUTCMonth() + 1)
this.overviewChart
.height(60)
.minWidth(600)
.width(null)
.margins({top: 0, right: 10, bottom: 30, left: 40})
.dimension(dateDimension)
.centerBar(false)
.x(scale.scaleTime().domain([minMonth, maxMonth]))
.round(time.timeMonths.round)
.xUnits(time.timeMonths)
.group(overviewGroup)
.on('filtered', () => { this.displayItems = ndx.allFiltered() })
This displays the correct data on the y axis but the bars are only 1px wide. The chart in question is the smaller, lower chart - it's supposed to be the range chart for the higher-resolution one above (which aggregates posts by day and is displaying correctly) but that's for another question!
I get better results with .xUnits(() => { return overviewGroup.all().length - 1 }) which produces a wider bar and is closer to my intended result but it's still not correct:
I've pulled my code into a fiddle however in the fiddle it works more or less as expected: https://jsfiddle.net/y1qby1xc/9/
Using bar chart:
actionsChart /* dc.barChart('#volume-month-chart', 'chartGroup') */
.width(actionsWidth)
.height(240)
.margins({top: 10, right: 50, bottom: 30, left: 40})
.dimension(dateDimension)
//...
.elasticX(true)
.elasticY(true)
.gap(1)
.alwaysUseRounding(true)
.x(d3.time.scale().domain( [ minDate, maxDate ] ) )
.round(d3.time.day.round)
.xUnits(d3.time.days)
.renderHorizontalGridLines(true)
//.xAxisLabel( 'Dan')
//.xAxisPadding(2)
.xAxisLabel( "Datum")
//.yAxisLabel( "Akcije" ) // OK, but already in title
.xAxisPadding(1)
//nok in dc: //.tickFormat(d3.time.format("%Y-%m-%d"))
//.label( function(d){ return JSON.stringify(d); })
;
It gets Label on x-axis unreadable (too much characters next to each other.
How to put label each 5 or 7 days, and customize format (day in month number, no week day) ?
Thank you.
dc.js mostly uses d3v3's d3.svg.axis to draw its axes.
You may be looking for d3.svg.axis.ticks() and d3.svg.axis.tickFormat().
You can get at the d3 axis object that dc.js uses by calling chart.xAxis() but I advise doing it in a separate statement from your other chart initialization because it gets confusing when you you chain function calls but they return different objects.
So, something like (untested):
chart.xAxis()
.ticks(d3.time.days, 7)
.tickFormat(d3.time.format('%e'));
d3v3 time formatting specifiers
If you can't get the automatic tick generator to do what you want, you can always specify the exact list of ticks using .tickValues(). You'd want to do this before each render and redraw, so (again, untested):
function calc_ticks(chart) {
var ticks = d3.time.weeks(chart.xAxisMin(), chart.xAxisMax()); // or days(chart.xAxisMin(), chart.xAxisMax(), 5)
chart.xAxis().tickValues(ticks);
}
chart.on('preRender', calc_ticks)
.on('preRedraw', calc_ticks);
I would like to create a bar chart based on dates in x-axis. Labels should be displayed as month (i.e. Jan, Jan'17 - preferred). Within my data I have always first date of following months, i.e. 01Jan, 01Feb, 01Mar. I have created a chart but I am not able to make it aligned.
var chart = dc.barChart("#" + el.id);
var chCategory = ndx.dimension(function(d) {return d[chCategoryName];});
chValues = chCategory.group().reduceSum(
return parseFloat(d[chValueName]);});
//set range for x-axis
var minDate = chCategory.bottom(1)[0][chCategoryName];
var maxDate = chCategory.top(1)[0][chCategoryName];
chart
.width(800)
.height(200)
.x(d3.time.scale().domain([minDate,maxDate]))
.xUnits(d3.time.months)
.dimension(chCategory)
.group(chValues)
.renderHorizontalGridLines(true)
// .centerBar(true) //does not look better
.controlsUseVisibility(true)
.ordinalColors(arrColors)
.transitionDuration(1000)
.margins({top: 10, left: 80, right: 5, bottom: 20})
I have already read post: dc.js x-axis will not display ticks as months, shows decimals instead
but I am not able to implement it in a way that will keep correct sorting for different years.
dc.js takes the domain pretty literally - the x axis stretches exactly from the beginning to the end, disregarding the width of the bars or their placement. It's a design bug.
Here are two workarounds.
keep bars centered and add padding
If you're using elasticX you can manually correct it like this:
chart.centerBar(true)
.xAxisPadding(15).xAxisPaddingUnit('day')
If you're just setting the domain manually, that's
minDate = d3.time.day.offset(minDate, -15);
maxDate = d3.time.day.offset(maxDate, 15);
align the ticks to the left of bars and correct the right side of the domain
You don't say what problem you run into when you don't center the bars. But I know the right bar can get clipped.
If you want the elasticX effect, you can implement it manually like this, offsetting the right side by a month (example):
function calc_domain(chart) {
var min = d3.min(chart.group().all(), function(kv) { return kv.key; }),
max = d3.max(chart.group().all(), function(kv) { return kv.key; });
max = d3.time.month.offset(max, 1);
chart.x().domain([min, max]);
}
chart.on('preRender', calc_domain);
chart.on('preRedraw', calc_domain);
Or without elasticX that's just:
maxDate = d3.time.month.offset(maxDate, 1);