How to delete old data points from graph after 10 points? - javascript

I am plotting my data using chartist and I am updating the chart dynamically but the chart is not looking good as it is appending values over and over. I want to remove the first array element as the 11th array data appends.
Basically I only want to show only 10 points in the screen. How I can do this? I am using python flask for web programming and data is coming from arduino and using the chartist library.
Here is the JavaScript used.
var mychart;
var now = new Date().getTime();
var getdata = $.get('/a');
getdata.done(function(r) {
var data = {
labels: [
],
series: [
r.r
]
};
var options = {
width : 1200,
height : 300
}
mychart = new Chartist.Line('.ct-chart', data, options);
});
var myupdate = setInterval(updatechart,1000);
function updatechart() {
var updatedata = $.get('/a');
updatedata.done(function(r) {
var data = {
labels: [
],
series: [
r.r
]
};
mychart.update(data);
});
}
Here is my python code. Where i am reading the continuous data from arduino.
from flask import Flask, render_template,request,redirect, url_for,jsonify
import flask
from shelljob import proc
import time
import eventlet
eventlet.monkey_patch()
from flask import Response
import serial
from time import time,gmtime,strftime
from datetime import datetime
import json
app = flask.Flask(__name__)
lj= serial.Serial( '/dev/ttyACM0' ,9600)
#app.route('/')
def home():
return render_template('dynamic2.html')
v=[]
#app.route('/a')
def a():
if (lj.inWaiting()>0):
mydata= lj.readline()
v.append(mydata)
print mydata
return jsonify({'r':v})
if __name__ == '__main__':
app.run(debug=True)

If I understand correctly, what you want is to use the first 10 returned items, so you should change
r.r
to
r.r.splice(r.r.length-10)
so you always retain the last 10 elements
But that'll only help in the client side. If you're not going to use more than 10 elements in the client, then you shouldn't send them from the server. It that the case, you should change
return jsonify({'r':v})
to
return jsonify({'r':v[-10:]})
so you'll only send the last 10 items to the client
If you're not going to use more than 10 elements on the server either, then you should change
v.append(mydata)
to
v.append(mydata)
if len(v) > 10
v.pop(0)
this way, each time you append a new item to the list, and it gets over 10 items, you delete the first one
For your other question (how to put current time in x axis) you should send the time from the server. Since you already imported time, you could do it like this
v={'r':[],'t':[]}
#app.route('/a')
def a():
if (lj.inWaiting()>0):
mydata= lj.readline()
v['r'].append(mydata)
v['t'].append(time.time())
print mydata
return jsonify(v)
I haven't really used the Chartist library, but a quick browse through its examples tells me that's what the labels property is there for. So you should update your Javascript to this
updatedata.done(function(r) {
var data = {
labels: r.t,
series: r.r
};
mychart.update(data);
});

Related

I'd like to code the array value I received from Python into JS and print it out on the web page

It is to draw a plot graph on a web page using Python and js code.
Here's my python code
from bottle import route, run, request, redirect, template
import pymysql as sql
#route('/print')
def show_print():
db = sql.connect(host='localhost', user='root', password='1234', db='testdb')
if db is None:
return template("index.tpl", text="Non", x_value=[], y_value=[])
else:
query = "select * from testdata;"
cur = db.cursor()
n = cur.execute(query)
tpl_no=[]
tpl_date=[]
tpl_hum=[]
tpl_tmp=[]
for i in range(n):
value = cur.fetchone()
tpl_no.append(value[0])
tpl_hum.append(value[2])
return template("index.tpl", x_value = tpl_no, y_value = tpl_hum)
db.close
And MySQL test data table content:
enter image description here
and plotly.js
<h1> {{x_value}} Hi There</h1>
<h1> {{y_value}} Hi There</h1>
<div id="myPlot" style="width:200;height:800px;"></div>
<script type="text/javascript">
var x_arry = new Array();
var y_arry = new Array();
for(var i = 0; i < {{x_value}}.length; i++){
x_arry.push({{x_value}}[i]);
}
for(var i = 0; i < {{y_value}}.length; i++){
y_arry.push({{y_value}}[i]);
}
var data = [{
x: x_arry,
y: y_arry,
mode: "lines"
}];
var layout = {
xaxis:{title:"X: Number"},
yaxis:{title:"Y: Hum"},
title:"This is my graph"
};
Plotly.newPlot("myPlot", data, layout);
I made it like this. But I can't see the plot graph on the web page
The x_arry value has been output normally in the js code.
However, the plot graph is not drawn on the web page because the y_arry value is not printed. I want to solve this.
I would recommend making two changes to your code.
Firstly, you'll need to convert the Decimal values you are getting from the database to Python floats, by replacing the line
tpl_hum.append(value[2])
with
tpl_hum.append(float(value[2]))
Secondly, the most reliable way to get data from your Python code to your JavaScript code is to get Python to serialize the data as a JSON string, and put this in your template. Try replacing the following line in your Python code
return template("index.tpl", x_value = tpl_no, y_value = tpl_hum)
with
json_data = json.dumps({"x_array": tpl_no, "y_array": tpl_hum})
return template("index.tpl", json_data=json_data)
You'll also need to add import json to your Python code.
The JavaScript within your template then becomes:
var jsonData = {{json_data}};
var data = [{
x: jsonData.x_array,
y: jsonData.y_array,
mode: "lines"
}];
var layout = {
xaxis:{title:"X: Number"},
yaxis:{title:"Y: Hum"},
title:"This is my graph"
};
Plotly.newPlot("myPlot", data, layout);
You don't need the loops to read the data into variables such as x_arry and y_arry. (In fact, it's arguable you wouldn't have needed them before.)
Disclaimer: I haven't tested this.

