How can I enter input successfully after writing the plt.show() line in a while loop? - python-3.8

As a beginner in Python3, I’ve been following the Python Crash Course book. The following code prints the graph but does not ask for an input as intended. I have tried placing plt.show() after the if statement but the program ends up loading for a long time, eventually failing to display the graph. Is there a way to fix this?
Code:
import matplotlib.pyplot as plt
from random_walk import RandomWalk
while True:
rw = RandomWalk()
rw.fill_walk()
plt.style.use('classic')
fig, ax = plt.subplots()
point_numbers = range(rw.num_points)
ax.scatter(rw.x_values, rw.y_values,c=point_numbers, cmap=plt.cm.Blues, edgecolors='none', s=15)
plt.show()
The program does not print the following for input
keep_running = input("Make another walk? (y/n):")
if keep_running == 'n':
break
The following is saved on a separate file
from random import choice
class RandomWalk:
def __init__(self, num_points=5000):
"""Initialize attributes of a walk"""
self.num_points = num_points
self.x_values = [0]
self.y_values = [0]
def fill_walk(self):
""" calculating all the points in the walk"""
# Keep taking steps until the walk reaches the desired length.
while len(self.x_values) < self.num_points:
# Decide which direction to go and how far to go in that direction.
x_direction = choice([1,-1])
x_distance = choice([0,1,2,3,4])
x_step = x_direction*x_distance
y_direction = choice([1,-1])
y_distance = choice([0,1,2,3,4])
y_step = y_direction*y_distance
# Reject moves that go nowhere
if x_step == 0 and y_step == 0:
continue
# Calculate the new position
x = self.x_values[-1] + x_step
y = self.y_values[-1] + y_step
self.x_values.append(x)
self.y_values.append(y)

Related

xarray : how to stack several pcolormesh figures above a map?

For a ML project I'm currently on, I need to verify if the trained data are good or not.
Let's say that I'm "splitting" the sky into several altitude grids (let's take 3 values for the moment) and for a given region (let's say, Europe).
One grid could be a signal reception strength (RSSI), another one the signal quality (RSRQ)
Each cell of the grid is therefor a rectangle and it has a mean value of each measurement (i.e. RSSI or RSRQ) performed in that area.
I have hundreds of millions of data
In the code below, I know how to draw a coloured mesh with xarray for each altitude: I just use xr.plot.pcolormesh(lat,lon, the_data_set); that's fine
But this will only give me a "flat" figure like this:
RSSI value at 3 different altitudes
I need to draw all the pcolormesh() of a dataset for each altitude in such way that:
1: I can have the map at the bottom
2: Each pcolormesh() is stacked and "displayed" at its altitude
3: I need to add a 3d scatter plot for testing my trained data
4: Need to be interactive as I have to zoom in areas
For 2 and 3 above, I managed to do something using plt and cartopy :
enter image description here
But plt/cartopy combination is not as interactive as plotly.
But plotly doesn't have the pcolormesh functionality
And still ... I don't know in anycase, how to "stack" the pcolormesh results that I did get above.
I've been digging Internet for few days but I didn't find something that could satisfy all my criteria.
What I did to get my pcolormesh:
import numpy as np
import xarray as xr
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
class super_data():
def __init__(self, lon_bound,lat_bound,alt_bound,x_points,y_points,z_points):
self.lon_bound = lon_bound
self.lat_bound = lat_bound
self.alt_bound = alt_bound
self.x_points = x_points
self.y_points = y_points
self.z_points = z_points
self.lon, self.lat, self.alt = np.meshgrid(np.linspace(self.lon_bound[0], self.lon_bound[1], self.x_points),
np.linspace(self.lat_bound[0], self.lat_bound[1], self.y_points),
np.linspace(self.alt_bound[0], self.alt_bound[1], self.z_points))
self.this_xr = xr.Dataset(
coords={'lat': (('latitude', 'longitude','altitude'), self.lat),
'lon': (('latitude', 'longitude','altitude'), self.lon),
'alt': (('latitude', 'longitude','altitude'), self.alt)})
def add_data_array(self,ds_name,ds_min,ds_max):
def create_temp_data(ds_min,ds_max):
data = np.random.randint(ds_min,ds_max,size=self.y_points * self.x_points)
return data
temp_data = []
# Create "z_points" number of layers in the z axis
for i in range(self.z_points):
temp_data.append(create_temp_data(ds_min,ds_max))
data = np.concatenate(temp_data)
data = data.reshape(self.z_points,self.x_points, self.y_points)
self.this_xr[ds_name] = (("altitude","longitude","latitude"),data)
def plot(self,dataset, extent=None, plot_center=False):
# I want t
if np.sqrt(self.z_points) == np.floor(np.sqrt(self.z_points)):
side_size = int(np.sqrt(self.z_points))
else:
side_size = int(np.floor(np.sqrt(self.z_points) + 1))
fig = plt.figure()
i_ax=1
for i in range(side_size):
for j in range(side_size):
if i_ax < self.z_points+1:
this_dataset = self.this_xr[dataset].sel(altitude=i_ax-1)
# Initialize figure with subplots
ax = fig.add_subplot(side_size, side_size, i_ax, projection=ccrs.PlateCarree())
i_ax += 1
ax.coastlines()
this_dataset.plot.pcolormesh('lon', 'lat', ax=ax, infer_intervals=True, alpha=0.5)
else:
break
plt.tight_layout()
plt.show()
if __name__ == "__main__":
# Wanted coverage :
lons = [-15, 30]
lats = [35, 65]
alts = [1000, 5000]
xarr = super_data(lons,lats,alts,10,8,3)
# Add some fake data
xarr.add_data_array("RSSI",-120,-60)
xarr.add_data_array("pressure",700,1013)
xarr.plot("RSSI",0)
Thanks for you help

