Trying to plot real time data in plotly - python

I have this code were i want to draw a live cahrt
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
style.use('fivethirtyeight')
# Create figure for plotting
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
xs = []
ys = []
def animate(i, xs, ys):
# Read temperature (Celsius) from TMP102
polarity = open("sentiment2.txt", "r").read()
lines = polarity.split('\n')
print(lines)
for line in lines:
if len(line) > 1:
x,y = line.split(',')
xs.append(dt.datetime.now().strftime('%H:%M:%S.%f'))
ys.append(line)
# Add x and y to lists
# Limit x and y lists to 20 items
xs = xs[-20:]
ys = ys[-20:]
# Draw x and y lists
ax.clear()
ax.plot(xs, ys)
# Format plot
plt.xticks(rotation=45, ha='right')
plt.subplots_adjust(bottom=0.30)
plt.title('Etehreum Sentiment')
plt.ylabel('Sentiment')
# Set up plot to call animate() function periodically
ani = animation.FuncAnimation(fig, animate, fargs=(xs, ys), interval=60000)
plt.show()
When i run it i get this error:
File "ploty.py", line 23, in animate
x,y = line.split(',')
ValueError: not enough values to unpack (expected 2, got 1)
I have this code from a tutorial and he does the same as i try to achieve so im not sure whats the problem here
What I try to achieve is to get the value(sentiment) from my text file- i run a senitment analysis every 10 minutes and whenever my sentiment.txt file gets updated i want to update my chart
the content of my sentiment.txt file:
-8.944388227513231
-7.731292989417991
-8.493252615440113
0.5413275613275612

Perhaps look at how you had generated the sentiment2.txt file.
Each line of your sentiment2.txt file has only one number and there is no comma.
so despite the line.split(','), there is only one of the coordinate but the code is expecting TWO, X AND Y.
Updated:
Currently, the chart is plotting xs and ys onto the chart; ys are values from the file and xs is the real-time when reading the value. If that is intended, then the split line is redundant and can be removed, and you can remove the 'commas' from your source file.
However, if the sentiment file should contains both x-axis and y-axis values; x and y should map into xs and ys instead:
x,y = line.split(',')
xs.append(x)
ys.append(y)
The latter can be improved further by working with panda which works with csv files see: Plot from CSV with Plotly Express

Related

Updating mpl_toolkits mplot3d quivers using AnimationFunc

I would like to update the data in my 3D quivers.
Project outline - I have an ESP32 feeding position data through my USB port and I want to chart it's travel vector in a single quiver, but quickly.
I am using matplotlib's animation.FuncAnimation() blit=True function to update a 2d line chart with it's roll/pitch/yaw status.
For the line chart I keep a rolling history of the last 200 data values. The code looks like:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits import mplot3d
# The number of values that are going to be charted
log_size = 200
# Create a figure instance and add a line chart
fig = plt.figure()
ax = fig.add_subplot(2, 1, 1)
# Create static arrays that data will pass through
xs = list(range(0, log_size))
ys_roll = [np.NaN] * log_size
# Set the expected chart limits,
# x-number of data points and y-data range
ax.set_xlim([0, log_size])
ax.set_ylim([-180, 180])
# Setup the lines that are going to be streamed
# with names for legend
line0, = ax.plot(xs, ys_roll, label="Roll")
# Establish chart parameters
plt.title('Roll, Pitch, and Yaw')
plt.xlabel('X')
plt.ylabel('Angle')
plt.legend()
X, Y, Z = 0, 0, 0
U, V, W = np.NaN, np.NaN, np.NaN
ax1 = fig.add_subplot(212, projection='3d')
ax1.set_xlim([-1, 1])
ax1.set_ylim([-1, 1])
ax1.set_zlim([-1, 1])
vec0 = ax1.quiver(X, Y, Z, U, V, W)
# Format plot
plt.xticks(rotation=45, ha='right')
plt.subplots_adjust(bottom=0.30)
ser = ... # Serial port setup here
def get_readings(ser):
# Function to get serial port readings and return an np.array() with 6 elements:
# Roll, Pitch, Yaw, and vector U, V, W.
...
return np.array([roll_val, pitch_val, yaw_val, U, V, W])
def animate(i, ser, ys_roll):
# Get USB readings from function above
pos = get_readings(ser)
# Element 0 is Roll
ys_roll.append(pos[0])
# Limit y lists to log_size
ys_roll = ys_roll[-log_size:]
# Update the y items
line0.set_ydata(ys_roll)
# Elements 3, 4, and 5 are vector U, V, W
ax1.quiver(0, 0, 0, pos[3], pos[4], pos[5], length=1)
return line0, ax1
# setup plot to call animate() funciton periodically
ani = animation.FuncAnimation(fig, animate, fargs=(ser, ys_roll), interval=10, blit=True)
plt.show()
I know I can use ax1.quiver(X, Y, Z, U, V, W) to create a new quiver, and but I have to run ax1.clear() in order to clear up the last quiver which also clears my last axis settings.
I would like to use something like the line0.set_ydata() function but more like vec0.set_udata() so that I can update the data behind the chart rather than rebuilding the whole chart (which is too slow).
I've tried looking at the variables in VisualStudio and I know the quiver is of the Line3DCollection but I can't see/I am not sure of any functions within that allow me to change the data.
Can the masters of mpl_toolkits or matplotlib offer any insight?
Typical after posting the question, I solve the problem 5 minutes later.
There is a function called set_segments() that I can call from vec0. The problem I was having was trying to return vec0 after calling the function where I should have returned ax1.
The code being
def animate(i, ser, ys_roll):
...
# Update vector segments
vec0.set_segments([[[0.0, 0.0, 0.0], [pos[3], pos[4], pos[5]]]])
return ..., ax1
What threw me was returning ax1 rather than returning vec0 - like in the line0 case.

