Python 3D plotting of measurement data - python

I have captured 3D measurement data on a sphere (this is an antenna radiation pattern, so the measurement antenna captured the radiation intensity from each phi,theta direction and logged this value as a function of phi,theta).
I am having great difficulty getting the data represented.
I have tried multiple options. This is the last one I am now trying:
import numpy as np
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
nElevationPoints = 16
nAzimuthPoints = 40
stepSizeRad = 0.05 * np.pi
def r(phi,theta):
radius = 1
return radius
phi = np.arange(0,nAzimuthPoints*stepSizeRad,stepSizeRad)
theta = np.arange(0,nElevationPoints*stepSizeRad,stepSizeRad)
x = (r(phi,theta)*np.outer(r(phi,theta)*np.cos(phi), np.sin(theta)))
y = (-r(phi,theta)*np.outer(np.sin(phi), np.sin(theta)))
z = (r(phi,theta)*np.outer(np.ones(np.size(phi)), np.cos(theta)))
fig = plt.figure(1)
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b')
plt.ioff()
plt.show()
This code in itself is working, and it plots a sphere. Now the thing is, that in accordance with the measurement data, I would actually need the radius not be a constant "1", but corresponding with the radiation intensity measured. So it needs to be a function of phi,theta.
However, as soon as I change the "r" function to anything containing the phi or theta parameter, I get an error about operands that could not be broadcast.
If there's any work around that loops through phi,theta that would be perfectly fine as well.
But I'm stuck now, so I'd appreciate any help :-)
BTW, the reason I went for the above approach is because I couldn't make sense of how the x,y,z should be defined in order to be acceptable to the plot_surface function.
I did manage to generate a scatter plot, by calculating the actual positions (x,y,z) from the phi,theta,intensity data, but this is only a representation by individual points and doesn't generate any well visible antenna radiation pattern plot. For this I assume that a contour plot would be better, but then again I am stuck at either the "r" function call or by understanding how x,y,z should be formatted (the documentation refers to x,y,z needing to be 2D-arrays, but this is beyond my comprehension as x,y,z usually are one dimensional arrays in themselves).
Anyway, looking forward to any help anyone may be willing to give.
-- EDIT --
With #M4rtini 's suggested changes I come to the following:
import numpy as np
from mayavi import mlab
def r(phi,theta):
r = np.sin(phi)**2
return r
phi, theta = np.mgrid[0:2*np.pi:201j, 0:np.pi:101j]
x = r(phi,theta)*np.sin(phi)*np.cos(theta)
y = r(phi,theta)*np.sin(phi)*np.sin(theta)
z = r(phi,theta)*np.cos(phi)
intensity = phi * theta
obj = mlab.mesh(x, y, z, scalars=intensity, colormap='jet')
obj.enable_contours = True
obj.contour.filled_contours = True
obj.contour.number_of_contours = 20
mlab.show()
This works, thanks, #M4rtini, and I now am able to have a phi,theta dependent "r" function.
However, noted that the example now ensures phi and theta to be of the same length (due to the mgrid function). This is not the case in my measurement. When declaring phi and theta separately and of different dimensions, it doesn't work still. So I now will have a look into measurement interpolation.

This might not be the exact answer you were looking for, but if you can accept using intensity values as a mapping of a color, this should work.
Actually, you could probably calculate a specific r here also. But i did not test that.
Using mayavi since it is, in my opinion, far superior than matplotlib for 3D.
import numpy as np
from mayavi import mlab
r = 1.0
phi, theta = np.mgrid[0:np.pi:200j, 0:2*np.pi:101j]
x = r*np.sin(phi)*np.cos(theta)
y = r*np.sin(phi)*np.sin(theta)
z = r*np.cos(phi)
intensity = phi * theta
obj = mlab.mesh(x, y, z, scalars=intensity, colormap='jet')
obj.enable_contours = True
obj.contour.filled_contours = True
obj.contour.number_of_contours = 20
mlab.show()
Output of example script, now this is in a interactive gui. so you can rotate, translate, scale as you please. And even interactively manipulate the data, and the representation options.

Related

To plot graph non linear function

I want to plot graph of this function:
y = 2[1-e^(-x+1)]^2-2
When I plot a linear function, I used this code :
import matplotlib.pyplot as plt
import numpy as np
x = np.array(...)
y = np.array(...)
z = np.polyfit(x, y, 2)
p = np.poly1d(z)
xp = np.linspace(...)
_ = plt.plot(x, y, '.', xp, p(xp), '-')
plt.ylim(0, 200)
plt.show()
When the function is non-linear, it does not works
becasue it hard to find each x,y value.
How can I plot a non-linear function?
I hate to be the one to break this news to you, but polynomials of order greater than one are technically nonlinear too.
When you plot in matplotlib, you're really supplying discreet x and y values at a resolution sufficient to be visually pleasing. In this case, you've chosen xp to determine the points you plot for the parabola. You then call p(xp) to generate an array of y-values at those locations.
There nothing stopping you from generating y-values for your formula of interest using simple numpy functions:
y = 2 * (1 - np.exp(1 - xp))**2 - 2

