Contourplot in matplot showing incorrect linestyle - python

I'm plotting a 2D matrix, which has positive and negative values, in matplotlib using contourplot. It is supposed to show solid lines for positive values and dashed lines for negative values:
loc = matplotlib.ticker.MaxNLocator(20)
Z = psi
lvls = loc.tick_values(Z.min(), Z.max())
fig, ax = plt.subplots(figsize=(7,7))
cp = plt.contour(X, Y, Z, 20, colors='k', linestyles=where(lvls >= 0, "-", "--"))
plt.xlabel('X')
plt.ylabel('Y')
plt.clabel(cp, inline=True, fontsize=10)
plt.gca().set_aspect('equal', adjustable='box')
plt.title('Stream function - Re = ' + str(Re) + ', t = {:.2f}'.format((t)*dt))
plt.savefig('SF' + '_Re' + str(Re) + '_N' + str(nx) + '_o' + str(order) + '_SF' + '.png')
plt.close()
However, this is what this code is plotting:
As you can see, there are dashed lines where it is supposed to show solid lines and solid lines where it is supposed to show dashed lines. Any ideas?
Edit: the code below works just fine:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker
nx = 100
ny = 100
# Generate 2D mesh
x = 2*np.pi*np.arange(0,nx,1)/(nx)
#x = linspace(0,Lx,nx,endpoint=True)
y = 2*np.pi*np.arange(0,ny,1)/(ny)
#y = linspace(0,Ly,ny,endpoint=True)
X, Y = np.meshgrid(x, y,indexing='ij')
Z = -np.sin(X/2)*np.cos(Y**1.5)
loc = matplotlib.ticker.MaxNLocator(20)
lvls = loc.tick_values(Z.min(), Z.max())
fig, ax = plt.subplots(figsize=(7,7))
cp = plt.contour(X,Y,Z,20, colors='k', linestyles=np.where(lvls >= 0, "-", "--"))
plt.clabel(cp, inline=True, fontsize=10)
plt.gca().set_aspect('equal', adjustable='box')
plt.show()
The output:

You should switch the order of the line styles. Currently, your condition will assign - (solid line) to contours where lvls >= 0 otherwise it will assign -- (dashed line). That's how the where argument works.
In pseudo form, np.where(condition, A, B) means if condition is True assign A else assign B
Your present code (Not desired):
linestyles=np.where(lvls >= 0, "-", "--")
Right style (desired style):
linestyles=np.where(lvls >= 0, "--", "-")

Related

How do I fill/shade a cetain section of my graph in python?

I have a function that I'd like to plot in python and shade the region of interest. I've tried using pyplot.fill_between() but can not quite get what I want. I've attached an image and shaded in orange the region I want to be filled:
I plot the function (in blue) and then the graph is bounded by y=0, y ≈ 0.05 and x = 0.And I wish to shade the relevant region (in orange).
Any tips as to how to go about this?
Thanks in advance.
import numpy as np
import matplotlib.pyplot as plt
def fn (M, r_min):
d = (1- 2*M/ r_min)
x = M/(r_min)**2
A_0 = d**-0.5
A_dot = np.arange(-0.6,0.5,0.0001) #X axis
a = np.zeros(len(A_dot))
for i in range(1,len(A_dot)):
a[i] = -3*d*A_dot[i]**2 -2*x*A_dot[i] + A_0**2*x**2 #Y axis
plt.plot(A_dot, a)
plt.xlim(-0.55,0.55)
plt.axhline(y = 0, color='black', linestyle='--')
plt.axhline(y = 0.049382716, color = 'black', linestyle = '--')
plt.axvline(x = 0,color ='black', linestyle = '--')
idx = np.argwhere(np.diff(np.sign(a))).flatten() #Finding intersection on x+axis
plt.plot(A_dot[idx], a[idx], 'ro')
plt.xlabel('$\\frac{dA_0}{d\tau}$')
plt.ylabel('$|a|^2$')
plt.show()
return(A_dot,a)
fn(1,3)
You need to give the x and y vectors as inputs to fill_between. To do that, you can define a mask selecting between the interception point and 0 (add to your fn function):
x_min = A_dot[idx[1]]
x_max = 0.0
mask_x = np.logical_and(A_dot >= x_min, A_dot <= x_max)
plt.fill_between(x=A_dot[mask_x], y1=a[mask_x], y2=0, color='orange')
Result:

Creating a modulo/folded plot in Python

