Drawing tangent plot in Python (matplotlib) - python

Today I decided to write simple program in Python, just to practice before exam. Firstly, I wanted to draw sin and cos plot, which wasn't so hard. But then, I decided to challenge myself and draw tangent plot.
import pylab as p
x= p.arange(-1.0,1.0,0.1)
y= (p.sin(2*p.pi*x)) / (p.cos(2*p.pi*x))
p.plot(x,y,'g-',lw=1)
p.show()
It returns... ugh... this:
As you can see, it looks more like ECK plot than tangent plot. Does anyone knows what's wrong?

If you increase the number of points in x,
import pylab as p
import numpy as np
x = p.linspace(-1.0, 1.0, 1000)
y = (p.sin(2 * p.pi * x)) / (p.cos(2 * p.pi * x))
p.plot(x, y, 'g-', lw=1)
p.show()
you get something like this:
Notice how large the y-range is getting. Matplotlib is not able to show you much of the small values in the tangent curve because the range is so large.
The plot can be improved by ignoring the extremely large values near the asymptotes. Using Paul's workaround to handle asymptotes,
import pylab as p
import numpy as np
x = p.linspace(-1.0, 1.0, 1000)
y = (p.sin(2 * p.pi * x)) / (p.cos(2 * p.pi * x))
tol = 10
y[y > tol] = np.nan
y[y < -tol] = np.nan
p.plot(x, y, 'g-', lw=1)
p.show()
you get

import pylab as p
x= p.arange(-1.0,1.0,0.01) # <- 0.01 step size.
y= (p.sin(2*p.pi*x)) / (p.cos(2*p.pi*x))
# y = p.tan(2*p.pi*x)
p.plot(x,y,'g-',lw=1)
p.ylim([-4, 4]) # <- Restricting the ylim so we don't see the ~inf values.
p.show()
This will be the result if you don't set ylim. (the values approach infinity)
Result with setting ylim.

Related

How to create a plot with a repeating color pattern?

