how to fill between multiple lines in matplotlib - python

I have this type of graphs:
code used to generate that graph is
X = np.linspace(0, 3, 5)
Y1 = X ** 2 + 3
Y2 = np.exp(X) + 2
Y3 = np.cos(X)
plt.plot(Y1, X, lw=4, label='A')
plt.plot(Y2, X, lw=4, label='B')
plt.plot(Y3, X, lw=4, label='C')
plt.legend()
I want to fill the graphs between the lines B and C. I tried using fill_between and also with fill methods available in matplotlib but didn't success. Any help would be appreciated.

Related

Revolution solid with MatplotLib Python

I'm trying to create a bottle as a revolution solid using MatplotLib. I've got this points:
Image of the coordinates
Which in terms of coordinates are:
coords = [(0.00823433249299356, 0.06230346394288128),
(0.04086905251958573, 0.0648935210878489),
(0.08386400112604843, 0.0648935210878489),
(0.11753474401062763, 0.06541153251684242),
(0.14239929260231693, 0.05712334965294601),
(0.19109236692770842, 0.05401528107898486),
(0.2278711783862488, 0.05142522393401722),
(0.24133947554008045, 0.04158300678314021)]
The polynomial (more or less accurate) is:
Lambda(x, -19493.7965633925*x**6 + 13024.3747084876*x**5 - 3228.16456296349*x**4 + 368.816080918066*x**3 - 20.500262217588*x**2 + 0.545840273670868*x + 0.0590464366057008)
Which I get by:
# Getting the polynomial:
z = np.polyfit(xdata, ydata, 6)
# Being xdata and ydata the 2 vector from the coordinates
x = sp.symbols('x', real=True)
P = sp.Lambda(x,sum((a*x**i for i,a in enumerate(z[::-1]))))
print(P)
The point describe the outline of the bottle (cast your imagination) being the bottle in the plane XY.
How can I get, from that curve, a solid of revolution that recreates a bottle?
My objective is to be able to rotate the generator curve and create a solid of revolution, what I've tried is:
# Create the polynomial
pol = sp.lambdify(x,P(x),"numpy")
# Create the matrix of points
X = np.linspace(xdata[0], xdata[-1], 50)
Y = pol(X)
X, Y = np.meshgrid(X, Y)
# As long as a bottle is no more than a big amount of small cylinders, my
# equation should be more or less like:
# Z = x**2 + y** -R**2
# So we create here the equation
Z = X**2 + Y**2 - (Y - 0.0115)**2
# We create the #D figure
fig = plt.figure()
ax = plt.axes(projection="3d")
# And we representate it
surf = ax.plot_surface(X, Y, Z)
# We change the labels
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_zlabel('$z$')
# And show the figure
plt.show()
The problem is that what I get is no longer a bottle (and I think is because how I'm using the plot_surface (I don't get very well how to use it by reading the documentation).
What I got is:
Image of the plotting. First I thought that was a problem related to the zoom, but I changed it and the figure is the same
I'll reference unutbu's answer to a similar question.
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as axes3d
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
# grab more points between your coordinates, say 100 points
u = np.linspace(0.00823433249299356, 0.24133947554008045, 100)
def polynomial(x):
return -19493.7965633925*x**6 + 13024.3747084876*x**5 - 3228.16456296349*x**4 + 368.816080918066*x**3 - 20.500262217588*x**2 + 0.545840273670868*x + 0.0590464366057008
v = np.linspace(0, 2*np.pi, 60)
U, V = np.meshgrid(u, v)
X = U
Y1 = polynomial(X)*np.cos(V)
Z1 = polynomial(X)*np.sin(V)
# Revolving around the axis
Y2 = 0*np.cos(V)
Z2 = 0*np.sin(V)
ax.plot_surface(X, Y1, Z1, alpha=0.3, color='red', rstride=6, cstride=12)
ax.plot_surface(X, Y2, Z2, alpha=0.3, color='blue', rstride=6, cstride=12)
# set the limits of the axes
ax.set_xlim3d(-0.3, 0.3)
ax.set_ylim3d(-0.3, 0.3)
ax.set_zlim3d(-0.3, 0.3)
plt.show()

Is it possible to create multiple axis plot side by side and have them connected?

