jqPlot repeating values on Y axis - jqplot

I have a graph which values on Y axis can quite vary. The only information I have is:
all values are integers
the value are >=0
Now if I have just few values with very low volatility like (lets take the extreme) [0,0,0,0,0,0,0] I got on my Y axis repetitive values. It looks like:
|
1+
|
1+
|
1+
|
0+
|
0+
|
0+-----------------------------------------
What I would like to achieve is to make jqPlot skip the repetitive values, possibly display only 2 ticks - 0,1 on the Y axis (the very bottom and very top one).
Any ideas? Adding my code for reference:
yaxis:{
label:'Count',
padMin: 0,
labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
tickRenderer: $.jqplot.CanvasAxisTickRenderer,
tickOptions:{
formatString:'%d'
}
}

This is no bug at all. jqplot uses some kind of zoom in their graphics, so when you say formatString:'%d' you're forcing to show the int value in the y-axis. Deleting this line it will fall back to values like 1.3, 1.8, ... or so. I guess you want to show tickInterval of int values (I found this post seeking this same problem without response).
Guess this can't be done yet

If you need to show only integer digits, you you may use custom string formatter:
tickOptions:{
formatString:'%d',
formatter: yourCustomFormater
}
Where 'yourCustomFormatter' is enhanced default '$.jqplot.DefaultTickFormatter' :
var yourCustomFormatter = function (format, val) {
if (typeof val == 'number') {
if (!format) {
format = $.jqplot.config.defaultTickFormatString;
}
if (val % 1 === 0) {
return $.jqplot.sprintf(format, val);
} else {
//ignore items with not integer values
return '';
}
}
else {
return String(val);
}
};
But be aware this formatter by default affects labels on your bar unless you use any other highlighter plugins.

its a bug with the plugin, I can reproduce it on their demo code and I have the same problem myself.
for now set the chart to a smaller dimension as that's the current flaw with the plugin.
hope this helped.

you can use this
axes:{
xaxis:{
numberTicks: 2
}
}

for me the below fixed the issue:
1- parameter tickOptions was tickOptions: {formatString: "%d"} I changed it to be tickOptions: {formatString: "%.2f"} because in case two elements rounded to the same integer value, its tick shows twice,
2- I've added extra parameter numberTicks: 4 to let axis has only 4 plots
with these two changes the current issue has been fixed but it may appears again according to the passed data to the graph

Related

dc.js rowChart to Filter by max key

