showing custom tooltip for Discrete chart using nvd3 - nvd3.js

I am looking for help to override default tooltip functionality for nvd3 bar (discrete) chart.
The default tooltip picks yAxis ticks and show in the tooltip.
However, in my case i don't want to show yAxis data (formatted in currency) in tooltip rather than i would like show actual yAxis value (original value without currency).
This is how i am pushing values to my data[].
#foreach (var item in Model.BarChart)
{
string yvalue = item.Cost== 0 ? "undefined" : item.Cost.ToString();
string xvalue = item.Date.ToString("yyyy/MM/dd");
#:data[0].values.push({y: #yvalue, x: '#xvalue'});
}
I am formatting yAxis tick using the following line of code
chart.yAxis.tickFormat(function(d) { return d3.format(".1s")(d) + " USD" });
Any hint?
Let me know if you need more info?
After googling, i found the following way but in this example 'y' is yAxis value and not the original value. So, how i can replace this 'y' with orginal value?
chart.tooltip(function (key, x, y, e, graph) {
return '<p><strong>' + key + '</strong></p>' +
'<p>' + y + ' in the month ' + x + '</p>';
});

Try e.value instead of y.
chart.tooltipContent(function (key, x, y, e, graph) {
return '<p><strong>' + key + '</strong></p>' +
'<p>' + e.value + ' in the month ' + x + '</p>';
});
e.point should also contain the original data, including any extra attributes. For example, if your input data has an "extra" attribute (like { label : "A" , value : -29.76, extra: 123 }), then you can use e.point.extra.

Related

line graph renders wrongly and problem with the X axis (time), cannot brush the graph - time series dc.js

I'm creating a dashboard for a scanning tool. I have created a bunch of graphs but got stuck with plotting a line chart to show kind of like a time series ( like how many records are populated at that particular scan time). I'm using dc.js and have tried a couple of ways, but the line does not render correctly and the axis has weird thing going with it.
I cannot brush the graph too, it is throwing "coordinate-grid-mixin.js:1083 Uncaught TypeError: undefined is not a function".
I have a UNIX timestamp, which I'm converting to datetime and then I'm using these to plot the line chart.
//Converting the timestamp
var dateFormatSpecifier = "%m/%d/%Y";
var day_FormatSpecifier = "%d %b %y";
var dateFormat = d3.timeFormat(dateFormatSpecifier);
var dayFormat = d3.timeFormat(day_FormatSpecifier)
var dateFormatParser = d3.timeParse(dateFormatSpecifier);
var numberFormat = d3.format(".2f");
facts.forEach(function(d) {
console.log('Before Change : ' + d.timestamp);
d.date = new Date(d.timestamp * 1000);
d.month = d3.timeMonth(d.date);
d.day = d3.timeDay(d.date);
d.date = dateFormat(d.date);
d.day = dayFormat(d.day)
console.log('After Change : ' + d.date + ' ' + d.month + ' '+ d.day);
// console.log('After Change Month: ' + d.month);
});
//Plotting
var dateDim = data.dimension(function(d) { return (d.day);});
var groupForSNR = dateDim.group().reduceCount(item => 1);
var time_chart = dc.lineChart("#time-chart");
var minDate = dateDim.bottom(1)[0].date;
console.log('min : ' + minDate);
var maxDate = dateDim.top(1)[0].date;
console.log('max : ' + maxDate);
var xmin = dayFormat(new Date(1559890800*1000))
console.log('xmin : ' + xmin);
var xmax = dayFormat(new Date(1561878000*1000))
console.log('xmin : ' + xmax);
time_chart
.height(150)
.transitionDuration(500)
.margins({top: 10, right: 10, bottom: 20, left: 40})
.dimension(dateDim)
.group(groupForSNR)
.brushOn(true)
.elasticY(true)
.y(d3.scaleLinear().domain([0,100]))
.x(d3.scaleOrdinal().domain(xmin, xmax))
.xUnits(d3.timeDay);
time_chart.xAxis().ticks(15);
time_chart.yAxis().ticks(10);
-
".x(d3.scaleTime().domain(xmin, xmax))" is not even displaying anything.
Thanks for your effort.
Your syntax is slightly off, and you always want to work with JavaScript Date objects when using D3 time scales. Only the ticks and labels (output) will be formatted as strings, all input will be Dates.
.domain() always takes an array.
You want
var minDate = dateDim.bottom(1)[0].day; // Date object
var maxDate = dateDim.top(1)[0].day;
.x(d3.scaleTime().domain([minDate, maxDate]))
Not sure if these are the only problems - everything else looks okay but it's hard to tell without trying.
Just keep studying, look very closely at what the examples are doing, you'll pick it up quickly!
EDIT: you will find examples where the dates are formatted as strings with an ordinal scale on input. There are only particular cases where is this a good idea, and I don't think this is one of them.

dc.js Adding count to the xAxis label

