A question about formatting a DataFrame for use with matplotlib - python

I have a solution to a PDE that I would like to plot. I have seen two ways to do this in the documentation, one of which works for me, and one that doesn't. No error is generated. One simply results in the correct plot (a sin wave), and the other generates a line with slope of 1. The second method may be useful to know in the future, even if I have code that works now. Thanks in advance.
Working solution:
plt.plot(arange(0, 16*pi, Dt), u[:, index])
plt.show()
This is great and super simple! The below method was found in the matplotlib documentation as well, but it yields an incorrect plot. I'd like to know my error:
Non working solution:
df = pd.DataFrame({'t':arange(0, 16*pi, Dt), 'u':u[:,index]})
plt.plot('t', 'u', data=df)
plt.show()
full code for context
from math import sin, cos, pi, fabs, log10, ceil, floor
from numpy import arange, zeros
import pandas as pd
from matplotlib import pyplot as plt
#function applies periodic boundary condition where h is the period
def apply_pbc(f, i, Dx, M, h):
f[i][0] = f[i][int(h/Dx)]
f[i][int((M + Dx)/Dx)] = f[i][int((M + Dx)/Dx - 1)]
return f
# function for finding an index associated with
# a particular data point of interest for plotting
# or other analysis
def find_index(start, stop, step, x):
counter = len(arange(start, stop, step))
for i in arange(counter):
x_i = start + i*step
if abs(x - x_i) < pow(10, -15):
index = i
print("x = ", x_i, "#index j = ", i)
break
return index
#main body
if __name__ == "__main__":
#constants
a = 0.25
b = 0.25
c = 1
#period of boundary conditions
h = 4*pi
#space and time endpoints
M = 4*pi
N = 16*pi
#mesh
Dx = 0.005*4*pi
Dt = (0.25*Dx)/c
#simplification of numeric method
r = (Dt*pow(c,2))/pow(Dx,2)
#get size of data set
rows = len(arange(0, N, Dt))
cols = len(arange(-Dx, M, Dx))
#initiate solution arrays
u = zeros((rows, cols))
v = zeros((rows, cols))
#apply initial conditions
for j in range(cols):
x = -Dx + j*Dx
u[0][j] = cos(x)
v[0][j] = 0
#solve
for i in range(1, rows):
for j in range(1, cols - 1):
u[i][j] = u[i-1][j] + v[i-1][j]*Dt \
+ (a/2)*(u[i-1][j+1] - 2*u[i-1][j] + u[i-1][j-1])
v[i][j] = v[i-1][j] \
+ r*(u[i-1][j+1] - 2*u[i-1][j] + u[i-1][j-1]) \
+ (b/2)*(v[i-1][j+1] - 2*v[i-1][j] + v[i-1][j-1])
apply_pbc(u, i, Dx, M, h)
apply_pbc(v, i, Dx, M, h)
print("done")
#we want to plot the solution u(t,x), where x = pi
index = find_index(-Dx, M + Dx, Dx, pi)
df = pd.DataFrame({'t':arange(0,16*pi, Dt), 'u':u[:,index]})
plt.plot('t', 'x', data=df)
# plt.plot(arange(0, 16*pi, Dt), u[:, index])
plt.show()

From the documentation of plt.plot():
Plotting labelled data
There's a convenient way for plotting objects with labelled data (i.e.
data that can be accessed by index obj['y']). Instead of giving the
data in x and y, you can provide the object in the data parameter and
just give the labels for x and y:
plot('xlabel', 'ylabel', data=obj)
I think there is just a typo in your code. In the full code you provide, this is the line that makes the plot:
plt.plot('t', 'x', data=df)
which does indeed give
while changing to
plt.plot('t', 'u', data=df)
works as expected:
Last bit of the code:
df = pd.DataFrame({'t':arange(0,16*pi, Dt), 'u':u[:,index]})
plt.plot('t', 'x', data=df) # <-- 'x' instead of 'u'
# plt.plot(arange(0, 16*pi, Dt), u[:, index])
plt.show()

Related

Avoiding plotting ODEs divergent solutions ODEint