Access seperate plots generated in a for-loop(matplotlib)

I have multiple graphs to compare to each other but it is very time consuming to compare the graphs then change the code to compare the next features. Thats why i decided to use buttons to show and hide the plots I need.
The graphs are computed in a for-loop because of the uncertain number of plots to show simultaniously. This is done in a fashion showed at the bottom.
The problem is that i can't access the plots inside the for-loop to switch them on.So I need three buttons total, one for every function.
for f in flist:
ax1.plot(f['x'], f['fx'], label=f['fname'] )
ax1.legend()
In the part above I generate the plots and in the following part I have to access the plots and labelnames
lines = [ax1]
But that doesn't work like I thougt. Do you have any suggestions how I could solve the problem?
I've got the code from a matplotlib example (See Check Buttons example). Shown below is a code example the way I want to use it and doesn't work.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
from matplotlib.widgets import CheckButtons
flist = []
xl = [np.linspace(0,8,265)]
for xi in xl:
flist.append({'x': xi, 'fx': -0.4*(xi-4)**2+3,'fname':'-0.4*(xi-4)**2+3'})
flist.append({'x': xi, 'fx': -0.4 * (xi - 5) ** 2 + 3, 'fname': ' -0.4*(xi-5)**2+3'})
flist.append({'x': xi, 'fx': -0.4 * (xi - 3.5) ** 2 + 3, 'fname': '-0.4*(xi-3.5)**2+3'})
fig = plt.figure(figsize=(12, 6))
gs = GridSpec(2, 2, width_ratios=[1, 2.5])
ax1 = plt.subplot(gs[:, :-1])
for f in flist:
ax1.plot(f['x'], f['fx'], label=f['fname'] )
ax1.legend()
ax1.set_xlabel("time")
ax1.set_ylabel("amplitude")
ax1.set_title('graphs')
lines = [ax1]
# Make checkbuttons with all plotted lines with correct visibility
rax = plt.axes([0.05, 0.4, 0.1, 0.15])
labels = [str(line.get_label()) for line in lines]
visibility = [line.get_visible() for line in lines]
check = CheckButtons(rax, labels, visibility)
def func(label):
index = labels.index(label)
lines[index].set_visible(not lines[index].get_visible())
plt.draw()
check.on_clicked(func)
plt.tight_layout()
plt.show()
lines should be a list of lines you want to toggle visibility for. Hence you would want to fill this list with the lines in the for loop.
lines = []
for f in flist:
line, = ax1.plot(... )
lines.append(line)

Linear Regression: Extending line past data and adding a legend

