I have this now:
I need to draw graph of this function:
-3.6 * Math.exp(-3.5 * Math.pow(x, 2)) * Math.sign(Math.cos(31 * x - 7));
I decided to use HighchartJS. There are no problems with it, but I am not sure, that it is correct to approximate function (highchartJS does this).
You can find my results here, on jsFiddle.
You can see, that values of the graph are "jumping" with some amplitude, highchartJS approx it and I am getting continious line. Actually, graph of my function differs from this result. You can see that here, for example.
Also, you can see the result on image below:
Line is interrupting. The question is how I can get same result with highchartJS (or maybe I should use another library?) ?
If graph is not running on Chrome try this, please
Firs of all, you are using Math.sign method which is not part of official standards (just draft). Simply add your method to get working example in chrome:
Math.sign = function(x){
if( +x === x ) {
return (x === 0) ? x : (x > 0) ? 1 : -1;
}
return NaN;
}
Now, The problem is that Highcharts connects all points with lines. In your case better solution is to use scatter type - since you have enough points to display expected result: http://jsfiddle.net/q2kSf/22/
$('#container').highcharts({
series: [{
type: 'scatter',
name: 'Values',
data: data,
marker: {
radius: 2
}
}]
});
Related
I'm trying to find a math function for animating, but so far without success. There must be some very simple solution for this, but I'm a bit confused. I need to get the value of a function like in the example image
.
[A1,B1] - range of possible argument value
[A,C] - range of function values
D - the value of the argument for which the function shows the minimum value
What type of functions is this? I would be grateful for any hints.
I see is piecewise curve: 2 quadratic + 2 line for example something like this in C++:
float x0,x1,x2,y0,y1,y2; // set these to your curve edge points
float fx(float x) // this returns y for any x
{
if (x<=x0) return y0;
if (x<=x1)
{
float a=(x-x1)/(x1-x0);
return y1+a*a*(y0-y1);
}
if (x<=x2)
{
float a=(x-x1)/(x2-x1);
return y1+a*a*(y2-y1);
}
return y2;
}
And here how it looks like:
I'm using c3 chart for js. My code is below:
c3.generate({
bindto:'#someChart',
data: {
columns: [
data
]
},
axis: {
x : {
type: 'categories',
categories:categories,
tick:{
count: 12
}
}
},
point:{
show:false
}
});
I have one problem. On x axis is showed only first and last value(12th).
It's a known bug in c3 where if the tick is due to occur at a fractional x value (i.e. the number of ticks means it should pop up say every 2.06666 categories) then it doesn't render --> https://github.com/c3js/c3/issues/1638
There's a fix that's offered there to run before you generate your chart -->
c3.chart.internal.fn.categoryName = function (i) {
var config = this.config, categoryIndex = Math.ceil(i);
return i < config.axis_x_categories.length ? config.axis_x_categories[categoryIndex] : i;
};
But I find while it now shows the right number of ticks, it often still doesn't line up the labels and ticks nicely to the data points (they're positioned partway in between).
On that point, it's better, if you can, to set the number of ticks (-1) to divide without a fraction into the number of data points (-1) you're wanting to show and they'll both avoid your initial issue and line up nicely.
e.g. (datapoints - 1)/(no.of.ticks - 1) == whole number
I'm currently using the whole idea of
var myQuantizeFunction = d3.scale.quantize()
.domain(minMaxFromData) // the minmax using d3.extent
.range(['bla-1', 'bla-2', 'bla-3', 'bla-4', 'bla-5']);
So this works fine when you want to generate a legend across your min-max. The issue is, I have some data which comes back as 0.
Here is an example legend for context :
As you can see, it's first or lowest value from the range is 0 - 4.7, what I want to really do is have 0 (ie none) as it's own legend item and have everything above ie 1 - 33 in this case as the other ranges.
I want to be able to specify that the first range is 0 and then the domain is split equally between values > 0.
Is there a d3 way of doing this? I'm sure someone else must have had this same problem before, I can't seem to find it but I may not be using the right search terms.
From the documentation:
quantize.domain([numbers])
If numbers is specified, sets the scale's input domain to the
specified two-element array of numbers. If the array contains more
than two numbers, only the first and last number are used. If the
elements in the given array are not numbers, they will be coerced to
numbers; this coercion happens similarly when the scale is called.
Thus, a quantize scale can be used to encode any type that can be
converted to numbers. If numbers is not specified, returns the scale's
current input domain.
As the name suggests d3 is 'data driven' so ignoring parts of your data set is not part of its ethos.
You need to write your own function to generate the [numbers] array.
Try:
data = [0,0,2,1,4,6,7,8,4,3,0,0];
min = undefined;
data.forEach(function (v) {
if (v > 0) {
if (typeof(min) === 'undefined') {
min = v;
} else if (v < min) {
min = v;
}
}
})
var myQuantizeFunction = d3.scale.quantize()
.domain([min, d3.max(data)])
.range(['bla-1', 'bla-2', 'bla-3', 'bla-4', 'bla-5']);
I improved the latest solution to use d3.min() and added code to the test the quantize function. Also I added a small function to colorize the output.
Everything done in the d3 datadriven way
data = [0,0,2,1,4,6,7,8,4,3,0,0];
range = ['bla-1', 'bla-2', 'bla-3', 'bla-4', 'bla-5'];
//strip the first element
reducedRange = range.slice();
reducedRange.shift();
var myQuantizeFunction =
d3.scale.quantize()
.domain([d3.min(data), d3.max(data)])
.range(reducedRange);
var filterQuantize = function(d){
if(d==0){
return range[0];
}else{
return myQuantizeFunction(d);
}
}
var colorize = d3.scale.category10();
// To test this we will put all the data in paragraphs
d3.select('body').selectAll('p').data(data) .enter()
.append('p')
.text(function(d){return d+':'+filterQuantize(d);})
.style('color',function(d){return colorize(d)});
View this code runing
Hope this helps, good luck!
Update: I stripped zero out of the scale to treat it as a special case as you pointed in the comment.
I'm trying to implement inertia for drag using D3 version 2.
The latest version of d3 (3.3.10) from the download page does not have it, while pjanik has modified a version of d3 which does that - http://bl.ocks.org/pjanik/raw/5872514/
In any case, how can I implement this in D3 version 2?
A very primitive function to achieve the feel, but would really prefer an elegant formula if anyone has any suggestions, much appreciated:
function inertia(value) {
var remainder = value,
output,
drag = 0.3,
stepTime = 60
//var x = 0
var interval = setInterval(function() {
output = remainder * drag
//x += output
//console.log(x)
//jQuery('.logo').css('margin-left', x)
console.log(output)
remainder = remainder - output
/*a hack to clear when value is small*/
if (output < value * .005)
clearInterval(interval)
}, stepTime)
}
//Any integer to be split for steps, can be distance, time, any value!
inertia(100)
I have a Google Chart with a continuous date-time X axis. My data comes in short bursts, with long delays between the bursts. I'd like to make the Chart have a non-continuous X axis, but still have the auto-generated timestamps during the samples. Is that possible?
Basically, say I have 3 samples, each which have 300 datapoints, recorded across 10 second intervals, but with hour gaps between them. I'd like to have my chart show the 30 seconds of data at a zoom level where it can be distinguished. Am I stuck?
Edit: Per #jmac's suggestion, here is an example of what the data looks like:
1360096658270, 10.228335
1360096658274, 10.308437
1360096658277, 10.294770
[...]
1360096673968, 9.014943
1360096673969, 8.971434
1360096673970, 9.041739
1360096673971, 9.097484
^^-- 15 seconds
<--- (~10 days)
1360989176509, 9.856928
1360989176513, 9.852907
1360989176517, 9.861740
1360989176523, 9.820416
1360989176527, 9.871401
Method 1: Multiple Charts
This is probably the simplest in concept (though still a hassle).
Summary:
Split data in to groups (eliminate the gaps)
Create a separate chart for each group
Eliminate the vAxis labels for every chart past the first
Create a consistent vAxis min/max value
Use CSS to line the charts up side to side
Details:
If you have a static data set, you can just split it by hand. If it isn't static, then you have to write some javascript to split up your data. I can't really help you here since I don't know how your data works.
As far as setting up the charts, I'll leave that up to you. I don't know how you want them formatted, so again I can't really help you with the current info.
To create a consistent axis value for all charts, you need to use some basic math in a javascript function to assign the same numbers to each vAxis max/min value. Here is a sample:
// Take the Max/Min of all data values in all graphs
var totalMax = 345;
var totalMin = -123;
// Figure out the largest number (positive or negative)
var biggestNumber = Math.max(Math.abs(totalMax),Math.abs(totalMin));
// Round to an exponent of 10 appropriate for the biggest number
var roundingExp = Math.floor(Math.log(biggestNumber) / Math.LN10);
var roundingDec = Math.pow(10,roundingExp);
// Round your max and min to the nearest exponent of 10
var newMax = Math.ceil(totalMax/roundingDec)*roundingDec;
var newMin = Math.floor(totalMin/roundingDec)*roundingDec;
// Determine the range of your values
var range = newMax - newMin;
// Define the number of gridlines (default 5)
var gridlines = 5;
// Determine an appropriate gap between gridlines
var interval = range / (gridlines - 1);
// Round that interval up to the exponent of 10
var newInterval = Math.ceil(interval/roundingDec)*roundingDec;
// Re-round your max and min to the new interval
var finalMax = Math.ceil(totalMax/newInterval)*newInterval;
var finalMin = Math.floor(totalMin/newInterval)*newInterval;
Method 2: Multiple Series
As long as the people viewing your data understand they are different sets, then there's no reason the axis needs to say the exact date/time as long as they can easily figure that out elsewhere.
Summary:
Separate your data in to different series for each 'sequence'
Artificially shorten the gaps between sequences (if they are 15 seconds each, then have a 5 second gap between series, or just start every 15 seconds)
Format each different series with a name labeling when the run started/ended
Details:
Again, you will have to split your data manually or create javascript to do it, but what you want to do is to move each set of numbers in to its own column, like so:
1360096658270, 10.228335, null
1360096658274, 10.308437, null
1360096658277, 10.294770, null
[...]
1360096673968, 9.014943, null
1360096673969, 8.971434, null
1360096673970, 9.041739, null
1360096673971, 9.097484, null
^^-- 15 seconds
<--- (~10 days)
1360989176509, null, 9.856928
1360989176513, null, 9.852907
1360989176517, null, 9.861740
1360989176523, null, 9.820416
1360989176527, null, 9.871401
This will make each series be a different color (and have a different label in the legend/on mouseover), so you can see the difference between runs, but also get a nice tooltip saying "This data was gathered from X to Y" so that if the time the data was taken is important, it's still in there (albeit not on the X axis).
These are the easiest ways.
Method 3: Manually Editing the X-Axis Labels
The third way is the most flexible but also takes the most work. You can create a custom javascript function to manipulate the X-axis labels in SVG. More details on this here by #jeffery_the_wind:
/*
*
* The following 2 functions are a little hacky, they have to be done after calling the "draw" function
* The bubble chart originally displays only numbers along the x and y axes instead of customer or product names
* These 2 functions replace those numbers with the words for the customers and products
*
*/
for ( var i = -2; i < products.length + 1; i ++ ){
$('#customer_product_grid svg text[text-anchor="start"]:contains("'+i+'")').text(function(j,t){
if (t == i){
if (i >= products.length || i < 0){
return " ";
}
return products[i];
}
});
}
for ( var i = -2; i <= customers.length + 3; i ++ ){
$('#customer_product_grid svg text[text-anchor="end"]:contains("'+i+'")').text(function(j,t){
if (i >= customers.length + 1 || i <= 0){
return " ";
}else if (t == i){
return customers[i-1];
}
});
}
Google's documentation on customizing axes describes how to do what you're asking. You can change the type of your column to a string and populate with formatted Date strings.