For my report, I'm creating a special color plot in jupyter notebook. There are two parameters, x and y.
import numpy as np
x = np.arange(-1,1,0.1)
y = np.arange(1,11,1)
with which I compute a third quantity. Here is an example to demonstrate the concept:
values = []
for i in range(len(y)) :
z = y[i] * x**3
# in my case the value z represents phases of oscillators
# so I will transform the computed values to the intervall [0,2pi)
values.append(z)
values = np.array(values) % 2*np.pi
I'm plotting y vs x. For each y = 1,2,3,4... there will be a horizontal line with total length two. For example: The coordinate (0.5,8) stands for a single point on line 8 at position x = 0.5 and z(0.5,8) is its associated value.
Now I want to represent each point on all ten lines with a unique color that is determined by z(x,y). Since z(x,y) takes only values in [0,2pi) I need a color scheme that starts at zero (for example z=0 corresponds to blue). For increasing z the color continuously changes and in the end at 2pi it takes the same color again (so at z ~ 2pi it becomes blue again).
Does someone know how this can be done in python?
The kind of structure for x, y and z you need, is easier using a meshgrid. Also, to have a lot of x-values between -1 and 1, np.linspace(-1,1,N) divides the range in N even intervals.
Using meshgrid, z can be calculated in one line using numpy's vectorization. This runs much faster.
To set a repeating color, a cyclic colormap such as hsv can be used. There the last color is the same as the starting color.
import numpy as np
from matplotlib import pyplot as plt
x, y = np.meshgrid(np.linspace(-1,1,100), np.arange(1,11,1))
z = (y * x**3) % 2*np.pi
plt.scatter(x, y, c=z, s=6, cmap='hsv')
plt.yticks(range(1,11))
plt.show()
Alternatively, a symmetric colormap could be built taken the colors from and existing map and combining them with the same colors in reverse order.
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.colors as mcolors
colors_orig = plt.cm.viridis_r(np.linspace(0, 1, 128))
# combine the colors with the reversed array and build a new colormap
colors = np.vstack((colors_orig, colors_orig[::-1]))
symcmap = mcolors.LinearSegmentedColormap.from_list('symcmap', colors)
x, y = np.meshgrid(np.linspace(-1,1,100), np.arange(1,11,1))
z = (y * x**3) % 2*np.pi
plt.scatter(x, y, c=z, s=6, cmap=symcmap)
plt.yticks(range(1,11))
plt.show()
Multicolored lines are somewhat more complicated than just scatter plots. The docs have an example using LineCollections. Here is the adapted code. Note that the line segments are colored using their start point, so make sure there are enough x values. Also, the x and y limits aren't set automatically any more.
The code also adds a colorbar to illustrate how the colors map to the z values. Some interesting code from Jake VanderPlas shows how to create ticks for multiples of π.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.collections import LineCollection
# code from Jake VanderPlas
def format_func(value, tick_number):
# find number of multiples of pi/2
N = int(np.round(2 * value / np.pi))
if N == 0:
return "0"
elif N == 1:
return r"$\pi/2$"
elif N == 2:
return r"$\pi$"
elif N % 2 > 0:
return r"${0}\pi/2$".format(N)
else:
return r"${0}\pi$".format(N // 2)
x = np.linspace(-1, 1, 500)
y_max = 10
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(0, 2 * np.pi)
for y in range(1, y_max + 1):
z = (y * x ** 3) % 2 * np.pi
y_array = y * np.ones_like(x)
points = np.array([x, y_array]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, cmap='hsv', norm=norm)
lc.set_array(z) # Set the values used for colormapping
lc.set_linewidth(2)
line = plt.gca().add_collection(lc)
# plt.scatter(x, y_array, c=z, s=10, norm=norm, cmap='hsv')
cbar = plt.colorbar(line) # , ticks=[k*np.pi for k in np.arange(0, 2.001, 0.25)])
cbar.locator = plt.MultipleLocator(np.pi / 2)
cbar.minor_locator = plt.MultipleLocator(np.pi / 4)
cbar.formatter = plt.FuncFormatter(format_func)
cbar.ax.minorticks_on()
cbar.update_ticks()
plt.yticks(range(1, y_max + 1)) # one tick for every y
plt.xlim(x.min(), x.max()) # the LineCollection doesn't force the limits
plt.ylim(0.5, y_max + 0.5)
plt.show()

how to use matplotlib quiver scale

I need to do a series of vector plots. I can get any number of plots with matplotlib's quiver routine. The thing is, quiver autoscales each plot, but I need the vectors in each plot to all represent the same scale. For instance, if 10 km/hr is represented by a vector of 1cm in one plot, then 10km/hr should be represented by a 1cm vector in all plots. (I don't really care if the vector is specifically 1cm. That's just an example.) I thought I could make this happen by adjusting the scale argument separately for each plot. But it doesn't seem to work.
For example, I find the maximum speed in the first plot, mxs1, and then for each plot I do something like
mxspd = np.max(speed[n])
pylab.quiver(x,y,vx[n],vy[n],scale=mxs1/mxspd)
But this does not adjust the lengths of the vectors enough. For instance, in the case I was trying, mxspd is about one half of mxs1, so the vectors in plot n should be about half as long as the ones in the first plot. But the vectors in the two plots have pretty much the same lengths.
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
x, y = np.mgrid[0:20, 0:25]
u = np.sin(2 *x * np.pi / 20)
v = np.cos(2 * y * np.pi / 25)
fig, (ax_l, ax_r) = plt.subplots(1, 2, figsize=(8, 4))
ax_r.quiver(x, y, u, v, scale=5, scale_units='inches')
ax_l.quiver(x, y, 2*u, 2*v, scale=5, scale_units='inches')
ax_l.set_title('2x')
ax_r.set_title('1x')
See the documentation for explainations of the scale and scale_units kwargs.
The answer above matches the scales of the two plots a priori.
The solution below matches the scales a posteri by taking the automatically determined scale from the first plot and applying it to the second.
This may not always work as it uses private calls, but solved my problem.
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
x, y = np.mgrid[0:20, 0:25]
u = np.sin(2 *x * np.pi / 20)
v = np.cos(2 * y * np.pi / 25)
fig, (ax_l, ax_r) = plt.subplots(1, 2, figsize=(8, 4))
Q = ax_r.quiver(x, y, u, v, scale=None, scale_units='inches')
Q._init()
assert isinstance(Q.scale, float)
ax_l.quiver(x, y, 2*u, 2*v, scale=Q.scale, scale_units='inches')
ax_l.set_title('2x')
ax_r.set_title('1x')
This issue of scaling confused me for ages. In general, I want to have a key that says, "this length of arrow is equivalent to this velocity" , and this means:
Nice to use quiverkey to add the key
Sometimes it can be helpful to set angles='xy', scale_units='xy', to plot the arrows scaling with x and y units, rather than fixed units. For example, if the axes are in meters and you want m/s
Thus if we assume the fake u,v data has units of m/s, I adapted the answer of tacaswell as follows (here the scale is fixed to 1).
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
x, y = np.mgrid[0:20, 0:25]
u = np.sin(2 *x * np.pi / 20)
v = np.cos(2 * y * np.pi / 25)
fig, (ax_l, ax_r) = plt.subplots(1, 2, figsize=(8, 4))
# set the key length
lkey=1
#set the scale factor
scalef=1
q_l=ax_l.quiver(x, y, 2*u, 2*v, angles='xy', scale_units='xy' , scale=scalef)
ax_l.quiverkey(q_l, X=0.3, Y=1.1, U=lkey,
label='Quiver key, length = '+str(lkey)+' m/s', labelpos='E')
q_r=ax_r.quiver(x, y, u, v, angles='xy', scale_units='xy', scale=scalef )
ax_r.quiverkey(q_r, X=0.3, Y=1.1, U=lkey,
label='Quiver key, length = '+str(lkey)+' m/s', labelpos='E')
ax_l.set_title('2x')
ax_r.set_title('1x')
giving:
That said, in general you might want to use the automatic scale factor and then adjust the key as this prevents overlap of arrows and manual fiddling with the scale factor. To illustrate this further, I'll scale the data by a factor of 5:
u = 5*np.sin(2 *x * np.pi / 20)
v = 5*np.cos(2 * y * np.pi / 25)
Now the left panel will have max velocity of 10 m/s (it is doubled)
So if we set the following options:
lkey=10
#set the scale factor to None for autoscaling
scalef=None
Then we get the following:
so here the plots look the same but the arrow on the left key is correctly half the length of that on the right.

Plot equation showing a circle

The following formula is used to classify points from a 2-dimensional space:
f(x1,x2) = np.sign(x1^2+x2^2-.6)
All points are in space X = [-1,1] x [-1,1] with a uniform probability of picking each x.
Now I would like to visualize the circle that equals:
0 = x1^2+x2^2-.6
The values of x1 should be on the x-axis and values of x2 on the y-axis.
It must be possible but I have difficulty transforming the equation to a plot.
You can use a contour plot, as follows (based on the examples at http://matplotlib.org/examples/pylab_examples/contour_demo.html):
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1.0, 1.0, 100)
y = np.linspace(-1.0, 1.0, 100)
X, Y = np.meshgrid(x,y)
F = X**2 + Y**2 - 0.6
plt.contour(X,Y,F,[0])
plt.show()
This yields the following graph
Lastly, some general statements:
x^2 does not mean what you think it does in python, you have to use x**2.
x1 and x2 are terribly misleading (to me), especially if you state that x2 has to be on the y-axis.
(Thanks to Dux) You can add plt.gca().set_aspect('equal') to make the figure actually look circular, by making the axis equal.
The solution of #BasJansen certainly gets you there, it's either very inefficient (if you use many grid points) or inaccurate (if you use only few grid points).
You can easily draw the circle directly. Given 0 = x1**2 + x**2 - 0.6 it follows that x2 = sqrt(0.6 - x1**2) (as Dux stated).
But what you really want to do is to transform your cartesian coordinates to polar ones.
x1 = r*cos(theta)
x2 = r*sin(theta)
if you use these substitions in the circle equation you will see that r=sqrt(0.6).
So now you can use that for your plot:
import numpy as np
import matplotlib.pyplot as plt
# theta goes from 0 to 2pi
theta = np.linspace(0, 2*np.pi, 100)
# the radius of the circle
r = np.sqrt(0.6)
# compute x1 and x2
x1 = r*np.cos(theta)
x2 = r*np.sin(theta)
# create the figure
fig, ax = plt.subplots(1)
ax.plot(x1, x2)
ax.set_aspect(1)
plt.show()
Result:
How about drawing x-values and calculating the corresponding y-values?
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 100, endpoint=True)
y = np.sqrt(-x**2. + 0.6)
plt.plot(x, y)
plt.plot(x, -y)
produces
This can obviously be made much nicer, but this is only for demonstration...
# x**2 + y**2 = r**2
r = 6
x = np.linspace(-r,r,1000)
y = np.sqrt(-x**2+r**2)
plt.plot(x, y,'b')
plt.plot(x,-y,'b')
plt.gca().set_aspect('equal')
plt.show()
produces:
Plotting a circle using complex numbers
The idea: multiplying a point by complex exponential () rotates the point on a circle
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
num_pts=20 # number of points on the circle
ps = np.arange(num_pts+1)
# j = np.sqrt(-1)
pts = np.exp(2j*np.pi/num_pts *(ps))
fig, ax = plt.subplots(1)
ax.plot(pts.real, pts.imag , '-o')
ax.set_aspect(1)
plt.show()

