Plot a graph with incoming random data in python - python

I am trying to plot a graph for the data being produced using the following code.
import time
import random
import datetime
mylist = []
ct = datetime.datetime.now()
for i in range(0,61):
x = random.randint(1,100)
mylist.append(x)
if len(mylist) == 11:
right_in_left_out = mylist.pop(0)
else:
right_in_left_out = None
print(mylist)
time.sleep(1)
I want the graph to show real time plotting and at one time only 10 points should be plotted. The graph should keep moving forward just like how to data is being printed. Almost like an animation.

As Julien stated already, the linked complex example is probably what you are looking for.
Taking your code as a basis and assuming that you mixed up x- and y-coordinates, are you looking for something like this?
import time
import random
import datetime
import matplotlib.pyplot as plt
def redraw_figure():
plt.draw()
plt.pause(0.00001)
mylist = []
ct = datetime.datetime.now()
#initialize the data
xData = []
x = np.arange(0,10,1)
y = np.zeros(10)
#plot the data
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_ylim([0, 100])
ax.set_xlim([0, 10])
line, = ax.plot(x, y)
for i in range(0,61):
y = random.randint(1,100)
mylist.append(y)
if len(mylist) == 11:
right_in_left_out = mylist.pop(0)
else:
right_in_left_out = None
xData.append(i)
#draw the data
line.set_ydata(mylist)
line.set_xdata(xData)
redraw_figure()
print(mylist)
time.sleep(1)

Related

Making parts of a line graph a different colour depending on their y value in Matplotlib

