d3.js 3D array interpolation - javascript

Code is here: http://jsfiddle.net/S48QX/.
I want to draw a image based on a 3D data set, for example:
var data = [
{x:1.428, y:0.500, energy:0.458},
{x:1.428, y:1.191, energy:0.616},
{x:1.428, y:1.882, energy:0.795},
{x:1.428, y:2.573, energy:0.642},
{x:1.428, y:3.264, energy:0.536},
{x:1.428, y:3.955, energy:0.498},
{x:1.428, y:4.646, energy:0.494},
{x:1.428, y:5.337, energy:0.517},
...
}
It's like scattered plot, but I need every pixel to be set, not just a bunch of color dots on the image. So, my question is how can I interpolate scattered dots with d3.js.
The generated image here is the best I can do so far, but is it possible to make it more smooth and beautiful?
I am seeking a way to generate a HEATMAP only based on partial/scattered data. I hope there is a way in d3.js that can interpolate the missing part.
(1,5) ? ? ? (5,5)
? ? ? ? ?
? ? ? ? ?
(1,2) ? ? ? (5,2)

I have one solution using svg filters.
Be careful as this may not be what you want since the mathematical interpretation of this interpolation would be more 'blur'. I mostly did it as an exercise on svg filters. However 2d interpolation end up with similar results: see cubic interpolation for example (http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.interpolate.interp2d.html in python)
I used circles (you could try with rectangles) slightly overlapping and semi transparent and applied to gaussian blur on them, resulting in a 'heatmap' looking thing.
var filter = svg.append("defs").append('filter')
.attr('id', 'blur')
.append("feGaussianBlur")
.attr("stdDeviation", 8);
then using .style('fill-opacity', 0.5).attr("filter", "url(#blur)") on the circles
See the fork http://jsfiddle.net/eqt1mkov/

With some effort you might be able to translate an existing algorithm to JavaScript.
Octave is open source and provides a method for scattered data interpolation:
http://www.dm.unibo.it/~achilles/calc/octave.html/Interpolation-on-Scattered-Data.html
The source code of Octave is available at
ftp://ftp.gnu.org/gnu/octave/
The file griddata.m and some referenced files can be found in the folder
octave_{version}\scripts\geometry
D3.js seems to provide some voronoi and delaunay functionality that might be helpful:
https://github.com/d3/d3/wiki/Voronoi-Geom
http://bl.ocks.org/mbostock/4341156
Python also provides a griddata method:
http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.interpolate.griddata.html#scipy.interpolate.griddata

Plotly.js is based on D3.js and able to create contour plots for scattered data:
https://jsfiddle.net/vwksaob3/
var data = [ {
x: [0, 1, 1, 0],
y: [0, 0, 1, 1],
z: [0, 0, 1, 1],
type: 'contour',
colorscale: 'Jet',
showscale: false,
autocontour: true
}];
var layout = {
margin: {
b: 0,
l: 0,
r: 0,
t: 0
},
height: 600,
width: 600,
title: '',
xaxis: {
ticks: '',
showticklabels: false
},
yaxis: {
ticks: '',
showticklabels: false
}
};
Plotly.newPlot('graph', data, layout, {displayModeBar: false});

Related

How to make a mirrored bar chart with amCharts5 XY column series stacked series

I need a bar chart that comparatively displays two sets of data side by side along a horizontal axis.
So I have a stacked (but not clustered) column series XY chart (amCharts 5) which looks like this:
The plotted data is coming from an array of objects:
[
{
current: 61,
previous: 29,
...
},
{
current: 60,
previous: 29,
...
},
...
]
Now, how can I achieve this:
Do I need to find the average between current and previous then set that as a base, or do I really need negative values (say for previous) to achieve that? Either way, I was unable to make it look like above.
Found an example with amCharts4 but I don't understand how should I modify my chart to achieve the same mirror bar chart look.
I've put the chart together in a Codesandbox playground, I would appreciate some help. Data is hardcoded and there's an object with previous negative values aswell.
To do that, you need two vertical axes with opposite directions. They could be configured like so:
let yAxisCurrent = chart.yAxes.push(
am5xy.ValueAxis.new(root, {
renderer: am5xy.AxisRendererY.new(root, {}),
calculateTotals: true,
visible: true,
min: 0
})
);
let yAxisPrevious = chart.yAxes.push(
am5xy.ValueAxis.new(root, {
renderer: am5xy.AxisRendererY.new(root, {
inversed: true // <--- DO NOT FORGET THIS
}),
calculateTotals: true,
visible: true,
min: 0
})
);
Then add this line of code:
chart.leftAxesContainer.set("layout", root.verticalLayout);
Here is the result: https://codesandbox.io/s/sleepy-hooks-vwucth

