Plotly.extendTraces only work with two traces but not with three - javascript

function getData() {
return Math.random();
};
function plotGraph(graph_div) {
let UPDATE_INTERVAL = 300;
Plotly.plot(graph_div, [{
y: [1, 2, 3].map(getData),
name: 'x',
mode: 'lines',
line: { color: '#80CAF6' }
}, {
y: [1, 2, 3].map(getData),
name: 'y',
mode: 'lines',
line: { color: '#DF56F1' }
}, {
y: [1, 2, 3].map(getData),
name: 'z',
mode: 'lines',
line: { color: '#4D92E9' }
}]);
var cnt = 0;
var interval = setInterval(function () {
var time = new Date();
Plotly.extendTraces(graph_div, {
y: [[getData()], [getData()], [getData()]]
}, [0, 1])
cnt = cnt+1;
if (cnt === 100) clearInterval(interval);
}, UPDATE_INTERVAL);
}
error:
plotly-latest.min.js:7 Uncaught Error: attribute y must be an array of length equal to indices array length
at plotly-latest.min.js:7
at R (plotly-latest.min.js:7)
at Object.t [as extendTraces] (plotly-latest.min.js:7)
at realtime_vis.js:40
point to
Plotly.extendTraces(graph_div, {
y: [[getData()], [getData()], [getData()]]
}, [0, 1])
Example from official documentation only shows how plot 2 lines, but that example not working with three lines.
Any help? I assume that I can explicitly specify the size of the array?!

The Plotly documentation isn't really clear here but the third parameter is an array of plot indexes you want to modify.
In your case you are telling Plotly to modify [0, 1] but you provide 3 new y-values. If you change it to [0, 1, 2] it should work, or you could provide only two new y-values.
function getData() {
return Math.random();
};
Plotly.plot(graph_div, [{
y: [1, 2, 3].map(getData),
name: 'x',
mode: 'lines',
line: { color: '#80CAF6' }
}, {
y: [1, 2, 3].map(getData),
name: 'y',
mode: 'lines',
line: { color: '#DF56F1' }
}, {
y: [1, 2, 3].map(getData),
name: 'z',
mode: 'lines',
line: { color: '#4D92E9' }
}]);
var cnt = 0;
var interval = setInterval(function () {
var time = new Date();
Plotly.extendTraces(graph_div, {
y: [[getData()], [getData()], [getData()]]
}, [0, 1, 2])
cnt = cnt+1;
if (cnt === 100) clearInterval(interval);
}, 300);
<head>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<div id="graph_div"></div>
</body>

Related

Add and visualize custom values assigned to nodes of plotly

