How to chart live updates to logfile using matplotlib? - python

Here is my code so far:
with open(logfile,'rb') as f:
while True:
lines = sum(1 for line in f)
print lines
X = np.arange(lines)
data = []
for line in f:
a = line.split(',')
data.append(a[1][:-2])
print data
Y = np.array(data)
plt.ion()
graph = plt.plot(X,Y)[0]
graph.set_y_data(Y)
plt.plot(data)
plt.draw()
plt.pause(0.01)
Right now when I print data or Y, it prints an empty array. Then it complains about X not being the same dimension as Y of course. I wonder if perhaps this is because data is not filled quickly enough before the print command is called? But python is supposed to execute sequentially, right?
In any case, I think the logic itself here is probably at fault. This is my best guess - open the file, and while True, try and read everything in and send it into the plot for plot.draw to use. Then as the file is growing as log files do, the chart data and the chart itself will update. How can I ensure that this works?

Use matplotlibs animation features
You need to make an animation like this example.
Version updating the data
Create an empty plot first and update along the way:
import time
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def read(logfile):
with open(logfile) as f:
data = []
while True:
line = f.readline()
time.sleep(0.1)
if line:
data.append(float(line.split(',')[1][:-2]))
yield data
def animate(values):
x = list(range(len(values)))
line.set_data(x, values)
ax.set_xlim(x[0], x[-1])
ax.set_ylim(min(values), max(values))
return line,
fig, ax = plt.subplots()
line, = ax.plot([])
ani = FuncAnimation(fig, animate, frames=read('log.txt'), interval=10)
plt.show()
Version creating a new plot each time
Less code, but works only for a few steps:
import time
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def read(logfile):
with open(logfile) as f:
data = []
while True:
line = f.readline()
time.sleep(0.1)
if line:
data.append(float(line.split(',')[1][:-2]))
yield data
def animate(values):
line, = plt.plot(values, color='blue')
return line,
fig = plt.figure(figsize = (5,5))
ani = FuncAnimation(fig, animate, frames=read('log.txt'), interval=10)
plt.show()

Related

How to plot simple line graph on matplotlib in real time when data is appending every second in list?

I am using an API which returns a dictionary containing a key whose value keeps changing every second.
This is the API call link which returns the data in this form.
{"symbol":"ETHUSDT","price":"588.15000000"}
Here.. the key "price" keeps changing its value every second.
This is my Python scrips which appends this value in a list.
import requests
import matplotlib.pyplot as plt
url = "https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDT"
def get_latest_crypto_price():
r = requests.get(url)
response_dict = r.json()
return float(response_dict['price'])
def main():
last_price = 10
eth_price =[]
while True:
price = get_latest_crypto_price()
if price != last_price:
eth_price.append(price)
fig, ax = plt.subplots()
ax.plot(eth_price)
plt.show()
last_price = price
main()
My Question?
My code does plot the line but every time I have to close the Plot Window and it reopens again showing the new appended value.
I know this is happening because my loop starts again after appending each value.
I am struck as to how to plot this in real time.
Kindly help me in correcting my script.
Thank You
Animation sets up the basic shape of the graph you want to animate, and then displays it by repeatedly assigning it to the line graph with the animation function. This example shows adding a value to the y-axis and getting a value in a slice and displaying it. 30 frames at 1 second intervals are drawn. You can understand the animation if you check the behavior of the animation while modifying the parameters.
I'm in a jupyterlab environment so I have several libraries in place.
I have also introduced another library for creating GIF images.
import requests
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML # for jupyter lab
from matplotlib.animation import PillowWriter # Create GIF animation
url = "https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDT"
eth_price =[]
last_price = 10
x = np.arange(31)
fig = plt.figure()
ax = plt.axes(xlim=(0, 31), ylim=(580, 600))
line, = ax.plot([], [], 'r-', marker='.', lw=2)
def ani_plot(i):
r = requests.get(url)
response_dict = r.json()
price = float(response_dict['price'])
if price != last_price:
eth_price.append(price)
line.set_data(x[:i], eth_price[:i])
# print(price)
return line,
anim = FuncAnimation(fig, ani_plot, frames=31, interval=1000, repeat=False)
plt.show()
anim.save('realtime_plot_ani.gif', writer='pillow')
# jupyter lab
#plt.close()
#HTML(anim.to_html5_video())

