jqplot Yaxis rescale - jqplot

I'm using jqplot for represent several parameters in series across the time. So I have one Xaxis represent the time and several Yaxes(yaxis, y2axis, y3axis, y4axis, etc.). Each parameter is in different units(temperature, voltage, current, pressure, etc.), so all the Yaxes set to auto for min and max because I dont know what values will come from the server. I'm using ajax request to fill the data and next for real time updating the series with new points.
So now I want for the user to be able to set manual min and max for any Y-axe. So I set by this way:
var axis_name="y2axis";
console.log(plot.axes[axis_name].max);
var new_max=prompt("Please enter max value?");
console.log(new_max);
var plotOptionsEval = "plotOptions = { axes: { "+axis_name+": { max: \""+new_max+"\" } } };";
eval(plotOptionsEval);
console.log(plotOptions);
plot.replot(plotOptions);
console.log(plot.axes[axis_name].max);
So , when I set new max for the first axis(yaxis) everythins is fine.
But when I try to set the max parameter of any other axis - y4axis for example something gone wrong and that max value has a some different value from this the user is entered.
This is a debug from console.log output
50
12
axes: y4axis: {max: "12"}
350
Any ideas?

So I have found a solution if someone asking the same.
The trick is you need to set min and max parameters for all Yaxis in your plot. So I have prepared the min/max axes object with its original values:
var plotOptions = {
axes: {
yaxis: {min: 11.568, max: 11.582}
y2axis: {min: 11.688, max: 11.702}
y3axis: {min: 6.390000000000001, max: 6.53}
y4axis: {min: -300, max: 50}
}
}
Next set the desired Yaxis min/max parameter in the plotOptions object:
var new_max=prompt("Please enter max value?");
plotOptions.axes.y2axis.max=parseFloat(new_max);
and finally replot it:
plot.replot(plotOptions);

Related

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.

crossfilter "double grouping" where key is the value of another reduction