This is a follow-up to my previous question posted here How to add custom values to nodes of plotly graph.
The solution posted works well for selecting a single node using plotly_click. As suggested in the comments, I tried to use plotly_selected for displaying multiple nodes.
I am trying to select multiple nodes and display the values associated with the nodes using the following code.
<html>
<head>
<script src="https://cdn.plot.ly/plotly-1.58.5.min.js"></script>
<style>
.graph-container {
display: flex;
justify-content: center;
align-items: center;
}
.main-panel {
width: 70%;
float: left;
}
.side-panel {
width: 30%;
background-color: lightgray;
min-height: 300px;
overflow: auto;
float: right;
}
</style>
</head>
<body>
<div class="graph-container">
<div id="myDiv" class="main-panel"></div>
<div id="lineGraph" class="side-panel"></div>
</div>
<script>
var nodes = [
{ x: 0, y: 0, z: 0, value: [1, 2, 3] },
{ x: 1, y: 1, z: 1, value: [4, 5, 6] },
{ x: 2, y: 0, z: 2, value: [7, 8, 9] },
{ x: 3, y: 1, z: 3, value: [10, 11, 12] },
{ x: 4, y: 0, z: 4, value: [13, 14, 15] }
];
var edges = [
{ source: 0, target: 1 },
{ source: 1, target: 2 },
{ source: 2, target: 3 },
{ source: 3, target: 4 }
];
var x = [];
var y = [];
var z = [];
for (var i = 0; i < nodes.length; i++) {
x.push(nodes[i].x);
y.push(nodes[i].y);
z.push(nodes[i].z);
}
var xS = [];
var yS = [];
var zS = [];
var xT = [];
var yT = [];
var zT = [];
for (var i = 0; i < edges.length; i++) {
xS.push(nodes[edges[i].source].x);
yS.push(nodes[edges[i].source].y);
zS.push(nodes[edges[i].source].z);
xT.push(nodes[edges[i].target].x);
yT.push(nodes[edges[i].target].y);
zT.push(nodes[edges[i].target].z);
}
var traceNodes = {
x: x, y: y, z: z,
mode: 'markers',
marker: { size: 12, color: 'red' },
type: 'scatter3d',
text: [0, 1, 2, 3, 4],
hoverinfo: 'text',
hoverlabel: {
bgcolor: 'white'
},
customdata: nodes.map(function(node) {
if (node.value !== undefined)
return node.value;
}),
type: 'scatter3d'
};
var layout = {
margin: { l: 0, r: 0, b: 0, t: 0 }
};
Plotly.newPlot('myDiv', [traceNodes], layout, { displayModeBar: false });
// max y value for the line plot
const ymax = Math.max(...nodes.map(n => n.value).flat());
document.getElementById('myDiv').on('plotly_selected', function(data){
var selectedNodeIndices = data.points.map(function(point) {
return point.pointNumber;
});
var values = [];
for (var i = 0; i < selectedNodeIndices.length; i++) {
values.push(nodes[selectedNodeIndices[i]].value);
}
var data = [];
for (var i = 0; i < values.length; i++) {
data.push({
type: 'scatter',
mode: 'lines',
x: [0, 1, 2],
y: values[i],
name: 'Node ' + selectedNodeIndices[i]
});
}
Plotly.newPlot('lineGraph', data, {
margin: { t: 0 },
yaxis: {autorange: false, range: [0, ymax + 1]}
});
});
</script>
</body>
</html>
There is some issue, I am not able to select multiple nodes and the line graph is not plotted when nodes are clicked.
Suggestions on how to fix this will be really helpful.
EDIT:
<html>
<head>
<script src="https://cdn.plot.ly/plotly-1.58.5.min.js"></script>
<style>
.graph-container {
display: flex;
justify-content: center;
align-items: center;
}
.main-panel {
width: 70%;
float: left;
}
.side-panel {
width: 30%;
background-color: lightgray;
min-height: 300px;
overflow: auto;
float: right;
}
</style>
</head>
<body>
<div class="graph-container">
<div id="myDiv" class="main-panel"></div>
<div id="lineGraph" class="side-panel"></div>
</div>
<script>
var nodes = [
{ x: 0, y: 0, z: 0, value: [1, 2, 3] },
{ x: 1, y: 1, z: 1, value: [4, 5, 6] },
{ x: 2, y: 0, z: 2, value: [7, 8, 9] },
{ x: 3, y: 1, z: 3, value: [10, 11, 12] },
{ x: 4, y: 0, z: 4, value: [13, 14, 15] }
];
var edges = [
{ source: 0, target: 1 },
{ source: 1, target: 2 },
{ source: 2, target: 3 },
{ source: 3, target: 4 }
];
var x = [];
var y = [];
var z = [];
for (var i = 0; i < nodes.length; i++) {
x.push(nodes[i].x);
y.push(nodes[i].y);
z.push(nodes[i].z);
}
const edge_x = [];
const edge_y = [];
const edge_z = [];
for (var i = 0; i < edges.length; i++) {
const a = nodes[edges[i].source];
const b = nodes[edges[i].target];
edge_x.push(a.x, b.x, null);
edge_y.push(a.y, b.y, null);
edge_z.push(a.z, b.z, null);
}
var traceNodes = {
x: x, y: y, z: z,
mode: 'markers',
marker: { size: 12, color: Array.from({length: nodes.length}, () => 'red') },
text: [0, 1, 2, 3, 4],
hoverinfo: 'text',
hoverlabel: {
bgcolor: 'white'
},
customdata: nodes.map(function(node) {
if (node.value !== undefined)
return node.value;
}),
type: 'scatter3d'
};
var traceEdges = {
x: edge_x,
y: edge_y,
z: edge_z,
type: 'scatter3d',
mode: 'lines',
line: { color: 'red', width: 2},
opacity: 0.8
};
var layout = {
margin: { l: 0, r: 0, b: 0, t: 0 }
};
Plotly.newPlot('myDiv', [traceNodes, traceEdges], layout, { displayModeBar: false });
// max y value for the line plot
const ymax = Math.max(...nodes.map(n => n.value).flat());
document.getElementById('myDiv').on('plotly_click', function(data){
var nodeIndex = data.points[0].pointNumber;
var values = nodes[nodeIndex].value;
// Change color of the selected node
traceNodes.marker.color = Array.from({length: nodes.length}, (_, i) => i === nodeIndex ? 'blue' : 'red');
Plotly.update('myDiv', {
marker: {
color: traceNodes.marker.color
}
}, [0]);
Plotly.newPlot('lineGraph', [{
type: 'scatter',
mode: 'lines',
x: [0, 1, 2],
y: values
}], {
margin: { t: 0 },
yaxis: {autorange: false, range: [0, ymax + 1]}
});
});
</script>
</body>
</html>
I tried to define the selected marker's color i.e. blue to indicate that it has been selected. Unfortunately, this is not working.
Regarding selecting multiple nodes,
Currently, one single curve is visible on the side panel. I would like to know how to select multiple nodes and display multiple curves.
Unfortunately selection features don't work with 3d scatter plot : lasso and select tools are not available and the event plotly_selected won't fire. See this issue for more info.
That said, it is still possible to handle multi-point selection manually using the plotly_click event with some extra code. The idea is to use a flag and keypress/keyup events to determine whether a given point should be added to or removed from the current selection, or if it should be added to a new selection (ie. the flag is on when holding the shift or ctrl key).
The second thing is to define a selection object that can persist across different click events, which means we need to define it outside the handler.
(only the end of the script changes)
// max y value for the line plot
const ymax = Math.max(...nodes.map(n => n.value).flat());
// Accumulation flag : true when user holds shift key, false otherwise.
let accumulate = false;
document.addEventListener('keydown', event => {
if (event.key === 'Shift') accumulate = true;
});
document.addEventListener('keyup', event => {
if (event.key === 'Shift') accumulate = false;
});
// Selected points {<nodeIndex> : <nodeData>}
let selection = {};
document.getElementById('myDiv').on('plotly_click', function(data){
if (data.points[0].curveNumber !== 0)
return;
const nodeIndex = data.points[0].pointNumber;
if (accumulate === false)
selection = {[nodeIndex]: data.points[0]};
else if (nodeIndex in selection)
delete selection[nodeIndex];
else
selection[nodeIndex] = data.points[0];
// Highlight selected nodes (timeout is set to prevent infinite recursion bug).
setTimeout(() => {
Plotly.restyle('myDiv', {
marker: {
size: 12,
color: nodes.map((_, i) => i in selection ? 'blue' : 'red')
}
});
}, 150);
// Create a line trace for each selected node.
const lineData = [];
for (const i in selection) {
lineData.push({
type: 'scatter',
mode: 'lines',
x: [0, 1, 2],
y: selection[i].customdata,
});
}
Plotly.react('lineGraph', lineData, {
margin: {t: 0},
yaxis: {autorange: false, range: [0, ymax + 1]},
});
});