Is it possible to recreate a pyqtgraph without data

I have a pyqt5 based application where I open a file and plot 2 different plots based on the data from the file. Now I want to open another similar file, but I want to save the status/view of the 2 plots, so that I could come quickly back to the previous plots/views, without having to plot it again by reading the data. Is it possible at all to save the state/view of the plots to recreate it very quickly?
You can guide from this answer .
Basically, you can use the PlotWidget class from pyqtgraph.
Generate a PlotWidget.
import pyqtgraph as pg
plot_widget = pg.PlotWidget()
Use the plot() method and store the PlotDataItem in a variable. The PlotDataItem will contain the information of that specific plot: x-data, y-data, the color of the line, the line width, ...
plot_item = plot_widget.plot(xData, yData)
With this you can add/remove the item from the plot every time you want with the addItem() and removeItem() methods
plot_widget.removeItem(plot_item)
plot_widget.addItem(plot_item)
EDIT:
To get the view state of the plot, you can use the viewRect() method of the PlotWidget class. It will return a QRectF object which contains the information of the view state like this:
PyQt5.QtCore.QRectF(x_0, y_0, w, h)
Where:
x_0 and y_0 are the coordinates where the view starts.
w and h are the width and height of the view area.
Also, you can restore the view using the setRange() method of the PlotWidget class.
Example:
Here is an example of the implementation of this:
import sys
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
class MyApp(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.central_layout = QtGui.QVBoxLayout()
self.buttons_layout = QtGui.QVBoxLayout()
self.boxes_layout = QtGui.QHBoxLayout()
self.save = QtGui.QPushButton('Save View')
self.set = QtGui.QPushButton('Set View')
self.boxes = [QtGui.QCheckBox(f"Box {i+1}") for i in range(3)]
self.plot_widget = pg.PlotWidget()
self.plot_data = [None for _ in range(3)]
self.state = [False for _ in range(3)]
self.setLayout(self.central_layout)
self.central_layout.addWidget(self.plot_widget)
self.central_layout.addLayout(self.buttons_layout)
self.buttons_layout.addLayout(self.boxes_layout)
self.buttons_layout.addWidget(self.save)
self.buttons_layout.addWidget(self.set)
for i in range(3):
self.boxes_layout.addWidget(self.boxes[i])
self.boxes[i].stateChanged.connect(self.box_changed)
self.create_data()
self.save.clicked.connect(self.save_view)
self.set.clicked.connect(self.set_view)
self.view_state = None
self.save_view()
def create_data(self):
x = np.linspace(0, 3.14, 100)
y = [np.sin(x), np.cos(x), np.sin(x)**2]
for i in range(3):
self.plot_data[i] = pg.PlotDataItem(x, y[i])
def box_changed(self):
for i in range(3):
if self.boxes[i].isChecked() != self.state[i]:
self.state[i] = self.boxes[i].isChecked()
if self.state[i]:
if self.plot_data[i] is not None:
self.plot_widget.addItem(self.plot_data[i])
else:
self.plot_data[i] = self.plot_widget.plot(*self.box_data[i])
else:
self.plot_widget.removeItem(self.plot_data[i])
break
def save_view(self):
self.view_state = self.plot_widget.viewRect()
def set_view(self):
self.plot_widget.setRange(self.view_state)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())

Why isn't my histogram showing on Jupyter notebook?