Here is my data about mac address. It is recorded per minute. For each minute, I have many unique Mac addresses.
mac_add,created_time
18:59:36:12:23:33,2016-12-07 00:00:00.000
1c:e1:92:34:d7:46,2016-12-07 00:00:00.000
2c:f0:ee:86:bd:51,2016-12-07 00:00:00.000
5c:cf:7f:d3:2e:ce,2016-12-07 00:00:00.000
...
18:59:36:12:23:33,2016-12-07 00:01:00.000
1c:cd:e5:1e:99:78,2016-12-07 00:01:00.000
1c:e1:92:34:d7:46,2016-12-07 00:01:00.000
5c:cf:7f:22:01:df,2016-12-07 00:01:00.000
5c:cf:7f:d3:2e:ce,2016-12-07 00:01:00.000
...
I would like to create 2 bar charts using dc.js and crossfilter. Please refer to the image for the charts.
The first bar chart is easy enough to create. It is brushable. I created the "created_time" dimension, and created a group and reduceCount by "mac_add", such as below:
var moveTime = ndx.dimension(function (d) {
return d.dd; //# this is the created_time
});
var timeGroup = moveTime.group().reduceCount(function (d) {
return d.mac_add;
});
var visitorChart = dc.barChart('#visitor-no-bar');
visitorChart.width(990)
.height(350)
.margins({ top: 0, right: 50, bottom: 20, left: 40 })
.dimension(moveTime)
.group(timeGroup)
.centerBar(true)
.gap(1)
.elasticY(true)
.x(d3.time.scale().domain([new Date(2016, 11, 7), new Date(2016, 11, 13)]))
.round(d3.time.minute.round)
.xUnits(d3.time.minute);
visitorChart.render();
The problem is on the second bar chart. The idea is that, one row of the data equals 1 minute, so I can aggregate and sum all minutes of each mac address to get the time length of each mac addresses, by creating another dimension by "mac_add" and do reduceCount on "mac_add" to get the time length. Then the goal is to group the time length by 30 minutes. So we can get how many mac address that have time length of 30 min and less, how many mac_add that have time length between 30 min and 1 hour, how many mac_add that have time length between 1 hour and 1.5 hour, etc...
Please correct me if I am wrong. Logically, I was thinking the dimension of the second bar chart should be the group of time length (such as <30, <1hr, < 1.5hr, etc). But the time length group themselves are not fix. It depends on the brush selection of the first chart. Maybe it only contains 30 min, maybe it only contains 1.5 hours, maybe it contains 1.5 hours and 2 hours, etc...
So I am really confused what parameters to put into the second bar chart. And method to get the required parameters (how to group a grouped data). Please help me to explain the solution.
Regards,
Marvin
I think we've called this a "double grouping" in the past, but I can't find the previous questions.
Setting up the groups
I'd start with a regular crossfilter group for the mac addresses, and then produce a fake group to aggregate by count of minutes.
var minutesPerMacDim = ndx.dimension(function(d) { return d.mac_add; }),
minutesPerMapGroup = minutesPerMacDim.group();
function bin_keys_by_value(group, bin_value) {
var _bins;
return {
all: function() {
var bins = {};
group.all().forEach(function(kv) {
var valk = bin_value(kv.value);
bins[valk] = bins[valk] || [];
bins[valk].push(kv.key);
});
_bins = bins;
// note: Object.keys returning numerical order here might not
// work everywhere, but I couldn't find a browser where it didn't
return Object.keys(bins).map(function(bin) {
return {key: bin, value: bins[bin].length};
})
},
bins: function() {
return _bins;
}
};
}
function bin_30_mins = function(v) {
return 30 * Math.ceil(v/30);
}
var macsPerMinuteCount = bin_keys_by_value(minutesPerMacGroup);
This will retain the mac addresses for each time bin, which we'll need for filtering later. It's uncommon to add a non-standard method bins to a fake group, but I can't think of an efficient way to retain that information, given that the filtering interface will only give us access to the keys.
Since the function takes a binning function, we could even use a threshold scale if we wanted more complicated bins than just rounding up to the nearest 30 minutes. A quantize scale is a more general way to do the rounding shown above.
Setting up the chart
Using this data to drive a chart is simple: we can use the dimension and fake group as usual.
chart
.dimension(minutesPerMacDim)
.group(macsPerMinuteCount)
Setting up the chart so that it can filter is a bit more complicated:
chart.filterHandler(function(dimension, filters) {
if(filters.length === 0)
dimension.filter(null);
else {
var bins = chart.group().bins(); // retrieve cached bins
var macs = filters.map(function(key) { return bins[key]; })
macs = Array.prototype.concat.apply([], macs);
var macset = d3.set(macs);
dimension.filterFunction(function(key) {
return macset.has(key);
})
}
})
Recall that we're using a dimension which is keyed on mac addresses; this is good because we want to filter on mac addresses. But the chart is receiving minute-counts for its keys, and the filters will contain those keys, like 30, 60, 90, etc. So we need to supply a filterHandler which takes minute-count keys and filters the dimension based on those.
Note 1: This is all untested, so if it doesn't work, please post an example as a fiddle or bl.ock - there are fiddles and blocks you can fork to get started on the main page.
Note 2: Strictly speaking, this is not measuring the length of connections: it's counting the total number of minutes connected. Not sure if this matters to you. If a user disconnects and then reconnects within the timeframe, the two sessions will be counted as one. I think you'd have to preprocess to get duration.
EDIT: Based on your fiddle (thank you!) the code above does seem to work. It's just a matter of setting up the x scale and xUnits properly.
chart2
.x(d3.scale.linear().domain([60,1440]))
.xUnits(function(start, end) {
return (end-start)/30;
})
A linear scale will do just fine here - I wouldn't try to quantize that scale, since the 30-minute divisions are already set up. We do need to set the xUnits so that dc.js knows how wide to make the bars.
I'm not sure why elasticX didn't work here, but the <30 bin completely dwarfed everything else, so I thought it was best to leave that out.
Fork of your fiddle: https://jsfiddle.net/gordonwoodhull/2a8ow1ay/2/

how can i limit number of ticks in jqplot (line plot)?

I'm using JQPLOT to create graphs. But I have a lot of data for the x-axis. Now, i need to limit the number of ticks. I'm using numberTicks for this but doesn't work.
xaxis: {
numberTicks:15,
max: x_limits.max,
min: x_limits.min,
renderer:$.jqplot.CategoryAxisRenderer,
rendererOptions:{tickRenderer:$.jqplot.CanvasAxisTickRenderer},
tickOptions:{formatString:'%#m/%#d/%Y'}
},
I'm using CategoryAxisRenderer. When i use DateAxisRenderer, it works. -.-
EDIT (wrong answer) It doesn't work because it takes account of your minimal and maximal xaxis values. Try to remove them to take into account the "numberTicks" options (or just specify one of them but I'm not sure it will be correct)
EDIT2 : You have to remove the renderer and rendererOptions from the xaxis part :
xaxis:{
//renderer:$.jqplot.CategoryAxisRenderer,
//rendererOptions:{tickRenderer:$.jqplot.CanvasAxisTickRenderer},
max: 40,
min: 1,
numberTicks: 5,
}
Please see a working example here with 5 ticks.