I have a dashboard where I'm showing Headcount over time. One is a line Graph that shows headcount over time period, the other is a rowChart that is split by HCLevel1 - that is simply there to allow users to filter.
I would like the rowChart to show Heads for the latest period within the date filter (rather than showing the full sum of heads for the full period which would be wrong).
I can do this by combining two fields into a dimension, but the problem with this is that when I use the rowChart to filter by business, I only see one month in the line chart - whereas I'd like to see the full period that's filtered. I can't work out how I could do this with a fake group, because the rowChart's dimension/key is HCLevel1.
My data is formatted like this:
var data = = [
{
"HCLevel1": "Commercial",
"HCLevel2": "Portfolio TH",
"Period": 201407,
"Heads": 720
},
I've tried to use this custom reduce (picked up from another SO question) but it doesn't work correctly (minus values, incorrect values etc).
function reduceAddAvgPeriods(p, v) {
if (v.Period in p.periodsArray) {
p.periodsArray[v.Period] += v.Heads;
} else {
p.periodsArray[v.Period] = 0;
p.periodCount++;
}
p.heads += v.Heads;
return p;
}
Currently, my jsfiddle example is combining 2 fields for the dimension, but as you can see, I can't then filter using the rowChart to show me the full period on the line chart.
I can use reductio to give me the average, but I'd like to provide actual Heads value for most recent date filtered.
https://jsfiddle.net/kevinelphick/4ybekqey/3/
I hope this is possible, any help would be much appreciated, thanks!
I glanced at this a few days ago, but it took me a little while to figure out. Tricky!
We can restrict the design by considering these two facts:
We want to filter the row chart by "Level". That's simply
var dimLevel = cf.dimension(function (d) { return d.HCLevel1 || ''; });
A group does not observe its own dimension's filters. So we probably want to use the dimension from #1 to produce the data (the group) for the row chart.
Given these two restrictions, maybe we can dimension and group by level, but inside the bins of the group, keep track of the periods that contribute to that bin?
This is a common pattern often used for stacked charts:
var levelPeriodGroup = dimLevel.group().reduce(
function(p, v) {
p[v.Period] = (p[v.Period] || 0) + v.Heads;
return p;
},
function(p, v) {
p[v.Period] -= v.Heads;
return p;
},
function() {
return {};
}
);
Here, we'll just 'peel off' the top stack, dropping any zeros:
function last_period(group, maxPeriod) {
return {
all: function() {
var max = maxPeriod();
return group.all().map(function(kv) {
return {key: kv.key, value: kv.value[max]};
}).filter(function(kv) {
return kv.value > 0;
});
}
};
}
To keep last_period somewhat general, maxPeriod is now a function, which we'll define like this:
function max_period() {
return dimPeriod.top(1)[0].Period;
}
Bringing it all together and supplying it to the row chart:
rowChart
.group(last_period(levelPeriodGroup, max_period))
.dimension(dimLevel)
.elasticX(true);
Since the period is no longer part of the labels of the chart, we can put it in a headline:
<h4>Last Period: <span id="last-period"></span></h4>
and update it whenever the row chart is drawn:
rowChart.on('pretransition', function(chart) {
d3.select('#last-period').text(max_period());
});

amCharts: Always show 0 on value axis and enable negative velues to be displayed

I have got this silly problem.
I am creating a simple serial chart, displaying columns for two simple data series. The values are quite clse to eachother so amCharts decides to hide the 0 value axis and dislay only the relevant data. This is all good, but the thing is that I need to be able compare my columns visually. I also want to hide the labels on the value axis at some point.
Generally what I get now is this:
As you can see, value axis starts counting from 22.5. I need it to always start counting from 0, so I can compare the columns relatively to each other in a visual way. I know I can set the minimum propert of the value axis to 0 to achieve my desired result. But when I set any of the values to be negative, it does not display on the chart.
This is what I get with the minimum property set to 0 and one of the data points set o a negative value:
Here is a demo of my problem:
http://jsfiddle.net/gregzx/scyhwws4/1/
minimum set to 0 and one of the values set to a negative value.
Summing up: I need to always display the 0 value on the value axis AND be able to display negative values. Any hints will be much appreciated!
You could use a negative value for the minimum setting as well. As an example, you could set your minimum value to -30 and the maximum setting to 30.
This also makes sure that the 0-line is in the vertical middle.
var chart = AmCharts.makeChart("chartdiv", {
"type": "serial",
// ...
"valueAxes": [{
"minimum": -30,
"maximum": 30
}],
// ...
});
I had this problem at work and just solved it perfectly, You need to determine if there are any negative values in the incoming chart data, and of course, you want to avoid all negative values, so you also need a Boolean value to determine if the values are all negative.
Here's how I did it:
// your chart component
const XXXChart = ({ chartData }) => {
const hasNegative = chartData.some((it) => it.value < 0)
const allNegative = chartData.every((it) => it.value < 0)
useLayoutEffect(() => {
...
valueAxis.min = 0 // set with zero, assume that chartData defaults to positive values
if(hasNegative){
valueAxis.min = undefined // when there are negative and positive values, valueAxis.min will be restored to undefined, because valueAxis.max is undefined by default, and then chart will automatically set zero line.
valueAxis.max = allNegative ? 0 : undefined // When all values are negative, valueAxis.max can be set directly to zero
}
...
}, [chartData])
}
This is the perfect solution to your current problem.

How to change D3.format to display real number suffix instead of Byte suffix

Newbie Alert! My experience with JavaScript and SVG is EXTREMELY dated and I am completely new to d3.
I've just learned that d3.format('s') will display a number like 160,000 to 160k and a number like 20,000,000,000 to 20G. AWESOME!! There is just one teeny-tiny problem. This is a format for Scientific Notation in Bytes. I assume (maybe incorrectly) there must be an additional parameter that d3 uses to display numbers in Thousands (16T) and Millions (160M) and Billions (20B) instead of Bytes. No?
I have found another question how to get localizable or customizable si codes with d3.format and have looked at other similar questions but have yet to find what I believe to be the real answer. If what I am trying to do is not a feature in d3 but there is a work-around, I might need a little help implementing it.
I have been stepping through the d3 code to try to understand how to use the the other input parameters, si codes, prefix, type, etc. and even tried to glean some information from a tutorial here: D3.format tutorial through examples
It's become pretty clear that one needs to be an expert in D3 alone. So, in the absence of time, I'm asking for a D3 guru to please help.
Here's a custom formatting function adapted from another question:
function getFormatter(digits) {
var notations = [
// { value: 1E12, suffix: "T" }, what you'll use for trillion?
{ value: 1E9, suffix: "B" },
{ value: 1E6, suffix: "M" },
{ value: 1E3, suffix: "T" }
]
rx = /\.0+$|(\.[0-9]*[1-9])0+$/
return function(num) {
var notation
for (var i = 0; i < notations.length; i++) {
notation = notations[i]
if (num >= notation.value) {
var value = num / notation.value
value = value.toFixed(digits)
value = value.replace(rx, "$1")
return value + notation.suffix
}
}
}
}
var numbers = [
20000000000,
160020,
18200000
]
d3.select('body')
.append('ul')
.selectAll('li')
.data(numbers)
.enter()
.append('li')
.text(getFormatter(2))
span {
display:
}
<script src="https://d3js.org/d3.v4.min.js"></script>
Hopefully only the notations variable is relevant to your problem. But let me know if you need any further clarification. Good luck!

Trouble with filters and triggers in dc.js - re-drawing is out of sync

I have two plots: a line plot and a bubble plot. When I click on the bubble plot, I want the line plot to be updated so that it is drawn with only the data related to that 'bubble'. This is different from the standard implementation whereby clicking would add or remove the data from the existing filter.
If you look at the image you can see that although 'model 0' is selected the plotted hazard (y-scale in plot 1) does not correspond.
And now when I click on 'model 5', I get the opposite.
My current implementation is posted as a jsfiddle here. I can see from the attached data table that I am achieving what I want, but the line plot does not re-draw correctly. In fact, it seems to re-draw with the last filter, not the new one.
This implementation is hacked from here: in particular, the renderlet and on("filtered", function (chart) { lines. However, to make this work, I have had to comment out the plot1.filter(chart.filter()); line for the second plot.
I don't really understand why a renderlet and the on("filtered" ... or on("postRedraw" ... listeners are needed together.
I have been round the houses on this one, so any suggestions would be very gratefully received.
I tried to simplify the jsfiddle to isolate the problem. Here is the adapted jsfiddle: http://jsfiddle.net/djmartin_umich/mKz7A/
Your plot2 keyAccessor accessed the df value from the p.value.df rather than using a dimension on df. My guess is that this is what was causing problems. Here is the adapted code:
dfDim = ndx.dimension(function (d) {return d.df;});
...
plot2.width(300)
.height(250)
.dimension(dfDim)
I also noticed that your plot2 valueAccessor and radiusAccessor were not using a computed average. Your code would overwrite est and estse for each record added or removed from the group. Here is the adapted code that computes the average:
dfGroup = dfDim.group().reduce(
//add
function (p, v) {
++p.count;
p.est += v.est;
p.avg_est = p.est / p.count;
p.estse += v.estse;
p.avg_estse = p.estse / p.count;
return p;
},
//remove
function (p, v) {
--p.count;
p.est -= v.est;
p.avg_est = p.est / p.count;
p.estse -= v.estse;
p.avg_estse = p.estse / p.count;
return p;
},
//init
function (p, v) {
return {
count: 0,
est: 0,
estse: 0,
avg_est: 0,
avg_estse: 0
};
});
After these changes, I believe the code behaves as you wanted.

Is an NVD3 Line Plot with Markers Possible?

I'm making an NVD3 line plot that will have significantly improved clarity if I can get markers to show for each data point instead of just the line itself. Unfortunately, I haven't been able to find an easy way to do this with NVD3 yet. I also considered using a scatter plot, but I couldn't figure out how to show connecting lines between the points. A third option I considered was to overlay a line and scatter plot, but this would show each series twice in the legend and may cause other unnecessary visual complications.
Is there a way to elegantly pull this off yet? Sample code of my formatting technique is listed below, but the 'size' and 'shape' attributes in test_data have no effect on the line plot with the current code.
test_data = [ { key: 'series1',
values: [
{ x: 1, y: 2.33, size:5, shape:"circle" },
{ x: 2, y: 2.34, size:5, shape:"circle" },
{ x: 3, y: 2.03, size:5, shape:"circle" },
] } ];
nv.addGraph(function() {
var test_chart = nv.models.lineChart();
test_chart.xAxis.axisLabel('Sample Number');
test_chart.yAxis
.axisLabel('Voltage (V)')
.tickFormat(d3.format('.02f'));
d3.select('#test_plot')
.datum(test_data)
.transition().duration(500)
.call(test_chart);
nv.utils.windowResize(test_chart.update);
return test_chart;
});
I also wanted to add markers in a project I was working on. Here is a solution my partner and I found.
First, you have to select all of the points in your chart and set the fill-opacity to 1:
#my-chart .nv-lineChart circle.nv-point
{
fill-opacity: 1;
}
Now your points will be visible. To adjust the size of each point you need to modify each one's "r" (for radius) attribute. This isn't a style so you can't do it with css. Here is some jQuery code that does the job. The 500 millisecond delay is so the code will not run before the chart is rendered. This snippet sets the radius to 3.5:
setTimeout(function() {
$('#my-chart .nv-lineChart circle.nv-point').attr("r", "3.5");
}, 500);
This puzzled me until I got help from the community:
css styling of points in figure
So here is my solution, based on css:
.nv-point {
stroke-opacity: 1!important;
stroke-width: 5px!important;
fill-opacity: 1!important;
}
If anyone has come here from rCharts, below is a rmarkdown template to create an nPlot with both lines and markers:
```{r 'Figure'}
require(rCharts)
load("data/df.Rda")
# round data for rChart tooltip display
df$value <- round(df$value, 2)
n <- nPlot(value ~ Year, group = 'variable', data = df, type = 'lineChart')
n$yAxis(axisLabel = 'Labor and capital income (% national income)')
n$chart(margin = list(left = 100)) # margin makes room for label
n$yAxis(tickFormat = "#! function(d) {return Math.round(d*100*100)/100 + '%'} !#")
n$xAxis(axisLabel = 'Year')
n$chart(useInteractiveGuideline=TRUE)
n$chart(color = colorPalette)
n$addParams(height = 500, width = 800)
n$setTemplate(afterScript = '<style>
.nv-point {
stroke-opacity: 1!important;
stroke-width: 6px!important;
fill-opacity: 1!important;
}
</style>'
)
n$save('figures/Figure.html', standalone = TRUE)
```
The current version of nvd3 use path instead of circle to draw markers. Here is a piece of css code that i used to show markers.
#chart g.nv-scatter g.nv-series-0 path.nv-point
{
fill-opacity: 1;
stroke-opacity: 1;
}
And I also write something about this in https://github.com/novus/nvd3/issues/321, you could find that how i change the shape of makers.
I don't know how to change the size of markers. Trying to find a solution.
Selectively enable points to some series using the following logic in nvd3.
//i is the series number; starts with 0
var selector = 'g.nv-series-'+i+' circle';
d3.selectAll(selector).classed("hover",true);
However an additional parameter( like say 'enable_points':'true') in the data would make better sense. I will hopefully push some changes to nvd3 with this idea.
For current version of NVD3 (1.8.x), I use this D3-based solution (scripting only, no CSS file or style block required):
nv.addGraph(function() {
// ...
return chart;
},
function() {
// this function is called after the chart is added to document
d3.selectAll('#myChart .nv-lineChart .nv-point').style("stroke-width",
"7px").style("fill-opacity", ".95").style("stroke-opacity", ".95");
}
);
The styles used are exactly the styles added by NVD3 by applying the "hover" class to each point (when hovered). Adjust them to your needs.

Resources