Remove the intersection between two curves - python

I'm having a curve (parabol) from 0 to 1 on both axes as follows:
I generate another curve by moving the original curve along the x-axis and combine both to get the following graph:
How can I remove the intersected section to have only the double bottoms pattern like this:
The code I use for the graph:
import numpy as np
import matplotlib.pyplot as plt
def get_parabol(start=-1, end=1, steps=100, normalized=True):
x = np.linspace(start, end, steps)
y = x**2
if normalized:
x = np.array(x)
x = (x - x.min())/(x.max() - x.min())
y = np.array(y)
y = (y - y.min())/(y.max() - y.min())
return x, y
def curve_after(x, y, x_ratio=1/3, y_ratio=1/2, normalized=False):
x = x*x_ratio + x.max() - x[0]*x_ratio
y = y*y_ratio + y.max() - y.max()*y_ratio
if normalized:
x = np.array(x)
x = (x - x.min())/(x.max() - x.min())
y = np.array(y)
y = (y - y.min())/(y.max() - y.min())
return x, y
def concat_arrays(*arr, axis=0, normalized=True):
arr = np.concatenate([*arr], axis=axis).tolist()
if normalized:
arr = np.array(arr)
arr = (arr - arr.min())/(arr.max() - arr.min())
return arr
x, y = get_parabol()
new_x, new_y = curve_after(x, y, x_ratio=1, y_ratio=1, normalized=False)
new_x = np.add(x, 0.5)
# new_y = np.add(y, 0.2)
xx = concat_arrays(x, new_x, normalized=True)
yy = concat_arrays(y, new_y, normalized=True)
# plt.plot(x, y, '-')
plt.plot(xx, yy, '--')
I'm doing a research on pattern analysis that requires me to generate patterns with mathematical functions.
Could you show me a way to achieve this? Thank you!

First off, I would have two different parabola functions such that:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 100)
y1 = np.add(x, 0.3)**2 # Parabola centered at -0.3
y2 = np.add(x, -0.3)**2 # Parabola centered at 0.3
You can choose your own offsets for y1 and y2 depending on your needs.
And then it's simply take the min of the two arrays
y_final = np.minimum(y1, y2)
plt.plot(x, y_final, '--')

This involves curve fitting. You need to find the intersection part before you drop the values. Since the values of x and y have been normalized, we would have to determine exactly where the two datasets meet. We can see that they meet when x[i] >x[i+1]. Using your cobined xx and yy from the data provided, We therefore can do the following:
data_intersect = int(np.where(np.r_[0,np.diff(xx)] < 0)[0])
x1 = xx[:data_intersect]
x2 = xx[data_intersect:]
y1 = yy[:data_intersect]
y2 = yy[data_intersect:]
difference = np.polyfit(x1, y1, 2) - np.polyfit(x2,y2,2)
meet = np.roots(difference) # all points where the two curves meet
meet = meet[(meet < max(x1)) & (meet >min(x1))] # only point curve meet
xxx = np.r_[x1[x1<meet], x2[x2>meet]]
yyy = np.r_[y1[x1<meet], y2[x2>meet]]
plt.plot(xxx, yyy, '--')

Related

How to meshgrid this non-rectangular shape (a trapezoid) - to be used for 3d volumteric visualtion - python

Im trying to create a volume with a custom shape, to add Z-axis data I need to mesh X-Y data.
Hence my issue. I'd like to have this shape as the base
Trapezoid Base
However, after doing
X,Y = np.mesh(x,y)
I get a symmetric rectangle rather than the trapezoid-looking like shape.
meshed data
is there another effecient way I can fill the trapezoid ?
here is the code:
x1 = np.zeros(20)
y1 = np.linspace(-2,2,20)
x2 = np.linspace(0,30,20)
y2 = np.sqrt(( 5/max(x2) * x2 +4 ))
x3 = np.linspace(0, 30, 20)
y3 = -np.sqrt((5 / max(x3) * x3 + 4))
x4 = np.ones(20)*30
y4 = np.linspace(-3,3,20)
x = np.concatenate((x1, x2, x3, x4))
y = np.concatenate((y1,y2,y3,y4))
# u,v = np.meshgrid(x,y)
# x = u.flatten()
# y = v.flatten()
plt.plot(x,y)
plt.show()
Trying to fill a trapizoid shape to construct a 3d volume representation using plotly
I think you can do something like this.
The key is to define a normalized version of y that has a domain of -1 to 1 and multiply it by your final equation for y
import numpy as np
import matplotlib.pyplot as plt
n = 20
x = np.linspace(0, 30, n)
y = np.linspace(-1, 1, n)
xx, yy = np.meshgrid(x, y)
yy *= np.sqrt(5/x.max() * x + 4)
plt.scatter(xx, yy)
plt.show()

Holoviz Polygon orientation settings

