# Adaptive Plots in Matplotlib

I could not find a solution for adaptive plots in Matplotlib. So, I wrote a small script that can evaluate a function in adaptive step sizes. „Adaptive“ means that the step size adapts to the output of the plot. This is to prevent coarse line segments with corners in the plot.

Here is the simple code for a function of one variable.

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

xc = a
yc = f(xc)
x = [xc]
y = [yc]
h = (b-a)/n
uplim = (2*h)**2
lowlim = uplim/6
eps = (b-a)/100000.
while xc<b:
xcnew=xc+h
if xcnew>=b:
xcnew=b
ycnew=f(xcnew)
d = h**2+(ycnew-yc)**2
if d>uplim and h>eps:
h = h/2
else:
if d<lowlim/3:
h = 2*h
xc = xcnew
yc = ycnew
x.append(xc)
y.append(yc)
return np.array(x),np.array(y)

def func(x):
return np.sin(1/x)

print(x.size,' points generated')
fig,ax = plt.subplots(figsize=(14,10))
ax.grid(True)
ax.plot(x,y);

To apply the function adaptive_ev(), you need a function of one variable, an interval and the target number of plot points. The function will double or half the step size as needed to keep the output under control.

This is a screenshot of the graph generated by the code above. The function is difficult to plot near 0. Thus, adaptive evaluate is the only chance to do that. The routine generated 4270 data points to create this plot.

Euler Math Toolbox has the adaptive evaluation built into the plot2d() routine. So, a similar code would look like this.

>aspect(1.2); plot2d("sin(1/x)",0.001,1,thickness=1.5,color=blue):


Admittedly, the anti-aliasing of Matplotlib is outstanding. I like this library a lot and recommend using it.

I also did a routine for curves in the plane, which are parameterized by two functions for the x- and y-coordinate.

def adaptive_ev2 (fx,fy,a,b,size=1,n=1000):
tc = a
xc = fx(tc)
yc = fy(tc)
x = [xc]
y = [yc]
h = size/n
uplim = (2*h)**2
lowlim = uplim/6
eps = size/100000.
while tc<b:
tcnew=tc+h
if tcnew>=b:
tcnew=b
xcnew=fx(tcnew)
ycnew=fy(tcnew)
d = (xcnew-xc)**2+(ycnew-yc)**2
if d>uplim and h>eps:
h = h/2
else:
if d<lowlim/3:
h = 2*h
xc = xcnew
yc = ycnew
tc = tcnew
x.append(xc)
y.append(yc)
## print((tc,xc,yc,h,d))
return np.array(x),np.array(y)

tc = a
xc = fx(tc)
yc = fy(tc)
x = [xc]
y = [yc]
h = size/n
uplim = (2*h)**2
lowlim = uplim/6
eps = size/100000.
while tc<b:
tcnew=tc+h
if tcnew>=b:
tcnew=b
xcnew=fx(tcnew)
ycnew=fy(tcnew)
d = (xcnew-xc)**2+(ycnew-yc)**2
if d>uplim and h>eps:
h = h/2
else:
if d<lowlim/3:
h = 2*h
xc = xcnew
yc = ycnew
tc = tcnew
x.append(xc)
y.append(yc)
## print((tc,xc,yc,h,d))
return np.array(x),np.array(y)

def funcx(t):
return np.exp(-t/10)*np.cos(t)
def funcy(t):
return np.exp(-t/10)*np.sin(t)

print(x.size,' points generated')
fig,ax = plt.subplots(figsize=(10,10))
ax.grid(True)
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
ax.plot(x,y);

The code follows the same ideas. However, it uses a plot size to set the dimensions of the curve in the plane. You only need a guideline. The step size will be approximately 1/1000 of this size.

Close to 0, the plot should avoid generating too many points. But it should look smooth. This plot generates 14509 points. That is 14 times the given size of 1. But one should be aware that it winds 20 times around the origin!

Euler Math Toolbox does the same, if two functions are fed into plot2d(). It uses a function adaptive() to compute the data points, which works for vector valued functions. This function could be ported to Python easily.

function adaptive (f$: call, a: number, b: number, eps, .. dmin, dmax, dinit, factor) ## Default for eps : 0.01 ## Default for dmin : 1e-05 ## Default for dmax : 100000 ## Default for dinit : 0.1 ## Default for factor : 2 sn=sign(b-a); t=a; sy=f$(t;args());
if sn==0 then return {a,sy}; endif;
if cols(sy)>1 then
error(f$|" does not produce a real or column vector"); endif x=zeros(1,1000); y=zeros(rows(sy),1000); n=1; h=dinit; x[:,1]=t; y[:,1]=sy; repeat repeat tn=t+sn*h; if sn*(tn-b)>=0 then tn=b; endif; syn=f$(tn;args());
d=sqrt(colsum(abs(syn-sy)^2));
if isNAN(d) then break; endif;
if d>eps and h>dmin then h=max(h/factor,dmin);
elseif d<eps/factor then h=min(h*factor,dmax); break;
else break;
endif;
end;
n=n+1;
if n>cols(x) then
x=x|x;
y=y|y;
endif;
x[:,n]=tn; y[:,n]=syn;
sy=syn;
t=tn;
if sn*(t-b)>=0 then break; endif;
end;
return {x[:,1:n],y[:,1:n]};
endfunction

I find Python very aspiring for mathematics. The next step will be to write an adaptive integration using Gauss formulas. Stand by.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.