I have data with 5 parameters, which I want to plot on multiple y axes and have them connected. Please see the example picture.
Currently I tried with normalizing all the values and create dictionary to do a scatter plot where on x axis would be values 1, 2, 3, 4, 5 and on y axis 5 parameter values for each data point. But this way I will need to add axis lines and values later on in Photoshop.
Is there a better way to create such graph using matplotlib and python?
One idea is to create 4 extra y axes, and set explicit limits for them. All the y-values can be rescaled to be compatible with the host axis.
(The code can still be optimized to make better use of numpy.)
import matplotlib.pyplot as plt
import numpy as np
fig, host = plt.subplots()
N = 20
y1 = np.random.uniform(10, 50, N)
y2 = np.sin(np.random.uniform(0, np.pi, N))
y3 = np.random.binomial(300, 0.9, N)
y4 = np.random.pareto(10, N)
y5 = np.random.uniform(0, 800, N)
ys = [y1, y2, y3, y4, y5]
y0min = ys[0].min()
dy = ys[0].max() - y0min
zs = [ys[0]] + [(y - y.min()) / (y.max() - y.min()) * dy + y0min for y in ys[1:]]
ynames = ['P1', 'P2', 'P3', 'P4', 'P5']
axes = [host] + [host.twinx() for i in range(len(ys) - 1)]
for i, (ax, y) in enumerate(zip(axes, ys)):
ax.set_ylim(y.min(), y.max())
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(False)
if ax != host:
ax.spines['left'].set_visible(False)
ax.yaxis.set_ticks_position('right')
ax.spines["right"].set_position(("axes", i / (len(ys) - 1)))
host.set_xlim(0, len(ys) - 1)
host.set_xticks(range(len(ys)))
host.set_xticklabels(ynames)
host.tick_params(axis='x', which='major', pad=7)
host.spines['right'].set_visible(False)
colors = plt.cm.tab20.colors
for j in range(len(ys[0])):
host.plot(range(len(ys)), [z[j] for z in zs], c=colors[j % len(colors)])
plt.show()

2D plot xy-lists of different length

I have an arbitrary, large number (50-1000) of lists, representing X and Y coordinates, I'd like to plot them in one figure.
The lists are of different length, usually 100-1000 elements each. I get the lists as pairs of x and y coordinates (see example), but could easily convert them to 2xN arrays. They need to be plotted in order, from first to the last element. Each line separately.
Is there a way to pack all my lists to one (or two; x and y) object that matplotlib can read?
This example gives the wanted output but is unhandy when there is a lot of data.
I'm happy for a solution that takes advantage of numpy.
from matplotlib import pyplot as plt
fig, ax = plt.subplots(1,1)
x1 = [1,2,5] # usually much longer and a larger number of lists
y1 = [3,2,4]
x2 = [1,6,5,3]
y2 = [7,6,3,2]
x3 = [4]
y3 = [4]
for x, y, in zip([x1, x2, x3],[y1, y2, y3]):
ax.plot(x,y, 'k.-')
plt.show()
I would prefer something like this:
# f() is the function i need, to formats the data for plotting
X = f(x1, x2, x3)
Y = f(y1, y2, y3)
#... so that I can do some processing of the arrays X, and Y here.
ax.plot(X, Y, 'k.-')
You can use a LineCollection for that. Unfortunately, if you want to have markers in your lines, LineCollection does not support that, so you would need to do some trick like adding a scatter plot on top (see Adding line markers when using LineCollection).
from matplotlib import pyplot as plt
from matplotlib.collections import LineCollection
fig, ax = plt.subplots(1,1)
x1 = [1,2,5]
y1 = [3,2,4]
x2 = [1,6,5,3]
y2 = [7,6,3,2]
x3 = [4]
y3 = [4]
# Add lines
X = [x1, x2, x3]
Y = [y1, y2, y3]
lines = LineCollection((list(zip(x, y)) for x, y in zip(X, Y)),
colors='k', linestyles='-')
ax.add_collection(lines)
# Add markers
ax.scatter([x for xs in X for x in xs], [y for ys in Y for y in ys], c='k', marker='.')
# If you do not use the scatter plot you need to manually autoscale,
# as adding the line collection will not do it for you
ax.autoscale()
plt.show()
If you are working with arrays, you may also do as follows:
import numpy as np
# ...
X = [x1, x2, x3]
Y = [y1, y2, y3]
lines = LineCollection((np.stack([x, y], axis=1) for x, y in zip(X, Y)),
colors='k', linestyles='-')
ax.add_collection(lines)
ax.scatter(np.concatenate(X), np.concatenate(Y), c='k', marker='.')

