Dash graph with javascript clientside callback - javascript

I'm struggling to get 2 traces on a plotly graph when updating the data from a javascript clientside callback.
I have this clientside callback:
app.clientside_callback(
ClientsideFunction(
namespace='clientside',
function_name='update_rms_graph'
),
Output('rmstrace-graph', 'extendData'),
(
Input('client-rms-data', 'data'),
Input('client-rms-2-data', 'data'),
Input('timer-refresh-rms-fft', 'n_intervals')
)
)
Which is defined as such:
window.dash_clientside = Object.assign({}, window.dash_clientside, {
clientside: {
update_rms_graph: function(data, data_rms, n_intervals) {
console.log(data.rms.length, data_rms.rms.length);
return [
{y: [data.rms], x: [data.t]},
//{y: [data_rms.rms], x: [data_rms.t]}, // This is where i'd like to add my new trace
[0],
data.max_points
]
}
}
)
The 'rmstrace-graph' is defined as such:
html.Div([
dcc.Graph(id='rmstrace-graph', figure=fig_rmstrace),
], className='six columns')
And the corresponding figure used for the dash graph is the following:
fig_rmstrace = go.Figure(data=go.Scattergl(y=[], x=[]))
How should the 'return' from the clientside callback be wrote if I like to get a second trace from the same dash graph ?

return [
{
y: [rms.rms, averaged_rms.rms],
x: [rms.t, averaged_rms.t],
}
[0, 1],
rms.max_points
]

Related

Successfully get the data from Flask but cannot render the website

I want to use the eCharts to draw the candletick picture. I get the data from flask using Axios:
request.post('/investor').then(res=>{
var arr=Object.keys(res.close)
for(let i=0;i<arr.length;i++){
this.stockdata[i]=[]
for(let j=0;j<4;j++){
var temp=[]
temp[0]=res.open[i]
temp[1]=res.close[i]
temp[2]=res.low[i]
temp[3]=res.high[i]
this.stockdata[i][j]=temp[j]
}
}
})
this my data:
The 2d array
but it cannot render the website
then i create the data using the method below(assign data manually):
this.stockdata= [
[20, 34, 10, 38],
[40, 35, 30, 50],
[31, 38, 33, 44],
[38, 15, 5, 42]
]
it successfully draw the picture,i don't know why.
and this my main function:
mounted(){
request.post('/investor').then(res=>{
var arr=Object.keys(res.close)
for(let i=0;i<arr.length;i++){
this.stockdata[i]=[]
for(let j=0;j<4;j++){
var temp=[]
temp[0]=res.open[i]
temp[1]=res.close[i]
temp[2]=res.low[i]
temp[3]=res.high[i]
this.stockdata[i][j]=temp[j]
}
}
})
console.log(this.stockdata)
var echarts = require('echarts');
var option = {
xAxis: {
data: ['2017-10-24', '2017-10-25', '2017-10-26', '2017-10-27']
},
yAxis: {},
tooltip: {
trigger: 'axis'
},
series: [
{
type: 'candlestick',
data:this.stockdata
}
]
};
var charts = echarts.init(this.$refs.myChart);
charts.setOption(option);
}
}
There is not enough information, but I can still guess.
I am going to assume that your Object.keys double for loop with i and j works fine. To be sure of that, please add console.log(this.stockdata) after both loops end running, to make sure you are building your data from the response the right way.
The error is probably because in your code you call the async function, which is calling your API.
// this *.then* happens after the initial render, and after a delay it gets a value,
// which is too late
request.post('/investor').then(res=>{
This causes that at the exact frame the App is being rendered, your response is not yet there, causing your App to break because this.stockdata is still undefined. One way to fix it is the following:
// in render function:
{this.stockdata && <YourComponent dataToRender={this.stockdata} />}
This will prevent rendering YourComponent until the API response is there

Dash Clientside Callbacks

I am struggling with Dash Clientside callbacks. I am looking to create a smooth animation, so I need the clientside callback to have the fast update rate. I have an example that seems to replicate the problem; I have a normal callback and that works as expected. When I convert the same callback to clientside, it no longer works. However, when I do a JSON.stringify to the clientside return, I see the data field updating. I do not understand the issue, though I expect it is an issue with my js. I do not know how to debug on the clientisde, so any advice for error recording would also be appreciated.
Here is the working 'normal' callback:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input,Output,State
fig_test={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
],
'layout': {
'title': 'Dash Data Visualization'
}
}
app = dash.Dash(__name__)
app.layout = html.Div([
html.Button("Button 1", id="btn1"),
dcc.Graph(id="graph", figure=fig_test),
dcc.Slider(
id='interval-component',
min=0,
max=36,
step=1,
value=10,
),
html.Div(id="log"),
html.Pre(
id='structure',
style={
'border': 'thin lightgrey solid',
'overflowY': 'scroll',
'height': '275px'
}
)
])
#app.callback(
Output("graph", "figure"),
Input('interval-component','value'),Input("graph", "figure"),Input("btn1", "n_clicks"))
def display_structure(value, figure, btn1):
figure['data'][0]['y'][1] = value
return {'data': figure['data'], 'layout':figure['layout']}
app.run_server(debug=False)
Here is the same callback implemented through clientside:
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input,Output,State
fig_test={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
],
'layout': {
'title': 'Dash Data Visualization'
}
}
app = dash.Dash(__name__)
app.layout = html.Div([
html.Button("Button 1", id="btn1"),
dcc.Graph(id="graph", figure=fig_test),
dcc.Slider(
id='interval-component',
min=0,
max=36,
step=1,
value=10,
),
html.Div(id="log"),
html.Pre(
id='structure',
style={
'border': 'thin lightgrey solid',
'overflowY': 'scroll',
'height': '275px'
}
)
])
app.clientside_callback(
"""
function(value, figure, btn1){
figure['data'][0]['y'][1] = value
return {'data': figure['data'], 'layout':figure['layout']};
}
""", Output("graph", "figure"), [Input('interval-component','value'),Input("graph", "figure"),Input("btn1", "n_clicks")])
app.run_server(debug=False)
If I implement the clientside to jsonify the output like this:
app.clientside_callback(
"""
function(value, figure, btn1){
figure['data'][0]['y'][1] = value
return JSON.stringify({'data': figure['data'], 'layout':figure['layout']});
}
""", Output("log", "children"), [Input('interval-component','value'),Input("graph", "figure"),Input("btn1", "n_clicks")])
I can see the value being updated, so I do not know what the issue is.
So I figured out the 'smooth animation' for layout updates, wherein 'extend data' is not possible and so the solution in this answer: Plotly/Dash display real time data in smooth animation
is not applicable. Further it allows smooth animations of live-updated data without being dependent on the 'animate' api. This is not an exact answer to the question I asked, but addresses the concept.
If you are unfamiliar with Frames, or otherwise unsure how to setup a figure, see plotly's example here: https://plotly.com/python/animations/
Psuedocode for setup:
## setup figure,
for data in some_data:
##do something
fig['frames'].append(frame)
fig1 = go.Figure(fig)
setup a store for your frames, and a store for the frames to be passed to a clientside callback. As I was setting up a process to simulate live data acquisition, I had a timer for 'polling' and a secondary one for the animation. If you don't want a timer to as a trigger, the main concept is still the same; have some 'animate' trigger, in this case 'interval-component', to kick off the quickly refreshing secondary timer.
app.layout = html.Div([
dcc.Store(id='frames-stored', data=fig1['frames']),
dcc.Store(id='frames'),
dcc.Interval(
id='interval-component',
interval=1*500, # in milliseconds
n_intervals=0
),
dcc.Interval(id='graph-refresher',
interval=1*25,
n_intervals=0,
max_intervals=50,
disabled=True),
dcc.Graph(id="graph", figure=fig1),
])
now a callback to catch your 'animation' trigger and pass frames to your clientside callback:
#app.callback(
Output("frames", "data"),Output("graph-refresher", "disabled"),
Output("graph-refresher", "max_intervals"),Output('graph-refresher','n_intervals'),
Input("interval-component", "n_intervals"),State("frames-stored", "data"))
def data_smoother(n_intervals,frames):
## can do whatever here as long as a list of frames are passed to the store
selected_frames = frames[n_intervals]
return selected_frames,False,'some_max',0
This callback turns on the timer for the clientside callback, and resets the max_intervals with 'some_max'. This is going to be dependent on whatever you are doing.
Now the clientside callback that handles the 'animation'.
app.clientside_callback(
"""
function(n_intervals, frames){
return {'data':frames[parseInt(n_intervals)]['data'], 'layout':frames[parseInt(n_intervals)]['layout']};
}
""",Output("graph", "figure"),Input('graph-refresher','n_intervals'), State("frames", "data"))
I hope this is useful for someone!

