Updating matplotlib figures within a for loop in python - loops

I am trying to move a magnetic object and update its magnetic field which is then plotted in a for loop. I want to update the matplotlib figures such that the last figure is deleted before the latest one is shown (with delay as can be seen). I want this to happen in one window (I might be using the wrong technical term) only. Currently it creates a new figure every time it updates the magnetic field. I tried using plt.cla(), plt.close(), and plt.clf() without success. The code is given below. Any help will be much appreciated
import matplotlib.pyplot as plt
import numpy as np
from magpylib.source.magnet import Box,Cylinder
from magpylib import Collection, displaySystem
# create magnets
s1 = Box(mag=(0,0,600), dim=(3,3,3), pos=(-4,0,20))
# calculate the grid
xs = np.linspace(-15,10,33)
zs = np.linspace(-5,25,44)
POS = np.array([(x,0,z) for z in zs for x in xs])
X,Z = np.meshgrid(xs,zs)
for i in range(20):
Bs = s1.getB(POS).reshape(44,33,3) #B-field
s1.move((0,0,-1))
# create figure
fig = plt.figure(figsize=(5,9))
# display field in xz-plane using matplotlib
U,V = Bs[:,:,0], Bs[:,:,2]
plt.streamplot(X, Z, U, V, color=np.log(U**2+V**2))
plt.show()
sleep(0.2)```

You want to make use of matplotlib's interactive mode by invoking plt.ion() and clear the axes after every frame in the loop using plt.cla():
import matplotlib.pyplot as plt
import numpy as np
from magpylib.source.magnet import Box,Cylinder
from magpylib import Collection, displaySystem
fig, ax = plt.subplots()
# create magnets
s1 = Box(mag=(0,0,600), dim=(3,3,3), pos=(-4,0,20))
# calculate the grid
xs = np.linspace(-15,10,33)
zs = np.linspace(-5,25,44)
POS = np.array([(x,0,z) for z in zs for x in xs])
X,Z = np.meshgrid(xs,zs)
plt.ion()
plt.show()
img=0
for i in range(20):
Bs = s1.getB(POS).reshape(44,33,3) #B-field
s1.move((0,0,-1))
U,V = Bs[:,:,0], Bs[:,:,2]
ax.streamplot(X, Z, U, V, color=np.log(U**2+V**2))
plt.gcf().canvas.draw()
plt.savefig('{}'.format(img))
plt.pause(0.01)
plt.clf()
img=img+1

Related

2D Array for Heatmap

I am trying to create a 2D array that I will use to plot a heatmap.
The array needs to be n by n and have the highest value be at its epicenter with diminishing values further away like in the diagram below.
How could I do that?
You can use numpy for the array and matplotlib for creating a heatmap respectively. Something like this:
import numpy as np
import matplotlib.pyplot as plt
# creating array using numpy
array=np.ones((9,9),dtype=int)
array[1:8,1:8]=2
array[2:7,2:7]=3
array[3:6,3:6]=4
array[4,4]=5
print(array)
fig, ax = plt.subplots()
im = ax.imshow(array,cmap="PuBuGn") # cmap can be Greys, YlGnBu, PuBuGn, BuPu etc
# Create colorbar
cbar = ax.figure.colorbar(im, ax=ax,ticks=[1,2,3,4,5])
cbar.ax.set_ylabel("My bar [1-5]", rotation=-90, va="bottom")
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_title("My heatmap")
fig.tight_layout()
plt.show()
For automatically create an array, use a loop.
import numpy as np
import matplotlib.pyplot as plt
lim=100
arr=np.ones((lim,lim),dtype=int)
for i in range(1,lim):
arr[i:len(arr)-i,i:len(arr)-i]=i+1
fig, ax = plt.subplots()
im = ax.imshow(arr,cmap="Purples") # cmap can be Greys, YlGnBu, PuBuGn, BuPu etc
# Create colorbar
cbar = ax.figure.colorbar(im, ax=ax,ticks=list(range(1,lim,5)))
cbar.ax.set_ylabel("My bar [1-50]", rotation=-90, va="bottom")
ax.set_xticklabels([])
ax.set_yticklabels([])
# Show all ticks and label them with the respective list entries
ax.set_title("My heatmap")
fig.tight_layout()
plt.show()

How do I create a multi saved chart as .png?

My goal is to save each plot after one pass of for loop. Here is what I've tried, but it shows the images overlapping. Below is my code:
import matplotlib.pyplot as plt
for i in range(10):
y = arr_df[i].flatten()
plt.title('Nitrat Graph')
plt.xlabel('X Axis')
plt.ylabel('Y Axis')
plt.plot(x_axis, y, color='blue')
plt.savefig(f'NO_{i}.png')
As long as y is a one-dimensional array, all you need to do is add plt.show() in the loop and it'll display after each iteration. Here is an example replacing your data with random data:
import matplotlib.pyplot as plt
import numpy as np
for i in range(10):
y = np.random.rand(10,1)
plt.title('Nitrat Graph')
plt.xlabel('X Axis')
plt.ylabel('Y Axis')
plt.plot(y, color='blue')
plt.savefig(f'NO_{i}.png')
plt.show()

Generating multiple maps from 2d array from netcdf

I need to generate multiple temperature plots for every month of every year, spanning from 2002 to 2018.
I have managed to develop one plot for all of the data in 2002 (about 6 months). I had import my netcdf files into an array and slice the 3d array to a 2d array of lat lon lst.
Using pcolormesh I plotted one singular lon, lat, data_2d plot but can't figure out how to define the months so I can plot them separately.
The data is not available online but I am looking for a general function or command that can iterate through the months and plot them separately onto a map.
import netCDF4
import numpy as np
import xarray as xr
import pandas as pd
import os
from netCDF4 import Dataset
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import cartopy.crs as ccrs
import cartopy.feature as cfeature
print ('All packages imported')
# make an empty np array
data_grid = []
# list of files
days = pd.date_range (start='4/7/2002', end='31/7/2002')
print ('Initiate for loop')
# for loop iterating through %date range to find path files
for day in days:
# defining what paths should look like
# %i = integer, %02d = integer with two characters, for year month and day
path = "/data/atsr/MMDB/AQUA_MODIS_L3E/2.00/%i/%02d/%02d/ESACCI-LST-L3C-LST-MODISA-LONDON_0.01deg_1DAILY_DAY-%i%02d%02d000000-fv2.00.nc" % (day.year, day.month, day.day, day.year, day.month, day.day)
print(path)
# os fetches contents
if os.path.isfile(path):
# open file and define lst daily mean
f = Dataset(path)
lst = f['lst'][:]
# populate numpy array, lst slice ignoring first index and accounting for NaN
data_grid.append(f['lst'][0,:,:].filled(np.nan))
f.close()
else: print ("%s not found" % path)
# stack array into three dimensions (lat, lon, time)
data_3d = np.dstack(data_grid)
# calculate mean of each grid point pixel and ignore NaN values
# make it 2d
data_2d = np.nanmean(data_3d, axis = 2)
# define lat lon from last file
f = netCDF4.Dataset ('/data/atsr/MMDB/AQUA_MODIS_L3E/2.00/2018/12/31/ESACCI-LST-L3C-LST-MODISA-LONDON_0.01deg_1DAILY_DAY-20181231000000-fv2.00.nc')
print (f)
# define lat and lons including all indicies
lat = f['lat'][:]
lon = f['lon'][:]
# plot data
# set up a map and size
fig = plt.figure(figsize=(12,12))
ax = fig.add_subplot(projection=ccrs.PlateCarree())
# define the coordinate system that the grid lons and grid lats are on
mapped_array = ax.pcolormesh(lon, lat, data_2d)
# add title
plt.title ('Aqua Modis London', fontsize=12)
# set axis labels
plt.xlabel('Latitude', fontsize=10)
plt.ylabel('Longitude',fontsize=10)
# add features
ax.coastlines()
ax.add_feature(cfeature.BORDERS, edgecolor='black')
# add colorbar
divider = make_axes_locatable(ax)
ax_cb = divider.new_horizontal(size="5%", pad=0.1, axes_class=plt.Axes)
fig.add_axes(ax_cb)
plt.colorbar(mapped_array, cax=ax_cb)
#add lat lon grids and ticks
gl = ax.gridlines(draw_labels=True, alpha=0.1)
gl.top_labels = False
gl.right_labels = False
# show and save
plt.show()
plt.close()
I would recommend using the xarray package for collecting all of your data into a single array. First put all of the netCDFs in the same directory, then merge with:
import xarray as xr
f = xr.open_mfdataset('/data/atsr/MMDB/AQUA_MODIS_L3E/2.00/ESACCI-LST-L3C-LST-MODISA-LONDON_0.01deg_1DAILY_DAY*.nc')
From there it looks like you want the monthly mean:
f_monthly = f.resample(time = 'MS', skipna = True).mean()
Then it is pretty straightforward to loop through each of the months/years and plot.
f_monthly.sel(time = '2018-12').plot()

only size-1 arrays can be converted to Python scalars / Rasterio

I have this code and my aim to calculate the sin of my raster in the power of 0.8.
import os
os.chdir('D:/NOA/Soil_Erosion/test_Project/Workspace/Input_Data_LS_Factor')
import rasterio
import math
data = rasterio.open('Slope_degrees_clipped.tif')
band = data.read(1) # array of float32 with size (3297,2537)
w = band.shape[0]
print(w)
h = band.shape[1]
print(h)
dtypes =data.dtypes[0]
band_calc = math.sin(band)**0.8 # the formula I would like to calculate
However, the following error pops up:
only size-1 arrays can be converted to Python scalars / Rasterio
May you know how I should fix this?
P.S. I tried to vectorize it (np.vectorize()) but it does not work as it needs a real number.
When I use the np.ndarray.flatten(band) the same error occurs.
I found the solution on Geographic Information Systems:
import os
os.chdir('D:/NOA/Soil_Erosion/test_Project/Workspace/Input_Data_LS_Factor')
import rasterio
import math
data = rasterio.open('Slope_degrees_clipped.tif')
from rasterio.plot import show
show(data)
band = data.read(1) # array of float32 with size (3297,2537)
w = band.shape[0]
print(w)
h = band.shape[1]
print(h)
dtypes =data.dtypes[0]
Calculate the sine of the raster in the power of 0.8
import numpy as np
band_calc2 = np.sin(band)**0.8 # the formula I would like to calculate
"""
another way to do it
band_calc = [ [] for i in range(len(band)) ]
for i,row in enumerate(band):
for element in row:
band_calc[i].append(math.sin(element*math.pi/180)**0.8)
"""

matplotlib.pyplot errorbar ValueError depends on array length?

Good afternoon.
I've been struggling with this for a while now, and although I can find similiar problems online, nothing I found could really help me resolve it.
Starting with a standard data file (.csv or .txt, I tried both) containing three columns (x, y and the error of y), I want to read in the data and generate a line plot including error bars.
I can plot the x and y values without a problem, but if I want to add errorbars using the matplotlib.pyplot errorbar utility, I get the following error message:
ValueError: yerr must be a scalar, the same dimensions as y, or 2xN.
The code below works if I use some arbitrary arrays (numpy or plain python), but not for data read from the file. I've tried converting the tuples which I obtain from my input code to numpy arrays using asarray, but to no avail.
import numpy as np
import matplotlib.pyplot as plt
row = []
with open("data.csv") as data:
for line in data:
row.append(line.split(','))
column = zip(*row)
x = column[0]
y = column[1]
yer = column[2]
plt.figure()
plt.errorbar(x,y,yerr = yer)
fig = plt.gcf()
fig.set_size_inches(18.5, 10.5)
fig.savefig('example.png', dpi=300)
It must be that I am overlooking something. I would be very grateful for any thoughts on the matter.
yerr should be the added/subtracted error from the y value. In your case the added equals the subtracted equals half of the third column.
import numpy as np
import matplotlib.pyplot as plt
data = np.loadtxt('data.csv', delimiter=',')
plt.figure()
yerr_ = np.tile(data[:, 2]/2, (2, 1))
plt.errorbar(data[:, 0], data[:, 1], yerr=yerr_)
plt.xlim([-1, 3])
plt.show()
data.csv
0,2,0.3
1,4,0.4
2,3,0.15

Resources