So I'm relatively new to coding and have recently taken the monstrous task of building a few climate models for my MSc thesis. Using this code I have adapted it and it now shows no error messages except now it doesn't show any figure as an output. Any solutions?
I input
%matplotlib notebook at the top of the code, and also put plt.show(); at the bottom of the script (as per some recommendations through some similar queries)... but still doesn't work. Prior to this it was showing <Figure Ssize 432x288 with 0 Axes> which i presumed may be the problem but i can't figure out why there are 0 axes?
Any recommendations/solutions?
Thanks!
As requested - my code:
import iris.quickplot as qplt
import iris.analysis.cartography
import matplotlib.dates as mdates
def main():
Current45 = '....X.nc'
Current45 = iris.load_cube(Current45)
lats = iris.coords.DimCoord(Current45.coords()[1].points[:,0], \
standard_name='latitude', units='degrees')
lons = Current45.coords()[2].points[0]
for i in range(len(lons)):
if lons[i]>100.:
lons[i] = lons[i]-360.
lons = iris.coords.DimCoord(lons, \
standard_name='longitude', units='degrees')
Current45.remove_coord('latitude')
Current45.remove_coord('longitude')
Current45.add_dim_coord(lats, 1)
Current45.add_dim_coord(lons, 2)
Current45.convert_units('Celsius')
Colombia = iris.Constraint(longitude=lambda v: -74.73 <= v <= -76.20, \
latitude=lambda v: 5.30 <= v <= 4.43)
Current45 = Current45.extract(Colombia)
iriscc.add_day_of_year(Current45, 'time')
Current45.coord('latitude').guess_bounds()
Current45.coord('longitude').guess_bounds()
Current45_grid_areas = iris.analysis.cartography.area_weights(Current45)
Current45 = Current45.collapsed(['latitude', 'longitude'],
iris.analysis.MEAN,
weights=Current45_grid_areas)
Histogram = Current45.data
#frq, bins, patches = plt.hist(Histogram, bins=np.arange(20,37,2))
frq, bins, patches = plt.hist(Histogram, bins=np.arange(16,45,2), color='blue')
print (frq)
thresh = 32
plt.axvline(x=thresh, color='green', linestyle='dashed', linewidth=2)
plt.xlabel("Daily Max Temperature / Celsius")
plt.ylabel("Number of days")
fig = plt.gcf()
plt.show();
My code with blank figure at the bottom
In the code, you are never calling the main function, so the figure you are showing is empty.
You should call main() at some point in your code before the plt.gcf() or plt.show.
Edit
In more detail:
You are writing your main() function in this snippet of code, and then, without indent, you are calling pyplot to get the current figure, where pyplot just gives you en empty figure back (the gcf()-call is not necessary anyways in your code) and plt.show() shows no an empty figure.
You can or cannot move the plt.show() into you main() function, but at one point you must definitely call that function otherwise none of it is executed.
Edit 2:
# function definition
def main():
...
# function call
main()
# show figure
plt.show()

How to plot a directed line in matplotlib?

In matplotlib, it's easy to draw a line from data points with plt.plot(xs, ys, '-'+marker). This gets you an undirected line, where you can't tell from looking at the resulting diagram, which end corresponds to the beginning of the arrays of data points and which to the end of the arrays. It happens that for what I'm doing, it's important to be able to tell which end is which, or equivalently, which direction the line is going. What is the recommended way to plot a line so as to obtain that visual distinction?
The following would be one option. It is to add some arrow heads along a line. This can be done using a FancyArrowPatch.
import numpy as np ; np.random.seed(7)
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch
class RL(object):
def __init__(self, n, d, s=0.1):
a = np.random.randn(n)*s
a[0] = np.random.rand(1)*np.pi*2
self.xy = np.random.rand(n,2)*5
self.xy[1,:] = self.xy[0,:] + np.array([d*np.cos(a[0]),d*np.sin(a[0])])
for i in range(2,n):
(x,y), = np.diff(self.xy[i-2:i,:], axis=0)
na = np.arctan2(y,x)+a[i]
self.xy[i,:] = self.xy[i-1,:] + np.array([d*np.cos(na),d*np.sin(na)])
self.x = self.xy[:,0]; self.y = self.xy[:,1]
l1 = RL(1000,0.005)
l2 = RL(1000,0.007)
l3 = RL(1000,0.005)
fig, ax = plt.subplots()
ax.set_aspect("equal")
ax.plot(l1.x, l1.y)
ax.plot(l2.x, l2.y)
ax.plot(l3.x, l3.y)
ax.plot(l1.x[0], l1.y[0], marker="o")
def arrow(x,y,ax,n):
d = len(x)//(n+1)
ind = np.arange(d,len(x),d)
for i in ind:
ar = FancyArrowPatch ((x[i-1],y[i-1]),(x[i],y[i]),
arrowstyle='->', mutation_scale=20)
ax.add_patch(ar)
arrow(l1.x,l1.y,ax,3)
arrow(l2.x,l2.y,ax,6)
arrow(l3.x,l3.y,ax,10)
plt.show()