I have a code:
import math
import numpy as np
import pylab as plt1
from matplotlib import pyplot as plt
uH2 = 1.90866638
uHe = 3.60187307
eH2 = 213.38
eHe = 31.96
R = float(uH2*eH2)/(uHe*eHe)
C_Values = []
Delta = []
kHeST = []
J_f21 = []
data = np.genfromtxt("Lamda_HeHCL.txt", unpack=True);
J_i1=data[1];
J_f1=data[2];
kHe=data[7]
data = np.genfromtxt("Basecol_Basic_New_1.txt", unpack=True);
J_i2=data[0];
J_f2=data[1];
kH2=data[5]
print kHe
print kH2
kHe = map(float, kHe)
kH2 = map(float, kH2)
kHe = np.array(kHe)
kH2= np.array(kH2)
g = len(kH2)
for n in range(0,g):
if J_f2[n] == 1:
Jf21 = J_f2[n]
J_f21.append(Jf21)
ratio = kHe[n]/kH2[n]
C = (((math.log(float(kH2[n]),10)))-(math.log(float(kHe[n]),10)))/math.log(R,10)
C_Values.append(C)
St = abs(J_f1[n] - J_i1[n])
Delta.append(St)
print C_Values
print Delta
print J_f21
fig, ax = plt.subplots()
ax.scatter(Delta,C_Values)
for i, txt in enumerate(J_f21):
ax.annotate(txt, (Delta[i],C_Values[i]))
plt.plot(np.unique(Delta), np.poly1d(np.polyfit(Delta, C_Values, 1))(np.unique(Delta)))
plt.plot(Delta, C_Values)
fit = np.polyfit(Delta,C_Values,1)
fit_fn = np.poly1d(fit)
# fit_fn is now a function which takes in x and returns an estimate for y
plt.scatter(Delta,C_Values, Delta, fit_fn(Delta))
plt.xlim(0, 12)
plt.ylim(-3, 3)
In this code, I am trying to plot a linear regression that extends past the data and touches the x-axis. I am also trying to add a legend to the plot that shows the slope of the plot. Using the code, I was able to plot this graph.
Here is some trash data I have been using to try and extend the line and add a legend to my code.
x =[5,7,9,15,20]
y =[10,9,8,7,6]
I would also like it to be a scatter except for the linear regression line.
Given that you don't provide the data you're loading from files I was unable to test this, but off the top of my head:
To extend the line past the plot, you could turn this line
plt.plot(np.unique(Delta), np.poly1d(np.polyfit(Delta, C_Values, 1))(np.unique(Delta)))
Into something like
x = np.linspace(0, 12, 50) # both 0 and 12 are from visually inspecting the plot
plt.plot(x, np.poly1d(np.polyfit(Delta, C_Values, 1))(x))
But if you want the line extended to the x-axis,
polynomial = np.polyfit(Delta, C_Values, 1)
x = np.linspace(0, *np.roots(polynomial))
plt.plot(x, np.poly1d(polynomial)(x))
As for the scatter plot thing, it seems to me you could just remove this line:
plt.plot(Delta, C_Values)
Oh right, as for the legend, add a label to the plots you make, like this:
plt.plot(x, np.poly1d(polynomial)(x), label='Linear regression')
and add a call to plt.legend() just before plt.show().

python animation reading data from file