I am using holoviews+bokeh, and I would like to encircle my scatter plot data with a measure of standard deviation. Unfortunately I can't seem to get the orientation setting right. I am confused by the available descriptions:
Orientation in the Cartesian coordinate system, the
counterclockwise angle in radians between the first axis and the
horizontal
and
you can set the orientation (in radians, rotating anticlockwise)
My script and data example:
def create_plot(x, y, nstd=5):
x, y = np.asarray(x), np.asarray(y)
cov_matrix = np.cov([x, y])
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)
order = eigenvalues.argsort()[0]
angle = np.arctan2(eigenvectors[1, order], eigenvectors[1, order])
x0 = np.mean(x)
y0 = np.mean(y)
x_dir = np.cos(angle) * x - np.sin(angle) * y
y_dir = np.sin(angle) * x + np.cos(angle) * y
w = nstd * np.std(x_dir)
h = nstd * np.std(y_dir)
return hv.Ellipse(x0, y0, (w, h), orientation=-angle) * hv.Scatter((x, y))
c2x = np.random.normal(loc=-2, scale=0.6, size=200)
c2y = np.random.normal(loc=-2, scale=0.1, size=200)
combined = create_plot(c2x, c2y)
combined.opts(shared_axes=False)
Here is a solution, which draws Ellipse around the data. You math is just simplified.
import numpy as np
import holoviews as hv
from holoviews import opts
hv.extension('bokeh')
x = np.random.normal(loc=-2, scale=0.6, size=200)
y = np.random.normal(loc=-2, scale=0.1, size=200)
def create_plot(x, y, nstd=5):
x, y = np.asarray(x), np.asarray(y)
x0 = np.mean(x)
y0 = np.mean(y)
w = np.std(x)*nstd
h = np.std(y)*nstd
return hv.Ellipse(x0, y0, (w, h)) * hv.Scatter((x, y))
combined = create_plot(c2x, c2y)
combined.opts()
This gives you a plot which looks like a circle. To make it more visiable that it is a Ellipse your could genereate the plot calling
def hook(plot, element):
plot.handles['x_range'].start = -4
plot.handles['x_range'].end = 0
plot.handles['y_range'].start = -2.5
plot.handles['y_range'].end = -1
combined.opts(hooks=[hook])
which set fixed ranges and deactivates the auto focus.
In your example w and h were nearly the same, that means, you drawed a cercle. The orientation didn't have any effect. With the code above you can turn the Ellipse like
hv.Ellipse(x0, y0, (w, h), orientation=np.pi/2)
to see that it is working, but there is no need to do it anymore.

Returning best support vectors from support vector machine

I'm using a support vector classifier from sklearn (in Python) to find the optimal boundary between a set of "0" and "1" labelled data.
See: https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html
However I want to perform some analysis after rotating the data about the boundary line and therefore I need to return the properties which will allow me to define the line to start with.
I carry out the SVC as follows:
Relevant imports:
from sklearn import svm
import numpy as np
import matplotlib.pyplot as plt
I define the classifier as:
clf = svm.SVC(kernel='linear',C = 1e-3 ,class_weight='balanced')
Which is then fit to the training data:
clf.fit(f_train, labels_train)
Whereupon the linear class boundary can be viewed using:
plt.figure()
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xx = np.linspace(xlim[0], xlim[1], 30)
yy = np.linspace(ylim[0], ylim[1], 30)
YY, XX = np.meshgrid(yy, xx)
xy = np.vstack([XX.ravel(), YY.ravel()]).T
Z = clf.decision_function(xy).reshape(XX.shape)
ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5,
linestyles=['--', '-', '--'])
As seen in: https://scikit-learn.org/stable/auto_examples/svm/plot_separating_hyperplane.html
But when calling:
clf.support_vectors_.shape
I'm not sure how to interpret the output as being relevant if trying to describe the linear boundary as the output has shape (4485, 2)
Any help with returning something that will allow me to define the boundary line will be greatly appreciated!
Based on Plotting 3D Decision Boundary From Linear SVM you can get a boundary line using clf.intercept_ and clf.coef_ attributes:
def decision_hyperplane(clf, x, y=None, dimension=2):
"""
Return a decision line (dimension 2, return y based on x) or a
decision plane (dimension 3, return z based on x and y).
Decision plane equation is wx + b = 0, so in 2d case:
w.dot(x) + b = w_x * x + w_y * y + b = 0
y = (-w_x * x - b) / w_y
In 3d:
w_x * x + w_y * y + w_z * z + b = 0
z = (-w_x * x - w_y * y - b) / w_z
"""
if dimension == 2:
return (-clf.intercept_[0] - clf.coef_[0][0] * x) / clf.coef_[0][1]
elif dimension == 3:
return (-clf.intercept_[0] - clf.coef_[0][0] * x - clf.coef_[0][1] * y) / clf.coef_[0][2]
If you use it with your code like this
ax.plot(xx, decision_hyperplane(clf, xx), color='red')
the result will be

Python: heat density plot in a disk

My goal is to make a density heat map plot of sphere in 2D. The plotting code below the line works when I use rectangular domains. However, I am trying to use the code for a circular domain. The radius of sphere is 1. The code I have so far is:
from pylab import *
import numpy as np
from matplotlib.colors import LightSource
from numpy.polynomial.legendre import leggauss, legval
xi = 0.0
xf = 1.0
numx = 500
yi = 0.0
yf = 1.0
numy = 500
def f(x):
if 0 <= x <= 1:
return 100
if -1 <= x <= 0:
return 0
deg = 1000
xx, w = leggauss(deg)
L = np.polynomial.legendre.legval(xx, np.identity(deg))
integral = (L * (f(x) * w)[None,:]).sum(axis = 1)
c = (np.arange(1, 500) + 0.5) * integral[1:500]
def r(x, y):
return np.sqrt(x ** 2 + y ** 2)
theta = np.arctan2(y, x)
x, y = np.linspace(0, 1, 500000)
def T(x, y):
return (sum(r(x, y) ** l * c[:,None] *
np.polynomial.legendre.legval(xx, identity(deg)) for l in range(1, 500)))
T(x, y) should equal the sum of c the coefficients times the radius as a function of x and y to the l power times the legendre polynomial where the argument is of the legendre polynomial is cos(theta).
In python: integrating a piecewise function, I learned how to use the Legendre polynomials in a summation but that method is slightly different, and for the plotting, I need a function T(x, y).
This is the plotting code.
densityinterpolation = 'bilinear'
densitycolormap = cm.jet
densityshadedflag = False
densitybarflag = True
gridflag = True
plotfilename = 'laplacesphere.eps'
x = arange(xi, xf, (xf - xi) / (numx - 1))
y = arange(yi, yf, (yf - yi) / (numy - 1))
X, Y = meshgrid(x, y)
z = T(X, Y)
if densityshadedflag:
ls = LightSource(azdeg = 120, altdeg = 65)
rgb = ls.shade(z, densitycolormap)
im = imshow(rgb, extent = [xi, xf, yi, yf], cmap = densitycolormap)
else:
im = imshow(z, extent = [xi, xf, yi, yf], cmap = densitycolormap)
im.set_interpolation(densityinterpolation)
if densitybarflag:
colorbar(im)
grid(gridflag)
show()
I made the plot in Mathematica for reference of what my end goal is
If you set the values outside of the disk domain (or whichever domain you want) to float('nan'), those points will be ignored when plotting (leaving them in white color).

Line fitting below points

I have a set of x, y points and I'd like to find the line of best fit such that the line is below all points using SciPy. I'm trying to use leastsq for this, but I'm unsure how to adjust the line to be below all points instead of the line of best fit. The coefficients for the line of best fit can be produced via:
def linreg(x, y):
fit = lambda params, x: params[0] * x - params[1]
err = lambda p, x, y: (y - fit(p, x))**2
# initial slope/intercept
init_p = np.array((1, 0))
p, _ = leastsq(err, init_p.copy(), args=(x, y))
return p
xs = sp.array([1, 2, 3, 4, 5])
ys = sp.array([10, 20, 30, 40, 50])
print linreg(xs, ys)
The output is the coefficients for the line of best fit:
array([ 9.99999997e+00, -1.68071668e-15])
How can I get the coefficients of the line of best fit that is below all points?
A possible algorithm is as follows:
Move the axes to have all the data on the positive half of the x axis.
If the fit is of the form y = a * x + b, then for a given b the best fit for a will be the minimum of the slopes joining the point (0, b) with each of the (x, y) points.
You can then calculate a fit error, which is a function of only b, and use scipy.optimize.minimize to find the best value for b.
All that's left is computing a for that b and calculating b for the original position of the axes.
The following does that most of the time, except when the minimization fails with some mysterious error:
from __future__ import division
import numpy as np
import scipy.optimize
import matplotlib.pyplot as plt
def fit_below(x, y) :
idx = np.argsort(x)
x = x[idx]
y = y[idx]
x0, y0 = x[0] - 1, y[0]
x -= x0
y -= y0
def error_function_2(b, x, y) :
a = np.min((y - b) / x)
return np.sum((y - a * x - b)**2)
b = scipy.optimize.minimize(error_function_2, [0], args=(x, y)).x[0]
a = np.min((y - b) / x)
return a, b - a * x0 + y0
x = np.arange(10).astype(float)
y = x * 2 + 3 + 3 * np.random.rand(len(x))
a, b = fit_below(x, y)
plt.plot(x, y, 'o')
plt.plot(x, a*x + b, '-')
plt.show()
And as TheodrosZelleke wisely predicted, it goes through two points that are part of the convex hull:

Categories

Resources