Adding a filter in dc.js / Crossfilter not updating the chart

jsFiddle:
http://jsfiddle.net/PYeFP/
I have a bar chart set up that graphs a users number of trips by day
tripVolume = dc.barChart("#trip-volume")
.width(980) // (optional) define chart width, :default = 200
.height(75) // (optional) define chart height, :default = 200
.transitionDuration(0) // (optional) define chart transition duration, :default = 500
.margins({ top: 10, right: 50, bottom: 30, left: 40 })
.dimension(tripsByDateDimension) // set dimension
.group(tripsByDateGroup) // set group
// (optional) whether chart should rescale y axis to fit data, :default = false
.elasticY(false)
// (optional) whether chart should rescale x axis to fit data, :default = false
.elasticX(false)
// define x scale
.x(d3.time.scale().domain([tripsByDateDimension.bottom(1)[0].startDate, tripsByDateDimension.top(1)[0].startDate ]))
// (optional) set filter brush rounding
.round(d3.time.day.round)
// define x axis units
.xUnits(d3.time.days)
// (optional) whether bar should be center to its x value, :default=false
.centerBar(true)
// (optional) render horizontal grid lines, :default=false
.renderHorizontalGridLines(true)
// (optional) render vertical grid lines, :default=false
.renderVerticalGridLines(true)
.brushOn(false);
The graph displays fine but I would like to filter it using some jQuery controls.
When the user selects the date I am trying to add a filter to the chart, the filter gets added but the chart does not change, even if I redraw() or render().
This is how the crossfilter is setup:
tripsCx = crossfilter(data.rows);
var allTripsGroup = tripsCx.groupAll();
var tripsByDateDimension = tripsCx.dimension(function (d) { return d.startDate; });
var tripsByDateGroup = tripsByDateDimension.group(d3.time.day);
The following are some of the methods I have used to try to apply a filter:
This should use the filterRange:
d.filter(d.dimension().top(20)[19], d.dimension().top(20)[0]);
FilterFunction:
d.filter(function (d) {
return d.getTime() > start.valueOf() && d.getTime() < end.valueOf();
});
FilterExact:
d.filter(d.dimension().top(20)[0]);
I also tried bypassing the chart and applying the filter directly on the dimension:
d.dimension().filterFunction(function (d) {
return d.getTime() > start.valueOf() && d.getTime() < end.valueOf()
});
Nothing I have done causes the chart to change.
I am beginning to think that I have the wrong expectation of what the filter function should do?
How can I manually filter the data in the dimension to have the chart updated?
I don't want to use a brush.
I will be filtering the data based on different criteria, I'm just trying to get the simple case working first.
I've spent a couple of days on this now and I'm at a loss as to what to try next.
Any help would be greatly appreciated.
Have you tried to reset your x property of the graph after setting the crossfilter filter
I have a somewhat similar case and what I do after each action that changes the filtered values is something along the lines of
.x(..).dimension(...).group(...)
after creating/setting the filters
Tried to do something like that
$('#filter').on('click', function(){
var minDate = tripsByDateDimension.top(5)[4].startDate;
var maxDate = tripsByDateDimension.top(5)[0].startDate;
console.log(tripVolume.filters());
tripVolume.filter([minDate, maxDate]);
tripVolume.x(d3.time.scale().domain([minDate,maxDate]));
console.log(tripVolume.filters());
dc.redrawAll()
});
http://jsfiddle.net/PYeFP/5/
Better answer per the discussion in the comment is to add the filter to the dimension, not the chart
Finally, one needs to realize what is mentioned in https://github.com/square/crossfilter/wiki/API-Reference#group-map-reduce
Note: a grouping intersects the crossfilter's current filters, except for the associated dimension's filter. Thus, group methods consider only records that satisfy every filter except this dimension's filter. So, if the crossfilter of payments is filtered by type and total, then group by total only observes the filter by type.
(also see https://groups.google.com/d/msg/dc-js-user-group/UFxvUND7hmY/btbAjqIIzl8J)

jqplot - x-axis

I have a long list of data for x-axis data when drawing a line chart (about 800 entries). The problem is it will not display correctly and overwrite each other. I am thinking a way to show (for example, every one hundred for a grid) and don't know how to do it. Please help me out.
Thanks.
You can specify an array of ticks to display on your chosen axis :
var xTicks = new Array(1,101,201,301,401,501,601,701,800);
--In your plot, add ticks option to your chosen axis :
axes: {
xaxis: {
ticks: xTicks --my ticks array
renderer: $.jqplot.CategoryAxisRenderer,
autoscale: false
}
}
It should do the trick.

Resources