How to loop through axis for subplots in a while loop? - python

How can i use my while loop conditions to loop through axis of subplots. I am very new to python. I have written a code where i plot the data based on some conditions in a while loop. The code works fine for plotting individual plots but when i have to plot all in one plot like subplots, i don't know how can i index that every time one round of plotting is finished the axis index should change and the next plotting is done on different index. As in the image it can be seen, the first row is plotted and rest everything is plotted all over again as the loop has no condition to go for axis indexing. How can i achieve is that every time the value of i,j.l,m in the code is incremented, the plots should move to next row in subplot figure.[]
import matplotlib.pyplot as plt
import xarray as xr
file="/home/arjun2516/hlrn/JOBS/cy_topo_ref4hr2/OUTPUT/cy_topo_ref4hr2_av_3d.002.nc"
Data=xr.open_dataset(file)
l=150
m=300
i = 75
j = 175
while i and j < 700 and l and m < 800 :
fig,axs = plt.subplots(ncols=3, nrows=3,figsize=(20,20))
Data.zusi[i:j,75:175].plot.contourf(ax=axs[0,0])
print(i,j)
# plt.show()
Data.zusi[l:m,250:400].plot.contourf(ax=axs[0,1])
# plt.show()
Data.zusi[l:m,450:600].plot.contourf(ax=axs[0,2])
# plt.show()
i += 200
j += 200
l += 200
m += 200
print(i,j)
print('ok')
I tried to introduce a for loop inside the while loop but it was also producing same results.

There is several problems in your code:
You are creating a new figure object (containing the grid of subplots) in every loop iteration, so the plots from different iterations will end up in different figures. Move the plt.subplots command before the loop.
In order to plot onto the axis of a different row in each loop iteration, you need an axis index that starts at zero (that is, indexing the first row) and is incremented in each iteration.
With these changes, your code becomes:
l=150
m=300
i = 75
j = 175
fig,axs = plt.subplots(ncols=3, nrows=3,figsize=(20,20))
ax_idx = 0
while i and j < 700 and l and m < 800 :
# Select axis based on the axis index
Data.zusi[i:j,75:175].plot.contourf(ax=axs[ax_idx,0])
print(i,j)
# plt.show()
Data.zusi[l:m,250:400].plot.contourf(ax=axs[ax_idx,1])
# plt.show()
Data.zusi[l:m,450:600].plot.contourf(ax=axs[ax_idx,2])
# plt.show()
i += 200
j += 200
l += 200
m += 200
# Increase the axis index
ax_idx += 1
print(i,j)
Note that you could also simplify your code by using a for loop. I would also highly recommend using xarray's capabilities for label-based indexing, in this case isel. It makes the code a little bit more verbose, but much more understandable.
n_rows = 3
fig,axs = plt.subplots(ncols=3, nrows=n_rows, figsize=(20,20))
ax_idx = 0
for ax_idx in range(n_rows):
# Compute the index values
l = 150 + ax_idx * 200
m = 300 + ax_idx * 200
i = 75 + ax_idx * 200
j = 175 + ax_idx * 200
# Index array based on named dimensions and plot it
Data.zusi.isel(x=slice(i, j), y=slice(75, 175)).plot.contourf(ax=axs[ax_idx, 0])
Data.zusi.isel(x=slice(l, m), y=slice(250, 400)).plot.contourf(ax=axs[ax_idx, 1])
Data.zusi.isel(x=slice(l, m), y=slice(450, 600)).plot.contourf(ax=axs[ax_idx, 2])
print(i,j)

Related

How to split data into two graphs with mat plot lib