Echart: How to set mark area to fill sections in xAxis

I have a problem with marking area: i need to be able to select a bar area based on xAxis, for example from 0 to 1, from 1 to 2, etc. But when i try to provide options for bar like
[{xAxis: 0, itemStyle: {color: red}},{xAxis: 1}]
it marks an area from a middle of xAxis area with an index of 0 to a middle of xAxis area with an index of 1. Is there a way to make it mark from start of an area to an end. Currently i managed to do so only with x option in pixels:
https://codesandbox.io/s/react-echart-markarea-ksj31?file=/src/index.js:714-726
Is there a better way to do it?
I can't imagine a method that would cover your requirements. It seems there is no such but nothing prevents to do it ourselves, see below.
When call function with join = true markedArea will calc as range from first to last.
calcMarkAreaByBarIndex(myChart, join = true, [4, 9])
When call function with join = false markedArea will calc for each bar.
calcMarkAreaByBarIndex(myChart, join = true, [4, 5, 6, 9])
var myChart = echarts.init(document.getElementById('main'));
var option = {
tooltip: {},
xAxis: {
data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
},
yAxis: {},
series: [
{
id: 'myBar',
name: 'Series',
type: 'bar',
data: [11, 11, 11, 11, 12, 13, 110, 123, 113, 134, 93, 109],
markArea: {
data: [
[{x: 184},{x: 216}],
[{x: 224},{x: 256}],
]
},
},
]
};
myChart.setOption(option);
function calcMarkAreaByBarIndex(chartInstance, join = false, barIdx){
var series = chartInstance.getModel().getSeriesByType('bar');
var seriesData = series.map((s, idx) => s.getData())[0];
var barNum = seriesData.count();
var barCoors = [];
var layout = idx => seriesData.getItemLayout(idx);
for(var i = 0; i < barNum; i++){
if(!barIdx.includes(i)) continue;
barCoors.push([
{ x: layout(i).x },
{ x: layout(i).x + layout(i).width },
])
}
if(join){
return [
[
{ x: barCoors[0][0].x },
{ x: barCoors[barCoors.length - 1][1].x }
]
]
} else {
return barCoors
}
}
var markedAreas = {
series: {
id: 'myBar',
markArea: {
data: calcMarkAreaByBarIndex(myChart, join = true, [4,9])
}
}
};
myChart.setOption(markedAreas);
<script src="https://cdn.jsdelivr.net/npm/echarts#4.7.0/dist/echarts.min.js"></script>
<div id="main" style="width: 600px;height:400px;"></div>
I found a solution, that worked for me:
Basically, you need to manually set yAxis's max props, add another xAxis, make it invisible, create a custom series with type 'bar' and set xAxisIndex to 1:
data: [maxYaxisValue,maxYaxisValue...], //length === xAxis.data.length
type: 'bar',
barWidth: '100%',
color: transparent,
xAxisIndex: 1,
And style a bar by index with background color and borderWidth
You can check the working example here
https://codesandbox.io/s/react-echart-markarea-m0mgq?file=/src/index.js