How to highlight and show tooltip for correct (left / right) step point instead of the closest point

I'm showing an area graph with step defined to "left" in order to show that the "valid" value between the points is still the one to the left until the next point (simple example: https://jsfiddle.net/ugvL413n/). However, I would also like the point highlighting and tooltip to behave similarly, i.e. that the highlighted point is not the one closest to the mouse, but always the point to the left of the mouse (or right, if step is "right"). For starters, this would be nice to understand how to do for one chart.
Moreover, I'm looking to do this for synchronized charts (https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/demo/synchronized-charts). So, I would need the series.searchPoint function to allow me to not only find the closest point, but in fact the closest point to the left or right depending on the callers needs. However, it doesn't look like it's supported. So currently, I'm trying to do all of this in the "mousemove" event handler with some kind of logic based on the normalized event.chartX and some computed comparable X value based on many private properties of the found point, compare if they are left / right of the event and I get it working as I want.
// All of these properties are private, please find a more sustainable way to find the comparable value to the normalized event.chartX value
function getComparableChartX(point) {
return point.plotX + point.distX + (point.graphic.width - (point.graphic.hasStroke ? point.graphic['stroke-width'] * 2 : 0));
}
But to be honest, it's quite a horrible solution to maintain and I'm looking for guidance if there's a better way to do this, since my expectation towards private properties is that they can be changed without any prior notice. And I'm not even sure these are all the / correct properties to compare with. I got this working by trial and error.
Here's my current working example: https://jsfiddle.net/8sew72uk/
I think you can try setting a condition in the tooltip.formatter and show a specific value in the tooltip.
And if you don't want to show some points then disabling markers in for given ranges should work.
Highcharts.chart('container', {
chart: {
type: 'area'
},
plotOptions: {
area: {
step: 'left',
},
seires: {
findNearestPointBy: 'xy'
}
},
tooltip: {
followPointer: true,
formatter: function() {
return `${this.y}`
}
},
xAxis: [{
crosshair: {
snap: false
}
}],
series: [{
data: [0, 1, {
y: 1,
marker: {
enabled: false
}
}, 1, 1, 2, 2, 2, 3, 3, 4, 2, 1, 1, 0, 0, 0, 0, 0]
}]
});
Demo:
https://jsfiddle.net/BlackLabel/vehz6yqx/
API References:
https://api.highcharts.com/highcharts/tooltip.formatter
https://api.highcharts.com/highcharts/plotOptions.series.marker.enabled

Updating candlestick data point using restyle in Plotly.js

I am using a WebSocket connection to update a candlestick chart with live data.
Creating the initial candlestick chart is relatively easy:
var candleDiv = document.getElementById('candle-chart');
var data = {
x: x, //Each of these is a single dimension array of the same length
open: open,
close: close,
high: high,
low: low,
type: 'candlestick',
};
var layout = {
datarevision: candleCount,
dragmode: 'zoom',
showlegend: false,
xaxis: {
type: 'date',
range: [x[x.length - 26], x[x.length - 1]], //Only show the last 25 entries so it's not zoomed out too far.
rangeslider: {
visible: false
},
yaxis: {
autorange: true,
}
}
}
data.xaxis = 'x';
data.yaxis = 'y';
data = [data];
Plotly.plot(candleDiv, data, layout);
However, the documentation for the restyle method doesn't talk much to the update of data. More about how the data is displayed. After much tinkering, I found a reasonable workaround of updating the data variable directly:
candleDiv.data[0].open[candleDiv.data[0].open.length - 1] = updatedOpenValue;
candleDiv.data[0].close[candleDiv.data[0].close.length - 1] = updatedCloseValue;
candleDiv.data[0].high[candleDiv.data[0].high.length - 1] = updatedHighValue;
candleDiv.data[0].low[candleDiv.data[0].low.length - 1] = updatedLowValue;
Plotly.restyle(candleDiv, 'data[0]', candleDiv.data[0], [0]);
This works, except that it appears to draw the new candle on the old candle. This becomes particularly distracting when the stick changes from a green (increasing) stick to a red (decreasing) stick.
Is there a correct syntax to achieve what I am attempting to do such that I don't get display issues?
I checked out this link from this post but I couldn't get the method used to work in the context of a candlestick chart.
You may want to look at the Plotly.react method instead of Plotly.restyle: https://plot.ly/javascript/plotlyjs-function-reference/#plotlyreact

Avoid float number labels on y-axis with dygraphs javascript and set different grid lines to secondary y axis