Python3 Matplotlib psutil script not running

So i have an issue. I have two Scripts running one which is a CPU and Time logger of every second to record the CPU usage to a text file. The other is a Script that reads the text file into a graph but the graph is not a uniform axis and does not increase in units and i get the wrong output view.
Script1: logs PSU and time to txt file
import psutil import time
print(str(time.strftime("%H:%M:%S", time.localtime())) + ", " +
str(psutil.cpu_percent(interval=1)))
f = open("example.txt", "a+")
f.write(str(time.strftime("%H:%M:%S", time.localtime())) +
", " + str(psutil.cpu_percent(interval=1)) + " \n")
Program 2: plots to a graph
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
style.use('fivethirtyeight')
fig = plt.figure() ax1 = fig.add_subplot(1,1,1)
def animate(i):
graph_data = open ('example.txt','r').read()
lines = graph_data.split('\n')
xs = []
ys = []
for line in lines:
if len(line) > 1:
x, y = line.split(',')
xs.append(x)
ys.append(y)
ax1.clear()
ax1.plot(xs, ys)
ani = animation.FuncAnimation(fig, animate, interval=1000) plt.show()
Script2 output
If this can be made into one Script then great but i am trying to learn the basics. I think it is a String issue with writing to txt files but dont know why a string would matter in a txt file.
I suppose that the string you need to write in the TXT o CSV file will be generated by (in CSV is much easier to read before):
import time
import psutil
import csv
num_measures = 10
with open("cpu_pcnt.csv", mode='w') as file:
for _ in range(num_measures):
str_time = time.strftime("%H:%M:%S")
cpu_pcnt = psutil.cpu_percent(interval=1)
str_data = f"{str_time},{cpu_pcnt}\n"
file.write(str_data)
Then, convert the time in datetime object to the plot and look to cast the pcu percent into float:
def animate(i):
xs = []
ys = []
with open("cpu_pcnt.csv") as file:
reader = csv.reader(file)
for line in reader:
xs.append(datetime.strptime(line[0], "%H:%M:%S"))
ys.append(float(line[1]))
ax1.clear()
ax1.plot(xs, ys)

How can I plot the animation from the csv data with date time information?

first I would like to share the data of csv file.
date, total_cases, total_deaths
12-5-2020,6,2
13-5-2020,7,3
14-5-2020,10,2
15-5-2020,18,5
Now I want to make an animated comparison graph where the x axis will be plotted the dates and y axis will be plotted the total_cases and total_deaths.
from matplotlib import dates as mdate
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import pandas as pd
m=pd.read_csv("covid-data.csv")
m['date']=pd.to_datetime(m['date'])
m.sort_values('date',inplace=True)
cdate=m['date']
ccase=m['total_cases']
cdeath=m['total_deaths']
fig = plt.figure()
ax1 = fig.add_subplot(111)
def animate(i):
ax1.clear()
ax1.plot(cdate,ccase)
ax1.plot(cdate,cdeath)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
Now
I can't get our desired output or animation. How can I overcome this issue and get a solution?
Sorry for my english
Check this code:
from matplotlib import dates as mdate
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import pandas as pd
m = pd.read_csv("covid-data.csv")
m['date'] = pd.to_datetime(m['date'], format = '%d-%m-%Y')
m.sort_values('date', inplace = True)
cdate = m['date']
ccase = m['total_cases']
cdeath = m['total_deaths']
fig = plt.figure()
ax1 = fig.add_subplot(111)
def animate(i):
ax1.clear()
ax1.plot(cdate[:i], ccase[:i], label = 'cases')
ax1.plot(cdate[:i], cdeath[:i], label = 'deaths')
ax1.legend(loc = 'upper left')
ax1.set_xlim([cdate.iloc[0],
cdate.iloc[-1]])
ax1.set_ylim([min(ccase.iloc[0], cdeath.iloc[0]),
max(ccase.iloc[-1], cdeath.iloc[-1])])
ax1.xaxis.set_major_locator(mdate.DayLocator(interval = 5))
ax1.xaxis.set_major_formatter(mdate.DateFormatter('%d-%m-%Y'))
ani = animation.FuncAnimation(fig, animate, interval = 1000)
plt.show()
I changed your animate function in order to use the i counter (which increases by 1 at each frame). You can control what you want to change during the animation with this counter. The I added some formatting in order to improve the visualization. The code above gives me this animation:
In order to get an appreciable animation, I added some "fake" data to the one you provided, in order to have more days over which run the animation. Replace them with your data.
In the case of the error
TypeError: 'builtin_function_or_method' object is not subscriptable
Replace the .iloc[0] with [m.index[0]] and the same for .iloc[-1] with [m.index[-1]]. For example ccase.iloc[0] becomes ccase[m.index[0]].

