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
Related
I have a function that calculates a z value from a given x and y coordinate. I then want to combine these values together to get a 3D array of x,y,z. I'm attempting to do this with the code below:
#import packages
import pandas as pd
import math
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.tri as tri
import matplotlib.pyplot as plt
from matplotlib import rcParams
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
from mpl_toolkits.mplot3d import Axes3D
#Define function to calculate z over a grid
def func(X, Y, x, y, Q):
return (Q / (2 * np.pi)) * np.arctan((y-Y)/(x-X))
#For initial testing just defining the IW explicitly, last step will be to read the input file and pull this data
X1=2417743.658
Y1=806346.704
Q1=5
X2=2417690.718
Y2=806343.693
Q2=5
X3=2417715.221
Y3=806309.685
Q3=5
#initiate the XY grid
xi = np.linspace(2417675,2417800,625)
yi = np.linspace(806300,806375,375)
#mesh the grid in to x,y space
x,y = np.meshgrid(xi,yi)
#calculate the values over the grid at every x,y using the defined function above
zi = (func(X1,Y1,x,y,Q1)+func(X2,Y2,x,y,Q2)+func(X3,Y3,x,y,Q3))
#reshape the xy space into 3d space - when i plot this grid it looks correct
xy = np.array([[(x, y) for x in xi] for y in yi])
#reshape z into 3d space - this appears to be where the issue begins
z = np.array(zi).reshape(xy.shape[0],xy.shape[1], -1)
#combined xyz into a single grid
xyz = np.concatenate((xy, z), axis = -1)
# Create figure and add axis
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111)
img = ax.imshow((xyz*255).astype(np.uint8))
output:
I do get an XYZ array and when i print it the values appear to be mapping correctly, however when I plot the data, it shows the y values "upside down" essentially. This is what the output should look like but "flipped" over the x over axis. Additionally the axes show node numbers and not the X,Y values. I want the 0,0 point to be the lower left hand corner like cartesian coordinates, and each x,y have a corresponding z which is calculated from that given x,y. I know there must be an easier way to go about this. Does anyone know a better way? or maybe what i'm doing wrong here?
Thanks
There is an option for ax.imshow() that allows to specify the origin point.
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html
origin{'upper', 'lower'}, default: rcParams["image.origin"] (default:
'upper') Place the [0, 0] index of the array in the upper left or
lower left corner of the Axes. The convention (the default) 'upper' is
typically used for matrices and images.
Note that the vertical axis points upward for 'lower' but downward for
'upper'.
See the origin and extent in imshow tutorial for examples and a more
detailed description.
Try to modify to this:
img = ax.imshow((xyz*255).astype(np.uint8), origin='lower')
For the axis labels they can be changed with the following commands
ax.set_xticks(LIST_OF_INDICIES)
ax.set_xticklabels(LIST_OF_VALUES)
I want to extract polar coordinates from the plot. There exists a matrix that has 10 rows and 2 columns a and b. This matrix has the numbers that created the polar plot.
But what I am looking for is a matrix that has the polar coordinates that are already plotted.
Example: the first row (meaning, information about the first point) would include the x,y,radius and theta/angle or any other useful information from the plot that were previously not there in the original matrix.
Think of polar plotting as a transformation that was implemented on the matrix in the for loop and I want to extract the new numbers resulted from the output polar plot.
import matplotlib.pyplot as plt
sample= [1,2,3,4,5,6,7,8,9,10]
fig = plt.figure()
fig.add_subplot(111, projection='polar')
data = sample
for i in data:
a=i+min(data)
b=i+max(data)
plt.polar(a,b, '.', c='black')
plt.show()
Even after your clarification I am a little confused by the code example. But I am pretty sure the code below answers your question.
import numpy as np
import matplotlib.pyplot as plt
x = np.random.random((10,1)) # same as "a" in your example
y = np.random.random((10,1)) # same as "b" in your example
plt.plot(x,y)
plt.title("Rectangular coordinates")
plt.show()
th = np.arctan2(y,x)
r = np.sqrt(x**2 + y**2)
# Should show the same plot as above, just with different margins/aspect ratio/grid lines
plt.polar(th,r)
plt.title("Polar coordinates")
plt.show()
It's just as easy to go in reverse, getting the rectangular coordinates if you assume your random data is representing data in polar coordinates.
I would like to plot the graphic of function: w(s) = 1/(1+s).
s is the product of the imaginary unit (1j) and the variable called omega; i.e. s = 1j*omega.
How I can plot that in the complex axis (Real and Imaginary) using Python (2.7 or 3.4) and matplotlib?
You're not clear in your question about what you want to plot, but assuming omega = x + iy is the number you wish to plot w(s) as a function of, you have to decide how to present the complex numbers w(s). You might choose a plot with Cartesian axes representing the real (x) and imaginary (y) axes and to plot the absolute value of w(s) as a colour, or you might choose to plot the real and imaginary parts separately.
For example,
import matplotlib as plt
import numpy as np
x = np.linspace(-0.5,0.5,100)
y = np.linspace(-3,0,100)
X, Y = np.meshgrid(x,y)
def f(x, y):
return 1./(1+1j*(x+1j*y))
import pylab
pylab.imshow(np.abs(f(X,Y)))
pylab.show()
I have 3 different parameters X,Y and Z over a range of values, and for each combination of these a certain value of V. To make it clearer, the data would look something like this.
X Y Z V
1 1 2 10
1 2 3 15
etc...
I'd like to visualize the data with a surface/contour plot, using V as a colour to see its value at that point, but I do not see how to add my custom colouring scheme into the mix using Python. Any idea on how to do this (or is this visualization outright silly)?
Thanks a lot!
Matplotlib allows one to pass the facecolors as an argument to e.g.
ax.plot_surface.
That would imply then that you would have to perform 2D interpolation on your
current array of colors, because you currently only have the colors in the
corners of the rectangular faces (you did mention that you have a rectilinear
grid).
You could use
scipy.interpolate.interp2d
for that, but as you see from the documentation, it is suggested to use
scipy.interpolate.RectBivariateSpline.
To give you a simple example:
import numpy as np
y,x = np.mgrid[1:10:10j, 1:10:10j] # returns 2D arrays
# You have 1D arrays that would make a rectangular grid if properly reshaped.
y,x = y.ravel(), x.ravel() # so let's convert to 1D arrays
z = x*(x-y)
colors = np.cos(x**2) - np.sin(y)**2
Now I have a similar dataset as you (one-dimensional arrays for x, y, z and
colors). Remark that the colors are defined for
each point (x,y). But when you want to plot with plot_surface, you'll
generate rectangular patches, of which the corners are given by those points.
So, on to interpolation then:
from scipy.interpolate import RectBivariateSpline
# from scipy.interpolate import interp2d # could 've used this too, but docs suggest the faster RectBivariateSpline
# Define the points at the centers of the faces:
y_coords, x_coords = np.unique(y), np.unique(x)
y_centers, x_centers = [ arr[:-1] + np.diff(arr)/2 for arr in (y_coords, x_coords)]
# Convert back to a 2D grid, required for plot_surface:
Y = y.reshape(y_coords.size, -1)
X = x.reshape(-1, x_coords.size)
Z = z.reshape(X.shape)
C = colors.reshape(X.shape)
#Normalize the colors to fit in the range 0-1, ready for using in the colormap:
C -= C.min()
C /= C.max()
interp_func = RectBivariateSpline(x_coords, y_coords, C.T, kx=1, ky=1) # the kx, ky define the order of interpolation. Keep it simple, use linear interpolation.
In this last step, you could also have used interp2d (with kind='linear'
replacing the kx=1, ky=1). But since the docs suggest to use the faster
RectBivariateSpline...
Now you're ready to plot it:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.cm as cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
r = ax.plot_surface(X,Y,Z,
facecolors=cm.hot(interp_func(x_centers, y_centers).T),
rstride=1, cstride=1) # only added because of this very limited dataset
As you can see, the colors on the faces have nothing to do anymore with the height of the dataset.
Note that you could have thought simply passing the 2D array C to facecolors would work, and matplotlib would not have complained. However, the result isn't accurate then, because matplotlib will use only a subset of C for the facecolors (it seems to ignore the last column and last row of C). It is equivalent to using only the color defined by one coordinate (e.g. the top-left) over the entire patch.
An easier method would have been to let matplotlib do the interpolation and obtain the facecolors and then pass those in to the real plot:
r = ax.plot_surface(X,Y,C, cmap='hot') # first plot the 2nd dataset, i.e. the colors
fc = r.get_facecolors()
ax.clear()
ax.plot_surface(X, Y, Z, facecolors=fc)
However, that won't work in releases <= 1.4.1 due to this recently submitted bug.
It really depends on how you plan on plotting this data. I like to plot graphs with gnuplot: it's easy, free and intuitive. To plot your example with gnuplot you'd have to print those line into a file (with only those four columns) and plot using a code like the following
reset
set terminal png
set output "out.png"
splot "file.txt" using 1:2:3:4 with lines palette
Assuming that you save your data into the file file.txt. splot stands for surface plot. Of course, this is a minimum example.
Alternatively you can use matplotlib, but that is not, in my opinion, as intuitive. Although it has the advantage of centering all the processing in 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.