I have series with simple integer values. So there is no need to have float numbers as y-axis labels.
I use the axisLabelFormatter to convert y to integers. But the result is, that I have duplicated integer values on the y-axis.
How can I get a y-axis, which is labeled only with single integers at the correct place?
I would like to have the secondary y-axis with a different grid too
See also the example at https://jsfiddle.net/eM2Mg/9559/.
I have tried your example, modified it and I have got this result
I have modified your code the next way
var graph2 = new Dygraph(document.getElementById("graph2"), data, {
axes: {
y2: {
axisLabelFormatter: function(y) {
return texts[y] || parseInt(y);
},
drawGrid: true,
independentTicks: true,
pixelsPerLabel: 100,
gridLinePattern: [2,2]
}
},
legend: "always",
series: {
"State": {
axis: "y2",
strokeWidth: 2,
stepPlot: true,
}
},
strokeWidth: 2,
title: "my try to get what I want"
});
I have set the option drawGrid and independentLabels to true.
It is necessary to adjust the pixelsPerLabel to the size you think the 3 values auto, on, off are better shown.
And the grid patterLine to show a different grid for the right y axis.
You can also remove the drawGrid if you consider it look better without the grid.
I hope this could be a solution for you! Regards!

Adding data to a highchart chart using an array with IDs

I want to add a series to a highchart scatterplot where I am naming each point in the series. I create a chart in the following way:
var chart; // globally available
makeCharts = function(){
chart = new Highcharts.Chart({
chart: {
renderTo: 'container1',
type: 'scatter'
},
series: [{
name: 'a',
data: [{
'id': 'point1',
'x': 1,
'y': 2
}, {
'id': 'point2',
'x': 2,
'y': 5
}]
}]
});
}
I would like to be able to update the points on the chart using something like:
chart.series[0].setData([{id:['point3', 'point4', 'point5'], y:[0,1,2], x:[1,2,3]}])
but this is not correct. Is it possible to update a chart using this approach where each point has an ID?
EDIT:
Just to clarify, I would like to be able to pass the arrays directly, rather than adding the data point by point using addPoint(). I could loop through an array and use addPoint() doing something like this:
id:['point3', 'point4', 'point5'];
y:[0,1,2];
x:[1,2,3];
for (i=0; i<x.length; i++)
{
chart.series[0].addPoint({
x: x[[i],
y: y[i],
id: id[i]
});
}
However, this is very slow. It's much quicker to add data using the following approach:
chart.series[0].setData([[1,0],[2,1],[3,2]]);
I have found that I can add data like this:
chart.series[0].setData([[1,0, 'point3'],[2,1, 'point4'],[3,2, 'point5']]);
but then the only way that I can access the id when the point is selected, is through this.point.config[2]. With the following approach I am unable to use chart.get('pointID') to identify a point as I did not set the ID. I want to be able to identify the point using just the ID.
Well broadly speaking there are two ways in which you can modify the chart data dynamically
Series.setData() Use this approach when you want to completely replace the existing data with some new data
Series.addPoint() Use this approach when you want to add a subset of the points dynamically. This method is not just for adding one point at a time, if you read the documentation carefully again you will find that this method takes a boolean redraw argument, and the argument detail is as following
redraw: Boolean
Defaults to true. Whether to redraw the chart after
the point is added. When adding more than one point, it is highly
recommended that the redraw option beset to false, and instead
chart.redraw() is explicitly called after the adding of points is
finished.
In your case, since you want to add a few points dynamically, but retaining the existing points, you should go with approach 2. But you need to use it inside a loop, with the redraw being set to false (hence solving the problem of being slow) and then after the loop, call the redraw method explicitly
Code
var id = ['point3', 'point4', 'point5'],
y = [0, 1, 2],
x = [1, 2, 3];
for (var i = 0; i < x.length; i++) {
chart.series[0].addPoint({
x: x[i],
y: y[i],
id: id[i]
},false);
}
chart.redraw();
Adding multiple points dynamically | Highcharts and Highstock # jsFiddle
Try using series.addPoint.
chart.series[0].addPoint({
x: 0,
y: 0,
id: 'anything'
});
But if you need to set data for series, use
chart.series[0].setData([{
x: 0,
y: 0,
id: 'anything'
},{
x: 2,
y: 2,
id: 'another'
}]);
As soon as you can pass your data like this:
chart.series[0].setData([[1,0, 'point3'],[2,1, 'point4'],[3,2, 'point5']]);
(as you stated in question), I can suggest you to use a little hack.
We'll need to add another statement to method applyOptions of Highcharts.Point prototype.
if (typeof options[0] === 'number' && options[2] && typeof options[2] === 'string') this.id = options[2];
Here you can see it in action.

Categories