Coloring the area between two curves with bokeh - python-3.x

I've got a code with bokeh. There is two math functions where there is an area zone between these two functions in the interval [0, 2]. How can I fill this area zone with a color? I can't use polygon because it is not a polygon.
Here's the code:
import numpy as np
from bokeh.plotting import *
N = 300
x0 = np.linspace(-1, 4, N)
x1 = np.linspace(0, 4, N)
y0 = 0.5 * (x0 ** 2)
y1 = np.sqrt(2 * x1)
y2 = -y1
# output to static HTML file
output_file('plotting_areas.html')
TOOLS = 'pan, wheel_zoom, box_zoom, reset,save, box_select, lasso_select'
p = figure(tools=TOOLS, width=350, height=350,
title=None, x_range=(-1, 5), y_range=(-5, 5))
p.line(x0, y0)
p.line(x1, y1)
p.line(x1, y2)
show(p)
And here is an image for more details.
Thanks

There is nothing built in to Bokeh that will do, e.g. a flood fill, which is really what would be needed. Your best bet is to compute a polygonal approximation to the area yourself.
Otherwise you could (in principle) create a custom extension to perform a flood-fill in JavaScript, but I'm not sure how much effort that would take.

Ok, I've found the solution with bokeh and it is very simple and possible. The key is making two vectors (arrays) with the images of every two math functions between the OX interval. For each vector make a polygon with patch bokeh instruction without border line.
Here is the code:
import numpy as np
from bokeh.plotting import *
N = 300
x0 = np.linspace(-1, 4, N)
x1 = np.linspace(0, 4, N)
y0 = 0.5 * (x0 ** 2)
y1 = np.sqrt(2 * x1)
y2 = -y1
def f1(x):
return 0.5 * (x**2)
def f2(x):
return np.sqrt(2 * x)
z = np.zeros(N)
w = np.zeros(N)
x = np.linspace(0, 2, N)
for i in np.arange(len(x)):
z[i] = f1(x[i])
w[i] = f2(x[i])
# output to static HTML file
output_file('plotting_areas.html')
TOOLS = 'pan, wheel_zoom, box_zoom, reset,save, box_select, lasso_select'
p = figure(tools=TOOLS, width=350, height=350,
title=None, x_range=(-1, 5), y_range=(-5, 5))
p.line(x0, y0)
p.line(x1, y1)
p.line(x1, y2)
p.patch(x, z, color='red')
p.patch(x, w, color='red')
show(p)
And here is an image with the optimal solution:
Thanks

There is VArea now which should do the trick. Perhaps you might want to restict the plotting range to f1 > f2.

Related

matplotlib draw a contour line on a colorbar plot

I used below code to generate the colorbar plot of an image:
plt.imshow(distance)
cb = plt.colorbar()
plt.savefig(generate_filename("test_images.png"))
cb.remove()
The image looks likes this:
I want to draw a single contour line on this image where the signed distance value is equal to 0. I checked the doc of pyplot.contour but it needs a X and Y vector that represents the coordinates and a Z that represents heights. Is there a method to generate X, Y, and Z? Or is there a better function to achieve this? Thanks!
If you leave out X and Y, by default, plt.contour uses the array indices (in this case the range 0-1023 in both x and y).
To only draw a contour line at a given level, you can use levels=[0]. The colors= parameter can fix one or more colors. Optionally, you can draw a line on the colorbar to indicate the value of the level.
import matplotlib.pyplot as plt
import numpy as np
from scipy import ndimage # to smooth a test image
# create a test image with similar properties as the given one
np.random.seed(20221230)
distance = np.pad(np.random.randn(1001, 1001), (11, 11), constant_values=-0.02)
distance = ndimage.filters.gaussian_filter(distance, 100)
distance -= distance.min()
distance = distance / distance.max() * 0.78 - 0.73
plt.imshow(distance)
cbar = plt.colorbar()
level = 0
color = 'red'
plt.contour(distance, levels=[level], colors=color)
cbar.ax.axhline(level, color=color) # show the level on the colorbar
plt.show()
Reference: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contour.html
You can accomplish this by setting the [levels] parameter in contour([X, Y,] Z, [levels], **kwargs).
You can draw contour lines at the specified levels by giving an array that is in increasing order.
import matplotlib.pyplot as plt
import numpy as np
x = y = np.arange(-3.0, 3.0, 0.02)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X ** 2 - Y ** 2)
Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2)
Z3 = np.exp(-(X + 1) ** 2 - (Y + 1) ** 2)
Z = (Z1 - Z2 - Z3) * 2
fig, ax = plt.subplots()
im = ax.imshow(Z, interpolation='gaussian',
origin='lower', extent=[-4, 4, -4, 4],
vmax=abs(Z).max(), vmin=-abs(Z).max())
plt.colorbar(im)
CS = ax.contour(X, Y, Z, levels=[0.9], colors='black')
ax.clabel(CS, fmt='%1.1f', fontsize=12)
plt.show()
Result (levels=[0.9]):