Highcharts Graph displaying 0 value continuously : Javascript Array

I am trying to display the points on a graph using PHP Mysql, I saved the data into an array in php Variable and then passed that array into a Javascript array.
Now, What I want to do is that I want to show the array elements one by one after every one second. But what is Happening is that the graph plots 0 value countinuously on the Highchart.
Here is my Code :
numArray = [1,5,3,5,6,3,3,7,4,6,7,3,5,3,6,7,5,2,5,7,4,6,4,5,3,6,7,8,5,4,3,6,7,8,5,7,8,8,5,3,2,4,6,7,4,6,7] ;
/* Just for understanding */
var json_array =numArray ;
var i = 0;
function next() {
return json_array[i];
i++;
}
Highcharts.chart('container', {
chart: {
type: 'line',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function() {
// set up the updating of the chart each second
var series = this.series[0],
chart = this;
setInterval(function() {
var x = (new Date()).getTime(), // current time
y =next();
console.log(y) ;
series.addPoint([x, y], false, true);
}, 1000);
setInterval(function() {
chart.redraw(false);
}, 1000);
}
}
},
time: {
useUTC: false
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
headerFormat: '<b>{series.name}</b><br/>',
pointFormat: '{point.x:%Y-%m-%d %H:%M:%S}<br/>{point.y:.2f}'
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
animation: false,
name: 'Random data',
data: (function() {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -1000; i <= 0; i += 1) {
data.push([
time + i * 10,
null
]);
}
return data;
}())
}]
});
Here is the Fiddle that I have created :
https://jsfiddle.net/abnitchauhan/v7tdLr1j/2/
The Chart runs till infinite loop and show only 0 Value. I just want to show the array values till the End
Check the below demo with the result you probably want to achieve.
Code:
const numArray = [1, 5, 3, 5, 6, 3, 3, 7, 4, 6, 7, 3, 5, 3, 6, 7, 5, 2, 5, 7, 4, 6, 4, 5, 3, 6, 7, 8, 5, 4, 3, 6, 7, 8, 5, 7, 8, 8, 5, 3, 2, 4, 6, 7, 4, 6, 7];
Highcharts.chart('container', {
chart: {
events: {
load: function() {
let chart = this,
now = (new Date()).getTime(),
i = 0;
const interval = setInterval(function() {
chart.series[0].addPoint([
now + i * 1000,
numArray[i]
]);
i++;
if (i === numArray.length) {
clearInterval(interval);
}
}, 1000);
}
}
},
xAxis: {
type: 'datetime'
},
series: [{}]
});
Demo:
https://jsfiddle.net/BlackLabel/vf906wam/
First of there is no endpoint in your setInterval method. It will keep on calling after 1 sec. You cant even stop. First, have a separate function to form data. Let's say have series data initialized. Have as a global variable. then have the Promise to generate you x point.
var promise1 = new Promise(function(resolve, reject) {
var points [];
setTimeout(function() {
var x = (new Date()).getTime(), // current time
y =next();
points.push([x, y], false, true);
if(your counter let says 1000 : points.lenght > 1000) {
resolve(points) //resolve your points
}
}, 300);});
wrap above thins in function and return a promise and call that.
callYourDefinedFun().then(function(points) {
// now you have points ready, series ready make your chart configuration and then
// call to render the chart
});