I am trying to "fold" an exponential plot (and a fit to it - see the first image below) around a discrete interval on the x-axis (a.k.a a "modulo plot"). The aim is that after 10 x-units the exponential is continued on the same plot from 0 for the 10 to 20 interval, as shown on a second "photoshopped" image below.
The MWE code is below:
import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt
Generate points
x=np.arange(20)
y=np.exp(-x/10)
Fit to data
def fit_func(x, t):
return np.exp(-x/t)
par, pcov = optimize.curve_fit(f=fit_func, xdata=x, ydata=y)
Plot data and fit function
fig, ax = plt.subplots()
ax.plot(x,y, c='g', label="Data");
ax.plot(x,fit_func(x, par), c='r', linestyle=":", label="Fit");
ax.set_xlabel("x (modulo 10)")
ax.legend()
plt.savefig("fig/mod.png", dpi=300)
What I have: Origianl exponential from 0 to 20
What I want: Modulo/folded exponential in intervals of 10
You could try to simply write:
ax.plot(x % 10,y, c='g', label="Data")
ax.plot(x % 10, f, c='r', linestyle=":", label="Fit")
but then you get confusing lines connecting the last point of one section to the first point of the next.
Another idea is to create a loop to plot every part separately. To avoid multiple legend entries, only the first section sets a legend label.
import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt
x=np.arange(40)
y=np.exp(-x/10)
def fit_func(x, t):
return np.exp(-x/t)
par, pcov = optimize.curve_fit(f=fit_func, xdata=x, ydata=y)
f = fit_func(x, par)
fig, ax = plt.subplots()
left = x.min()
section = 1
while left < x.max():
right = left+10
filter = (x >= left) & (x <= right)
ax.plot(x[filter]-left,y[filter], c='g', label="Data" if section == 1 else '')
ax.plot(x[filter]-left, f[filter], c='r', linestyle=":", label="Fit" if section == 1 else '')
left = right
section += 1
ax.set_xlabel("x (modulo 10)")
ax.legend()
#plt.savefig("fig/mod.png", dpi=300)
plt.show()
Assuming that x is a sorted array, we'll have :
>>> y_ = fit_func(x, par)
>>> temp_x = []
>>> temp_y = []
>>> temp_y_ = []
>>> fig, ax = plt.subplots()
>>> for i in range(len(x)):
if x[i]%10==0 or i == len(x)-1:
ax.plot(temp_x,temp_y, c='g', label="Data");
ax.plot(temp_x,temp_y_, c='r', linestyle=":", label="Fit")
temp_x,temp_y,temp_y_ = [],[],[]
else:
temp_x.append(x[i]%10)
temp_y.append(y[i])
temp_y_.append(y_[i])
>>> plt.show()
and this would be the resulting plot :

How do I get this to show the legend on the plot?

I am trying to get this code to show a legend on it, but everything I try is not working. Here is my code. I have tried put.legend() in the past and it has worked for me and I am confused why this is not working.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
#declaring my plot
fig1 = plt.figure()
#declaring xvalues
xes = np.arange(-10, 10, 0.01)
xlen = len(xes)
#zeros for yvalues along the axis
yes = np.zeros(xlen)
#declaring my variables
Efieldx = np.zeros((xlen, 1))
Efieldy = np.zeros((xlen, 1))
#locations of my two particles
p1x = 0;
p1y = 1;
p2x = 0;
p2y = -1
q = 1;
Efieldx1 = q/((xes-p1x)*(xes-p1x) + (yes-p1y)*(yes-p1y))**(1.5)*(xes-p1x)
Efieldy1 = q/((xes-p1x)*(xes-p1x) + (yes-p1y)*(yes-p1y))**(1.5)*(yes-p1y)
Efieldx2 = q/((xes-p2x)*(xes-p2x) + (yes-p2y)*(yes-p2y))**(1.5)*(xes-p2x)
Efieldy2 = q/((xes-p1x)*(xes-p1x) + (yes-p1y)*(yes-p1y))**(1.5)*(yes-p2y)
Efieldx = Efieldx1 + Efieldx2
Efieldy = Efieldy1 + Efieldy2
#Efieldx = -1/(xs * xs + ys * ys)^(0.5)
#let's define a function instead:
def f_Efield(q, x, y, xs, ys):
Ex = q*((xs-x)*(xs-x) + (ys-y)*(ys-y))**(-1.5)*(xs-x)
Ey = q/((xs-x)*(xs-x) + (ys-y)*(ys-y))**(1.5)*(ys-y)
return Ex, Ey
#using my new function
Exhere, Eyhere = f_Efield(2, 0, 0,xes, yes)
#plotting:
l, = plt.plot(xes, Efieldx, 'g-')
l, = plt.plot(xes, Exhere, 'r--')
plt.xlim(-10, 10)
plt.ylim(-2, 2)
plt.xlabel('x')
plt.title('Electric field along x-direction \n Andrew Richardson')
#adding a legend
plt.legend()
#displaying the plot
plt.show()
#saving the plot
fig1.savefig('Efield.pdf')
Exhere, Eyhere = f_Efield(-1, 0, 0, xes, yes)
You need to either specify the label property for your plots or pass handles (optional but recommended) and labels to your call to legend otherwise matplotlib has no way of knowing what text to put in the legend
# Using label kwarg
plt.plot(xes, Efieldx, 'g-', label='Efieldx')
plt.plot(xes, Exhere, 'r--', label='Exhere')
plt.legend()
# Using explicit plot handles and labels
p1 = plt.plot(xes, Efieldx, 'g-')
p2 = plt.plot(xes, Exhere, 'r--')
plt.legend([p1, p2], ['Efieldx', 'Exhere'])
# Using just the labels (not recommended)
plt.plot(xes, Efieldx, 'g-')
plt.plot(xes, Exhere, 'r--')
plt.legend(['Efieldx', 'Exhere'])