Pass python dictionary to javascript

I have a Python + JS application connected to a PostgreSQL database. The database contains data about users in different countries, which is queried by the server.py file. The result of this query is a dictionary that would look something like this:
{'US': 2,
'CA': 5}
This dictionary needs to be passed to my map.js file, which populates a world map according to the country code (key) and volume (value). This dictionary updates with user activity, so it needs to be passed every time someone loads the map.
How can I pass the data over? It seems like I need to create a JSON file. I'm not sure how to create that file within python or how to call it from javascript.
I want to replace the hardcoded 'var data' values from map.js with my query results from country_count on server.py.
my server.py:
#app.route("/map")
def show_mapjs():
country_count = {
"US": 0, "CA": 0,
}
country_code = session.get("country_code")
for country_code, _ in country_count.items():
records_count = User_Records.query.filter_by(country_code=country_code).count()
country_count[country_code] = records_count
print(f"=== {country_count}")
return country_count
(US & CA are initialized at 0 and the records_count query updates the count as user activity increases over time.)
my map.js:
fetch('/map')
anychart.onDocumentReady(function () {
var data = [
{'id': 'US', 'value': 5},
{'id': 'CA', 'value': 2}
]
var dataSet = anychart.data.set(data);
var mapData = dataSet.mapAs({ description: 'description' });
var map = anychart.map();
what a fun project!
Let's get the work under way.
On your server side,
import json
#app.route('/map')
def show_mapjs():
country_count = {
"US": 0, "CA": 0,
}
#place your own code here to get the data from the database#
country_list = []
for country, count in country_count.items():
country_list.append({"id": country, "value": count})
# Serializing json
json_object = json.dumps(country_list)
return json_object
On your client side,
First, include the below js libs in the HTML, so the next code can use it.
<script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-core.min.js" type="text/javascript"></script>
<script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-map.min.js" type="text/javascript"></script>
<script src="https://cdn.anychart.com/geodata/latest/custom/world/world.js"></script>
<script src="https://cdn.anychart.com/releases/v8/js/anychart-data-adapter.min.js"></script>
Use the map js function as below,
<script>
anychart.onDocumentReady(function () {
anychart.data.loadJsonFile("/map",
function (data) {
var map = anychart.map();
map.geoData(anychart.maps.world);
var dataSet = anychart.data.set(data);
// set the series
var series = map.choropleth(dataSet);
// disable labels
series.labels(false);
// set the container
map.container('container');
map.draw();
}
);
});
</script>
You should do this way to avoid out-of-sync data loading and map rendering. This will ensure that the json is downloaded and then processed by the map.
Let me know if you have issues getting this working.

Filter data with Javascript callback in Python's Bokeh

apologies in advance for unprecise/unappreciated wording as this is my first question here.
Feel free to point out how I can improve it in the future.
I have been reading through all of Bokeh's user guide and various forums but belief this question is still insufficiently covered as it appears over and over again without an answer that can be applied generically.
My task is to construct a scatterplot in Python's Bokeh that can interactively be filtered based on a categorical variable. My limited understanding of Javascript (and how the data is structured) prevents me from figuring this out by myself.
I found, that one solution is to append x&y values that fulfill the condition (f.e. Filtering Bokeh LabelSet with Javascript). However, I want to keep all the other variables as well, since I use them to define graphic parameters / hover information in the plot.
Therefore my question, how can I append whole rows to the new output data if one of the columns fulfills a certain condition in Javascript? I am also unsure if I call the callback correctly such that the plot would actually react to my selection. So please fell free to point out any mistakes here as well.
See some example code here:
#Packages
import pandas as pd
import numpy as np
from bokeh.plotting import figure, output_file, show
import bokeh.events as bev
import bokeh.models as bmo
import bokeh.layouts as bla
#Data
data = pd.DataFrame(data = np.array([[1,1,'a',0.5],
[2,2,'a',0.5],
[3,3,'a',0.75],
[4,4,'b',1],
[5,5,'b',2]]),
columns = ['x', 'y', 'category', 'other information'])
#Setup
output_file('dashboard.html')
source = bmo.ColumnDataSource(data)
#Define dropdown options
dropdown_options = [('All', 'item_1'), None] + [(cat, str('item_' + str(i))) for i, cat in enumerate(sorted(data['category'].unique()), 2)]
#Generate dropdown widget
dropdown = bmo.Dropdown(label = 'Category', button_type = 'default', menu = dropdown_options)
#Callback
callback = bmo.CustomJS(args = dict(source = source),
code = """
var data = source.data;
var cat = cb_obj.value;
if (cat = 'All'){
data = source.data
} else {
var new_data = [];
for (cat i = 0; i <= source.data['category'].length; i++){
if (source.data['category'][i] == cat) {
new_data.push(source.data[][i])
}
}
data = new_data.data
}
source.data = data
source.change.emit();
""")
#Link actions
dropdown.js_on_event(bev.MenuItemClick, callback)
#Plot
p = figure(plot_width = 800, plot_height = 530, title = None)
p.scatter(x = 'x', y = 'y', source = source)
show(bla.column(dropdown, p))
Unsurprisingly, the filter does not work. As said, any help highly appreciated since I do not know how to index whole rows in Javascript and whatever else I am doing wrong.
Best regards,
Oliver
I wrote a solution for your issue. I am no Bokeh expert so I might not know everything but hope that helps to understand what is going on. Some explanation:
You had some syntax errors to start with: at your for loop you used cat i, you probably meant var i
In your if you were assigning All to cat, you need to do the comparison: with either cat == 'All' or cat === 'All'
your cb_obj.value did not work for some reason and was returning undefined. You can check your variables with simple console.log(variableName) and open dev console in the browser to see callbacks in action. I changed your list comprehension to be tuple of the same values instead of (category_name, item_category_number). Now cb_obj.item returns category_name which you can do comparison with.
You should understand what format your data is in, you can do so with console.log(source.data) for example. source.data here is object of arrays (or dictionary of lists if you were to describe that in Python). Because of that you could not push the data the way you did in for loop and also you had a syntax error: source.data[][i] - you won't access what you want with empty bracket. I wrote two functions to handle this functionality. generateNewDataObject creates object of empty arrays that we can append with addRowToAccumulator
The last thing is that I needed were two data_sources. First that we will not do changes on and second that we will modify and use to display on the plot. If we were to modify the first one then after the first filter all other categories would be dropped and we could get them back only by refreshing the page. The 'immutable' data_source allows us to reference it and not lose filtered data in the process.
I hope that helps.
# Packages
import bokeh.events as bev
import bokeh.layouts as bla
import bokeh.models as bmo
import numpy as np
import pandas as pd
from bokeh.plotting import figure, output_file, show
# Data
data = pd.DataFrame(
data=np.array(
[
[1, 1, 'a', 0.5],
[2, 2, 'a', 0.5],
[3, 3, 'a', 0.75],
[4, 4, 'b', 1],
[5, 5, 'b', 2]
]
),
columns=['x', 'y', 'category', 'other information']
)
# Setup
output_file('dashboard.html')
source = bmo.ColumnDataSource(data)
# Define dropdown options
dropdown_options = [
('All', 'All'), None
] + [(cat, cat)
for i, cat in enumerate(sorted(data['category'].unique()), 2)
]
# Generate dropdown widget
dropdown = bmo.Dropdown(label='Category', button_type='default', menu=dropdown_options)
filtered_data = bmo.ColumnDataSource(data)
# Callback
callback = bmo.CustomJS(
args=dict(unfiltered_data=source, filtered_data=filtered_data),
code="""
var data = unfiltered_data.data;
var cat = cb_obj.item;
function generateNewDataObject(oldDataObject){
var newDataObject = {}
for (var key of Object.keys(oldDataObject)){
newDataObject[key] = [];
}
return newDataObject
}
function addRowToAccumulator(accumulator, dataObject, index) {
for (var key of Object.keys(dataObject)){
accumulator[key][index] = dataObject[key][index];
}
return accumulator;
}
if (cat === 'All'){
data = unfiltered_data.data;
} else {
var new_data = generateNewDataObject(data);
for (var i = 0; i <= unfiltered_data.data['category'].length; i++){
if (unfiltered_data.data['category'][i] == cat) {
new_data = addRowToAccumulator(new_data, unfiltered_data.data, i);
}
}
data = new_data;
}
filtered_data.data = data;
filtered_data.change.emit();
"""
)
# Link actions
dropdown.js_on_event(bev.MenuItemClick, callback)
# Plot
p1 = figure(plot_width=800, plot_height=530, title=None)
p1.scatter(x='x', y='y', source=filtered_data)
show(bla.column(dropdown, p1))

Flask to Dygraph - how to pass data?

If I have a simple Python time data series like this:
graphdata = []
graphdata.append( [(datetime.date(2008, 5, 7)),75])
graphdata.append([(datetime.date(2008, 5, 8)), 85])
graphdata.append([(datetime.date(2008, 5, 10)), 60])
How can I pass the data to a Flask page running Dygraph?
Do I need to use GViz?
Any examples would be helpful.
Thanks
Bill
No need to pass the data as a list containing datetime objects. Dygraphs reads CSV format with ease. So just pass the data as one long CSV string. For your case, first formulate that string containing your data:
graphdata = ''
graphdata = graphdata + '2008-05-07, 75\n'
graphdata = graphdata + '2008-05-08, 85\n'
graphdata = graphdata + '2008-05-10, 60\n'
Now, let's say this you wish to render this data on your index page, then do this in your views.py:
#app.route('/')
def index():
return render_template('index.html',graphdata)
Finally, this data is received by your index.html and rendered using the following code:
<div id="graphdiv"></div>
<script type="text/javascript">
g = new Dygraph(
// containing div
document.getElementById("graphdiv"),
// CSV or path to a CSV file.
{{ graphdata }}
);
</script>
Make sure that dygraph.js is included in your Flask app.

Use python to add to javascript array in javascript file

I have this array stored in versions.js
var cc_versions = [
"2016-01-22",
"2016-01-21",
];
var cs_versions = [
"2016-01-23",
"2016-01-21",
];
I have been trying to figure out a way to write new data to the top of the array inside the javascript file with python. I run a python script on my computer almost every day and I want to update this array when I run it so that I can have a website load the versions. Is it possible to do this? I know I could write to the file, but it would just go to the bottom of the file outside of the array. Also there will be multiple arrays, so I'm trying to find some python script that can interact with a javascript file so I can target specific arrays.
You could also try using Genshi template language. One of the tags there is
<?python ... ?>
for example:
<?python
import json
dateInit = []
selectInit = []
for fi in form_items:
if (fi["type"] == "date"):
dateInit.append(fi["id"])
if "attrs" in fi and "format-select" in fi["attrs"]:
selectInit.append(fi["id"])
if not "attrs" in fi:
fi["attrs"] = {}
jsonDateInit = json.dumps(dateInit)
jsonSelectInit = json.dumps(selectInit)
?>
Your response should contain form_items dictionary processed somewhere on the back-end. Then in javascript you can 'accept' the variables using '$'-sign:
var dateitems = JSON.stringify($jsonDateInit);
var select_items = JSON.stringify($jsonSelectInit);
import json
NEW_VARIABLES = [1, 2, 3]
with open('your/json/file.json') as data_file:
json_data = json.load(data_file)
# insert at the beginning of the list
json_data['cc_version'].insert(0, 'something new')
json_data['cs_version'] = NEW_VARIABLES + json_data['cs_version']
# write to json file after modifying data
with open('your/json/file.json', 'w') as output_file:
json.dump(json_data, output_file)
I ended up using json as suggested in the comments
import json
with open("cc_versions.txt") as data:
json = json.load(data)
dates = json["dates"]
dates.append("2016-01-21")
dates = set(dates)
dates = sorted(dates, key=lambda d: map(int, d.split('-')))
dates = dates[::-1]
import json
with open("cc_versions.txt", "w") as outfile:
json.dump({'dates':dates}, outfile, indent=4)

Categories

Resources