"Play with this data!" not showing up for my plotly.js plot

I was under the assumption the "Play with this data!" link was supposed the show up by default. Any ideas on why it may not appear? I am just working with a basic scatter plot.
Note that this code below is not standalone as is, it is just the excerpt that does the plotly work.
var xData = [];
var yData = [];
var h = results;
for(var k in h) {
var localdate = k;
var plotdate = moment(localdate).format('YYYY-MM-DD HH:mm:ss');
xData.push(plotdate);
if (currentPort === "t") {
yData.push(CtoF(h[k]));
} else {
yData.push(h[k]);
};
}
var plotdata = [
{
x: xData,
y: yData,
type: 'scatter',
mode: 'markers+lines',
line: {
'color': HELIUM_BLUE
},
marker: {
'symbol': 'circle',
'color': HELIUM_PINK,
'maxdisplayed': 50
}
}
];
var layout = {
title: currentData,
xaxis: {
'title': 'Date / Time'
},
yaxis: {
'title': title
}
};
Plotly.newPlot(plotHolder, plotdata, layout);
You would need to add {showLink: true} as the fourth argument (after layout). I guess the default value changed from true to false.
If you want to change the caption of the button, use {showLink: true, "linkText": "Play with this data"}
var xData = [1, 2, 3, 4, 5];
var yData = [10, 1, 25, 12, 9];
var plotdata = [
{
x: xData,
y: yData,
type: 'scatter',
mode: 'markers+lines',
}
];
var layout = {
title: 'Edit me',
xaxis: {
'title': 'x'
},
yaxis: {
'title': 'y'
}
};
Plotly.newPlot(plotHolder, plotdata, layout, {showLink: true, "linkText": "Play with this data"});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id='plotHolder'>
</div>

Restacking cumulative columns in Highcharts marimekko charts

I've got a basic variable width column chart (aka Marimekko) set up using Highcharts but am having trouble getting it to restack the columns properly to eliminate the data gap once a series has been removed or hidden.
JSFIDDLE DEMO <-- I've set up a demo of the issue here.
You'll notice clicking on a legend item removes the series from the chart, but it also removes all of the following data points in the array (i.e. clicking on series C removes series C, D, and E whereas it should redraw to A-B-D-E). Since the y-axis data is meant to display a cumulative sum of all series, these should re-shuffle as adjacent columns with no gaps. How can I get this to render properly?
THIS POST uses similar demo code and attempting to solve the same problem, however the answer is somewhat elusive and I am unable to get it working.
Thanks in advance!
$(function () {
var dataArray = [
{ name: 'A', x: 200, y: 120 },
{ name: 'B', x: 380, y: 101 },
{ name: 'C', x: 450, y: 84 },
{ name: 'D', x: 198, y: 75 },
{ name: 'E', x: 95, y: 55 }
];
function makeSeries(listOfData) {
var sumX = 0.0;
for (var i = 0; i < listOfData.length; i++) {
sumX += listOfData[i].x;
}
var allSeries = []
var x = 0.0;
for (var i = 0; i < listOfData.length; i++) {
var data = listOfData[i];
allSeries[i] = {
name: data.name,
data: [
[x, 0], [x, data.y],
{
x: x + data.x / 2.0,
y: data.y,
dataLabels: { enabled: false, format: data.x + ' x {y}' }
},
[x + data.x, data.y], [x + data.x, 0]
],
w: data.x,
h: data.y
};
x += data.x + 0;
}
return allSeries;
}
$('#container').highcharts({
chart: { type: 'area' },
xAxis: {
tickLength: 0,
labels: { enabled: true}
},
yAxis: {
title: { enabled: false}
},
plotOptions: {
series: {
events: {
legendItemClick: function () {
var pos = this.index;
var sname = this.name;
var chart = $('#container').highcharts();
while(chart.series.length > 0) {
chart.series[pos].remove(true);
}
dataArray[pos]= { name: sname, x: 0, y: 0 };
chart.series[0].setData(dataArray);
}
}
},
area: {
lineWidth: 0,
marker: {
enabled: false,
states: {
hover: { enabled: false }
}
}
}
},
series: makeSeries(dataArray)
});
});

Categories

Resources