Linear inequalities with python graphs - python

I have graphed linear inequalities with matplotlib. How can I print the equations along the lines ? I do not want to label them on the right but right on the top of every lines.
Here is my code for the moment :
import numpy as np
import matplotlib.pyplot as plt
# Construct lines
# x > 0
x = np.linspace(0, 20, 2000)
# x1 >= 0
y1 = (x*0) + 0
# 2x1+x2<=10
y2 = 10-2*x
# x2 >= 4x-8
y3 = (4*x-8)/4.0
# y <= 2x - 5
y4 = (5 * x +2)/2
plt.xlabel(r'$x_2>=0$')
plt.ylabel(r'$x_1>=0$')
# Make plot
plt.plot(x, y1)
plt.plot(x, y2)
plt.plot(x, y3)
plt.plot(x, y4)
plt.xlim((0, 16))
plt.ylim((0, 11))
# Fill feasible region
y5 = np.minimum(y2, y4)
y6 = np.maximum(y1, y3)
plt.fill_between(x, y5, y6, where=y5>y6, color='grey', alpha=0.5)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.grid()
plt.show()

Related

Line plot with arrows in matplotlib for all 4 lines plotted with lists of 4 elements

These variables are all lists containing 4 elements. I am trying to get the arrows to show in between these 4 elements for all the lists below in the line plot. In another words, I will have 3 arrows for each of the 4 lines in the plot shown below.
I came across this post that suggested the use of quiver but I am not sure how to apply it to my use case Line plot with arrows in matplotlib Any suggestion is appreciated.
median1
median2
median3
median4
median_a
median_b
median_c
median_d
import matplotlib.pyplot as plt
x1 = median_a
x2 = median_b
x3 = median_c
x4 = median_d
y1 = median1
y2 = median2
y3 = median3
y4 = median4
line1 = plt.plot(x1, y1,'bo-',label='B0')
line2 = plt.plot(x2, y2,'go-',label='B1')
line3 = plt.plot(x3, y3,'ko-',label='B2')
line4 = plt.plot(x4, y4,'ro-',label='B3')
plt.title("Flow")
plt.ylabel("Speed")
plt.xlabel("Size")
plt.legend(bbox_to_anchor=(1.5, 1),
bbox_transform=plt.gcf().transFigure)
import numpy as np
import matplotlib.pyplot as plt
median_a, median_b, median_c, median_d = [np.random.random((4, 1)) for _ in range(4)]
median1, median2, median3, median4 = [np.random.random((4, 1)) for _ in range(4)]
x1 = median_a
x2 = median_b
x3 = median_c
x4 = median_d
y1 = median1
y2 = median2
y3 = median3
y4 = median4
deltaX1, deltaX2, deltaX3, deltaX4 = [x[1:]-x[:-1] for x in [x1, x2, x3, x4]]
deltaY1, deltaY2, deltaY3, deltaY4 = [y[1:]-y[:-1] for y in [y1, y2, y3, y4]]
line1 = plt.plot(x1, y1,'bo-',label='B0')
line2 = plt.plot(x2, y2,'go-',label='B1')
line3 = plt.plot(x3, y3,'ko-',label='B2')
line4 = plt.plot(x4, y4,'ro-',label='B3')
arrows1 = plt.quiver(x1[:-1], y1[:-1], deltaX1, deltaY1, scale_units='xy', angles='xy', scale=1)
arrows2 = plt.quiver(x2[:-1], y2[:-1], deltaX2, deltaY2, scale_units='xy', angles='xy', scale=1)
arrows3 = plt.quiver(x3[:-1], y3[:-1], deltaX3, deltaY3, scale_units='xy', angles='xy', scale=1)
arrows4 = plt.quiver(x4[:-1], y4[:-1], deltaX4, deltaY4, scale_units='xy', angles='xy', scale=1)
plt.title("Flow")
plt.ylabel("Speed")
plt.xlabel("Size")
plt.legend()
plt.show()
To prevent the double plotting in the other answers, you can specify the arrow colour directly. Also, since you are plotting four times it would be easier to do it in a loop.
xx = [median_a, median_b, median_c, median_d]
yy = [median1, median2, median3, median4]
colors = ['b', 'g', 'k', 'r']
for idx, (x, y) in enumerate(zip(xx, yy)):
Q = plt.quiver(x[:-1], y[:-1], x[1:]-x[:-1], y[1:]-y[:-1], color=colors[idx], scale_units='xy', angles='xy', scale=1)
plt.quiverkey(Q, 0.95, 1-idx/10, 0.05, label=f'B{idx}', labelpos='N')
plt.title("Flow")
plt.ylabel("Speed")
plt.xlabel("Size")
plt.show()