I'm trying to plot the time evolution of a function f(x,t). The data is stored in a file which has the following format:
1st row:f(0,0) f(0,1) f(0,2) ....f(0,N)
2nd row:f(1,0) f(1,1) f(1,2) ....f(1,N)
Mth row:f(M,0) f(M,1) f(M,2) ....f(M,N)
where N is the no: of points of the simulation box and M is the number of timesteps.
I used basic_animation by Jake Vanderplas (https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/) to start with, the original example works fine as long as i put blit=False.
Then i tried to replace x by :
x= np.arange(0,192)
and y by the contents of the file mentioned above.
If i do just plt.plot(x,y), it does plot f(x,t) at a given time t, but I want the animation of f(x,t) in time.
set_data should accept 2 1Darrays and I've checked that len(x)=len(y).
But I get the following error message:
'RuntimeError: xdata and ydata must be the same length'
This is the code (in the future i would like to plot multiple functions):
"""
Modified Matplotlib Animation Example
original example:
email: vanderplas#astro.washington.edu
website: http://jakevdp.github.com
license: BSD
Feel free to use and modify this, but keep the above information.
"""
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from itertools import islice
filename = 'DensityByPropagation__a_0_VHxcS_kick'
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 192), ylim=(-2, 2))
lineS, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
lineS.set_data([], [])
return lineS,
# animation function. This is called sequentially
def animate(i):
w = np.linspace(0, 2, 1000)
z = np.sin(2 * np.pi * (w - 0.01 * i))
x= np.arange(0,192)
with open(filename) as fobj:
ketchup = islice(fobj, 0, None, 10)
for line in ketchup:
x,y = x,zip(*([float(y) for y in line.split("\t")] for line in fobj))
#plt.plot(x,y)
#plt.show()
#print len(x)
#print len(y)
#lineS.set_data(w,z)
lineS.set_data(x,y)
return lineS,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=False)
# save the animation as an mp4. This requires ffmpeg or mencoder to be
# installed. The extra_args ensure that the x264 codec is used, so that
# the video can be embedded in html5. You may need to adjust this for
# your system: for more information, see
# http://matplotlib.sourceforge.net/api/animation_api.html
anim.save('movieJoh.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()
I'm not sure what exactly is causing your error, but let me point something out, then I'll make a toy example that should help clarify what's happening.
These lines seem unnecessarily complicated.
with open(filename) as fobj:
ketchup = islice(fobj, 0, None, 10)
for line in ketchup:
x,y = x,zip(*([float(y) for y in line.split("\t")] for line in fobj))
If your data is in fact in the simple format you stated, i.e., values separated by spaces, np.loadtxt() would load all the values into an easy to manage array.
Example
Lets assume this is your data file (10 time steps, 2 points on plot at each step):
0 0 0 0 0 0 0 0 0 0
9 8 7 6 5 4 3 2 1 0
Now some code:
filename = 'data.txt'
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 1), ylim=(0, 9))
lineS, = ax.plot([], [], lw=2)
x = range(2) # the domain
Now we load the data in with np.loadtxt(), this creates a 2-d matrix with t in the columns and x in the rows. We then transpose it to make indexing each time step possible in animate().
# load the data from file
data = np.loadtxt(filename)
# transpose so we could easily index in the animate() function
data = np.transpose(data)
Now for animation functions. This part is really quite simple. animate(i) takes one argument - the frame number. Using the frame number, we extract the values of f(x,t=frameNumber) and set that as the data on the plot.
# initialization function: plot the background of each frame
def init():
lineS.set_data([], [])
return lineS,
# animation function. This is called sequentially
def animate(i):
lineS.set_data(x, data[i])
return lineS,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=10, interval=100, blit=True)
plt.show()
This is the working code:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# Input
filename = 'PeriodicDensity' # Filename
x = np.linspace(-7.5,7.5,192) # Domain
xLimits=[-7.5,7.5] # x limits
yLimits=[0,1] # y limits
framesToUse = range(1,9000,150)# The time-steps to plot
# load the data from file
data = np.loadtxt(filename)
# Set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=xLimits,ylim=yLimits)
lineS, = ax.plot([], [], lw=2)
# Initialisation function
def init():
lineS.set_data([],[])
return lineS,
# Animation function (called sequentially)
def animate(i):
lineS.set_data(x,data[i])
return lineS,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,interval=1000, frames=framesToUse, blit=True)
# save the animation as an mp4. This requires ffmpeg or mencoder to be
# installed. The extra_args ensure that the x264 codec is used, so that
# the video can be embedded in html5. You may need to adjust this for
# your system: for more information, see
# http://matplotlib.sourceforge.net/api/animation_api.html
anim.save('movieDensity.mp4', fps=1, extra_args=['-vcodec', 'libx264'])
plt.show()

Plot data being misdisplayed in matplotlib

Why are my datapoints being displayed like this when new data is being dumped via stream into "twitter-out"? Seems like it has to do with the animation, because when I rerun the file without streaming in new data it plots just fine.
style.use('ggplot')
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
def animate(self):
pullData = open("twitter-out.txt", "r").read()
lines = pullData.split('\n')
xar = []
yar = []
x = 0
y = 0
for l in lines[:]:
x += 1
if "['pos']" in l:
y += 1
elif "['neg']" in l:
y -= 1
xar.append(x)
yar.append(y)
ax1.plot(xar, yar,color='r')
ax1.set_xlabel('Number of Tweets')
ax1.set_ylabel('Sentiment')
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
Fix 1:
The main fix for this is very easy : just add ax1.cla() before you call ax1.plot()
Explanation :
The reason for what you see is that the animation draws a new plot each time the animate function is called and stack it above all the previous ones.
So the figure you have attached in your question is actualy a superposition of the dozen figures that were drawn from the animate calls.
To solve that you just have to clear the previous plots that were contained in your axes with the clear axes command ax.cla().
Fix 2:
The reason for the horizontal bars to be there at all is because your dataPulled string always ends with a new line that turns in an empty string at the end of your list line.
See that example:
>>> 'a\nb\n'.split('\n')
['a', 'b', '']
So you have to cut that last one out in your for loop:
for l in lines[:-1]:

Categories