I'm trying to plot data that is acquire in real-time by a set of sensors disposed in a test room. We succeed doing that using matplotlib, however it will be better to use plotly, since the graphs could be exported and visualized in other environments (considering our final goals). I saw some examples here and here but it does not fit what we have, because the data list is being constantly updated by the sensors data acquisition, not by a equation (depending on the time step set). The sensors are read by the nidaqmx package (National Instruments sensors) - but no problems with this part apparently. Here it is the working code that we used to plot using matplotlib (the sensors' name are stored in lists, not described here):
import nidaqmx
from nidaqmx.constantsimport(TerminalConfiguration,VoltageUnits,ThermocoupleType,CJCSource,TemperatureUnits,ResistanceConfiguration,ExcitationSource,RTDType)
from datetime import datetime,date
import time
import numpy as np
import pandas as pd
import plotly.express as px
import matplotlib.pyplot as plt
import matplotlib.animation as animation
sensor_name = "sensor_1"
list_voltage_rse = [...]
date_time = []
data = []
tempo = []
t_min = 2
t_max = 40
time_step = 10
N = 100
x_len = N
k = 1
i = 0
y_range = [t_min, t_max]
#read data from DAQ (specified sensor)
def readdaq():
with nidaqmx.Task() as task:
first_inicio = datetime.now()
if sensor_name in list_voltage_rse:
task.ai_channels.add_ai_voltage_chan(sensor_name,terminal_config=TerminalConfiguration.RSE,units=VoltageUnits.VOLTS)
first_fim = datetime.now()
tempo_leitura = (first_fim - first_inicio).total_seconds()
task.start()
value = task.read()
print(value)
task.stop()
#Write Data Function
def writefiledata(t, x):
# Open File
file = open("tempdata.txt", "a")
# Write Data
time = str(t)
value = str(round(x, 2))
file.write(time + "\t" + value)
file.write("\n")
# Close File
file.close()
#Create figure for plotting
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
xs = list(range(0, N))
ys = [0] * x_len
ax.set_ylim(y_range)
#Create a blank line. We will update the line in animate
line, = ax.plot(xs, ys)
#Configure Plot
plt.title('Temperature')
plt.xlabel('t [s]')
plt.ylabel('Temp [degC]')
plt.grid()
#Logging Temperature Data from DAQ Device
def logging(i, ys):
inicio = datetime.now()
value = readdaq()
print("T =", round(value,1), "[degC]")
data.append(value)
final = datetime.now()
tempo_leitura = (final - inicio).total_seconds()
print(tempo_leitura)
time.sleep(time_step - tempo_leitura)
global k
k = k + 1
writefiledata(k*time_step, value)
# Add y to list
ys.append(value)
# Limit y list to set number of items
ys = ys[-x_len:]
# Update line with new Y values
line.set_ydata(ys)
return line,
ani = animation.FuncAnimation(fig,logging,fargs=(ys,),interval=100,blit=True)
plt.show()
I hope this code helped to give an idea of what I'm looking for.
Related
I am designing a currency converter app and I had an idea to add graphical currency analysis to it.
for this I've started using matplotlib.pyplot . I am taking from date(i.e. date from which graph compares data ) as input from user.And using this data , i am taking real time currency data from certain sources.
But here came the main issue.When i drew the graph the x - axis is really bad😫.
Ill insert the output i am getting--> graph and a rough code of mine.The main isuue i want to eliminate is that i want only certain parts of x-axis visible.
import matplotlib.pyplot as plt
import requests
x = []
y = []
for i in range(fyear,tyear):
for j in range(fmonth,tmonth):
for k in range(fday,tday):
response = requests.get("https://api.ratesapi.io/api/{}-{}-{}?base={}&symbols{}".format(i,j,k,inp_curr,out_curr))
data = response.json()
rate = data['rates'][out_curr]
y.append(rate)
x.append("{}/{}/{}".format(j,i,k))
plt.plot(x,y)
OBTAINED OUTPUT:
enter image description here
need answer quickly.....
If for parts you mean to set only few labels along x axis you could use xticks and locator_params. See docs here: https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.xticks.html
import matplotlib.pyplot as plt
import numpy as np
import requests
# use some fake data for testing - use your params
fyear = 2019
tyear = 2020
fmonth = 1
tmonth = 13
fday=1
tday=28
inp_curr = "EUR"
out_curr = "GBP"
# init lists
x = []
y = []
for i in range(fyear,tyear):
for j in range(fmonth,tmonth):
for k in range(fday,tday):
response = requests.get("https://api.ratesapi.io/api/{}-{}-{}?base={}&symbols{}".format(i,j,k,inp_curr,out_curr))
data = response.json()
rate = data['rates'][out_curr]
y.append(rate)
x.append("{}/{}/{}".format(j,i,k))
# create subplot
fig, ax = plt.subplots(1,1, figsize=(20, 11))
# plot image
img = ax.plot(x, y)
# set the total number of x_ticks (the ticks on the x label)
ax.set_xticks(np.arange(len(x)))
# set the labels for each x_tick (actually is x list)
ax.set_xticklabels(x)
# set the number of ticks you want to visualize
# you can just select a number i.e. 10 and you will visualize onlu 10 ticks
# in order to visualize, say the first day of each month set this
n = round(len(x)/(tday-fday))
plt.locator_params(axis='x', nbins=n)
# change labels position to oblique
ax.get_figure().autofmt_xdate()
fig.tight_layout()
Remember to import numpy! Hope it helps you. Here you can see my output.
I'm trying to update a graph in real time from an updating .txt Notepad++ file. In other words, random numbers are printed to the text file, then the python script reads the numbers from the text file and plots it.
I assumed that Multi-threading would be required so the two functions can run at the same time. I'm using the schedule module so I can print random numbers onto the text file every 3 seconds. Here is my code:
import random
import schedule
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from threading import Thread
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
def gen_text():
num1 = random.randint(1, 100)
num2 = random.randint(1, 100)
print(str(num1) + ',' + str(num2), file=open('TEXT FILE', 'a'))
schedule.every(3).seconds.do(gen_text)
pull_data = open('TEXT FILE', 'r').read()
def animate(i):
data_array = pull_data.split('\n')
xar = []
yar = []
for each_line in data_array:
if len(each_line) > 1:
x, y = each_line.split(',')
xar.append(int(x))
yar.append(int(y))
ax.clear()
ax.plot(xar, yar)
if __name__ == '__main__':
Thread(target=animate).start()
Thread(target=gen_text).start()
ani = animation.FuncAnimation(fig, animate, interval=3000)
plt.show()
while True:
schedule.run_pending()
for line in pull_data:
Type = line.split(",")
x = Type[0]
y = Type[1]
print(x, y)
This code just shows a Matplotlib graph with lines plotted from previous runs of the code. Not an updating real-time one. Is multi-threading the correct approach? If not, then how could I solve this?
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:
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)
I want to make a real time plot of temeperature vs. iteration but I will end up having so many points that it would not make sense to have them on the same plot. Does anyone know of any good ways to only show the most recent (lets say 100) data points so that after the first 100 the plot starts to replace the old data points with the new data points?
I thought it would be easier without code but here is the real time ploting that I have now.
from time import sleep
from labjack import ljm
import pylab as pl
import matplotlib.pyplot as plt
# Open T7 over USB
handle = ljm.openS("T7", "USB", "ANY")
# Configure thermocouple line on AIN0
ljm.eWriteName(handle, "AIN0_EF_INDEX", 22) # Feature index for type K thermocouple
ljm.eWriteName(handle, "AIN0_EF_CONFIG_A", 1) # Units. Default = Kelvin. 1 = Celsius. 2 = Fahrenheit.
ljm.eWriteName(handle, "AIN0_EF_CONFIG_B", 60052) # CJC source, address for device temperature sensor
ljm.eWriteName(handle, "AIN0_EF_CONFIG_D", 1.0) # Slope for CJC reading
ljm.eWriteName(handle, "AIN0_EF_CONFIG_E", 0.0) # Offset for CJC reading
temperature = []
x = list()
y = list()
x1 = list()
y1 = list()
dT_tol = .5
plt.ion()
fig=plt.figure()
# Read loop
for i in range(60):
# Get the thermocouple reading on AIN0.
tempC = ljm.eReadName(handle, "AIN0_EF_READ_A")
temperature.append(tempC)
dT = temperature[i]-temperature[i-1]
if -dT_tol<dT<dT_tol:
print "Temperature:","%.3f"% temperature[i]," " "dT:", "%.3f"% dT, " " "Steady State"
sleep(1)
else:
print "Temperature:","%.3f"% temperature[i]," " "dT:", "%.3f"% dT
sleep(1)
#Plotting
plt.figure(1)
plt.subplot(211)
plt.axis([0,60,0,80])
x.append(i)
y.append(temperature[i])
plt.scatter(x,y)
plt.ylabel('Temperature (C)')
plt.subplot(212)
plt.axis([0,60,-4,4])
x1.append(i)
y1.append(dT)
plt.scatter(x1,y1,zorder = 2)
#Set dT steady state boundaries
plt.axhspan(-dT_tol, dT_tol, color='#87CEFA', alpha=1, zorder = 1)
plt.ylabel('dT')
plt.xlabel('Time (s)')
plt.show()
plt.pause(.0001)
# Close handle
ljm.close(handle)
you can use list of array to show the all data given for a while.
for example
tempaturelist=[]
for i in range(50):
enter code here
tempaturelist.append(tempature)
print tempaturelist
There is a overwriting if you use same variable for all values.
Thats why you see only most recent values .
Edit:
You might consider using a deque object to improve performance. It is like a stack/queue hybrid, which may be faster than numpy.roll. I left the old code in for example..
from collections import deque
You can use something like this, just update it to fit your needs ( I am just going to make up random data because im too lazy to use your example)
import numpy as np
import pylab as plt
buffer_size = 100 # how many data points you want to plot at any given time
#data_buffer = np.zeros( buffer_size) # this is where you will keep the latest data points
data_buffer = deque()
for i in range( buffer_size):
data_buffer.append(0)
temperatures = np.random.random( 200 ) # some random temperature data, i dunno
# setup the figure
fig = plt.figure(1)
plt.suptitle('Previous %d temperatures'%buffer_size, fontsize=12)
ax = plt.gca()
for i,Temp in enumerate( temperatures ):
#data_buffer = np.roll( data_buffer, shift=-1)
#data_buffer[ -1] = Temp
data_buffer.popleft()
data_buffer.append( Temp)
ax.plot( data_buffer, 'rs', ms=6) # whatever style you want...
plt.draw()
plt.pause(0.01)
plt.cla() # clears the axis
I won't post the output of this plot because it will always be changing, but try it yourself ;)