How to generate the plot of custom shape using numpy or other python libraries

I am interested in generating some random plots for data-based classification problems. These plots are generated inside the x-y plane. The maximum value of x and y is one. The main purpose of this is to generate a dummy dataset for a classification algorithm. The below figure is an example of the expected plot. Other than this I am also written a small code.
import numpy as np
import matplotlib.pyplot as plt
x=np.linspace(0.15, 0.95, 100, endpoint = True)
x= x.reshape(100, 1)
noise =np.random.normal(0,0.1, [100,1])*0.25
x=x+noise;
s=(100,1);
y=0.5*(np.ones(s));
xy=np.hstack((x,y));
plt.plot(x,y)
x1 = np.linspace(0.15*np.pi, 0.95*np.pi, 100)
x1max=max(x1)
x1=x1/x1max;
y1 = 2*np.cosh(x1/2)
y1max=max(y1)
y1=y1/y1max;
plt.plot(x1, y1)
x2 = np.linspace(0.15*np.pi, 2*np.pi, 100)
x2max=max(x2)
x2=x2/x2max;
y2 = np.sin(x2)
y2max=max(y2)
y2=y2/y2max;
plt.plot(x2, y2)
def cart2pol(a, b):
rho = np.sqrt(a**2 +b**2)
theta = np.arctan2(b,a)
return(rho, theta)
def pol2cart(rho, phi):
a = rho * np.cos(theta)
b = rho * np.sin(theta)
return(a, b)
[x3,y3]=cart2pol(x,y)
x3max=max(x3)
x3=x3/x2max;
y3max=max(y3)
y3=y3/y3max;
plt.plot(x3, y3)
[x4,y4]=cart2pol(x1,y1)
x4max=max(x4)
x4=x4/x4max;
y4max=max(y4)
y4=y4/y4max;
plt.plot(x4, y4)

plotting a 3d-vector field with colors in dependence of the z component of the vectors

