How to draw 2 graphs at the same time in python? - python

I have charts like this:
I need to draw these graphs in python so that later I can evenly distribute the points inside this curve. I tried to implement like this, but it's far from what I need:
import matplotlib.pyplot as plt
X = range(0,3)
y = [x*x for x in X]
z = [2-x for x in X]
plt.plot(X, y, color='r', label='x^2')
plt.plot(X, z, color='g', label='2-x')
plt.xlabel("X")
plt.ylabel("Y")
plt.legend()
plt.show()

Two remarks:
The lines you get are "overlaying" since you share the same x. However, in your desired output, the x of the square function extends from 0 to 1 and the x of the linear function extends from 1 to 2.
You get only three connected points, because range() only returns discrete integer values. numpy.linspace() returns evenly spaced numbers over a specified interval.
As #tdy mentioned in the comment, you should create two different x's for each curve:
x1 = np.linspace(0, 1)
x2 = np.linspace(1, 2)
Then you calculate the function values:
y1 = x1 ** 2
y2 = 2 - x2
Then you plot both:
plt.plot(x1, y1)
plt.plot(x2, y2)

Related

Fill area of regions of two intersecting lines

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!

In matplotlib, how to use a secondary axis that is input to the function together with the primary axis?

I have a function with two arguments (x1, x2). I would like to plot the function with its result on the y axis, x1 on the x axis and x2 on the right y axis (secondary axis).
My problem is that the values of the x1 and x2 axes do not correspond together to the function point.
For example:
I want the value read on the primary y axis to match the x1 and x2 inputs of the other two axes.
Code:
x1 = np.linspace(0, 10, 10)
x2 = np.linspace(0, 5, 10)
f = lambda x1, x2: np.exp(-x1) / 10 + np.exp(-x2) / 10
resp = []
for i, j in zip(x1, x2):
resp.append(f(i, j))
resp = np.array(resp)
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(x1, resp)
ax1.set_xlabel('(x1)')
ax1.grid(True)
ax1.set_ylabel('(y)')
ax2.set_ylabel('(x2)')
ax2.set_yticks(x2)
plt.xticks(np.arange(min(x1), max(x1)))
plt.show()
If your input arguments are in lockstep, you do not have a function of two arguments. A function of two arguments has two independent inputs. Your inputs are dependent, so rather than writing f(x1, x2), you have f(x, g(x)), which is just f'(x). In the specific example that you have x1 = np.linspace(0, 10, 10). Rather than writing x2 = np.linspace(0, 5, 10), you can just write x2 = 0.5 * x1. The exponential you have can be written as
x = np.linspace(0, 10, 10)
y = np.exp(-x) / 10 + np.exp(-x / 2) / 10
Notice that you do not need a function definition or a loop to compute the y values. Using a loop defeats the entire purpose of using numpy. Your original five lines could have been reduced to y = np.exp(-x1) / 10 + np.exp(-x2) / 10 in the same way.
Now if you want to see the secondary x values in the plot of y vs x, you can take a page out of the tutorials and do something like:
fig, ax1 = plt.subplots()
ax1.plot(x, y)
ax1.set_xlabel('(x1)')
ax1.grid(True)
ax1.set_ylabel('(y)')
ax2 = ax1.secondary_xaxis('top', functions=(lambda x: x / 2, lambda x: 2 * x))
ax2.set_xlabel('(x2)')
plt.show()
The result shows "both" inputs in lockstep:
Now if you really did want to have a function of two variables, then any combination of the inputs would produce a valid y value. In this case, you would have to use a feature of numpy called broadcasting, which matches array dimensions by lining them up on the right.
Let's say you defined one of the inputs as a transpose:
x1 = np.linspace(0, 10, 10) # Shape (10,)
x2 = np.linspace(0, 5, 10).reshape(-1, 1) # Shape (10, 1)
The result of an operation on these values will be a (10, 10) 2D array. Now you can meaningfully compute y as function of two independent variables:
y = np.exp(-x1) / 10 + np.exp(-x2) / 10
To plot such an array, you will need two x-axes and a y-axis, in other words a 3D plot. Here is one way to display something like that in matplotlib:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
s = ax.plot_surface(x1, x2, y, cm=cm.jet)
ax.set_xlabel('(x1)')
ax.set_ylabel('(x2)')
ax.set_zlabel('(y)')
fig.colorbar(s)
Here is the resulting plot:
It is up to you which representation of a function you want.

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.

Visualizing difference between two distributions [duplicate]

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:

Matplotlib fill between multiple lines

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:

Categories

Resources