How to draw "two directions widths line" in matplotlib

How to use matplotlib or pyqtgraph draw plot like this:
Line AB is a two-directions street, green part represents the direction from point A to point B, red part represents B to A, width of each part represents the traffic volume. Widths are measured in point, will not changed at different zoom levels or dpi settings.
This is only an example, in fact I have hunderds of streets. This kind of plot is very common in many traffic softwares. I tried to use matplotlib's patheffect but result is frustrated:
from matplotlib import pyplot as plt
import matplotlib.patheffects as path_effects
x=[0,1,2,3]
y=[1,0,0,-1]
ab_width=20
ba_width=30
fig, axes= plt.subplots(1,1)
center_line, = axes.plot(x,y,color='k',linewidth=2)
center_line.set_path_effects(
[path_effects.SimpleLineShadow(offset=(0, -ab_width/2),shadow_color='g', alpha=1, linewidth=ab_width),
path_effects.SimpleLineShadow(offset=(0, ba_width/2), shadow_color='r', alpha=1, linewidth=ba_width),
path_effects.SimpleLineShadow(offset=(0, -ab_width), shadow_color='k', alpha=1, linewidth=2),
path_effects.SimpleLineShadow(offset=(0, ba_width), shadow_color='k', alpha=1, linewidth=2),
path_effects.Normal()])
axes.set_xlim(-1,4)
axes.set_ylim(-1.5,1.5)
One idea came to me is to take each part of the line as a standalone line, and recalculate it's position when changing zoom level, but it's too complicated and slow.
If there any easy way to use matplotlib or pyqtgraph draw what I want? Any suggestion will be appreciated!
If you can have each independent line, this can be done easily with the fill_between function.
from matplotlib import pyplot as plt
import numpy as np
x=np.array([0,1,2,3])
y=np.array([1,0,0,-1])
y1width=-1
y2width=3
y1 = y + y1width
y2 = y + y2width
fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(x,y, 'k', x,y1, 'k',x,y2, 'k',linewidth=2)
ax.fill_between(x, y1, y, color='g')
ax.fill_between(x, y2, y, color='r')
plt.xlim(-1,4)
plt.ylim(-3,6)
plt.show()
Here I considered the center line as the reference (thus the negative y1width), but could be done differently. The result is then:
If the lines are 'complicated', eventually intersecting at some point, then the keyword argument interpolate=True must be used to fill the crossover regions properly. Another interesting argument probably useful for your use case is where, to condition the region, for instance, where=y1 < 0. For more information you can check out the documentation.
One way of solving your issue is using filled polygons, some linear algebra and some calculus. The main idea is to draw a polygon along your x and y coordinates and along shifted coordinates to close and fill the polygon.
These are my results:
And here is the code:
from __future__ import division
import numpy
from matplotlib import pyplot, patches
def road(x, y, w, scale=0.005, **kwargs):
# Makes sure input coordinates are arrays.
x, y = numpy.asarray(x, dtype=float), numpy.asarray(y, dtype=float)
# Calculate derivative.
dx = x[2:] - x[:-2]
dy = y[2:] - y[:-2]
dy_dx = numpy.concatenate([
[(y[1] - y[0]) / (x[1] - x[0])],
dy / dx,
[(y[-1] - y[-2]) / (x[-1] - x[-2])]
])
# Offsets the input coordinates according to the local derivative.
offset = -dy_dx + 1j
offset = w * scale * offset / abs(offset)
y_offset = y + w * scale
#
AB = zip(
numpy.concatenate([x + offset.real, x[::-1]]),
numpy.concatenate([y + offset.imag, y[::-1]]),
)
p = patches.Polygon(AB, **kwargs)
# Returns polygon.
return p
if __name__ == '__main__':
# Some plot initializations
pyplot.close('all')
pyplot.ion()
# This is the list of coordinates of each point
x = [0, 1, 2, 3, 4]
y = [1, 0, 0, -1, 0]
# Creates figure and axes.
fig, ax = pyplot.subplots(1,1)
ax.axis('equal')
center_line, = ax.plot(x, y, color='k', linewidth=2)
AB = road(x, y, 20, color='g')
BA = road(x, y, -30, color='r')
ax.add_patch(AB)
ax.add_patch(BA)
The first step in calculating how to offset each data point is by calculating the discrete derivative dy / dx. I like to use complex notation to handle vectors in Python, i.e. A = 1 - 1j. This makes life easier for some mathematical operations.
The next step is to remember that the derivative gives the tangent to the curve and from linear algebra that the normal to the tangent is n=-dy_dx + 1j, using complex notation.
The final step in determining the offset coordinates is to ensure that the normal vector has unity size n_norm = n / abs(n) and multiply by the desired width of the polygon.
Now that we have all the coordinates for the points in the polygon, the rest is quite straightforward. Use patches.Polygon and add them to the plot.
This code allows you also to define if you want the patch on top of your route or below it. Just give a positive or negative value for the width. If you want to change the width of the polygon depending on your zoom level and/or resolution, you adjust the scale parameter. It also gives you freedom to add additional parameters to the patches such as fill patterns, transparency, etc.