This is my first question and I hope I can describe my issue properly.
I tried to write down a minimal example. My goal is to get a nice plot of a vector field in the xy plane (so just one layer, but a 3d view) where the colors of my arrows should be completely red (blue) if they are pointing completely in the positive (negative) z-direction and gray if they are located in the xy plane. (Slightly red resp. red if they have some positive resp. negative z component etc - so I thought about a 'coolwarm' colormap. But I do not really know how to tho this. I tried to solve my problem with this question and the answers Adding colors to a 3d quiver plot in matplotlib and with the way I am adding my color bars to pcolormesh-plots where it is working fine.
, but I didn't really manage to do it properly, as you can see here:
plot obtained from my code
I do not really understand some of the code they used there and it would be nice if someone could help me with that :)
I do not understand what this part does q.set_array(np.linspace(-1,1,3)) and why I need q.set_edgecolor(c) and q.set_facecolor(c).
Besides I am
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import BoundaryNorm
from matplotlib.ticker import MaxNLocator
# Make the grid
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
np.arange(0.0, 0.6, 0.5))
# Make the direction data for the arrows
u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z)
v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z)
w = 0.2 + np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) * np.sin(np.pi * z)
#define colorbar like I usually do it for 2d density plot etc where it works
cmap = 'coolwarm'
cm = plt.get_cmap(cmap)
plot_min = -1.
plot_max = 1.
levels = MaxNLocator(nbins=100).tick_values(plot_min, plot_max)
norm = BoundaryNorm(levels, ncolors=cm.N, clip=True)
# Color by z-component of vectors (u,v,w) angle
c = w
# Flatten and normalize
c = (c.ravel() - c.min()) / c.ptp()
# Repeat for each body line and two head lines
c = np.concatenate((c, np.repeat(c, 2)))
# Colormap
c = getattr(plt.cm, cmap)(c)
fig = plt.figure(figsize=(10,7))
ax = fig.gca(projection='3d')
q = ax.quiver(x, y, z, u, v, w, colors=c, cmap = cmap, length=0.1, normalize=norm)
q.set_array(np.linspace(-1,1,3))
cbar = fig.colorbar(q, ticks=[-1, 0, 1], fraction=0.015)
cbar.ax.set_yticklabels(['-1', '0', '1'])
cbar.ax.tick_params(labelsize=15)
q.set_edgecolor(c)
q.set_facecolor(c)
#ax.set_zlim(-0.4, 0.4)
ax.view_init(azim=90, elev=20)
ax.grid(False)
plt.axis('off')
plt.show()
if this would work, it would be super cool!
Is there a way to make the arrows look nicer? It would be perfect if the arrows could look like the ones in Mathematica-plots like this:
example from Mathematica
Thank you a lot in advance!
"Tube" Arrows in Python
I found this awesome post. It was exactly the way I want my arrows to look like in the end :)

Draw curves with triple colors and width by using matplotlib and LineCollection [duplicate]

The figure above is a great artwork showing the wind speed, wind direction and temperature simultaneously. detailedly:
The X axes represent the date
The Y axes shows the wind direction(Southern, western, etc)
The variant widths of the line were stand for the wind speed through timeseries
The variant colors of the line were stand for the atmospheric temperature
This simple figure visualized 3 different attribute without redundancy.
So, I really want to reproduce similar plot in matplotlib.
My attempt now
## Reference 1 http://stackoverflow.com/questions/19390895/matplotlib-plot-with-variable-line-width
## Reference 2 http://stackoverflow.com/questions/17240694/python-how-to-plot-one-line-in-different-colors
def plot_colourline(x,y,c):
c = plt.cm.jet((c-np.min(c))/(np.max(c)-np.min(c)))
lwidths=1+x[:-1]
ax = plt.gca()
for i in np.arange(len(x)-1):
ax.plot([x[i],x[i+1]], [y[i],y[i+1]], c=c[i],linewidth = lwidths[i])# = lwidths[i])
return
x=np.linspace(0,4*math.pi,100)
y=np.cos(x)
lwidths=1+x[:-1]
fig = plt.figure(1, figsize=(5,5))
ax = fig.add_subplot(111)
plot_colourline(x,y,prop)
ax.set_xlim(0,4*math.pi)
ax.set_ylim(-1.1,1.1)
Does someone has a more interested way to achieve this? Any advice would be appreciate!
Using as inspiration another question.
One option would be to use fill_between. But perhaps not in the way it was intended. Instead of using it to create your line, use it to mask everything that is not the line. Under it you can have a pcolormesh or contourf (for example) to map color any way you want.
Look, for instance, at this example:
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp1d
def windline(x,y,deviation,color):
y1 = y-deviation/2
y2 = y+deviation/2
tol = (y2.max()-y1.min())*0.05
X, Y = np.meshgrid(np.linspace(x.min(), x.max(), 100), np.linspace(y1.min()-tol, y2.max()+tol, 100))
Z = X.copy()
for i in range(Z.shape[0]):
Z[i,:] = c
#plt.pcolormesh(X, Y, Z)
plt.contourf(X, Y, Z, cmap='seismic')
plt.fill_between(x, y2, y2=np.ones(x.shape)*(y2.max()+tol), color='w')
plt.fill_between(x, np.ones(x.shape) * (y1.min() - tol), y2=y1, color='w')
plt.xlim(x.min(), x.max())
plt.ylim(y1.min()-tol, y2.max()+tol)
plt.show()
x = np.arange(100)
yo = np.random.randint(20, 60, 21)
y = interp1d(np.arange(0, 101, 5), yo, kind='cubic')(x)
dv = np.random.randint(2, 10, 21)
d = interp1d(np.arange(0, 101, 5), dv, kind='cubic')(x)
co = np.random.randint(20, 60, 21)
c = interp1d(np.arange(0, 101, 5), co, kind='cubic')(x)
windline(x, y, d, c)
, which results in this:
The function windline accepts as arguments numpy arrays with x, y , a deviation (like a thickness value per x value), and color array for color mapping. I think it can be greatly improved by messing around with other details but the principle, although not perfect, should be solid.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x = np.linspace(0,4*np.pi,10000) # x data
y = np.cos(x) # y data
r = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [lambda x: 1-x/(2*np.pi), 0]) # red
g = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [lambda x: x/(2*np.pi), lambda x: -x/(2*np.pi)+2]) # green
b = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [0, lambda x: x/(2*np.pi)-1]) # blue
a = np.ones(10000) # alpha
w = x # width
fig, ax = plt.subplots(2)
ax[0].plot(x, r, color='r')
ax[0].plot(x, g, color='g')
ax[0].plot(x, b, color='b')
# mysterious parts
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# mysterious parts
rgba = list(zip(r,g,b,a))
lc = LineCollection(segments, linewidths=w, colors=rgba)
ax[1].add_collection(lc)
ax[1].set_xlim(0,4*np.pi)
ax[1].set_ylim(-1.1,1.1)
fig.show()
I notice this is what I suffered.