Inline labels in Matplotlib

In Matplotlib, it's not too tough to make a legend (example_legend(), below), but I think it's better style to put labels right on the curves being plotted (as in example_inline(), below). This can be very fiddly, because I have to specify coordinates by hand, and, if I re-format the plot, I probably have to reposition the labels. Is there a way to automatically generate labels on curves in Matplotlib? Bonus points for being able to orient the text at an angle corresponding to the angle of the curve.
import numpy as np
import matplotlib.pyplot as plt
def example_legend():
plt.clf()
x = np.linspace(0, 1, 101)
y1 = np.sin(x * np.pi / 2)
y2 = np.cos(x * np.pi / 2)
plt.plot(x, y1, label='sin')
plt.plot(x, y2, label='cos')
plt.legend()
def example_inline():
plt.clf()
x = np.linspace(0, 1, 101)
y1 = np.sin(x * np.pi / 2)
y2 = np.cos(x * np.pi / 2)
plt.plot(x, y1, label='sin')
plt.plot(x, y2, label='cos')
plt.text(0.08, 0.2, 'sin')
plt.text(0.9, 0.2, 'cos')
Update: User cphyc has kindly created a Github repository for the code in this answer (see here), and bundled the code into a package which may be installed using pip install matplotlib-label-lines.
Pretty Picture:
In matplotlib it's pretty easy to label contour plots (either automatically or by manually placing labels with mouse clicks). There does not (yet) appear to be any equivalent capability to label data series in this fashion! There may be some semantic reason for not including this feature which I am missing.
Regardless, I have written the following module which takes any allows for semi-automatic plot labelling. It requires only numpy and a couple of functions from the standard math library.
Description
The default behaviour of the labelLines function is to space the labels evenly along the x axis (automatically placing at the correct y-value of course). If you want you can just pass an array of the x co-ordinates of each of the labels. You can even tweak the location of one label (as shown in the bottom right plot) and space the rest evenly if you like.
In addition, the label_lines function does not account for the lines which have not had a label assigned in the plot command (or more accurately if the label contains '_line').
Keyword arguments passed to labelLines or labelLine are passed on to the text function call (some keyword arguments are set if the calling code chooses not to specify).
Issues
Annotation bounding boxes sometimes interfere undesirably with other curves. As shown by the 1 and 10 annotations in the top left plot. I'm not even sure this can be avoided.
It would be nice to specify a y position instead sometimes.
It's still an iterative process to get annotations in the right location
It only works when the x-axis values are floats
Gotchas
By default, the labelLines function assumes that all data series span the range specified by the axis limits. Take a look at the blue curve in the top left plot of the pretty picture. If there were only data available for the x range 0.5-1 then then we couldn't possibly place a label at the desired location (which is a little less than 0.2). See this question for a particularly nasty example. Right now, the code does not intelligently identify this scenario and re-arrange the labels, however there is a reasonable workaround. The labelLines function takes the xvals argument; a list of x-values specified by the user instead of the default linear distribution across the width. So the user can decide which x-values to use for the label placement of each data series.
Also, I believe this is the first answer to complete the bonus objective of aligning the labels with the curve they're on. :)
label_lines.py:
from math import atan2,degrees
import numpy as np
#Label line with line2D label data
def labelLine(line,x,label=None,align=True,**kwargs):
ax = line.axes
xdata = line.get_xdata()
ydata = line.get_ydata()
if (x < xdata[0]) or (x > xdata[-1]):
print('x label location is outside data range!')
return
#Find corresponding y co-ordinate and angle of the line
ip = 1
for i in range(len(xdata)):
if x < xdata[i]:
ip = i
break
y = ydata[ip-1] + (ydata[ip]-ydata[ip-1])*(x-xdata[ip-1])/(xdata[ip]-xdata[ip-1])
if not label:
label = line.get_label()
if align:
#Compute the slope
dx = xdata[ip] - xdata[ip-1]
dy = ydata[ip] - ydata[ip-1]
ang = degrees(atan2(dy,dx))
#Transform to screen co-ordinates
pt = np.array([x,y]).reshape((1,2))
trans_angle = ax.transData.transform_angles(np.array((ang,)),pt)[0]
else:
trans_angle = 0
#Set a bunch of keyword arguments
if 'color' not in kwargs:
kwargs['color'] = line.get_color()
if ('horizontalalignment' not in kwargs) and ('ha' not in kwargs):
kwargs['ha'] = 'center'
if ('verticalalignment' not in kwargs) and ('va' not in kwargs):
kwargs['va'] = 'center'
if 'backgroundcolor' not in kwargs:
kwargs['backgroundcolor'] = ax.get_facecolor()
if 'clip_on' not in kwargs:
kwargs['clip_on'] = True
if 'zorder' not in kwargs:
kwargs['zorder'] = 2.5
ax.text(x,y,label,rotation=trans_angle,**kwargs)
def labelLines(lines,align=True,xvals=None,**kwargs):
ax = lines[0].axes
labLines = []
labels = []
#Take only the lines which have labels other than the default ones
for line in lines:
label = line.get_label()
if "_line" not in label:
labLines.append(line)
labels.append(label)
if xvals is None:
xmin,xmax = ax.get_xlim()
xvals = np.linspace(xmin,xmax,len(labLines)+2)[1:-1]
for line,x,label in zip(labLines,xvals,labels):
labelLine(line,x,label,align,**kwargs)
Test code to generate the pretty picture above:
from matplotlib import pyplot as plt
from scipy.stats import loglaplace,chi2
from labellines import *
X = np.linspace(0,1,500)
A = [1,2,5,10,20]
funcs = [np.arctan,np.sin,loglaplace(4).pdf,chi2(5).pdf]
plt.subplot(221)
for a in A:
plt.plot(X,np.arctan(a*X),label=str(a))
labelLines(plt.gca().get_lines(),zorder=2.5)
plt.subplot(222)
for a in A:
plt.plot(X,np.sin(a*X),label=str(a))
labelLines(plt.gca().get_lines(),align=False,fontsize=14)
plt.subplot(223)
for a in A:
plt.plot(X,loglaplace(4).pdf(a*X),label=str(a))
xvals = [0.8,0.55,0.22,0.104,0.045]
labelLines(plt.gca().get_lines(),align=False,xvals=xvals,color='k')
plt.subplot(224)
for a in A:
plt.plot(X,chi2(5).pdf(a*X),label=str(a))
lines = plt.gca().get_lines()
l1=lines[-1]
labelLine(l1,0.6,label=r'$Re=${}'.format(l1.get_label()),ha='left',va='bottom',align = False)
labelLines(lines[:-1],align=False)
plt.show()
#Jan Kuiken's answer is certainly well-thought and thorough, but there are some caveats:
it does not work in all cases
it requires a fair amount of extra code
it may vary considerably from one plot to the next
A much simpler approach is to annotate the last point of each plot. The point can also be circled, for emphasis. This can be accomplished with one extra line:
import matplotlib.pyplot as plt
for i, (x, y) in enumerate(samples):
plt.plot(x, y)
plt.text(x[-1], y[-1], f'sample {i}')
A variant would be to use the method matplotlib.axes.Axes.annotate.
Nice question, a while ago I've experimented a bit with this, but haven't used it a lot because it's still not bulletproof. I divided the plot area into a 32x32 grid and calculated a 'potential field' for the best position of a label for each line according the following rules:
white space is a good place for a label
Label should be near corresponding line
Label should be away from the other lines
The code was something like this:
import matplotlib.pyplot as plt
import numpy as np
from scipy import ndimage
def my_legend(axis = None):
if axis == None:
axis = plt.gca()
N = 32
Nlines = len(axis.lines)
print Nlines
xmin, xmax = axis.get_xlim()
ymin, ymax = axis.get_ylim()
# the 'point of presence' matrix
pop = np.zeros((Nlines, N, N), dtype=np.float)
for l in range(Nlines):
# get xy data and scale it to the NxN squares
xy = axis.lines[l].get_xydata()
xy = (xy - [xmin,ymin]) / ([xmax-xmin, ymax-ymin]) * N
xy = xy.astype(np.int32)
# mask stuff outside plot
mask = (xy[:,0] >= 0) & (xy[:,0] < N) & (xy[:,1] >= 0) & (xy[:,1] < N)
xy = xy[mask]
# add to pop
for p in xy:
pop[l][tuple(p)] = 1.0
# find whitespace, nice place for labels
ws = 1.0 - (np.sum(pop, axis=0) > 0) * 1.0
# don't use the borders
ws[:,0] = 0
ws[:,N-1] = 0
ws[0,:] = 0
ws[N-1,:] = 0
# blur the pop's
for l in range(Nlines):
pop[l] = ndimage.gaussian_filter(pop[l], sigma=N/5)
for l in range(Nlines):
# positive weights for current line, negative weight for others....
w = -0.3 * np.ones(Nlines, dtype=np.float)
w[l] = 0.5
# calculate a field
p = ws + np.sum(w[:, np.newaxis, np.newaxis] * pop, axis=0)
plt.figure()
plt.imshow(p, interpolation='nearest')
plt.title(axis.lines[l].get_label())
pos = np.argmax(p) # note, argmax flattens the array first
best_x, best_y = (pos / N, pos % N)
x = xmin + (xmax-xmin) * best_x / N
y = ymin + (ymax-ymin) * best_y / N
axis.text(x, y, axis.lines[l].get_label(),
horizontalalignment='center',
verticalalignment='center')
plt.close('all')
x = np.linspace(0, 1, 101)
y1 = np.sin(x * np.pi / 2)
y2 = np.cos(x * np.pi / 2)
y3 = x * x
plt.plot(x, y1, 'b', label='blue')
plt.plot(x, y2, 'r', label='red')
plt.plot(x, y3, 'g', label='green')
my_legend()
plt.show()
And the resulting plot:
matplotx (which I wrote) has line_labels() which plots the labels to the right of the lines. It's also smart enough to avoid overlaps when too many lines are concentrated in one spot. (See stargraph for examples.) It does that by solving a particular non-negative-least-squares problem on the target positions of the labels. Anyway, in many cases where there's no overlap to begin with, such as the example below, that's not even necessary.
import matplotlib.pyplot as plt
import matplotx
import numpy as np
# create data
rng = np.random.default_rng(0)
offsets = [1.0, 1.50, 1.60]
labels = ["no balancing", "CRV-27", "CRV-27*"]
x0 = np.linspace(0.0, 3.0, 100)
y = [offset * x0 / (x0 + 1) + 0.1 * rng.random(len(x0)) for offset in offsets]
# plot
with plt.style.context(matplotx.styles.dufte):
for yy, label in zip(y, labels):
plt.plot(x0, yy, label=label)
plt.xlabel("distance [m]")
matplotx.ylabel_top("voltage [V]") # move ylabel to the top, rotate
matplotx.line_labels() # line labels to the right
plt.show()
# plt.savefig("out.png", bbox_inches="tight")
A simpler approach like the one Ioannis Filippidis do :
import matplotlib.pyplot as plt
import numpy as np
# evenly sampled time at 200ms intervals
tMin=-1 ;tMax=10
t = np.arange(tMin, tMax, 0.1)
# red dashes, blue points default
plt.plot(t, 22*t, 'r--', t, t**2, 'b')
factor=3/4 ;offset=20 # text position in view
textPosition=[(tMax+tMin)*factor,22*(tMax+tMin)*factor]
plt.text(textPosition[0],textPosition[1]+offset,'22 t',color='red',fontsize=20)
textPosition=[(tMax+tMin)*factor,((tMax+tMin)*factor)**2+20]
plt.text(textPosition[0],textPosition[1]+offset, 't^2', bbox=dict(facecolor='blue', alpha=0.5),fontsize=20)
plt.show()
code python 3 on sageCell