Python Taylor series sin function graph

I'm trying to draw a Taylor series sin(x) graph using python with Jupyter notebook. I created a short function. The graph will appear correctly until y2, but it will fail at y3. It is difficult to draw a graph with a value of x = 2.7 in y3. I don't know how to fix y3.
This is my code:
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
def f(x) :
result = x - x**3/6 + x**5/120
return result
x = np.linspace(0.0, 7.0, 100)
y = np.sin(x)
y2 = x - x**3/6 + x**5/120
y3 = f(2.7)
plt.title("taylor sin graph")
plt.xlim(0, 7+0.2)
plt.ylim(-5, 5+1)
plt.plot(x, y, label='sin(x)')
plt.plot(x, y2, label='x=0')
plt.plot(x, y3, label='x=2.7')
plt.legend()
plt.show()
I want to add y3 here:
After your comment, it got clarified that you do not need a single point but a horizontal line. In that case you can simply input an x-mesh which has the same value 2.7.
To do so, you first define an array containing values 2.7 by using np.ones(100) * 2.7 and then just pass it to the function.
y3 = f(2.7*np.ones(100))
plt.plot(x, y3, label='x=2.7')
For plotting a single point at x=2.7, there are two ways (among possible others).
First option is to just specify the two x-y numbers and plot using a marker as
plt.plot(2.7, y3, 'bo', label='x=2.7')
Second option is to use plt.scatter. s=60 is just to have a big marker.
plt.scatter(2.7, y3, s=60, label='x=2.7')
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
def f(x) :
result = x - x**3/6 + x**5/120
return result
x = np.linspace(0.0, 7.0, 100)
y = np.sin(x)
y2 = x - x**3/6 + x**5/120
y3 = f(2.7)
plt.title("taylor sin graph")
plt.xlim(0, 7+0.2)
plt.ylim(-5, 5+1)
plt.plot(x, y, label='sin(x)')
plt.plot(x, y2, label='x=0')
plt.plot(2.7, y3, label='x=2.7', marker=11)
plt.legend()
plt.show()
You have to add point - not an array in x-axis and scalar on y-axis.
I think
plt.plot([2.7], [y3], '-o', label='x=2.7')
would work. You can't plot(x,y3) when x is a linspace and y3 is just one number.
Also, Taylor approximation of sin function works only in the interval (-pi, pi).

Wire_frame in matplotlib 1.5 doesn't plot correctly

I am trying to plot a wireframe using plot_wireframe from the Axes3D module. My dataset: three 2D arrays: two created with np.arange and then np.meshgrid. The third one is an array containing results of function "f2" (f2 = f(x, y)). I expected 3D plot, but result is 2D plot in 3D space. Now I made some code that looks like a part of documantation of mpl, but still doesn't work. Code:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
def f2(x, y):
'''Return f(x, y) = exp(-(x * x + y * y)) * sin(-5. * (x * x + y * y))'''
return np.exp(-(x**2 + y**2)) * np.sin(-5.0 * (x**2 + y**2))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x2 = np.arange(-1.5, 1.5, 0.02)
y2 = np.arange(1.5, -1.5, -0.02)
X, Y = np.meshgrid(x2, y2)
z2 = f2(X, Y)
ax.plot_wireframe(X, X, z2, rstride=10, cstride=10, linewidth=0.1,
label='$\exp(-(x^2 + y^2)){sin}(-5.0(x^2 + y^2)$')
plt.show()
That is what I want:
Beautiful 3D plot
But the reality is cruel:
"A plot I've got"
I have no idea what I'm doing wrong. Is it a problem with values of vectors?
Change
ax.plot_wireframe(X, X, z2, rstride=10, cstride=10, linewidth=0.1,
label='$\exp(-(x^2 + y^2)){sin}(-5.0(x^2 + y^2)$')
to
ax.plot_wireframe(X, Y, z2, rstride=10, cstride=10, linewidth=0.1,
label='$\exp(-(x^2 + y^2)){sin}(-5.0(x^2 + y^2)$')
You are using X instead of Y as the second argument to ax.plot_wireframe. Thus, all points fall on top of the diagonal Y=X.

Categories