I would be so thankful if someone would be able to help me with this. I am creating a graph in matplotib however I would to love to split up the 14 lines created from the while loop into the x and y values of P, so instead of plt.plot(t,P) it would be plt.plot(t,((P[1])[0]))) and
plt.plot(t,((P[1])[1]))). I would love if someone could help me very quick, it should be easy but i am just getting errors with the arrays
`
#Altering Alpha in Tumor Cells vs PACCs
#What is alpha? α = Rate of conversion of cancer cells to PACCs
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from google.colab import files
value = -6
counter = -1
array = []
pac = []
while value <= 0:
def modelP(x,t):
P, C = x
λc = 0.0601
K = 2000
α = 1 * (10**value)
ν = 1 * (10**-6)
λp = 0.1
γ = 2
#returning odes
dPdt = ((λp))*P*(1-(C+(γ*P))/K)+ (α*C)
dCdt = ((λc)*C)*(1-(C+(γ*P))/K)-(α*C) + (ν***P)
return dPdt, dCdt
#initial
C0= 256
P0 = 0
Pinit = [P0,C0]
#time points
t = np.linspace(0,730)
#solve odes
P = odeint(modelP,Pinit,t)
plt.plot(t,P)
value += 1
#plot results
plt.xlabel('Time [days]')
plt.ylabel('Number of PACCs')
plt.show()
`
You can use subplots() to create two subplots and then plot the individual line into the plot you need. To do this, firstly add the subplots at the start (before the while loop) by adding this line...
fig, ax = plt.subplots(2,1) ## Plot will 2 rows, 1 column... change if required
Then... within the while loop, replace the plotting line...
plt.plot(t,P)
with (do take care of the space so that the lines are within while loop)
if value < -3: ## I am using value = -3 as the point of split, change as needed
ax[0].plot(t,P)#, ax=ax[0]) ## Add to first plot
else:
ax[1].plot(t,P)#,ax=ax[1]) ## Add to second plot
This will give a plot like this.

Fit a time series in python with a mean value as boundary condition

I have the following boundary conditions for a time series in python.
The notation I use here is t_x, where x describe the time in milliseconds (this is not my code, I just thought this notation is good to explain my issue).
t_0 = 0
t_440 = -1.6
t_830 = 0
mean_value = -0.6
I want to create a list that contains 83 values (so the spacing is 10ms for each value).
The list should descibe a "curve" that starts at zero, has the minimum value of -1.6 at 440ms (so 44 in the list), ends with 0 at 880ms (so 83 in the list) and the overall mean value of the list should be -0.6.
I absolutely could not come up with an idea how to "fit" the boundaries to create such a list.
I would really appreciate help.
It is a quick and dirty approach, but it works:
X = list(range(0, 830 +1, 10))
Y = [0.0 for x in X]
Y[44] = -1.6
b = 12.3486
for x in range(44):
Y[x] = -1.6*(b*x+x**2)/(b*44+44**2)
for x in range(83, 44, -1):
Y[x] = -1.6*(b*(83-x)+(83-x)**2)/(b*38+38**2)
print(f'{sum(Y)/len(Y)=:8.6f}, {Y[0]=}, {Y[44]=}, {Y[83]=}')
from matplotlib import pyplot as plt
plt.plot(X,Y)
plt.show()
With the code giving following output:
sum(Y)/len(Y)=-0.600000, Y[0]=-0.0, Y[44]=-1.6, Y[83]=-0.0
And showing following diagram:
The first step in coming up with the above approach was to create a linear sloping 'curve' from the minimum to the zeroes. I turned out that linear approach gives here too large mean Y value what means that the 'curve' must have a sharp peak at its minimum and need to be approached with a polynomial. To make things simple I decided to use quadratic polynomial and approach the minimum from left and right side separately as the curve isn't symmetric. The b-value was found by trial and error and its precision can be increased manually or by writing a small function finding it in an iterative way.
Update providing a generic solution as requested in a comment
The code below provides a
meanYboundaryXY(lbc = [(0,0), (440,-1.6), (830,0), -0.6], shape='saw')
function returning the X and Y lists of the time series data calculated from the passed parameter with the boundary values:
def meanYboundaryXY(lbc = [(0,0), (440,-1.6), (830,0), -0.6]):
lbcXY = lbc[0:3] ; meanY_boundary = lbc[3]
minX = min(x for x,y in lbcXY)
maxX = max(x for x,y in lbcXY)
minY = lbc[1][1]
step = 10
X = list(range(minX, maxX + 1, step))
lenX = len(X)
Y = [None for x in X]
sumY = 0
for x, y in lbcXY:
Y[x//step] = y
sumY += y
target_sumY = meanY_boundary*lenX
if shape == 'rect':
subY = (target_sumY-sumY)/(lenX-3)
for i, y in enumerate(Y):
if y is None:
Y[i] = subY
elif shape == 'saw':
peakNextY = 2*(target_sumY-sumY)/(lenX-1)
iYleft = lbc[1][0]//step-1
iYrght = iYleft+2
iYstart = lbc[0][0] // step
iYend = lbc[2][0] // step
for i in range(iYstart, iYleft+1, 1):
Y[i] = peakNextY * i / iYleft
for i in range(iYend, iYrght-1, -1):
Y[i] = peakNextY * (iYend-i)/(iYend-iYrght)
else:
raise ValueError( str(f'meanYboundaryXY() EXIT, {shape=} not in ["saw","rect"]') )
return (X, Y)
X, Y = meanYboundaryXY()
print(f'{sum(Y)/len(Y)=:8.6f}, {Y[0]=}, {Y[44]=}, {Y[83]=}')
from matplotlib import pyplot as plt
plt.plot(X,Y)
plt.show()
The code outputs:
sum(Y)/len(Y)=-0.600000, Y[0]=0, Y[44]=-1.6, Y[83]=0
and creates following two diagrams for shape='rect' and shape='saw':
As an old geek, i try to solve the question with a simple algorithm.
First calculate points as two symmetric lines from 0 to 44 and 44 to 89 (orange on the graph).
Calculate sum except middle point and its ratio with sum of points when mean is -0.6, except middle point.
Apply ratio to previous points except middle point. (blue curve on the graph)
Obtain curve which was called "saw" by Claudio.
For my own, i think quadratic interpolation of Claudio is a better curve, but needs trial and error loops.
import matplotlib
# define goals
nbPoints = 89
msPerPoint = 10
midPoint = nbPoints//2
valueMidPoint = -1.6
meanGoal = -0.6
def createSerieLinear():
# two lines 0 up to 44, 44 down to 88 (89 values centered on 44)
serie=[0 for i in range(0,nbPoints)]
interval =valueMidPoint/midPoint
for i in range(0,midPoint+1):
serie[i]=i*interval
serie[nbPoints-1-i]=i*interval
return serie
# keep an original to plot
orange = createSerieLinear()
# work on a base
base = createSerieLinear()
# total except midPoint
totalBase = (sum(base)-valueMidPoint)
#total goal except 44
totalGoal = meanGoal*nbPoints - valueMidPoint
# apply ratio to reduce
reduceRatio = totalGoal/totalBase
for i in range(0,midPoint):
base[i] *= reduceRatio
base[nbPoints-1-i] *= reduceRatio
# verify
meanBase = sum(base)/nbPoints
print("new mean:",meanBase)
# draw
from matplotlib import pyplot as plt
X =[i*msPerPoint for i in range(0,nbPoints)]
plt.plot(X,base)
plt.plot(X,orange)
plt.show()
new mean: -0.5999999999999998
Hope you enjoy simple things :)

