I want to fill between 3 lines in the following problem. Here's the code:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# Construct lines
# x > 0
x = np.linspace(0, 20, 2000)
# C1
y1 = (36-2*x)/6
# C2
y2 = (30-5*x)/3
# C3
y3 = (40-8*x)/2
# C4
# y4 = 0*x
# Make plot
plt.plot(x, y1, label=r'$2 x_{1} + 6 x_{2}\leq 36$')
plt.plot(x, y2, label=r'$x_{1} + 3 x_{2}\leq 30$')
plt.plot(x, y3, label=r'$x_{1} + 2 x_{2}\leq 40$')
# plt.plot(x, y4, label=r'$x_{1}, x_{2}\geq 0$')
plt.xlim((0, 16))
plt.ylim((0, 11))
plt.xlabel(r'$x_1$')
plt.ylabel(r'$x_2$')
# Fill feasible region
y5 = np.minimum(0, 0)
y6 = np.maximum(y2, y3)
plt.fill_between(x, y1, y2, color='grey', alpha=0.5,
interpolate=True)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
I'd like to fill what is hatched in red in the image below (between y1, y2, y3, and zero)
You could fill between zero and the minimum of the three curves:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 20, 2000)
y1 = (36 - 2 * x) / 6
y2 = (30 - 5 * x) / 3
y3 = (40 - 8 * x) / 2
plt.plot(x, y1, label=r'$2 x_{1} + 6 x_{2}\leq 36$')
plt.plot(x, y2, label=r'$x_{1} + 3 x_{2}\leq 30$')
plt.plot(x, y3, label=r'$x_{1} + 2 x_{2}\leq 40$')
plt.xlim((0, 16))
plt.ylim((0, 11))
plt.xlabel(r'$x_1$')
plt.ylabel(r'$x_2$')
plt.fill_between(x, y1, y2, color='grey', alpha=0.5,
interpolate=True)
plt.fill_between(x, 0, np.min([y1, y2, y3], axis=0), color='red', alpha=0.5, hatch='//',
interpolate=True, label='$intersection$')
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.tight_layout()
plt.show()
Note that:
np.vstack([y1, y2, y3]) creates a 3-row array from your 3 "y" arrays.
….min(0) computes minimum along each column, so it is actually
a minimum of your 3 source arrays (also with negative values for higher x).
….clip(min=0) transforms the above negative elements to 0.
So add to your code:
plt.fill_between(x, 0, np.vstack([y1, y2, y3]).min(0).clip(min=0),
color='yellow', alpha=0.5, interpolate=True)
e.g. after your first fill_between.
For your data, and code with the above instruction added, I got:
If you want, change the filling color to whatever suits your needs.
Related
I am trying to fill the regions below two intersecting lines and above both lines, using matplotlib. I can fill between both lines, but haven't found a simple way to invert the region obtained previously. The only workaround I have is to created some extra functions (a low one and a min one for the bottom, and the equivalents for the top), which is a bit cumbersome and requires manual inputs (see below). Any better solutions?
import numpy as np
import matplotlib.pyplot as plt
# Doesn't work
def f1(x): return 32.0 * x + 2.0
def f2(x): return -55.0 * x
xRng=[-1, 1]
plt.plot(xRng, [f1(x) for x in xRng], 'b-')
plt.plot(xRng, [f2(x) for x in xRng], 'r-')
plt.fill_between(xRng, [f1(x) for x in xRng], [f2(x) for x in xRng], color='g') # Would like the fill inverted
plt.title('Not good'); plt.show()
# Works, but clumsy
def fLo(x): return -100
def fHi(x): return 100
def fMin(x): return min(f1(x), f2(x))
def fMax(x): return max(f1(x), f2(x))
xRng=np.linspace(-1, 1, 100)
plt.plot(xRng, [f1(x) for x in xRng], 'b-')
plt.plot(xRng, [f2(x) for x in xRng], 'r-')
plt.fill_between(xRng, [fMin(x) for x in xRng], [fLo(x) for x in xRng], color='g')
plt.fill_between(xRng, [fMax(x) for x in xRng], [fHi(x) for x in xRng], color='g')
plt.title('Complicated'); plt.show()
EDIT: swapping BG and FG colors as suggested by #Mad Physicist will work if basic case, but not if there are several such areas to overlay
It appears that fill_between does not do well with infinite values (e.g. Fill area under curve in matlibplot python on log scale). However, if you're only trying to plot those specific lines, you could just invert the colors of the plot:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 100)
y1 = 32.0 * x + 2.0
y2 = -55.0 * x
fig, ax = plt.subplots()
ax.set_facecolor('g')
ax.plot(x, y1, 'b-')
ax.plot(x, y2, 'r-')
ax.fill_between(x, y1, y2, color='w')
ax.set_xlim(x.min(), x.max())
plt.show()
This is very hacky and won't work well with interactive plots, but it will display the plot you want, hopefully fairly painlessly.
A slightly better approach might be to set the background of only the region covered by x to a green patch:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 100)
y1 = 32.0 * x + 2.0
y2 = -55.0 * x
fig, ax = plt.subplots()
ax.plot(x, y1, 'b-')
ax.plot(x, y2, 'r-')
ax.axvspan(x.min(), x.max(), color='g')
ax.fill_between(x, y1, y2, color='w')
ax.set_xlim(x.min(), x.max())
plt.show()
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.
This question already has an answer here:
How to fill space to border with in Matplotlib [duplicate]
(1 answer)
Closed 2 years ago.
I need to fill the area between y1 and y but but I don't understand how to limit the area under y2
import numpy as np
import matplotlib.pyplot as plt
y = lambda z: (4 * z - z ** 2) ** (1 / 2)
y1 = lambda x: (8 * x - x ** 2) ** (1 / 2)
y2 = lambda c: c * 3 ** (1 / 2)
x = np.linspace(0, 12, 500)
z = np.linspace(0, 12, 500)
c = np.linspace(0, 12, 500)
plt.ylim(0, 4)
plt.xlim(0, 4)
plt.plot(z, y(z), color='blue', label="$y=\\sqrt{4x-x^2}$")
plt.plot(c, y2(c), color='black', label='$y=x\\sqrt{3}$')
plt.plot(x, y1(x), color='red', label='$y=\\sqrt{8x-x^2}$')
plt.plot([0, 4], [0, 0], color='yellow', label='y=0')
plt.grid(True, zorder=5)
plt.fill_between(x, y(z), y1(x), where=(y2(c) >= y1(x)), alpha=0.5)
plt.legend()
plt.show()
Do you want to fill between the minimum of y1, y2 and y?
miny = np.minimum(y2(x),y1(x))
plt.fill_between(x, y(x), miny, where=(miny>=y(x)), alpha=0.5)
plt.legend()
plt.show()
Output:
I have followed this example (Drawing lines between two plots in Matplotlib) but am running into problems. I believe it has something to do with the fact that I essentially have two different y points, but am not sure how to amend the code to fix it. I would like the line to start at one point and end at the other point directly below it, as well as plotting for all lines.
fig=plt.figure(figsize=(22,10), dpi=150)
ax1 = fig.add_subplot(1, 1, 1)
ax2 = ax1.twinx()
n = 10
y1 = np.random.random(n)
y2 = np.random.random(n) + 1
x1 = np.arange(n)
ax1.scatter(x1, y1)
ax2.scatter(x1, y2)
i = 1
xy = (x1[i],y1[i])
con = ConnectionPatch(xyA=xy, xyB=xy, coordsA="data", coordsB="data",
axesA=ax1, axesB=ax2, color="red")
ax2.add_artist(con)
ax1.plot(x1[i],y1[i],'g+',markersize=12)
ax2.plot(x1[i],y1[i],'g+',markersize=12)
Just iterate over zipped (x, y1, y2):
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import ConnectionPatch
fig = plt.figure(figsize=(10, 5), dpi=100)
ax1 = fig.add_subplot(1, 1, 1)
ax2 = ax1.twinx()
n = 10
y1 = np.random.random(n)
y2 = np.random.random(n) + 1
x1 = np.arange(n)
# I add some colors blue for left y-axis, red for right y-axis
ax1.scatter(x1, y1, c='b')
ax2.scatter(x1, y2, c='r')
# Now iterate over paired x, and 2 y values:
for xi, y1i, y2i in zip(x1, y1, y2):
con = ConnectionPatch(
xyA=(xi, y1i),
xyB=(xi, y2i),
coordsA="data",
coordsB="data",
axesA=ax1,
axesB=ax2,
color='g',
)
ax1.add_artist(con)
plt.show()
Out:
I'm trying to make a 3D Surface plot easier to look at, with a low number of data points. I'm doing this by creating 1000 linearly-connected points in between each of my x and y data points. The flaw with this, is that it expands my data in only orthogonal directions, while leaving disparity along a diagonal axis.
You can see in this plot, the two highest points are diagonal to each other, and have a curved dip between them. However, I wouldn't expect this dip to be there if I had actual data points to represent it. Is there a way to interpolate this more accurately?
import numpy as np
from matplotlib import pyplot as plt
import pandas as pd
from matplotlib import cm
from scipy.interpolate import griddata
x = np.array([1200, 900, 600, 300, 1200, 900, 600, 300, 1200, 900, 600, 300])
y = np.array([32698675.77, 32698675.77, 32698675.77, 32698675.77, 22235099.52, 22235099.52, 22235099.52, 22235099.52, 11117549.76, 11117549.76, 11117549.76, 11117549.76])
z = np.array([ 157039.99991206, 112843.30660347, 86955.86661797, 110282.34660491, 99997.52952157, 211376.31395759, 126638.66680572, 88166.23539093, 246550.43164327, 127657.17661076, 84181.01970028, 111822.90208357])
xyz = {'x': x, 'y': y, 'z': z}
df = pd.DataFrame(xyz, index=range(len(xyz['x'])))
x1 = np.linspace(df['x'].min(), df['x'].max(), len(df['x'].unique()))
y1 = np.linspace(df['y'].min(), df['y'].max(), len(df['y'].unique()))
x2, y2 = np.meshgrid(x1, y1)
xt = np.array([])
for mina in range(len(x1)):
if mina < len(x1)-1:
xn = np.linspace(x1[mina], x1[mina+1], 1000)
xt = np.hstack((xt, xn))
yt = np.array([])
for mina in range(len(y1)):
if mina < len(y1)-1:
yn = np.linspace(y1[mina], y1[mina+1], 1000)
yt = np.hstack((yt, yn))
x3, y3 = np.meshgrid(xt, yt)
z2 = griddata((df['x'], df['y']), df['z'], (x2, y2), method='cubic')
z3 = griddata((df['x'], df['y']), df['z'], (x3, y3), method='cubic')
fig = plt.figure()
ax = fig.add_subplot(3, 2, 1, projection='3d')
surf = ax.plot_surface(x2, y2, z2, cmap=cm.YlGnBu)
ax.set_ylabel('y')
ax.set_xlabel('x')
ax.set_zlabel('z')
ax.view_init(20, 100)
ax.yaxis.labelpad=20
ax.xaxis.labelpad=10
ax.zaxis.labelpad=20
ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
ax.ticklabel_format(style='sci', axis='z', scilimits=(0,0))
plt.ylim(np.max(y), np.min(y))
ax.set_xticks(np.unique(x))
ax.set_yticks(np.unique(y))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.tight_layout()
ax = fig.add_subplot(3, 2, 2, projection='3d')
surf = ax.plot_surface(x3, y3, z3, cmap=cm.YlGnBu)
ax.set_ylabel('y.')
ax.set_xlabel('x')
ax.set_zlabel('z')
ax.view_init(20, 100)
ax.yaxis.labelpad=20
ax.xaxis.labelpad=10
ax.zaxis.labelpad=20
ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
ax.ticklabel_format(style='sci', axis='z', scilimits=(0,0))
plt.ylim(np.max(y), np.min(y))
ax.set_xticks(np.unique(x))
ax.set_yticks(np.unique(y))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.tight_layout()
plt.show()
Example of before and after plots are in the code, image below.