This is related to the dc.js boxPlot example. Is there an easy way to add the number of data values to each xAxis label?
exp-1 [10]
I know I can configure the xAxis label by:
chart.xAxis().tickFormat(function(k) {return k + ' []';});
But I only have access to the key and not the value count.
I would do this by keeping a map from keys to counts, refreshing it before the chart is rendered or redrawn:
var counts = {};
function get_counts() {
speedArrayGroup.all().forEach(function(kv) {
counts[kv.key] = kv.value.length;
});
}
chart.on('preRender', get_counts)
.on('preRedraw', get_counts);
Now that we're sure counts is initialized whenever the axis is drawn, we can use it in the tickFormat:
chart.xAxis().tickFormat(function(k) {return k + ' [' + counts[k] + ']';});
Not so interesting with the standard example, but it works:

Add annotation text to boxTick

I would very much appreciate support on Mike Bostocks' Box Plots
Until now, I have been unable to figure how to add specific text annotations - for example with .append("text") - adjacent to each of the boxTick and whiskerTick for example to describe to the viewer what these values are, like "Q3" or whatever.
What would be a good way to achieve this?
Here's a working demo:
Plunkr with description for box and whisker ticks in a box plot
I'm just using the same logic (using index i) used to determine the position of the tick i.e. dx
Relevant code changes:
For box ticks:
.text(function(d, i) {
var quartile = i & 1 ? 'Median' : !i ? 'Q1' : 'Q3';
return quartile + ': ' + format(d);
})
For whisker texts:
.text(function(d, i) {
return (!i ? 'LW: ' : 'UW: ') + format(d);
})
Let me know if you have any questions. Hope this helps.

d3 retrieve and add to current selection's attribute value

I'm trying to get the values for an element's translation.
For example, if I select the x axis:
d3.select('.x.axis').attr("transform")
then I get
"translate(0,112)"
How do I get the 0 and the 112 without parsing a regexp?
I'm trying to do it so that I can add to the value. In pseudocode:
d3.selectAll('.x.axis').attr('transform', 'translate('
.attr('transform').match(/(\d+)(\.\d+)?/g)[0] // <-- clearly won't work
+ additional_value
+ ', 0)');
D3 provides the transform() function for exactly this purpose:
var t = d3.transform(d3.select('.x.axis').attr("transform")),
x = t.translate[0],
y = t.translate[1];
if you would like to use selectAll you could try something like this:
// move ticks to the center of the x-axis
var transform;
d3.selectAll('.tick').attr('transform', function(){
transform = d3.transform(d3.select(this).attr("transform"));
return "translate("+transform.translate[0]+", -3)";
});

D3.js graph displaying only one dataset

I having trouble getting the data on the graph. I only get one data set bar in.
You can see it here : http://infinite-fjord-1599.herokuapp.com/page2.html
But when I console.log the foreach for it. It displays all the objects:
data.days.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d.values[name]}; });
console.log(d.ages);
});
The code on jsFiddle. http://jsfiddle.net/arnir/DPM7y/
I'm very new to d3.js and working with json data so I'm kinda lost here. I took the example of the d3.js example site and modified it.
See the updated fiddle here: http://jsfiddle.net/nrabinowitz/NbuFJ/4/
You had a couple of issues here:
Your x0 scale was set to a domain that displayed a formatted date, but when you were calling it later you were passing in d.State (which didn't exist, so I assume it was a copy/paste error). So the later days were being rendered on top of the first day.
There was a mismatch between the way you were selecting the group g element and the way you were appending it - not actually a root cause here, but likely to cause problems later on.
To fix, move the date formatting to a different function:
function formatDate(d) {
var str = d.modified;
d.date = parseDate( str.substring(0, str.length - 3) );
var curr_month = d.date.getMonth() + 1;
var curr_date = d.date.getDate();
var nicedate = curr_date + "/" + curr_month;
return nicedate;
}
and then use the same function for the scale setup:
x0.domain(data.days.map(formatDate));
and the transform (note the fix in the selector and class here as well):
var state = svg.selectAll("g.day")
.data(data.days)
.enter().append("g")
.attr("class", "day")
.attr("transform", function(d) {
return "translate(" + x0(formatDate(d)) + ",0)";
});
There are a couple of small things that threw you off. First, the domain of the x0 scale should be an array of datetime objects, not an array of strings:
x0.domain(data.days.map(function(d) {
var str = d.modified;
d.date = parseDate( str.substring(0, str.length - 3) );
return d.date;
}));
will return datetimes, not strings like it was before (minor nitpick: really not a fan of this use of map, I would add the date property separately in a forEach function as the data is loaded).
Second, x0 needs to be passed a property that actually exists:
var state = svg.selectAll(".state")
.data(data.days)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.date) + ",0)"; });
Before, you were using x0(d.state) which is a vestige from the grouped bar example (several others still exist; I've changed the minimum to get your project working). Since the value didn't exist, all of the rectangles were getting drawn over each other.
Additionally, we need to format the axis labels so we aren't printing out the entire datetime object all over the labels:
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom")
.tickFormat(d3.time.format("%m-%d"));
Finally, I noticed that the newest dates were being printed on the left instead of the right. You could sort the results of data.days.map( ... ) to fix that, I just reversed the range of x0:
var x0 = d3.scale.ordinal()
.rangeRoundBands([width, 0], .1);
fixed files

Resources