polar plot in python

I am trying to make a polar plot of 1/t. What I have so far is below (which may be wrong). How can I finish this or make it work?
from pylab import *
import matplotlib.pyplot as plt
theta = arange(0, 6 * pi, 0.01)
def f(theta):
return 1 / theta
I think the problem is that your first value of f(theta) is 1/0 = inf
theta = np.arange(0, 6*np.pi, .01)[1:]
def f(x):
return 1/x
plt.polar(theta, f(theta))
and it looks even nicer if you zoom in:
from mpl_toolkits.axes_grid.axislines import SubplotZero
from matplotlib.ticker import MultipleLocator, FuncFormatter
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
fig = plt.figure(1)
ax = SubplotZero(fig, 111)
fig.add_subplot(ax)
for dir in ax.axis:
ax.axis[dir].set_visible(dir.endswith("zero"))
ax.set_xlim(-.35,.4)
ax.set_ylim(-.25,.45)
ax.set_aspect('equal')
tick_format = lambda x, i: '' if x == 0.0 else '%.1f' % x
for a in [ax.xaxis, ax.yaxis]:
a.set_minor_locator(MultipleLocator(0.02))
a.set_major_formatter(FuncFormatter(tick_format))
theta = np.arange(2*np.pi/3,6*np.pi,0.01)
r = 1 / theta
ax.plot(r*np.cos(theta), r*np.sin(theta), lw=2)
plt.show()
raw_input()
If you want a square plot like Mathematica gave you, the standard plot function just takes an array of x values and an array of y values. Here, f(theta) is the radius, and cos and sin give the x and y directions, so
plt.plot(f(theta)*cos(theta), f(theta)*sin(theta))
should do the job. This will show all of the data, rather than a cleverly chosen subset like in Mathematica, so you might want to limit it. For example:
plt.xlim((-0.35,0.43))
plt.ylim((-0.23,0.45))
gives me the ranges in your version.

Categories

Resources