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.
Related
I have two lines:
y=3x-4
y=-x+5
They intersect and form 4 regions in space (see image below).
Mathematically, the regions can be determined with the following inequations:
Region1: y<3x-4 & y>-x+5
Region2: y>3x-4 & y>-x+5
Region3: y>3x-4 & y<-x+5
Region4: y<3x-4 & y<-x+5
I want to fill all of those regions independently. However when I try to use plt.fill_between I can only get regions 1 and/or 3. How can I fill regions 2 and 4 using matplotlib and fill_between?
The code that I tried for region 3 is the following (source):
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0,6,0.1)
y = np.arange(0,6,0.1)
# The lines to plot
y1 = 3*x - 4
y2 = -x+5
# The upper edge of polygon (min of lines y1 & y2)
y3 = np.minimum(y1, y2)
# Set y-limit, making neg y-values not show in plot
plt.ylim(0, 6)
plt.xlim(0, 6)
# Plotting of lines
plt.plot(x, y1,
x, y2)
# Filling between (region 3)
plt.fill_between(x, y2, y3, color='grey', alpha=0.5)
plt.show()
It's because you have set the limits incorrectly. Setting them as 2 horizontal lines, 6 and 0, does the trick. Note that I have changed the step of arange because, to have a neat picture, the intersection point (2.25 in your case) needs to belong to x range
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0,6,0.05)
# The lines to plot
y1 = 3*x - 4
y2 = -x+5
# Set y-limit, making neg y-values not show in plot
plt.ylim(0, 6)
plt.xlim(0, 6)
# Filling between (region 3)
plt.fill_between(x, np.maximum(y1, y2), [6] * len(x), color='grey', alpha=0.5)
plt.fill_between(x, np.minimum(y1, y2), [0] * len(x), color='cyan', alpha=0.5)
plt.show()
The trick here is to define a new polygon based on the intersections of the lines, and use $y(x) = 0$ as your bottom border.
The implementation of this requires the following code. First we externally calculate and hardcode the intersection points between the lines and the a-axis:
# calculate intersections between lines and x axis
intersect_x = 9/4
y1_0 = 4/3
y2_0 = 5
Once we have those stored, we go through every x value. If the x value is greater than our starting point (where the climbing curve crosses the x-axis) but less than the intersection point, we add the climbing curve point at that x value. If the x-value is greater than the intersection point but less than the end point (where the falling curve crosses the x-axis), we add the falling curve point at that x value. At each step, we add a y=0 point to a new list to be our floor for the fill-in operation.
# here we manually perform the operation you were trying to do
# with np.minimum; we also add an (x, 0) point for
# every point we add to our new polygon
new_x = []
new_y = []
bottom_y = []
for x_dx, xx in enumerate(x):
if xx > y1_0 and xx < intersect_x:
new_x.append(xx)
new_y.append(y1[x_dx])
bottom_y.append(0)
elif xx < y2_0 and xx > intersect_x:
new_x.append(xx)
new_y.append(y2[x_dx])
bottom_y.append(0)
Now we just replace our fill_between code with the new curves:
plt.fill_between(new_x, new_y, bottom_y, color='grey', alpha=0.5)
This is the full code:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0,6,0.1)
y = np.arange(0,6,0.1)
# The lines to plot
y1 = 3*x - 4
y2 = -x+5
# calculate intersections between lines and x axis
intersect_x = 9/4
y1_0 = 4/3
y2_0 = 5
# here we manually perform the operation you were trying to do
# with np.minimum; we also add an (x, 0) point for
# every point we add to our new polygon
new_x = []
new_y = []
bottom_y = []
for x_dx, xx in enumerate(x):
if xx > y1_0 and xx < intersect_x:
new_x.append(xx)
new_y.append(y1[x_dx])
bottom_y.append(0)
elif xx < y2_0 and xx > intersect_x:
new_x.append(xx)
new_y.append(y2[x_dx])
bottom_y.append(0)
# The upper edge of polygon (min of lines y1 & y2)
y3 = np.minimum(y1, y2)
# Set y-limit, making neg y-values not show in plot
plt.ylim(0, 6)
plt.xlim(0, 6)
# Plotting of lines
plt.plot(x, y1,
x, y2)
# Filling between (region 3)
plt.fill_between(new_x, new_y, bottom_y, color='grey', alpha=0.5)
plt.show()
And this is the result:
Happy coding!
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.
I have two sets of arrays x1, y1, t1 and x2, y2, t1 -- x data, y data and time measurement.
I would like to plot two these sets as lines with time as an x argument in plot(), so that lines are aligned with respect to time precedence of events.
However, I would also like to see the corresponding x1 and x2 on the plot in a form of xlabels (say at the top and at the bottom of the plot), as well as have two scales for y values (i.e. on the left and on the right of the figure).
import numpy as np
t1 = np.linspace(0, 10, 10)
y1 = np.arange(10)
x1 = (np.cumsum(np.random.rand(10)) * 1000000000).astype(int)
x1 = (x1 / 100000).astype(int) * 10
x2 = (np.cumsum(np.random.rand(10)) * 1000000000).astype(int)
x2 = (x2 / 1000000).astype(int)
y2 = 2 * np.arange(10)
t2 = np.linspace(0, 10, 10) + 2
from matplotlib import pyplot as plt
fig, ax1 = plt.subplots()
ax1.plot(t1, y1)
ax1.set_ylabel("y1 label")
ax1.set_xticklabels(x1)
ax1.set_xlabel("x1 label")
ax2 = ax1.twinx()
ax2.plot(t2, y2, c='r')
ax2.set_ylabel("y2 label")
ax3 = ax2.twiny()
ax3.xaxis.set_ticks_position('top')
ax3.set_xticklabels(x2);
ax3.set_xlabel("x2 label")
The code produces
which is good, but has two problems:
xlabels are not aligned with the data: blue line on the plot starts at x1 ticklabel 104500, while x1[0] = 29380.
I an unable to apply sci format for the x1 and x2 ticks, i.e. the line
ax1.ticklabel_format(style='sci', axis='x', scilimits=(0,0))
fails with This method only works with the ScalarFormatter, which is reasonable, since I have replaced labels of ticks, not ticks themselves. On the other hand, I cannot assign x1 to xticks, since this will change limits of xaxis.
How could I overcome two these problems?
I would like to fill between 3 lines in matplotlib.pyplot but unfortunately the fill_between gives me opportunity to fill between only two lines. Any ideas how to deal with this?
Edit:
Ok, I did not explain what I really mean since I cannot add the picture with my current reputation so maybe in that way:
I try to fill the polygon bounded by these lines and I have no idea how because fill_between gives me opportunity to fill only area between two of them. Below the fill equation:
y <= 4- 2x
y <= 3 - 1/2x
y <= 1 - x
y >= 0
x >= 0
the x and y bigger than 0 is obvious. I start the plot from (0,0) but I still have 3 lines...
y <= 4- 2x
y <= 3 - 1/2x
y <= 1 - x
If you start the plot in point (0, 0), and therefore do not need to consider the area of the polygon not in the first quadrant, then this should do the trick in this particular situation:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0,10,0.1)
# The lines to plot
y1 = 4 - 2*x
y2 = 3 - 0.5*x
y3 = 1 -x
# The upper edge of polygon (min of lines y1 & y2)
y4 = np.minimum(y1, y2)
# Set y-limit, making neg y-values not show in plot
plt.ylim(0, 5)
# Plotting of lines
plt.plot(x, y1,
x, y2,
x, y3)
# Filling between line y3 and line y4
plt.fill_between(x, y3, y4, color='grey', alpha='0.5')
plt.show()
To use fill_between, specify the X values first, than the two Y sets that you want to "fill between". An example is show below:
import pylab as plt
import numpy as np
X = np.linspace(0,3,200)
Y1 = X**2 + 3
Y2 = np.exp(X) + 2
Y3 = np.cos(X)
plt.plot(X,Y1,lw=4)
plt.plot(X,Y2,lw=4)
plt.plot(X,Y3,lw=4)
plt.fill_between(X, Y1,Y2,color='k',alpha=.5)
plt.fill_between(X, Y1,Y3,color='y',alpha=.5)
plt.show()
If, instead, you only wanted to fill between Y2 and Y3:
plt.fill_between(X, Y2,Y3,color='m',alpha=.5)
this would give you
Just compute the corner points of the polygon, i.e., the points where the lines intersect.
Then plot the polygon using pyplot.fill.
Example:
import matplotlib.pyplot as plt
# define corner points
x = [1,2,1,0]
y = [2,1,0,1]
# plot
plt.fill(x,y)
plt.show()
resulting Image:
I would like to fill between 3 lines in matplotlib.pyplot but unfortunately the fill_between gives me opportunity to fill between only two lines. Any ideas how to deal with this?
Edit:
Ok, I did not explain what I really mean since I cannot add the picture with my current reputation so maybe in that way:
I try to fill the polygon bounded by these lines and I have no idea how because fill_between gives me opportunity to fill only area between two of them. Below the fill equation:
y <= 4- 2x
y <= 3 - 1/2x
y <= 1 - x
y >= 0
x >= 0
the x and y bigger than 0 is obvious. I start the plot from (0,0) but I still have 3 lines...
y <= 4- 2x
y <= 3 - 1/2x
y <= 1 - x
If you start the plot in point (0, 0), and therefore do not need to consider the area of the polygon not in the first quadrant, then this should do the trick in this particular situation:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0,10,0.1)
# The lines to plot
y1 = 4 - 2*x
y2 = 3 - 0.5*x
y3 = 1 -x
# The upper edge of polygon (min of lines y1 & y2)
y4 = np.minimum(y1, y2)
# Set y-limit, making neg y-values not show in plot
plt.ylim(0, 5)
# Plotting of lines
plt.plot(x, y1,
x, y2,
x, y3)
# Filling between line y3 and line y4
plt.fill_between(x, y3, y4, color='grey', alpha='0.5')
plt.show()
To use fill_between, specify the X values first, than the two Y sets that you want to "fill between". An example is show below:
import pylab as plt
import numpy as np
X = np.linspace(0,3,200)
Y1 = X**2 + 3
Y2 = np.exp(X) + 2
Y3 = np.cos(X)
plt.plot(X,Y1,lw=4)
plt.plot(X,Y2,lw=4)
plt.plot(X,Y3,lw=4)
plt.fill_between(X, Y1,Y2,color='k',alpha=.5)
plt.fill_between(X, Y1,Y3,color='y',alpha=.5)
plt.show()
If, instead, you only wanted to fill between Y2 and Y3:
plt.fill_between(X, Y2,Y3,color='m',alpha=.5)
this would give you
Just compute the corner points of the polygon, i.e., the points where the lines intersect.
Then plot the polygon using pyplot.fill.
Example:
import matplotlib.pyplot as plt
# define corner points
x = [1,2,1,0]
y = [2,1,0,1]
# plot
plt.fill(x,y)
plt.show()
resulting Image: