How can I draw more lines?(python sympy) - python

when I type,
from sympy import *
from sympy.plotting import *
from sympy.plotting.plot import *
x, y = symbols('x y')
f = Function('f')
g = Function('g')
f = 1/((x+0.3)**2 + y**2) - 1/((x-0.3)**2 + y**2 )
g = (x+0.3)/sqrt((x+0.3)**2 + y**2) - (x-0.3)/sqrt((x-0.3)**2 + y**2)
p0 = Plot(ContourSeries(f,(x,-1.5,1.5),(y,-1.5,1.5)))
p1 = Plot(ContourSeries(g,(x,-1.5,1.5),(y,-1.5,1.5)))
p0.show()
p1.show()
p0 shows like first picture. The number of line is few.
I want to draw more line like second picture.
What is the solution?

The ContourSeries class doesn't expose the right info to do this but it is easy to extend. I called the parameter levels, is passed directly to matplotlib.
class MyContourSeries(ContourSeries):
def __init__(self, expr, var_start_end_x, var_start_end_y, **kwargs):
super(MyContourSeries, self).__init__(expr, var_start_end_x, var_start_end_y)
self.nb_of_points_x = kwargs.get('nb_of_points_x', 50)
self.nb_of_points_y = kwargs.get('nb_of_points_y', 50)
self.levels = kwargs.get('levels', 5)
def get_meshes(self):
mesh_x, mesh_y, f = super().get_meshes()
return (mesh_x, mesh_y, f, self.levels)
This should work with sympy 1.2 and 1.3 at least. Sympy plotting is using matplotlibs countour for this serie (https://github.com/sympy/sympy/blob/master/sympy/plotting/plot.py#L909)
elif s.is_contour:
self.ax.contour(*s.get_meshes())
which signature is matplotlib.pyplot.contour([X, Y,] Z, [levels], **kwargs)
As with the matplotlib contour function, you can used a fixed number of levels
p0 = Plot(MyContourSeries(f, (x, -1.5, 1.5), (y, -1.5, 1.5),
nb_of_points_x=50, nb_of_points_y=50, levels=100))
p0.show()
or pass the levels directly
p0 = Plot(MyContourSeries(f, (x, -1.5, 1.5), (y, -1.5, 1.5),
nb_of_points_x=50, nb_of_points_y=50, levels=np.arange(-50,50)))
p0.show()

Related

Reconstructing polynomials from scipy.interpolate.RectBivariateSpline

I have fitted a 2-D cubic spline using scipy.interpolate.RectBivariateSpline. I would like to access/reconstruct the underlying polynomials within each rectangular cell. How can I do this? My code so far is written below.
I have been able to get the knot points and the coefficients with get_knots() and get_coeffs() so it should be possible to build the polynomials, but I do not know the form of the polynomials that the coefficients correspond to. I tried looking at the SciPy source code but I could not locate the underlying dfitpack.regrid_smth function.
A code demonstrating the fitting:
import numpy as np
from scipy.interpolate import RectBivariateSpline
# Evaluate a demonstration function Z(x, y) = sin(sin(x * y)) on a mesh
# of points.
x0 = -1.0
x1 = 1.0
n_x = 11
x = np.linspace(x0, x1, num = n_x)
y0 = -2.0
y1 = 2.0
n_y = 21
y = np.linspace(y0, y1, num = n_y)
X, Y = np.meshgrid(x, y, indexing = 'ij')
Z = np.sin(np.sin(X * Y))
# Fit the sampled function using SciPy's RectBivariateSpline.
order_spline = 3
smoothing = 0.0
spline_fit_func = RectBivariateSpline(x, y, Z,
kx = order_spline, ky = order_spline, s = smoothing)
And to plot it:
import matplotlib.pyplot as plt
# Make axes.
fig, ax_arr = plt.subplots(1, 2, sharex = True, sharey = True, figsize = (12.0, 8.0))
# Plot the input function.
ax = ax_arr[0]
ax.set_aspect(1.0)
d_x = x[1] - x[0]
x_edges = np.zeros(n_x + 1)
x_edges[:-1] = x - (d_x / 2.0)
x_edges[-1] = x[-1] + (d_x / 2.0)
d_y = y[1] - y[0]
y_edges = np.zeros(n_y + 1)
y_edges[:-1] = y - (d_y / 2.0)
y_edges[-1] = y[-1] + (d_y / 2.0)
ax.pcolormesh(x_edges, y_edges, Z.T)
ax.set_title('Input function')
# Plot the fitted function.
ax = ax_arr[1]
ax.set_aspect(1.0)
n_x_span = n_x * 10
x_span_edges = np.linspace(x0, x1, num = n_x_span)
x_span_centres = (x_span_edges[1:] + x_span_edges[:-1]) / 2.0
#
n_y_span = n_y * 10
y_span_edges = np.linspace(y0, y1, num = n_y_span)
y_span_centres = (y_span_edges[1:] + y_span_edges[:-1]) / 2.0
Z_fit = spline_fit_func(x_span_centres, y_span_centres)
ax.pcolormesh(x_span_edges, y_span_edges, Z_fit.T)
x_knot, y_knot = spline_fit_func.get_knots()
X_knot, Y_knot = np.meshgrid(x_knot, y_knot)
# Plot the knots.
ax.scatter(X_knot, Y_knot, s = 1, c = 'r')
ax.set_title('Fitted function and knots')
plt.show()

fitting a plane into a cloud of points (3d)

I have a data file containing multiple columns of data,I would like to extract 3 columns (that indicate the coordinates ) out of this data file and put them in another file, then using the newly created file I would like to fit a plane or surface (or whatever you would like to call it) using scipy.optimize.curve_fit. Here is my code:
# -*- coding: utf-8 -*-
from pylab import *
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats
from scipy.optimize import curve_fit
### processing function
def store(var,textfile):
data=loadtxt(textfile,skiprows=1)
p0=[]
p1=[]
p2=[]
for i in range(0,len(data)):
p0.append(float(data[i,2]))
p1.append(float(data[i,3]))
p2.append(float(data[i,4]))
var.append(p0)
var.append(p1)
var.append(p2)
#extracting the data from a textfile
datafile1='cracks_0101005_5k_tensionTestCentreCrack_l0.001a0_r0.01.txt'
a1=[]
store(a1, datafile1)
rcParams.update({'legend.numpoints':1,'font.size': 20,'axes.labelsize':25,'xtick.major.pad':10,'ytick.major.pad':10,'legend.fontsize':14})
lw=2
ms=10
#fitting a surface(curve) into the data
def func(data, m, n, o):
return m*data[:,0] + n*data[:,2] + o
guess=(1,1,1)
params, pcov = curve_fit(func, a1[::2, :2], a1[:,1], guess)
print (params)
And I am getting the following error message:
Traceback (most recent call last):
File "fitcurve.py", line 41, in <module>
params, pcov = curve_fit(func, a1[::2, :2], a1[:,1], guess)
TypeError: list indices must be integers, not tuple
Would you please tell me what I am doing wrong?
Just to make it more clear :
I am trying to have Y as my dependent function, so it would be a function of X and Z.
Apparently a1[] is a list and not an array right?
But even when I change it to an array Myarray=np.asarray(a1) I get some other weird messages.
I would appreciate if someone could help me understand the issue here.
Cheers
Here possible errors I noticed in your code:
you want to fit y as function of x,z so the X array you want to sent is probably a1[:, ::2]. But that means that func already gets a m,2 array so here it must be return m*data[:,0] + n*data[:,1] + o
Still I think it should be a two parameter fit not three. You can calculate a possible m, n, o from the according result.
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib import style
from random import random
style.use('ggplot')
from scipy.optimize import curve_fit
""" make some data and save to file """
data = []
a, b, s = 1.233, -2.17, 5.2
for i in range(88):
x = 10 * (2 * random() - 1)
y = 10 * (2 * random() - 1)
z = a * x + b * y+ s * (2 * random() - 1) * 0.5
data += [[x, y, z]]
data = np.array(data)
np.savetxt("data.txt", data)
""" get the data and use unpack to directly write into x,y,z variables"""
xData, yData, zData = np.loadtxt("data.txt", unpack=True)
"""...while I actally need the packed version as well, so I could load again"""
#allData = np.loadtxt("data.txt")
"""...or..."""
allData = np.array( zip(xData, yData, zData) )
def func(data, m, o):
return m * data[:,0] + o * data[:, 1]
guess = (1, 1)
params, pcov = curve_fit(func, allData[:, ::2], allData[:,1], guess)
""" showing data and fit result"""
x = np.linspace(-10, 10, 10)
y = np.linspace(-10, 10, 10)
X, Y = np.meshgrid(x, y)
Z = -params[0] / params[1] * X + 1 / params[1] * Y
fig1 = plt.figure(1)
ax = fig1.add_subplot( 1, 1, 1, projection='3d')
ax.scatter(xData, yData, zData)
ax.plot_wireframe(X, Y, Z, color='#4060a6', alpha=0.6 )
ax.set_title(
"({:1.2f},{:1.2f})".format(
-params[0] / params[1], 1 / params[1]
)
)
plt.show()
Note that while you fitted y = m * x + o * z I plot a wireframe of z = a * x + b * y mit b = 1 / o und a = -m / o, i.e. n = 1. You can rescale your m, n, o accordingly.
Here is an example of linear multiple regression of a flat surface:
import numpy as np
# the below "columns" of data could be i.e., x, y**2, sin(x), log(y), etc.
# numpy's array transpose can also be handy in formatting the data in this way
# first "column" will regress to an offset parameter (a * 1.0, or just a)
# second "column" will regress the X data (b * X)
# third "column" will regress the Y data (c * Y)
indepData = np.array([
[1.0, 11.0, 0.1], # first data point
[1.0 ,22.0, 0.2], # second data point
[1.0, 33.0, 0.3], # third data point
[1.0, 35.0, 0.5] # fourth data point
])
# Z data
depData = np.array([5.0, 60.0, 70.0, 185.0])
coeffs = np.linalg.lstsq(indepData, depData)[0]
print(coeffs)
X = 25.0
Y = 0.2
a = coeffs[0]
b = coeffs[1]
c = coeffs[2]
regressionPredictedValue = a + b*X + c*Y
print(regressionPredictedValue)

How to fit a two-term exponential in python?

I need to fit some data with a two-term exponential following the equation a*exp(b*x)+c*exp(d*x). In matlab, it's as easy as changing the 1 to a 2 in polyfit to go from a one-term exponential to a two-term. I haven't found a simple solution to do it in python and was wondering if there even was one? I tried using curve_fit but it is giving me lots of trouble and after scouring the internet I still haven't found anything useful. Any help is appreciated!
Yes you can use curve_fit from scipy. Here is an example for your specific fit function.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
x = np.linspace(0,4,50) # Example data
def func(x, a, b, c, d):
return a * np.exp(b * x) + c * np.exp(d * x)
y = func(x, 2.5, 1.3, 0.5, 0.5) # Example exponential data
# Here you give the initial parameters for a,b,c which Python then iterates over
# to find the best fit
popt, pcov = curve_fit(func,x,y,p0=(1.0,1.0,1.0,1.0))
print(popt) # This contains your three best fit parameters
p1 = popt[0] # This is your a
p2 = popt[1] # This is your b
p3 = popt[2] # This is your c
p4 = popt[3] # This is your d
residuals = y - func(x,p1,p2,p3,p4)
fres = sum( (residuals**2)/func(x,p1,p2,p3,p4) ) # The chi-sqaure of your fit
print(fres)
""" Now if you need to plot, perform the code below """
curvey = func(x,p1,p2,p3,p4) # This is your y axis fit-line
plt.plot(x, curvey, 'red', label='The best-fit line')
plt.scatter(x,y, c='b',label='The data points')
plt.legend(loc='best')
plt.xlabel('x')
plt.ylabel('y')
plt.show()
You can do it with leastsq. Something like:
from numpy import log, exp
from scipy.optimize.minpack import leastsq
## regression function
def _exp(a, b, c, d):
"""
Exponential function y = a * exp(b * x) + c * exp(d * x)
"""
return lambda x: a * exp(b * x) + c * exp(d * x)
## interpolation
def interpolate(x, df, fun=_exp):
"""
Interpolate Y from X based on df, a dataframe with columns 'x' and 'y'.
"""
resid = lambda p, x, y: y - fun(*p)(x)
ls = leastsq(resid, [1.0, 1.0, 1.0, 1.0], args=(df['x'], df['y']))
a, b, c, d = ls[0]
y = fun(a, b, c, d)(x)
return y

Extracting 1D ellipse from 2D image

I've trying to simulate a 2D Sérsic profile and then testing an extraction routine on it. However, when I do a test by extracting all the points lying along an ellipse supposedly aligned with an image, I get a periodic function. It is meant to be a straight line since all points along the ellipse should have equal intensity, although there will be a small amount of deviation due to rounding errors in the rough coordinate estimation (get_I()).
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import NearestNDInterpolator
def rotate(x, y, angle):
x1 = x*np.cos(angle) + y*np.sin(angle)
y1 = y*np.cos(angle) - x*np.sin(angle)
return x1, y1
def sersic_1d(R, mu0, h, n, zp=0):
exponent = (R / h) ** (1 / n)
I0 = np.exp((zp - mu0) / 2.5)
return I0 * np.exp(-1.* exponent)
def sersic_2d(x, y, e, i, mu0, h, n, zp=0):
xp, yp = rotate(x, y, i)
alpha = np.arctan2(yp, xp * (1-e))
a = xp / np.cos(alpha)
b = a * (1 - e)
# R2 = (a*a) + ((1 - (e*e)) * yp*yp)
return sersic_1d(a, mu0, h, n, zp)
def ellipse(x0, y0, a, e, i, theta):
b = a * (1 - e)
x = a * np.cos(theta)
y = b * np.sin(theta)
x, y = rotate(x, y, i)
return x + x0, y + y0
def get_I(x, y, Z):
return Z[np.round(x).astype(int), np.round(y).astype(int)]
if __name__ == '__main__':
n = np.linspace(-100,100,1000)
nx, ny = np.meshgrid(n, n)
Z = sersic_2d(nx, ny, 0.5, 0., 0, 50, 1, 25)
theta = np.linspace(0, 2*np.pi, 1000.)
a = 100.
e = 0.5
i = np.pi / 4.
x, y = ellipse(0, 0, a, e, i, theta)
I = get_I(x, y, Z)
plt.plot(I)
# plt.imshow(Z)
plt.show()
However, What I actually get is a massive periodic function. I've checked the alignment and it's correct and the float-> int rounding errors can't account for this kind of shift?
Any ideas?
There are two things that strike me as odd, one of which for sure is not what you wanted, the other I'm not sure about because astronomy is not my field of expertise.
The first is in your function get_I:
def get_I(x, y, Z):
return Z[np.round(x).astype(int), np.round(y).astype(int)]
When you call that function, x an y outline an ellipse, with its center at the origin (0,0). That means x and y both become negative at some point. The indexing you perfom in that function will then take values from the array's last elements, because Z[0,0] is in fact the top left corner of the image (which you plotted, but commented), while Z[-1, -1] is the bottom right corner. What you want is to take the values of Z that are on the ellipse contour, but both have to have the same center. To do that, you would first make sure you use an uneven amount of samples for n (which ultimately defines the shape of Z) and second, you would add an indexing offset:
def get_I(x, y, Z):
offset = Z.shape[0]//2
return Z[np.round(y).astype(int) + offset, np.round(x).astype(int) + offset]
...
n = np.linspace(-100,100,1001) # changed from 1000 to 1001 to ensure a point of origin is present and that the image exhibits point symmetry
Also notice that I changed the order of y and x in get_I: that's because you first index along the rows (for which we usually take the y-coordinate) and only then along the columns (which map to the x-coordinate in most conventions).
The second item that struck me as unusual is that your ellipse has its axes at an angle of pi/4 with respect to the horizontal axis, whereas your sersic (which maps to the 2D array of Z) does not have a tilt at all.
Changing all that, I end up with this code:
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
def rotate(x, y, angle):
x1 = x*np.cos(angle) + y*np.sin(angle)
y1 = y*np.cos(angle) - x*np.sin(angle)
return x1, y1
def sersic_1d(R, mu0, h, n, zp=0):
exponent = (R / h) ** (1 / n)
I0 = np.exp((zp - mu0) / 2.5)
return I0 * np.exp(-1.* exponent)
def sersic_2d(x, y, e, ang, mu0, h, n, zp=0):
xp, yp = rotate(x, y, ang)
alpha = np.arctan2(yp, xp * (1-e))
a = xp / np.cos(alpha)
b = a * (1 - e)
return sersic_1d(a, mu0, h, n, zp)
def ellipse(x0, y0, a, e, i, theta):
b = a * (1 - e) # half of a
x = a * np.cos(theta)
y = b * np.sin(theta)
x, y = rotate(x, y, i) # rotated by 45deg
return x + x0, y + y0
def get_I(x, y, Z):
offset = Z.shape[0]//2
return Z[np.round(y).astype(int) + offset, np.round(x).astype(int) + offset]
#return Z[np.round(y).astype(int), np.round(x).astype(int)]
if __name__ == '__main__':
n = np.linspace(-100,100,1001) # changed
nx, ny = np.meshgrid(n, n)
ang = 0;#np.pi / 4.
Z = sersic_2d(nx, ny, 0.5, ang=0, mu0=0, h=50, n=1, zp=25)
f, ax = plt.subplots(1,2)
dn = n[1]-n[0]
ax[0].imshow(Z, cmap='gray', aspect='equal', extent=[-100-dn/2, 100+dn/2, -100-dn/2, 100+dn/2])
theta = np.linspace(0, 2*np.pi, 1000.)
a = 20. # decreased long axis of ellipse to see the intensity-map closer to the "center of the galaxy"
e = 0.5
x, y = ellipse(0,0, a, e, ang, theta)
I = get_I(x, y, Z)
ax[0].plot(x,y) # easier to see where you want the intensities
ax[1].plot(I)
plt.show()
and this image:
The intensity variations look like quantisation noise to me, with the exception of the peaks, which are due to the asymptote in sersic_1d.

Triangular contour with meshgrid

I'm trying to recreate the diagram (or just a contour plot would be okay) for the Dirichlet distribution that's on Wikipedia using matplotlib and numpy. I am having trouble easily generating a triangular contourf. The first problem is that meshgrid doesn't return a triangle of points. Even if I get a triangle of points, will contourf handle the non-rectangular input?
Here's what I have so far:
#!/usr/bin/env python
from __future__ import division
import matplotlib
matplotlib.use("TkAgg")
matplotlib.rc('text', usetex=True)
matplotlib.rcParams['text.latex.preamble']=r"""\usepackage{amsmath}
"""
import math
import scipy.special
root_three_over_two = np.sqrt(3) / 2
def new_figure():
# 1.45
plt.figure(figsize = [2.6, 2.6 * root_three_over_two], dpi = 1200)
plt.axes([0.05, 0.10, 0.90, 0.90], frameon = False)
xsize = 1.0
ysize = root_three_over_two * xsize
plt.axis([0, xsize, 0, ysize])
resolution = 0.05
R = inclusive_arange(0.0, 1.0, resolution)
x, y = np.meshgrid(inclusive_arange(0.0, 1.0, resolution),
inclusive_arange(0.0, 1.0, resolution))
# UNFORTUNATELY x, and y include a lot of points where x+y>1
x = []
y = []
for yy in R:
x.append(list(inclusive_arange(0.0, 1.0 - yy, resolution)))
y.append([yy for xx in R])
print x
print y
z = 1 - x - y
# We can use these to convert to and from the equilateral triangle.
M = [[1, 0.5], [0, root_three_over_two]]
Mi = np.linalg.inv(M)
def dirichlet(x, y, z, a, b, c):
if z < 0:
return 0
return x ** (a - 1) * y ** (b - 1) * z ** (c - 1) \
* math.gamma(a + b + c) \
/ (math.gamma(a) * math.gamma(b) * math.gamma(c))
dirichlet = np.frompyfunc(dirichlet, 6, 1)
for (dirichlet_parm, filename) in [((5.0, 1.5, 2.5), "dir_small.pdf")]:
new_figure()
height = dirichlet(x, y, z, *dirichlet_parm)
M = np.max(height)
cs = plt.contourf(x, y, height, 50)
S = sum(dirichlet_parm)
plt.savefig(filename)
You do not need a rectilinear meshgrid to create a contour plot. You can define the perimeter and perform a Delaunay triangulation. The coordinates, of course will still be rectilinear and (it appears) you'll need to do a transformation. This example should be enough to create a 2D contour. I have produced some 3D surface plots with a non-rectangle perimeter and you get some edge artifacts that can be unsightly. A very fine grid might ameliorate some of these.

Categories