Parsing JSON data into an array for a C3.js chart

I'm sending a json variable from django to C3.js in this format:
{
datetime.date(2015, 5, 1): 22792461.479999978,
datetime.date(2015, 6, 1): 24807797.38999998,
datetime.date(2015, 7, 1): 25261456.609999962
}
I need to extract the values and compile them into an array ready for use by c3.js. Here's my script:
<script>
{% include "croydon_dashboards/dashboard_includes/gb_locale.html" %}
var row_value = {{ row_value }};
var sum_list = [];
for(var i in row_value)
sum_list.push(row_value[i])
sum_list = JSON.stringify(sum_list);
var chart = c3.generate({
data: {
columns: [
sum_list
],
type: 'bar'
},
bar: {
width: {
ratio: 0.5
}
}
});
</script>
Hard coding values into the chart works, so I know the c3.js is right, but there's something wrong with the way I'm building the array.
I know that there's an answer here: JSON Data doesn't show on c3.js bar chart
but I'm new to javascript and can't work out how to parse the json that I'm working with.
You do not need to JSON.stringify() your data to use it with c3.
But you may have to JSON.parse() it to make it look like this:
var row_value = {
"2015-05-01": 22792461.479999978,
"2015-06-01": 24807797.38999998,
"2015-07-01": 25261456.609999962
};
And don't forget to add text label at the beginning of data array:
// This first value of array if the name of your data
var sum_list = ['something'];
The rest of your code works.
for(var i in row_value)
sum_list.push(row_value[i])
var chart = c3.generate({
data: {
columns: [
sum_list
],
type: 'bar'
},
bar: {
width: {
ratio: 0.5
}
}
});
See this fiddle.