matplotlib streamplot with a change of variables

Hi I'm an happy user of streamplot module in matplotlib (version 1.3). I've used it for plotting a stream flow in the usual way (vr(t,r),vphi(t,r) are velocity in 2D space, t the time basis and r 1D coordinate where I have measurements of vr and vphi)
from matplotlib import *
speed = np.sqrt(vr * vr + vphi * vphi)
lw = 15 * speed / speed.max()
fig = plt.figure(figsize=(10.,6.0))
ax = fig.add_subplot(111)
ax.streamplot(t, r, vt, vr, linewidth = lw, color='blue')
Now suppose that I've a variable u as function of t (u(t)). It has a monotonic dependence on t, i.e. it varies linearly with t. Now I would like to create the streamplot as a function of (u,r), i.e. something as
ax.streamplot(u,r,vt,vr,linewidth=lw,color='blue')
What I'm wondering is that, considering the algorithm at the basis of streamplot should I actually make a complete change of variables? i.e.
vt1(u) = vt(t)*d(u(t))/dt
vr1(u) = vr(t)*d(u(t))/dt
ax.streamplot(u,r,vt1,vr1,linewidth=lw,color='blue')
Am I right or there is something I do not understand?

How to plot complex numbers (Argand Diagram) using matplotlib

I'd like to create an Argand Diagram from a set of complex numbers using matplotlib.
Are there any pre-built functions to help me do this?
Can anyone recommend an approach?
Image by LeonardoG, CC-SA-3.0
I'm not sure exactly what you're after here...you have a set of complex numbers, and want to map them to the plane by using their real part as the x coordinate and the imaginary part as y?
If so you can get the real part of any python imaginary number with number.real and the imaginary part with number.imag. If you're using numpy, it also provides a set of helper functions numpy.real and numpy.imag etc. which work on numpy arrays.
So for instance if you had an array of complex numbers stored something like this:
In [13]: a = n.arange(5) + 1j*n.arange(6,11)
In [14]: a
Out[14]: array([ 0. +6.j, 1. +7.j, 2. +8.j, 3. +9.j, 4.+10.j])
...you can just do
In [15]: fig,ax = subplots()
In [16]: ax.scatter(a.real,a.imag)
This plots dots on an argand diagram for each point.
edit: For the plotting part, you must of course have imported matplotlib.pyplot via from matplotlib.pyplot import * or (as I did) use the ipython shell in pylab mode.
To follow up #inclement's answer; the following function produces an argand plot that is centred around 0,0 and scaled to the maximum absolute value in the set of complex numbers.
I used the plot function and specified solid lines from (0,0). These can be removed by replacing ro- with ro.
def argand(a):
import matplotlib.pyplot as plt
import numpy as np
for x in range(len(a)):
plt.plot([0,a[x].real],[0,a[x].imag],'ro-',label='python')
limit=np.max(np.ceil(np.absolute(a))) # set limits for axis
plt.xlim((-limit,limit))
plt.ylim((-limit,limit))
plt.ylabel('Imaginary')
plt.xlabel('Real')
plt.show()
For example:
>>> a = n.arange(5) + 1j*n.arange(6,11)
>>> from argand import argand
>>> argand(a)
produces:
EDIT:
I have just realised there is also a polar plot function:
for x in a:
plt.polar([0,angle(x)],[0,abs(x)],marker='o')
If you prefer a plot like the one below
one type of plot
or this one second type of plot
you can do this simply by these two lines (as an example for the plots above):
z=[20+10j,15,-10-10j,5+15j] # array of complex values
complex_plane2(z,1) # function to be called
by using a simple jupyter code from here
https://github.com/osnove/other/blob/master/complex_plane.py
I have written it for my own purposes. Even better it it helps to others.
To get that:
You can use:
cmath.polar to convert a complex number to polar rho-theta coordinates. In the code below this function is first vectorized in order to process an array of complex numbers instead of a single number, this is just to prevent the use an explicit loop.
A pyplot axis with its projection type set to polar. Plot can be done using pyplot.stem or pyplot.scatter.
In order to plot horizontal and vertical lines for Cartesian coordinates there are two possibilities:
Add a Cartesian axis and plot Cartesian coordinates. This solution is described in this question. I don't think it's an easy solution as the Cartesian axis won't be centered, nor it will have the correct scaling factor.
Use the polar axis, and translate Cartesian coordinates for projections into polar coordinates. This is the solution I used to plot the graph above. To not clutter the graph I've shown only one point with its projected Cartesian coordinates.
Code used for the plot above:
from cmath import pi, e, polar
from numpy import linspace, vectorize, sin, cos
from numpy.random import rand
from matplotlib import pyplot as plt
# Arrays of evenly spaced angles, and random lengths
angles = linspace(0, 2*pi, 12, endpoint=False)
lengths = 3*rand(*angles.shape)
# Create an array of complex numbers in Cartesian form
z = lengths * e ** (1j*angles)
# Convert back to polar form
vect_polar = vectorize(polar)
rho_theta = vect_polar(z)
# Plot numbers on polar projection
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
ax.stem(rho_theta[1], rho_theta[0])
# Get a number, find projections on axes
n = 11
rho, theta = rho_theta[0][n], rho_theta[1][n]
a = cos(theta)
b = sin(theta)
rho_h, theta_h = abs(a)*rho, 0 if a >= 0 else -pi
rho_v, theta_v = abs(b)*rho, pi/2 if b >= 0 else -pi/2
# Plot h/v lines on polar projection
ax.plot((theta_h, theta), (rho_h, rho), c='r', ls='--')
ax.plot((theta, theta_v), (rho, rho_v), c='g', ls='--')
import matplotlib.pyplot as plt
from numpy import *
'''
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
This draws the axis for argand diagram
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
'''
r = 1
Y = [r*exp(1j*theta) for theta in linspace(0,2*pi, 200)]
Y = array(Y)
plt.plot(real(Y), imag(Y), 'r')
plt.ylabel('Imaginary')
plt.xlabel('Real')
plt.axhline(y=0,color='black')
plt.axvline(x=0, color='black')
def argand(complex_number):
'''
This function takes a complex number.
'''
y = complex_number
x1,y1 = [0,real(y)], [0, imag(y)]
x2,y2 = [real(y), real(y)], [0, imag(y)]
plt.plot(x1,y1, 'r') # Draw the hypotenuse
plt.plot(x2,y2, 'r') # Draw the projection on real-axis
plt.plot(real(y), imag(y), 'bo')
[argand(r*exp(1j*theta)) for theta in linspace(0,2*pi,100)]
plt.show()
https://github.com/QuantumNovice/Matplotlib-Argand-Diagram/blob/master/argand.py