Divide one-to-two x-y data into top and bottom sets

I have a data set with two y values associated with each x value. How can I divide the data into "upper" and "lower" values?
Below, I show an example with such a data set. I show an image of the desired "top" and "bottom" groupings (the red is the top and the purple is the bottom). My best idea so far is to find a line dividing the top and bottom data using an iterative approach.This solution is complicated and does not work very well, so I did not include it.
import matplotlib.pyplot as plt
import numpy as np
# construct data using piecewise functions
x1 = np.linspace(0, 0.7, 70)
x2 = np.linspace(0.7, 1, 30)
x3 = np.linspace(0.01, 0.999, 100)
y1 = 4.164 * x1 ** 3
y2 = 1 / x2
y3 = x3 ** 4 - 0.1
# concatenate data
x = np.concatenate([x1, x2, x3])
y = np.concatenate([y1, y2, y3])
# I want to be able divide the data by top and bottom,
# like shown in the chart. The black is the unlabeled data
# and the red and purple show the top and bottom
plt.scatter(x, y, marker='^', s=10, c='k')
plt.scatter(x1, y1, marker='x', s=0.8, c='r')
plt.scatter(x2, y2, marker='x', s=0.8, c='r')
plt.scatter(x3, y3, marker='x', s=0.8, c='purple')
plt.show()
You can create a dividing line by re-ordering your data. Sort everything by x then apply a Gaussian filter. The two data sets are strictly above or below the results of the Gaussian filter:
import matplotlib.pyplot as plt
from scipy.ndimage.filters import gaussian_filter1d
import numpy as np
# construct data using piecewise functions
x1 = np.linspace(0, 0.7, 70)
x2 = np.linspace(0.7, 1, 30)
x3 = np.linspace(0.01, 0.999, 100)
y1 = 4.164 * x1 ** 3
y2 = 1 / x2
y3 = x3 ** 4 - 0.1
# concatenate data
x = np.concatenate([x1, x2, x3])
y = np.concatenate([y1, y2, y3])
# I want to be able divide the data by top and bottom,
# like shown in the chart. The black is the unlabeled data
# and the red and purple show the top and bottom
idx = np.argsort(x)
newy = y[idx]
newx = x[idx]
gf = gaussian_filter1d(newy, 5)
plt.scatter(x, y, marker='^', s=10, c='k')
plt.scatter(x1, y1, marker='x', s=0.8, c='r')
plt.scatter(x2, y2, marker='x', s=0.8, c='r')
plt.scatter(x3, y3, marker='x', s=0.8, c='purple')
plt.scatter(newx, gf, c='orange')
plt.show()
I would try as follows:
sort the points by increasing X if necessary;
maintain two indexes to the upper and lower subsets;
moving from left to right, for every new point assign it to the closest subset and update the corresponding index.
Initialization of the process seems a little tricky. Start with the first two points (they have high chance of belonging to the same subset). Progress until the two points have a significant separation so that you are sure they belong to different subsets. Then backtrack to the left.

how to fill area between lines in matplotlib