Plots not showing when calling matplotlib from r block in rmarkdown

I have the following function to plot some matrices using python:
import os
import matplotlib.pyplot as plt
import numpy as np
def plot_freq_bin_mats(subject, freq_bins):
fig = plt.figure(figsize=(8,8))
for i, f in enumerate(freq_bins, start=1):
subj_file = base_dir + subject + '_' + f + '.txt'
print(subj_file)
if os.path.exists(subj_file):
freq_mat = np.loadtxt(subj_file, delimiter=',')
else:
freq_mat = np.zeros((46,46))
ax = fig.add_subplot(3,4,i)
ax.set_title("{0}".format(f))
ax.spy(freq_mat, markersize=1, color='black')
ax.set_yticks([])
ax.set_xticks([])
fig.tight_layout()
fig.suptitle("{0}".format(subject), fontsize=14, y=1)
plt.show()
I am trying to use this code from an R block in an Rmarkdown document so I can take advantage of par(mfrow) to create a grid layout from a for loop. However, when I run the python source code from an R block, no plot is shown. I get no error messages. the following is the code I am attempting to use:
source_python('/path/to/script.py')
plot_freq_bin_mats(py$all_subjects[1], py$freq_bins)
And here is my Rmarkdown setup:
knitr::opts_chunk$set(echo = TRUE)
knitr::knit_engines$set(python=reticulate::eng_python)
library(reticulate)
use_python('/opt/anaconda3/bin/python')
matplotlib <- import("matplotlib")
matplotlib$use("Agg", force = TRUE)
I've tried it both with and without the final line but neither work. Is there a way to show matplotlib plots from an R code block in Rmarkdown?

Animate multiple shapes in python3 using matplotlib

Trying to animate multiple objects at once in python3 while using matplotlib animation function.
code written below is where I am thus far. I am able to create the multiple objects and display them in the figure. I did this by using a for loop containing a patches function for a rectangle. From here I was hoping to move all the individual rectangles over by a set amount by using the animation function.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation
fig = plt.figure()
ax = fig.add_subplot(111)
plt.xlim(-100, 100)
plt.ylim(-100, 100)
width = 5
bars = 25
RB = [] # Establish RB as a Python list
for a in range(bars):
RB.append(patches.Rectangle((a*15-140,-100), width, 200,
color="blue", alpha=0.50))
def init():
for a in range(bars):
ax.add_patch(RB[a])
return RB
def animate(i):
for a in range(bars):
temp = np.array(RB[i].get_xy())
temp[0] = temp[0] + 3;
RB[i].set_XY = temp
return RB
anim = animation.FuncAnimation(fig, animate,
init_func=init,
frames=15,
interval=20,
blit=True)
plt.show()
Currently, nothing moves or happens once I run the code. I have tried to follow the examples found on the python website; but it usually results in a 'AttributeError: 'list' object has no attribute 'set_animated''.
You have to use
RB[i].set_xy(temp)
instead of set_XY = temp
The indexes in RB is wrong actually. You should change the animate function as:
def animate(i):
for a in range(bars):
temp = RB[a].get_x() + 3
RB[a].set_x(temp)
return RB

Categories