I'm making a program which takes a random list of data and will plot it.
I want the colour of the graph to change if it goes above a certain value.
https://matplotlib.org/gallery/lines_bars_and_markers/multicolored_line.html
Matplotlib has an entry on doing just this but it seems to require using a function as input for the graph not using lists.
Does anyone know how to either convert this to work for lists or another way of doing so?
Here's my code so far (without my horrific failed attempts to colour code them)
from matplotlib import pyplot as plt
import random
import sys
import numpy as np
#setting the max and min values where I want the colour to change
A_min = 2
B_max = 28
#makes lists for later
A_min_lin = []
B_max_lin = []
#simulating a corruption of the data where it returns all zeros
sim_crpt = random.randint(0,10)
print(sim_crpt)
randomy = []
if sim_crpt == 0:
randomy = []
#making the empty lists for corrupted data
for i in range(0,20):
randomy.append(0)
print(randomy)
else:
#making a random set of values for the y axis
for i in range(0,20):
n = random.randint(0,30)
randomy.append(n)
print(randomy)
#making an x axis for time
time = t = np.arange(0, 20, 1)
#Making a list to plot a straight line showing where the maximum and minimum values
for i in range(0, len(time)):
A_min_lin.append(A_min)
B_max_lin.append(B_max)
#Testing to see if more than 5 y values are zero to return if it's corrupted
tracker = 0
for i in (randomy):
if i == 0:
tracker += 1
if tracker > 5:
sys.exit("Error, no data")
#ploting and showing the different graphs
plt.plot(time,randomy)
plt.plot(time,A_min_lin)
plt.plot(time,B_max_lin)
plt.legend(['Data', 'Minimum for linear', "Maximum for linear"])
plt.show
You can use np.interp to generate the fine-grain data to plot:
# fine grain time
new_time = np.linspace(time.min(), time.max(), 1000)
# interpolate the y values
new_randomy = np.interp(new_time, time, randomy)
# this is copied from the link with few modification
points = np.array([new_time, new_randomy]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
fig, axs = plt.subplots()
norm = plt.Normalize(new_randomy.min(), new_randomy.max())
lc = LineCollection(segments, cmap='viridis', norm=norm)
# Set the values used for colormapping
lc.set_array(new_randomy[1:])
lc.set_linewidth(2)
line = axs.add_collection(lc)
fig.colorbar(line, ax=axs)
# set the limits
axs.set_xlim(new_time.min(), new_time.max())
axs.set_ylim(new_randomy.min(), new_randomy.max())
plt.show()
Output:

Pyplot Not Displaying My Graph

I'm trying to graph the recaman sequence as a scatter plot and as far as I can tell my script is setup correctly. Also I'm pretty sure its not the back-end because I can run scripts like:
import matplotlib.pyplot as plt
plt.plot([1,2,3,4])
plt.ylabel('some numbers')
plt.show()
and it works fine. Here's what my code looks like:
import matplotlib.pyplot as plt
import os
while(True):
try:
itterations = int(input("Itterations: "))
break
except ValueError:
os.system("cls")
def recaman(n):
arr = [0] * n
arr[0] = 0
for i in range(1, n):
curr = arr[i-1] - i
for j in range(0, i):
if ((arr[j] == curr) or curr < 0):
curr = arr[i-1] + i
break
arr[i] = curr
return(arr)
def genX(n):
x = []
for i in range(0,n):
i += 1
x.append(i)
return(x)
xaxis = genX(itterations)
yaxis = recaman(itterations)
for i in range (0,itterations):
plt.plot(xaxis[i],yaxis[i])
plt.show()
Instead of plotting individual invisible points in the loop, plot the whole curve with plt.plot(xaxis,yaxis) or (better) plt.scatter(xaxis,yaxis).
If you prefer to plot the individual points, at least make them visible:
for i in range (0,itterations):
plt.plot(xaxis[i],yaxis[i],"o")
plt.show()

Set maximum of datapoints per plot

Im using the following code:
import matplotlib.pyplot as pyplot
import pandas as pandas
from datetime import datetime
dataset = pandas.read_csv("HugLog_17.01.11.csv", sep=",", header=0)
print('filter data for SrcAddr')
dataset_filtered = dataset[dataset['SrcAddr']=='0x1FD3']
print('get Values')
varY = dataset_filtered.Battery_Millivolt.values
varX = dataset_filtered.Timestamp.values
print('Convert the date-strings in date-objects.')
dates_list = [datetime.strptime(date, '%y-%m-%d %H:%M:%S') for date in varX]
fig = pyplot.figure()
ax1 = fig.add_subplot(1,1,1)
ax1.set_xlabel('Time')
ax1.set_ylabel('Millivolt')
ax1.bar(dates_list, varY)
pyplot.locator_params(axis='x',nbins=10)
pyplot.show()
The problem i have is, its a large datacollection with 180k datapoints.
And pyplot displays all points an the graph which makes it slow and the bars overlap. Is there a way to set a maximum-limit on how much datapoints a displayed at a "view".
What i mean by that is, that as soon as the graph is render ther are only 50 datapoints and when i zoomm in i only get a maximum of 50 datapoints again.
Resampling can be done with the resample function from pandas.
Note that the resample syntax has changed between version 0.17 and 0.19 of pandas. The example below uses the old style. See e.g. this tutorial for the new style.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# generate some data for every second over a whole day
times = pd.date_range(start='2017-01-11',periods=86400, freq='1S')
df = pd.DataFrame(index = times)
df['data'] = np.sort(np.random.randint(low=1300, high=1600, size=len(df.index)) )[::-1] + \
np.random.rand(len(df.index))*100
# resample the data, taking the mean over 1 hours ("H")
t = "H" # for hours, try "T" for minutes as well
width=1./24 #matplotlib default uses a width of 1 day per bar
# try width=1./(24*60) for minutes
df_resampled = pd.DataFrame()
df_resampled['data'] = df.data.resample(t, how="mean")
fig, ax = plt.subplots()
#ax.bar(df.index, df['data'], width=1./(24*60*60)) # original data, takes too long to plot
ax.bar(df_resampled.index, df_resampled['data'], width=width)
ax.xaxis_date()
plt.show()
Automatic adaption of the resampling when zooming would indeed require some manual work. There is a resampling example on the matplotlib event handling page, which does not work out of the box but could be adapted accordingly.
This is how it would look like:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import datetime
import matplotlib.dates
class Sampler():
def __init__(self,df):
self.df = df
def resample(self, limits):
print limits
dt = limits[1] - limits[0]
if (type(dt) != pd.tslib.Timedelta) and (type(dt) != datetime.timedelta):
dt = datetime.timedelta(days=dt)
print dt
#see #http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases
if dt > datetime.timedelta(hours=5):
t = "H"; width=1./24
elif dt > datetime.timedelta(minutes=60):
t = "15T"; width=15./(24.*60)
elif dt > datetime.timedelta(minutes=5):
t = "T"; width=1./(24.*60)
elif dt > datetime.timedelta(seconds=60):
t = "15S"; width=15./(24.*60*60)
else:
#dt < datetime.timedelta(seconds=60):
t = "S"; width=1./(24.*60*60)
self.resampled = pd.DataFrame()
self.resampled['data'] = self.df.data.resample(t, how="mean")
print t, len(self.resampled['data'])
print "indextype", type(self.resampled.index[0])
print "limitstype", type(limits[1])
if type(limits[1]) == float or type(limits[1]) == np.float64 :
dlowlimit = matplotlib.dates.num2date(limits[0])
duplimit = matplotlib.dates.num2date(limits[1])
print type(duplimit), duplimit
self.resampled = self.resampled.loc[self.resampled.index <= duplimit]
self.resampled = self.resampled.loc[self.resampled.index >= dlowlimit]
else:
self.resampled = self.resampled.loc[self.resampled.index <= limits[1]]
self.resampled = self.resampled.loc[self.resampled.index >= limits[0]]
return self.resampled.index,self.resampled['data'],width
def update(self, ax):
print "update"
lims = ax.viewLim
start, stop = lims.intervalx
ax.clear()
x,y,width = self.resample([start, stop])
ax.bar(x,y, width=width)
ax.set_xlim([start, stop])
ax.callbacks.connect('xlim_changed', self.update)
ax.figure.canvas.draw()
times = pd.date_range(start='2017-01-11',periods=86400, freq='1S')
df = pd.DataFrame(index = times)
df['data'] = np.sort(np.random.randint(low=1300, high=1600, size=len(df.index)) )[::-1] + \
np.random.rand(len(df.index))*500
sampler = Sampler(df)
x,y,width = sampler.resample( [df.index[0],df.index[-1] ] )
fig, ax = plt.subplots()
ax.bar(x,y, width=width)
ax.xaxis_date()
# connect to limits changes
ax.callbacks.connect('xlim_changed', sampler.update)
plt.show()
One thing you can do is plot a random subset of the data by using the sample method on your pandas DataFrame. Use the frac argument to determine the fraction of points you want to use. It ranges from 0 to 1.
After you get your dataset_filtered DataFrame, take a sample of it like this
dataset_filtered_sample = dataset_filtered.sample(frac=.001)

Using Python loop to compile single graph

I'm attempting to compile 12 boxplots on a single set of axes using a loop. However, when I run the code as show below, I end up with 12 different figures. Can anyone provide input on how to make sure they all stay on one figure?
import numpy as np
import matplotlib.pyplot as plt
mons =['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
for i in range(len(mons)):
data = np.genfromtxt('Z:/Dan/SnowStatData/Errors/'+mons[i]+'ErrorStats.txt',skip_header=4)
lat = data[:,1]
lon = data[:,2]
corr = data[:,3]
corp = data[:,4]
RMSE = data[:,5]
MAE = data[:,6]
SDE = data[:,7]
sigcorrs = []
clat = []
clon = []
for j in range(len(lat)):
if corp[j] < 0.05:
sigcorrs = np.append(sigcorrs,corr[j])
clat = np.append(clat,lat[j])
clon = np.append(clon,lon[j])
else:
continue
plt.boxplot(sigcorrs,positions=[i])
plt.show()
The trick is to create the figure before you start looping:
fig = plt.figure()
ax = plt.subplot(111)
Now, in the loop don't use plt.boxplot which might create a new figure, but call the function directly from the axes object:
ax.boxplot(sigcorrs,positions=[i])
And then call plt.show() after the loop
It looks like you're calling plt.show() every time through the loop, so it's drawing the figure every time. Try putting it after the loop.

No exponential form of the z-axis in matplotlib-3D-plots

I have a similar problem as described in How to prevent numbers being changed to exponential form in Python matplotlib figure:
I don't want that (in my special case) weird scientific formatting of the axis. My problem is different as I have this problem at my z-Axis. For 2-D plots I can use ax.get_yaxis().get_major_formatter().set_useOffset(False). And there is no function ax.get_zaxis()
What do I use to format my z-Axis the same way?
EDIT: Example:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import sys
import matplotlib
import matplotlib.pyplot as pyplot
def func(xi, ti):
res = 10e3 + np.cos(ti) * np.sin(xi)
return res
if __name__ == '__main__':
timeSpacing = 20
timeStart = 0
timeEnd = 1
time = np.linspace(timeStart, timeEnd, timeSpacing)
widthSpacing = 50
widthStart = 0
widthEnd = 3
width = np.linspace(widthStart, widthEnd, widthSpacing)
resList = []
matplotlib.rcParams['legend.fontsize'] = 10
fig = pyplot.figure()
ax = fig.gca(projection = '3d')
for i, item in enumerate(time):
ti = [item for t in width]
res = func(width, ti)
ax.plot(width, ti, res, 'b')
ax.set_xlabel('x')
ax.set_ylabel('t')
ax.set_zlabel('f(x,t)')
pyplot.show()
As you say, there is no get_zaxis() method. But, fortunately, there is zaxis field (so don't add ()). There are also xaxis and yaxis fields, so you can use all of those uniformly instead of get_...axis() if you like.
For example:
if __name__ == '__main__':
...
ax = fig.gca(projection = '3d')
ax.zaxis.get_major_formatter().set_useOffset(False) # here
for i, item in enumerate(time):
...
and the end result should look something like this:
As you can see, for large numbers it might not look so well...

Categories