Axis labels not spacing correctly

I am trying to plot column data vs the row label of a data frame. When I do so, the plot looks good but the the Y axis starts to look illegible as the number of rows is increased. What I don't get it why does the automatic spacing for the X axis work fine but not the same for the Y axis.
x1 = M.iloc[:,1]
plt.plot(x1,x)
Where the variable "x" represents Column 0 values of dataframe "M" below
The "M" dataframe:
0.0 0.5 1.0
0 300 300.000000 1550
1.00e-01 s 300 300.769527 1550
2.00e-01 s 300 301.538106 1550
3.00e-01 s 300 302.305739 1550
.
.
.
2.80e+00 s 300 321.192396 1550
2.90e+00 s 300 321.935830 1550
Edit
So it seems it's the formatting of the first column being in scientific notation that is messing things up, still not sure why however
x = [0]
i=1
while i < 30:
q = i*0.1
xx = str('{:.2e}'.format(q)) + ' s'
x.append(xx)
i = i + 1
M = pd.DataFrame(index=x, columns=3)
So in the code above, it is the line xx = str('{:.2e}'.format(q)) + ' s' that is making the Y-labels go crazy. I unfortunately can't take it out as I need them to be in scientific notation.
You can try tick-spacing if okay to eliminate few tick labels. Other options are to increase you plot size or decrase font size for y labels.
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
x1 = M.iloc[:,1]
tick_spacing = 2 # or whatever label gap you want to use.
fig, ax = plt.subplots(1,1)
apx.plot(x1,x)
ax.yaxis.set_major_locator(ticker.MultipleLocator(tick_spacing))
plt.show()