Mixing custom function with predefined ones in reductio for crossfilter

I am trying to use both the .std() and the .custom() function in the reductio library.
My code is as follows for the reductio part:
dims.theme1 = myCrossfilter.dimension(function(d) {return(d.theme1);});
groups.theme1 = dims.theme1.group();
var reducer = reductio()
.custom({initial:reduceInit,add:reduceAdd,remove:reduceRemove})
.std("pl");
reducer(groups.theme1);
My code for the custom functions is :
reduceAdd = function(p,v) {
if (!p.fundsData.hasOwnProperty(v.AdyneAccount)) {
p.fundsData[v.AdyneAccount]=0;
}
if (!p.stratsData.hasOwnProperty(v.Strategy)) {
p.stratsData[v.Strategy]=0;
}
p.fundsData[v.AdyneAccount]+=+v.plfund;
p.stratsData[v.Strategy]+=+v.plstrat;
p.value+=+v.pl;
return(p);
};
reduceRemove = function(p,v) {
p.fundsData[v.AdyneAccount]-=+v.plfund;
p.stratsData[v.Strategy]-=+v.plstrat;
p.value-=+v.pl;
return(p);
};
reduceInit = function(p,v) {
return({
value:0,
fundsData:{},
stratsData:{}
});
};
I would expect my result (by doing groups.theme1.all()[0]) to look like the below (the values I have put in are random for this example) :
{
"key": "theTheme",
"value": {
"value": 10,
"fundsData": {
"a": 10,
"b": 5,
"c": 4
},
"stratsData": {
"somename": 8
},
"count": null,
"sum": null,
"sumOfSq": null,
"std": 0
}
}
And it does but it doesn't produce the values for count, sum and sumOfSq (so for std neither of course).
When I run .std("pl") by itself without my custom function on the exact same set of records, it works as expected. I do not understand why the addition of a custom function would prevent the correct calculation for the .std("pl") part.
All help welcome !
It looks to me like this is a bug in Reductio. The test names indicate that this shouldn't interfere with other aspects of reducers, but the content of the test makes clear that it does wipe them out.
https://github.com/crossfilter/reductio/blob/cd99f5043990a838b7e04ea86dbae3c1a1203119/test/custom.spec.js#L48
I've created an issue for this. No idea when I'll be able to get to it though. You'll probably want to just implement a normal Crossfilter custom reducer for this until I can fix it or someone sends a pull request.

Converting Poloniex API Callback JSON into format suitable for Highcharts.Stockchart

I am trying to get JSON from Poloniex's public API method (specifically the returnChartData method) to display chart history of cryptocurrencies against one another into a Highchart Stockchart graph (looking like the demo one here.).
This is part of my JavaScript code to use the Poloniex returnChartData callback, get the JSON from it and implement it into the 'data' segment of the chart. So far it is not working and I can't for the life of me figure out what I need to change.
var poloniexUrl = "https://poloniex.com/public?command=returnChartData&currencyPair=BTC_XMR&start=1405699200&end=9999999999&period=14400";
$.getJSON(poloniexUrl, function(data){
results = data;
});
// Creates Chart
var chart = new Highcharts.StockChart({
chart: {
renderTo: 'cryptoChart',
backgroundColor: 'white'
},
title: {
text: currentTitle
},
series: [{
data: results,
turboThreshold: 1000
}],
xAxis: {
original: false
},
rangeSelector: {
selected: 1
},
plotOptions: {
line: {
gapSize: 2
}
}
});
Would love any help!
Refer to this live demo: http://jsfiddle.net/kkulig/0f4odg5q/
If you use turboThreshold the points' options need to be given as an integer or an array (Explanation: https://api.highcharts.com/highstock/plotOptions.series.turboThreshold). In your case the format is JSON, so I disabled turboThreshold to prevent Higcharts error 12 (https://www.highcharts.com/errors/12):
turboThreshold: 0
$.getJSON is asynchronous - the best way to make sure that data variable is initialized is using it inside callback function (second argument of getJSON):
$.getJSON(poloniexUrl, function(data) {
// Creates Chart
var chart = new Highcharts.StockChart({
chart: {
(...)
The data that you fetch looks like candlestick series - I changed the type of the series:
type: 'candlestick'
Date will be properly understood by Highcharts if it's kept in the x property of JSON object (not date):
data: data.map((p) => {
p.x = p.date;
return p
}),

Categories

Resources