Determine intercepts of noisy data with constant [duplicate]

How can I get from a plot in Python an exact value on y - axis? I have two arrays vertical_data and gradient(temperature_data) and I plotted them as:
plt.plot(gradient(temperature_data),vertical_data)
plt.show()
Plot shown here:
I need the zero value but it is not exactly zero, it's a float.
I did not find a good answer to the question of how to find the roots or zeros of a numpy array, so here is a solution, using simple linear interpolation.
import numpy as np
N = 750
x = .4+np.sort(np.random.rand(N))*3.5
y = (x-4)*np.cos(x*9.)*np.cos(x*6+0.05)+0.1
def find_roots(x,y):
s = np.abs(np.diff(np.sign(y))).astype(bool)
return x[:-1][s] + np.diff(x)[s]/(np.abs(y[1:][s]/y[:-1][s])+1)
z = find_roots(x,y)
import matplotlib.pyplot as plt
plt.plot(x,y)
plt.plot(z, np.zeros(len(z)), marker="o", ls="", ms=4)
plt.show()
Of course you can invert the roles of x and y to get
plt.plot(y,x)
plt.plot(np.zeros(len(z)),z, marker="o", ls="", ms=4)
Because people where asking how to get the intercepts at non-zero values y0, note that one may simply find the zeros of y-y0 then.
y0 = 1.4
z = find_roots(x,y-y0)
# ...
plt.plot(z, np.zeros(len(z))+y0)
People were also asking how to get the intersection between two curves. In that case it's again about finding the roots of the difference between the two, e.g.
x = .4 + np.sort(np.random.rand(N)) * 3.5
y1 = (x - 4) * np.cos(x * 9.) * np.cos(x * 6 + 0.05) + 0.1
y2 = (x - 2) * np.cos(x * 8.) * np.cos(x * 5 + 0.03) + 0.3
z = find_roots(x,y2-y1)
plt.plot(x,y1)
plt.plot(x,y2, color="C2")
plt.plot(z, np.interp(z, x, y1), marker="o", ls="", ms=4, color="C1")

Resources