weighted average from an array in python

I need to form a new sequence of numbers by replacing every data value, starting with the 4th entry and ending with the 4th from the last entry, with a weighted average of the seven points around it, using the following formula:
(y[i-3] + 2y[i-2] + 3y[i-1] + 3y[i] + 3y[i+1] + 2y[i+2] + y[i+3]) // 15
(NOTE. The i- or i+ some number is a subscript in case that wasn't apparent.)
Here is the code I have which produces a raw graph, but I need to smooth a new graph with the above formula. The data file produces an array of integers set up as [-24, 4, -4, -12, -52...]. I am not even sure where to begin with the formula any help would be appreciated.
from matplotlib import pyplot as plt
with open('2_Record2308.dat', 'r') as f:
data = [int(x) for x in f]
graph = data
fig, ax = plt.subplots()
ax.plot(graph)
ax.legend()
ax.set_ylabel('Raw')
plt.tight_layout()
plt.show()
This code should do the trick:
avg = [(sum(y) + sum(y[1:-1]) + sum(y[2:-2])) // 15
for y in zip(data[:-6], data[1:-5], data[2:-4], data[3:-3], data[4:-2], data[5:-1], data[6:])]
Here zip(data[:-6], data[1:-5], ...) creates the successive 7-tuples.
And sum(y) takes the 7 numbers each once. sum(y[1:-1]) takes the 5 inner numbers once again. sum(y[2:-2]) takes the 3 inner numbers a third time.
By the way, adding 7 before dividing by 15 would be closer to averaging. In the original formulation the average always gets rounded downwards.
So, I would suggest (sum(y) + sum(y[1:-1]) + sum(y[2:-2]) + 7) // 15
Here is a test based on your code and random-walk data.
from matplotlib import pyplot as plt
import random
def do_averaging_7(data):
return [(sum(y) + sum(y[1:-1]) + sum(y[2:-2]) + 7) // 15
for y in zip(data[:-6], data[1:-5], data[2:-4], data[3:-3], data[4:-2], data[5:-1], data[6:])]
data = [random.randrange(-100,101) for _ in range(100)]
for i in range(1,len(data)):
data[i] += data[i-1]
avg = do_averaging_7(data)
fig, ax = plt.subplots()
ax.plot(range(len(data)), data, "blue")
ax.plot(range(3, 3+len(avg)), avg, color="red")
ax.set_ylabel('Raw')
plt.tight_layout()
plt.show()
Resulting plot:

How to I set different colors to subsets of line plot iterations in matplotlib?

I am iteratively plotting the np.exp results of 12 rows of data from a 2D array (12,5000), out_array. All data share the same x values, (x_d). I want the first 4 iterations to all plot as the same color, the next 4 to be a different color, and next 4 a different color...such that I have 3 different colors each corresponding to the 1st-4th, 5th-8th, and 9th-12th iterations respectively. In the end, it would also be nice to define these sets with their corresponding colors in a legend.
I have researched cycler (https://matplotlib.org/examples/color/color_cycle_demo.html), but I can't figure out how to assign colors into sets of iterations > 1. (i.e. 4 in my case). As you can see in my code example, I can have all 12 lines plotted with different (default) colors -or- I know how to make them all the same color (i.e. ...,color = 'r',...)
plt.figure()
for i in range(out_array.shape[0]):
plt.plot(x_d, np.exp(out_array[i]),linewidth = 1, alpha = 0.6)
plt.xlim(-2,3)
I expect a plot like this, only with a total of 3 different colors, each corresponding to the chunks of iterations described above.
An other solution
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
color = ['r', 'g', 'b', 'p']
for i in range(12):
plt.plot(x, i*x, color[i//4])
plt.show()
plt.figure()
n = 0
color = ['r','g','b']
for i in range(out_array.shape[0]):
n = n+1
if n/4 <= 1:
c = 1
elif n/4 >1 and n/4 <= 2:
c = 2
elif n/4 >2:
c = 3
else:
print(n)
plt.plot(x_d, np.exp(out_array[i]),color = color[c-1])
plt.show()

Categories

Resources