Legend not working for live data and while loop configuration

My code takes a continuously updating input from raspberry pi, which is then plotted onto a graph. I'm trying to use the legend to display the current frequency (most recent output of y_data) however I can't seem to get it to display. Placing plt.legend() just before plt.show() results in a display, however freezing of the graph. Any help would be greatly appreciated.
import matplotlib
matplotlib.use('qt5agg')
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import RPi.GPIO as GPIO
import time
import numpy as np
x_data = []
y_data = []
GPIO.setmode(GPIO.BCM)
INPUT_PIN = 26
GPIO.setup(INPUT_PIN, GPIO.IN)
fig, ax = plt.subplots()
line, = plt.plot([],[], 'k-',label = 'data', drawstyle = 'steps')
avr, = plt.plot([],[], 'g--',label = 'mean')
plt.show(block = False)
def update(x_data, y_data, average):
line.set_ydata(y_data)
line.set_xdata(x_data)
avr.set_xdata(x_data)
avr.set_ydata([average]*len(x_data))
fig.canvas.draw()
ax.draw_artist(ax.patch)
ax.draw_artist(line)
ax.draw_artist(avr)
ax.relim()
ax.autoscale_view()
data = round(y_data[-1], 1)
ax.legend((line, avr), (data, 'mean'))
fig.canvas.update()
fig.canvas.flush_events()
while True: #Begin continuous loop
NUM_CYCLES = 10 #Loops to be averaged over
start = time.time()
for impulse_count in range(NUM_CYCLES):
GPIO.wait_for_edge(INPUT_PIN, GPIO.FALLING)
duration = time.time() - start #seconds to run for loop
frequency = NUM_CYCLES / duration #Frequency in Hz
bpm = (frequency/1000)*60 #Frequency / no. of cogs per breath * min
x_data.append(time.time()) #add new data to data lists
y_data.append(bpm)
average = sum(y_data)/float(len(y_data))
update(x_data,y_data, average) #call function to update graph contents
I think you should call fig.canvas.draw() at the end of the update function, not in the middle of it. I'm not sure why you add all the artists again in the update function, so you may leave that out. Concerning the legend, It's probably best to create it once at the beginning and inside the update function only update the relevant text.
Commenting out all the GPIO stuff, this is a version which works fine for me:
import matplotlib
#matplotlib.use('qt5agg')
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
#import RPi.GPIO as GPIO
import time
import numpy as np
x_data = []
y_data = []
#GPIO.setmode(GPIO.BCM)
#INPUT_PIN = 26
#GPIO.setup(INPUT_PIN, GPIO.IN)
fig, ax = plt.subplots()
line, = plt.plot([],[], 'k-',label = 'data', drawstyle = 'steps')
avr, = plt.plot([],[], 'g--',label = 'mean')
# add legend already at the beginning
legend = ax.legend((line, avr), (0.0, 'mean'))
plt.show(block = False)
def update(x_data, y_data, average):
line.set_ydata(y_data)
line.set_xdata(x_data)
avr.set_xdata(x_data)
avr.set_ydata([average]*len(x_data))
#fig.canvas.draw() <- use this at the end
#ax.draw_artist(ax.patch) # useless?
#ax.draw_artist(line) # useless?
#ax.draw_artist(avr) # useless?
ax.relim()
ax.autoscale_view()
data = round(y_data[-1], 1)
# only update legend here
legend.get_texts()[0].set_text(str(data))
#fig.canvas.update() # <- what is this one needed for?
fig.canvas.draw()
fig.canvas.flush_events()
while True: #Begin continuous loop
NUM_CYCLES = 10 #Loops to be averaged over
start = time.time()
#for impulse_count in range(NUM_CYCLES):
# GPIO.wait_for_edge(INPUT_PIN, GPIO.FALLING)
a = np.random.rand(700,800) # <- just something that takes a little time
duration = time.time() - start #seconds to run for loop
frequency = NUM_CYCLES / duration #Frequency in Hz
bpm = (frequency/1000)*60 #Frequency / no. of cogs per breath * min
x_data.append(time.time()) #add new data to data lists
y_data.append(bpm)
average = sum(y_data)/float(len(y_data))
update(x_data,y_data, average) #call function to update graph contents
Add plt.draw() (or fig.canvas.draw_idle() for a more OO approach) at the end of update.