I want to fill the maximized area in from the below equation after plotting in matplotlib
Tried all possibilities but could not fill the desired area.
import numpy as np
import matplotlib.pyplot as plt
A = np.linspace(0, 100, 2000)
# 3A+4B≤30
y1 = (30 - A * 3 ) /4
# 5A+6B≤60
y2 = (60 - A * 5)/6
# 1.5A+3B≤21
y3 = (21 - A * 1.5)/3.0
plt.plot(A, y1, label=r'$3A+4B\leq30$')
plt.plot(A, y2, label=r'$5A+6B\leq60$')
plt.plot(A, y3, label=r'$1.5A+3B\leq21$')
plt.xlim((0, 20))
plt.ylim((0, 15))
plt.xlabel(r'$x values$')
plt.ylabel(r'$y values$')
plt.fill_between(A, y3, where = y2<y3,color='grey', alpha=0.5)
plt.legend(bbox_to_anchor=(.80, 1), loc=2, borderaxespad=0.1)
plt.show()
want to fill the area of maxim which is x = 2.0 and y = 6.0
This is one solution based on this link. The only difference from the linked solution is that for your case, I had to use fill_betweenx to cover the whole x-axis common to the curves and switch the order of x and Y. The idea is to first find the intersection point within some tolerance and then take the values from one curve lying left to the point and the other curve lying right to the intersection. I also had to add an additional [0] in the ind to get it working
import numpy as np
import matplotlib.pyplot as plt
A = np.linspace(0, 100, 2000)
y1 = (30 - A * 3 ) /4
y2 = (60 - A * 5)/6
y3 = (21 - A * 1.5)/3.0
plt.plot(A, y1, label=r'$3A+4B\leq30$')
plt.plot(A, y2, label=r'$5A+6B\leq60$')
plt.plot(A, y3, label=r'$1.5A+3B\leq21$')
plt.xlim((0, 20))
plt.ylim((0, 12))
plt.xlabel(r'$x values$')
plt.ylabel(r'$y values$')
plt.legend(bbox_to_anchor=(.65, 0.95), loc=2, borderaxespad=0.1)
def fill_below_intersection(x, S, Z):
"""
fill the region below the intersection of S and Z
"""
#find the intersection point
ind = np.nonzero( np.absolute(S-Z)==min(np.absolute(S-Z)))[0][0]
# compute a new curve which we will fill below
Y = np.zeros(S.shape)
Y[:ind] = S[:ind] # Y is S up to the intersection
Y[ind:] = Z[ind:] # and Z beyond it
plt.fill_betweenx(Y, x, facecolor='gray', alpha=0.5) # <--- Important line
fill_below_intersection(A, y3, y1)
I am assuming you want to fill the area between y1 and y3 until they intersect with each other, because you specified (2, 6) as a point? Then use:
plt.fill_between(A, y1, y3, where = y1<y3)
Analogously replace y3 for y2 if you meant the other curve. "Maximized area" is a bit misleading, as #gmds already commented.

How to fill area between two sets of data connected by line?

I have two sets of data that are shown like lines in graph. How to fill with color area between them?
import matplotlib.pyplot as plt
curve1, = plt.plot(xdata, ydata)
curve2, = plt.plot(xdata, ydata)
I tried:
x = np.arange(0,12,0.01)
plt.fill_between(x, curve1, curve2, color='yellow')
Thank you
You have to use the ydata as arguments for your fill_between, not the curves.
Either use ydata directly, or get them from your curve1/2 objects like ydata=curve1.get_ydata().
Here is an example adapted from the docs:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-5, 5, 0.01)
y1 = -5*x*x + x + 10
y2 = 5*x*x + x
c1, = plt.plot(x, y1, color='black')
c2, = plt.plot(x, y2, color='black')
# If you want/have to get the data form the plots
# x = c1.get_xdata()
# y1 = c1.get_ydata()
# y2 = c2.get_ydata()
plt.fill_between(x, y1, y2, where=y2 >y1, facecolor='yellow', alpha=0.5)
plt.fill_between(x, y1, y2, where=y2 <=y1, facecolor='red', alpha=0.5)
plt.title('Fill Between')
plt.show()
In the end you get:

How to add padding to right hand side of plot, equal to distance between left edge and left axis?

I'm trying to get the output of a saved figure, with two side-by-side subplots, to have an equal looking spacing on the left and right of the plots, so that when the figure is included in a document, it looks like the plots are collectively centred.
In other words, add padding of width 'a' from the figure to the right hand side of the figure.
Here is some sample code (with the data replaced):
import numpy as np
import matplotlib.pyplot as plt
x1 = np.linspace(0.0, 5.0)
x2 = np.linspace(0.0, 2.0)
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
y2 = np.cos(2 * np.pi * x2)
fig, axes = plt.subplots(nrows=1,
ncols=2,
dpi=220,
squeeze=False,
figsize=(3.45, 2.1)
)
axes[0][0].plot(x1, y1, 'o-')
axes[0][0].set_ylabel('Damped oscillation')
axes[0][0].set_xlabel('time (s)')
axes[0][1].plot(x2, y2, '.-')
axes[0][1].set_xlabel('time (s)')
for i in [0, 1]:
x0, x1 = axes[0][i].get_xlim()
x_diff = x1 - x0
y0, y1 = axes[0][i].get_ylim()
y_diff = y1 - y0
axes[0][i].set_aspect(x_diff / y_diff)
axes[0][i].tick_params(labelbottom='off', labelleft='off', axis=u'both', which=u'both', length=0)
axes[0][i].xaxis.labelpad = 7
axes[0][i].yaxis.labelpad = 7
fig.savefig('clustering.pdf',
bbox_inches='tight',
pad_inches=0,
dpi='figure')

Categories

Resources