matplotlib: filling under line in 3d polar plot

I'd like to plot a sine wave on a circle: that is, the circle is in the x,y-plane and the sine wave wraps around it perpendicular to that plane (sticking up the z-axis). I can do this, but when I try to fill the areas between the circle and the sine wave with a polygon (ie paint on the surface of the imaginary cylinder on which my sine wave lives), I can't get it quite right - matplotlib seems to XOR the regions that overlap in a view of the plot instead of giving me a view in which the ones in front occlude those behind.
Here's the relevant bit of my code:
fig = plt.figure()
ax = fig.gca(projection='3d')
ax._axis3don = False
theta = np.linspace(0., 2 * np.pi, 1000)
r = 1.
x = r * np.sin(theta)
y = r * np.cos(theta)
sinez = N * np.sin(theta * m)
ax.plot(x, y, sinez, color='r')
xv = np.append(x, x[::-1])
yv = np.append(y, y[::-1])
zv = np.append(sinez, np.zeros(n))
verts = [zip(xv,yv,zrev),]
poly = Poly3DCollection(verts, facecolors = [cc('r'), cc('b')],
edgecolor='None')
poly.set_alpha(0.7)
ax.add_collection3d(poly)
Here's what it looks like:
matplotlib's main reason for existence is 2D plotting, the 3D stuff is just some clever transforms and can be buggy/hacky. One of the inherent limitations is that matplotlib draws in layers, so it has no notion of 'in front' or 'behind', it only knows the order in which in draws the curves to the canvas (which is confusingly called z-order).
If you want to get this to look right with out re-writing the 3D code, split the sine wave up into pieces and make sure you set the z-order right by hand (see How to draw intersecting planes? for a simpler version of this), but you won't be able to rotate the image.
If you need real 3D, I would suggest looking into mayavi from enthought which is OpenGL based.
In the docs, the devs claim that Poly3DCollection
does a bit of magic with the _facecolors and _edgecolors properties.
which I believe is the XOR effect that you can see here, and looking at the code it's the function do_3d_projection that seems to be doing the magic.
As I see it, you could either subclass Poly3DCollection and rewrite do_3d_projection to get what you want, or maybe think of another way to plot this (perhaps treating the sinusoid and circle as separate objects somehow).

Categories

Resources