I'm trying to plot a phase plane and I want it to look nice. However, some solutions of the system of equations diverge because of the initial conditions. Is there some way that I can make a try/except chain in order when the solution diverges it doesn't plot it. Here is my code:
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import odeint
import pylab as pl
def aux_func(x):
y = x[0]-x[1]
if (np.abs(y) <= 1):
f = y**3 + 0.5*y
else:
f = 2*y - np.sign(y)
return f
def function(x,t):
x1_dot = x[1]
x2_dot = -x[1] - aux_func(x)
return [x1_dot,x2_dot]
ts = np.linspace(0, 20, 300)
ic_1 = np.linspace(-1,1,10)
ic_2 = np.linspace(-1,1,10)
for r1 in ic_1:
for r2 in ic_2:
x0 = (r1,r2)
try:
xs = odeint(function, x0, ts)
plt.plot(xs[:,0], xs[:,1],"r-",linewidth=.8)
except:
pass
# Nombre de los ejes, limites,
plt.xlabel("$x_1$", fontsize=12)
plt.ylabel("$x_2$", fontsize=12)
# plt.tick_params(labelsize=10)
# plt.xticks(np.linspace(0,1,11))
# plt.yticks(np.linspace(0,1,11))
plt.xlim(-1, 1)
plt.ylim(-1, 1)
# Grafica el campo vectorial
X1, X2 = np.mgrid[-1:1:20j,-1:1:20j]
u=X2
d= X1-X2
t = np.zeros(np.shape(d))
for i in range(len(d)):
for j in range(len(d[0])):
if np.abs(d[i][j]) > 1:
t[i][j]= 2*d[i][j]-0.5*np.sign(d[i][j])
else:
t[i][j] =d[i][j]**3 + 0.5*d[i][j]
v=-X2-t
pl.quiver(X1, X2, u, v, color = 'b',width = .002)
plt.grid()
plt.title('Plano de Fase Punto 1')
#plt.savefig('FasePunto4.png')
plt.show()
The code is plotting the following:
Appreciate the help.
This can be solved by avoiding the wrong divergences at all, so that there is no need for exception handling.
This is a discontinuous ODE which can lead to unusual effects like a sliding mode. One way to quickly work around that is to mollify the jump by implementing a blending zone where the vector field changes quickly but continuously from one phase to the other (see Unsure about how to use event function in Matlab for other generic work-arounds). The changes for that can be implemented as
def aux_func(x):
def softsign(u): return np.tanh(1e4*u)
y = x[0]-x[1]
h = 0.5*(1+softsign(y**2-1)
# h is about zero for |y|<1 and about 1 for |y|>1
f1 = y**3 + 0.5*y # for |y|<1
f2 = 2*y - softsign(y) # for |y|>1, note the second mollification
return (1-h)*f1+h*f2
With no further changes to the code this gives the plot
Note that pylab is obsolete, all its functionality can also be accessed via plt=matplotlib.pyplot.

Programming of 4th order Runge-Kutta in advection equation in python

%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from math import pi
# wave speed
c = 1
# spatial domain
xmin = 0
xmax = 1
#time domain
m=500; # num of time steps
tmin=0
T = tmin + np.arange(m+1);
tmax=500
n = 50 # num of grid points
# x grid of n points
X, dx = np.linspace(xmin, xmax, n+1, retstep=True);
X = X[:-1] # remove last point, as u(x=1,t)=u(x=0,t)
# for CFL of 0.1
CFL = 0.3
dt = CFL*dx/c
# initial conditions
def initial_u(x):
return np.sin(2*pi*x)
# each value of the U array contains the solution for all x values at each timestep
U = np.zeros((m+1,n),dtype=float)
U[0] = u = initial_u(X);
def derivatives(t,u,c,dx):
uvals = [] # u values for this time step
for j in range(len(X)):
if j == 0: # left boundary
uvals.append((-c/(2*dx))*(u[j+1]-u[n-1]))
elif j == n-1: # right boundary
uvals.append((-c/(2*dx))*(u[0]-u[j-1]))
else:
uvals.append((-c/(2*dx))*(u[j+1]-u[j-1]))
return np.asarray(uvals)
# solve for 500 time steps
for k in range(m):
t = T[k];
k1 = derivatives(t,u,c,dx)*dt;
k2 = derivatives(t+0.5*dt,u+0.5*k1,c,dx)*dt;
k3 = derivatives(t+0.5*dt,u+0.5*k2,c,dx)*dt;
k4 = derivatives(t+dt,u+k3,c,dx)*dt;
U[k+1] = u = u + (k1+2*k2+2*k3+k4)/6;
# plot solution
plt.style.use('dark_background')
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
line, = ax1.plot(X,U[0],color='cyan')
ax1.grid(True)
ax1.set_ylim([-2,2])
ax1.set_xlim([0,1])
def animate(i):
line.set_ydata(U[i])
return line,
I want to program in Python an advection equation which is (∂u/∂t) +c (∂u/∂x) = 0. Time should be discretized with Runge-kutta 4th order. Spatial discretiziation is 2nd order finite difference. When I run my code, I get straight line which transforms into sine wave. But I gave as initial condition sine wave. Why does it start as straight line? And I want to have sine wave moving forward. Do you have any idea on how to get sine wave moving forward? I appreciate your help. Thanks in advance!
While superficially your computation steps are related to the RK4 method, they deviate from the RK4 method and the correct space discretization too much to mention it all.
The traditional way to apply ODE integration methods is to have a function derivatives(t, state, params) and then apply that to compute the Euler step or the RK4 step. In your case it would be
def derivatives(t,u,c,dx):
du = np.zeros(len(u));
p = c/(2*dx);
du[0] = p*(u[1]-u[-1]);
du[1:-1] = p*(u[2:]-u[:-2]);
du[-1] = p*(u[0]-u[-2]);
return du;
Then you can do
X, dx = np.linspace(xmin, xmax, n+1, retstep=True);
X = X[:-1] # remove last point, as u(x=1,t)=u(x=0,t)
m=500; # number of time steps
T = tmin + np.arange(m+1);
U = np.zeros((m+1,n),dtype=float)
U[0] = u = initial_u(X);
for k in range(m):
t = T[k];
k1 = derivatives(t,u,c,dx)*dt;
k2 = derivatives(t+0.5*dt,u+0.5*k1,c,dx)*dt;
k3 = derivatives(t+0.5*dt,u+0.5*k2,c,dx)*dt;
k4 = derivatives(t+dt,u+k3,c,dx)*dt;
U[k+1] = u = u + (k1+2*k2+2*k3+k4)/6;
This uses dt as computed as the primary variable in the time stepping, then constructs the arithmetic sequence from tmin with step dt. Other ways are possible, but one has to make tmax and the number of time steps compatible.
The computation up to this point should now be successful and can be used in the animation. In my understanding, you do not produce a new plot in each frame, you only draw the graph once and after that just change the line data
# animate the time data
line, = ax1.plot(X,U[0],color='cyan')
ax1.grid(True)
ax1.set_ylim([-2,2])
ax1.set_xlim([0,1])
def animate(i):
line.set_ydata(U[i])
return line,
etc.

Graphing polynomials

With some help I have produced the following code. Below are some of the desired outputs for given inputs. However I am having some trouble completing the last task of this code. Looking for some help with this, any guidance or help is greatly appreciated, thanks!
flops = 0
def add(x1, x2):
global flops
flops += 1
return x1 + x2
def multiply(x1, x2):
global flops
flops += 1
return x1 * x2
def poly_horner(A, x):
global flops
flops = 0
p = A[-1]
i = len(A) - 2
while i >= 0:
p = add(multiply(p, x), A[i])
i -= 1
return p
def poly_naive(A, x):
global flops
p = 0
flops = 0
for i, a in enumerate(A):
xp = 1
for _ in range(i):
xp = multiply(xp, x)
p = add(p, multiply(xp, a))
return p
Given the following inputs, I got the following outputs:
poly_horner([1,2,3,4,5], 2)
129
print(flops)
8
poly_naive([1,2,3,4,5, 2])
129
print(flops)[![enter image description here][1]][1]
20
np.polyval([5,4,3,2,1], 2)
129
I assume you want to create a figure, though your question is quite vague...but I have a few minutes to kill while my code runs. Anyway, it seems you MIGHT be having difficulty plotting.
import numpy as np
import pylab as pl
x = np.arange(10)
y = x * np.pi
# you can calculate a line of best fit (lobf) using numpy's polyfit function
lobf1 = np.polyfit(x, y, 1) # first degree polynomial
lobf2 = np.polyfit(x, y, 2) # second degree polynomial
lobf3 = np.polyfit(x, y, 3) # third degree polynomial
# you can now use the lines of best fit to calculate the
# value anywhere within the domain using numpy's polyval function
# FIRST, create a figure and a plotting axis within the fig
fig = pl.figure(figsize=(3.25, 2.5))
ax0 = fig.add_subplot(111)
# now use polyval to calculate your y-values at every x
x = np.arange(0, 20, 0.1)
ax0.plot(x, np.polyval(lobf1, x), 'k')
ax0.plot(x, np.polyval(lobf2, x), 'b')
ax0.plot(x, np.polyval(lobf3, x), 'r')
# add a legend for niceness
ax0.legend(('Degree 1', 'Degree 2', 'Degree 3'), fontsize=8, loc=2)
# you can label the axes whatever you like
ax0.set_ylabel('My y-label', fontsize=8)
ax0.set_xlabel('My x-label', fontsize=8)
# you can show the figure on your screen
fig.show()
# and you can save the figure to your computer in different formats
# specifying bbox_inches='tight' helps eliminate unnecessary whitespace around
# the axis when saving...it just looks better this way.
pl.savefig('figName.png', dpi=500, bbox_inches='tight')
pl.savefig('figName.pdf', bbox_inches='tight')
# don't forget to close the figure
pl.close('all')

using trapezoidal numerical integration in python

I need to integrate this function using trapezoidal rule in python:
theta = .518/r^2 * dr/(sqrt(2*1.158 + 2/r - .518^2/2r^2))
I have written my code and I should be seeing an ellipsoidal structure when plotted. theta should run from 0 to 2pi and r_min = .16 & r_max = .702
import numpy as np
import matplotlib.pyplot as plt
def trapezoidal(f, a, b, n):
h = float(b-a)/n
result = 0.5*f(a) + 0.5*f(b)
for i in range(1, n):
result += f(a + i*h)
result *= h
return result
intg =[]
v = lambda r: (0.5108/(r**2))* (1./np.sqrt(2*1.158+(2/r)-.5108**2/(2*r**2)))
n = np.arange(1,1000,100)
theta = np.arange(0,2*np.pi,100)
for j in n:
numerical = trapezoidal(v, .16,.702 , j)
intg.append(numerical)
plt.plot(numerical,theta)
plt.show()
I am doing some very elementary mistake I guess, because I am getting no plot out of it. I think the trapezoidal routine is correct, because it worked for other functions. your help is very appreciated
Alternatively, you could use quadpy (a project of mine).
import numpy as np
import quadpy
val = quadpy.line_segment.integrate_split(
lambda r: 0.5108/r**2 / np.sqrt(2*1.158 + 2/r - 0.5108**2/(2*r**2)),
0.15, 0.702, 100,
quadpy.line_segment.Trapezoidal()
)
print(val)
gives 0.96194633532. The trapezoidal formula is mostly implemented for historical purposes, however. A better and equally simple rule is quadpy.line_segment.Midpoint. An even better approach is certainly adaptive quadrature
val, error_estimate = quadpy.line_segment.integrate_adaptive(
lambda r: 0.5108/r**2 / np.sqrt(2*1.158 + 2/r - 0.5108**2/(2*r**2)),
[0.15, 0.702],
1.0e-10
)
print(val)
which gives the more accurate 0.961715309492, or even tanh-sinh quadrature
val, error_estimate = quadpy.line_segment.tanh_sinh(
lambda r: 0.5108/r**2 / np.sqrt(2*1.158 + 2/r - 0.5108**2/(2*r**2)),
0.15, 0.702,
1.0e-30
)
print(val)
which gives 0.9617153094932353183036398697528.
There are a couple of issues here.
First one is that the third argument in np.arrange is not the number of values to be generated but the step. This means that theta will have only one value and that n and thus intg will have 10 instead of 100 values.
Assuming that was your intention (100 values) you can do this
intg =[]
v = lambda r: (0.5108/(r**2))* (1./np.sqrt(2*1.158+(2/r)-.5108**2/(2*r**2)))
n = np.arange(1,1000,10)
theta = np.arange(0,2*np.pi,2*np.pi/100)
#print theta
for j in n:
numerical = trapezoidal(v, .16,.702 , j)
intg.append(numerical)
Then you're plotting numerical which is basically a single number and what you probably wanted to plot was the integral value intg - to do so you also need to convert intg from a list into np.array:
intg = np.array(intg)
With these changes the program works as intended,
plt.plot(intg,theta)
plt.show()
If you print the lengths of your numerical and theta, you will see that they are empty lists/arrays.
Try the following:
import numpy as np
import matplotlib.pyplot as plt
def trapezoidal(f, a, b, n):
h = float(b-a)/n
result = 0.5*f(a) + 0.5*f(b)
for i in range(1, n):
result += f(a + i*h)
result *= h
return result
intg =[]
v = lambda r: (0.5108/(r**2))* (1./np.sqrt(2*1.158+(2/r)-.5108**2 /(2*r**2)))
n = np.arange(1, 1001)
theta = np.linspace(0,2.*np.pi,1000)
for j in n:
numerical = trapezoidal(v, .16,.702 , j)
intg.append(numerical)
plt.plot(intg,theta)
plt.show()

Separating gaussian components of a curve using python

I am trying to deblend the emission lines of low resolution spectrum in order to get the gaussian components. This plot represents the kind of data I am using:
After searching a bit, the only option I found was the application of the gauest function from the kmpfit package (http://www.astro.rug.nl/software/kapteyn/kmpfittutorial.html#gauest). I have copied their example but I cannot make it work.
I wonder if anyone could please offer me any alternative to do this or how to correct my code:
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize
def CurveData():
x = np.array([3963.67285156, 3964.49560547, 3965.31835938, 3966.14111328, 3966.96362305,
3967.78637695, 3968.60913086, 3969.43188477, 3970.25463867, 3971.07714844,
3971.89990234, 3972.72265625, 3973.54541016, 3974.36791992, 3975.19067383])
y = np.array([1.75001533e-16, 2.15520995e-16, 2.85030769e-16, 4.10072843e-16, 7.17558032e-16,
1.27759917e-15, 1.57074192e-15, 1.40802933e-15, 1.45038722e-15, 1.55195653e-15,
1.09280316e-15, 4.96611341e-16, 2.68777266e-16, 1.87075114e-16, 1.64335999e-16])
return x, y
def FindMaxima(xval, yval):
xval = np.asarray(xval)
yval = np.asarray(yval)
sort_idx = np.argsort(xval)
yval = yval[sort_idx]
gradient = np.diff(yval)
maxima = np.diff((gradient > 0).view(np.int8))
ListIndeces = np.concatenate((([0],) if gradient[0] < 0 else ()) + (np.where(maxima == -1)[0] + 1,) + (([len(yval)-1],) if gradient[-1] > 0 else ()))
X_Maxima, Y_Maxima = [], []
for index in ListIndeces:
X_Maxima.append(xval[index])
Y_Maxima.append(yval[index])
return X_Maxima, Y_Maxima
def GaussianMixture_Model(p, x, ZeroLevel):
y = 0.0
N_Comps = int(len(p) / 3)
for i in range(N_Comps):
A, mu, sigma = p[i*3:(i+1)*3]
y += A * np.exp(-(x-mu)*(x-mu)/(2.0*sigma*sigma))
Output = y + ZeroLevel
return Output
def Residuals_GaussianMixture(p, x, y, ZeroLevel):
return GaussianMixture_Model(p, x, ZeroLevel) - y
Wave, Flux = CurveData()
Wave_Maxima, Flux_Maxima = FindMaxima(Wave, Flux)
EmLines_Number = len(Wave_Maxima)
ContinuumLevel = 1.64191e-16
# Define initial values
p_0 = []
for i in range(EmLines_Number):
p_0.append(Flux_Maxima[i])
p_0.append(Wave_Maxima[i])
p_0.append(2.0)
p1, conv = optimize.leastsq(Residuals_GaussianMixture, p_0[:],args=(Wave, Flux, ContinuumLevel))
Fig = plt.figure(figsize = (16, 10))
Axis1 = Fig.add_subplot(111)
Axis1.plot(Wave, Flux, label='Emission line')
Axis1.plot(Wave, GaussianMixture_Model(p1, Wave, ContinuumLevel), 'r', label='Fit with optimize.leastsq')
print p1
Axis1.plot(Wave, GaussianMixture_Model([p1[0],p1[1],p1[2]], Wave, ContinuumLevel), 'g:', label='Gaussian components')
Axis1.plot(Wave, GaussianMixture_Model([p1[3],p1[4],p1[5]], Wave, ContinuumLevel), 'g:')
Axis1.set_xlabel( r'Wavelength $(\AA)$',)
Axis1.set_ylabel('Flux' + r'$(erg\,cm^{-2} s^{-1} \AA^{-1})$')
plt.legend()
plt.show()
A typical simplistic way to fit:
def model(p,x):
A,x1,sig1,B,x2,sig2 = p
return A*np.exp(-(x-x1)**2/sig1**2) + B*np.exp(-(x-x2)**2/sig2**2)
def res(p,x,y):
return model(p,x) - y
from scipy import optimize
p0 = [1e-15,3968,2,1e-15,3972,2]
p1,conv = optimize.leastsq(res,p0[:],args=(x,y))
plot(x,y,'+') # data
#fitted function
plot(arange(3962,3976,0.1),model(p1,arange(3962,3976,0.1)),'-')
Where p0 is your initial guess. By the looks of things, you might want to use Lorentzian functions...
If you use full_output=True, you get all kind of info about the fitting. Also check out curve_fit and the fmin* functions in scipy.optimize. There are plenty of wrappers around these around, but often, like here, it's easier to use them directly.

Categories