How to draw vertical lines interactively in matplotlib?

I have a time series plot and I need to draw a moving vertical line to show the point of interest.
I am using the following toy example to accomplish the same. However, it prints all the lines at the same time while I wanted to show these vertical line plotting one at a time.
import time
ion() # turn interactive mode on
# initial data
x = arange(-8, 8, 0.1);
y1 = sin(x)
y2 = cos(x)
line1, = plt.plot(x, y1, 'r')
xvals = range(-6, 6, 2);
for i in xvals:
time.sleep(1)
# update data
plt.vlines(i, -1, 1, linestyles = 'solid', color= 'red')
plt.draw()
If I understood well, you want to use the animation tools of matplotlib. An example (adapted from the doc):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
X_MIN = -6
X_MAX = 6
Y_MIN = -1
Y_MAX = 1
X_VALS = range(X_MIN, X_MAX+1) # possible x values for the line
def update_line(num, line):
i = X_VALS[num]
line.set_data( [i, i], [Y_MIN, Y_MAX])
return line,
fig = plt.figure()
x = np.arange(X_MIN, X_MAX, 0.1);
y = np.sin(x)
plt.scatter(x, y)
l , v = plt.plot(-6, -1, 6, 1, linewidth=2, color= 'red')
plt.xlim(X_MIN, X_MAX)
plt.ylim(Y_MIN, Y_MAX)
plt.xlabel('x')
plt.ylabel('y = sin(x)')
plt.title('Line animation')
line_anim = animation.FuncAnimation(fig, update_line, len(X_VALS), fargs=(l, ))
#line_anim.save('line_animation.gif', writer='imagemagick', fps=4);
plt.show()
Resulting gif looks like this:
Could you try calling plt.draw after plt.vlines? plt.draw is used to interactively redraw the figure after its been modified.

How to put circles on top of a polygon?

I use matplotlib to generate an image in the following way:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.fill(border[0],border[1], color='g', linewidth=1, fill=True, alpha = 0.5)
patches = []
for x1,y1,r in zip(x, y, radii):
circle = Circle((x1,y1), r)
patches.append(circle)
p = PatchCollection(patches, cmap='cool', alpha=1.0)
p.set_array(c)
ax.add_collection(p)
plt.colorbar(p)
plt.savefig(fig_name)
What I want to have is a polygon (given by its border) and colored circles on the top of this polygon. However, I get the polygon on the top of the circles.
This is strange because I plot the polygon first and then I add circles to the plot.
Does anybody know why it happens and how this problem can be resolved?
ADDED
As requested, here is fully working example:
import pandas
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.patches import Circle, Polygon
import numpy as np
def plot_xyc(df, x_col, y_col, c_col, radius, fig_name, title, zrange):
resolution = 50
x = df[x_col]
y = df[y_col]
c = df[c_col]
x0 = (max(x) + min(x))/2.0
y0 = (max(y) + min(y))/2.0
dx = (max(x) - min(x))
dy = (max(y) - min(y))
delta = max(dx, dy)
radii = [delta*radius for i in range(len(x))]
fig = plt.figure()
plt.title(title)
ax = fig.add_subplot(111)
border = ([-3, 3, 3, -3], [-3, -3, 3, 3])
ax.fill(border[0],border[1], color='g', linewidth=1, fill=True, alpha = 1.0)
patches = []
for x1,y1,r in zip(x, y, radii):
circle = Circle((x1,y1), r)
patches.append(circle)
patches.append(Circle((-100,-100), r))
patches.append(Circle((-100,-100), r))
p = PatchCollection(patches, cmap='cool', alpha=1.0)
p.set_array(c)
max_ind = max(c.index)
c.set_value(max_ind + 1, min(zrange))
c.set_value(max_ind + 2, max(zrange))
plt.xlim([x0 - delta/2.0 - 0.05*delta, x0 + delta/2.0 + 0.05*delta])
plt.ylim([y0 - delta/2.0 - 0.05*delta, y0 + delta/2.0 + 0.05*delta])
ax.add_collection(p)
plt.colorbar(p)
plt.savefig(fig_name)
if __name__ == '__main__':
df = pandas.DataFrame({'x':[1,2,3,4], 'y':[4,3,2,1], 'z':[1,1,2,2]})
plot_xyc(df, 'x', 'y', 'z', 0.1, 'test2.png', 'My Titlle', (0.0, 3.0))
You're looking for zorder.
In matplotlib, all additional arguments are just passed up the class heirarchy. zorder is a kwarg of the Artist class, so you just need to make sure that at some point it gets zorder.
You can do it two ways in your example;
either add it in here:
ax.fill(border[0],border[1], color='g', linewidth=1, fill=True, alpha = 1.0, zorder=1)
or here:
p = PatchCollection(patches, cmap='cool', alpha=1.0, zorder=2)
or if you want, both. Objects with a higher zorder sit on top of those with lower values.

Categories