I am trying to plot this radiation pattern figure
of the following equation:
I can make it partially:
%matplotlib inline
import matplotlib.ticker
import matplotlib.pyplot as plt
plt.style.use('seaborn-white')
import numpy as np
import pylab as pl
def gain_dip(theta, phi):
return 1.641*(np.cos(np.pi/2*np.cos(theta))/np.sin(theta))**2
theta = np.arange(-np.pi, np.pi,0.01)
# plot
ax = plt.subplot(111, polar=True)
# set zero west
ax.set_theta_zero_location('W')
ax.set_theta_direction('clockwise')
# let set an azimuth for example, pi
plt.plot(theta, gain_dip(theta, np.pi))
which plots the elevation angle (theta) and is similar to the elevation of the example. However, it has a line at 180 degrees angle that should not appear.
I would like to plot the azimuthal angle, phi, too, but when I try it gives error:
phi = np.arange(-np.pi, np.pi,0.01)
ax = plt.subplot(111, polar=True)
# set zero west
ax.set_theta_zero_location('W')
ax.set_theta_direction('clockwise')
plt.plot(phi, gain_dip(np.pi/2, phi))
ValueError: x and y must have same first dimension, but have shapes (629,) and (1,)
And, it is possible to plot both angles like in the first plot?
Related
I would like to plot a heat map on the unit sphere using the matplotlib library of python. There are several places where this question is discussed. Just like this: Heat Map half-sphere plot
I can do this partially. I can creat the sphere and the heatplot. I have coordinate matrices X,Y and Z, which have the same size. I have another variable of the same size as X, Y and Z, which contains scalars used to creat the heat map. However in case c contains scalars differ from zero in its first and last rows, just one polar cap will be colored but not the other. The code generates the above mentioned result is the next:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
#Creating the theta and phi values.
theta = np.linspace(0,np.pi,100,endpoint=True)
phi = np.linspace(0,np.pi*2,100,endpoint=True)
#Creating the coordinate grid for the unit sphere.
X = np.outer(np.sin(theta),np.cos(phi))
Y = np.outer(np.sin(theta),np.sin(phi))
Z = np.outer(np.cos(theta),np.ones(100))
#Creating a 2D matrix contains the values used to color the unit sphere.
c = np.zeros((100,100))
for i in range(100):
c[0,i] = 100
c[99,i] = 100
#Creat the plot.
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.set_axis_off()
ax.plot_surface(X,Y,Z, rstride=1, cstride=1, facecolors=cm.plasma(c/np.amax(c)), alpha=0.22, linewidth=1)
m = cm.ScalarMappable(cmap=cm.plasma)
m.set_array(c)
plt.colorbar(m)
#Show the plot.
plt.show()
The plot which was generated:
Could somebody help me what's going on here?
Thank you for your help in advance!
There are a number of small differences with your example but an
important one, namely the shape of the values array c.
As mentioned in another
answer the grid that
defines the surface is larger (by one in both dimensions) than the
grid that defines the value in each quadrangular patch, so that by
using a smaller array for c it is possible to choose correctly the
bands to color not only with respect to the beginnings of the c
array but also with respect to its ends, as I tried to demonstrate in
the following.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
# Creating the theta and phi values.
intervals = 8
ntheta = intervals
nphi = 2*intervals
theta = np.linspace(0, np.pi*1, ntheta+1)
phi = np.linspace(0, np.pi*2, nphi+1)
# Creating the coordinate grid for the unit sphere.
X = np.outer(np.sin(theta), np.cos(phi))
Y = np.outer(np.sin(theta), np.sin(phi))
Z = np.outer(np.cos(theta), np.ones(nphi+1))
# Creating a 2D array to be color-mapped on the unit sphere.
# {X, Y, Z}.shape → (ntheta+1, nphi+1) but c.shape → (ntheta, nphi)
c = np.zeros((ntheta, nphi)) + 0.4
# The poles are different
c[ :1, :] = 0.8
c[-1:, :] = 0.8
# as well as the zones across Greenwich
c[:, :1] = 0.0
c[:, -1:] = 0.0
# Creating the colormap thingies.
cm = mpl.cm.inferno
sm = mpl.cm.ScalarMappable(cmap=cm)
sm.set_array([])
# Creating the plot.
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=cm(c), alpha=0.3)
plt.colorbar(m)
# Showing the plot.
plt.show()
The values in the arrays define the edges of the grid. The color of the ith face is determined by the ith value in the color array. However, for n edges you only have n-1 faces, such that the last value is ignored.
E.g. if you have 4 grid values and 4 colors, the plot will have only the first three colors in the grid.
Thus a solution for the above would be to use a color array with one color less than gridpoints in each dimension.
c = np.zeros((99,99))
c[[0,98],:] = 100
I am trying to rotate a figure horizontally in python. With the mouse I am only able to rotate through one degree of freedom as well as being able to spin the figure, for example this is what I can do at the momement:
These movements are only vertical. I would like to be able to rotate the figure in all directions, for example:
This would allow me to put the figure on its side etc. However at the moment I cannot do this, all I can do it rotate along one degree of freedom.
Here is my code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
import math
fig = plt.figure()
ax = fig.gca(projection='3d')
nphi,nz= 13, 101
r=1
phi = np.linspace(0,360, nphi)/180.0*np.pi
z= np.linspace(0,350,nz)
cols=[]
verts2 = []
for i in range(len(phi)-1):
cp0= r*np.cos(phi[i])
cp1= r*np.cos(phi[i+1])
sp0= r*np.sin(phi[i])
sp1= r*np.sin(phi[i+1])
for j in range(len(z)-1):
z0=z[j]
z1=z[j+1]
verts=[]
verts.append((cp0, sp0, z0))
verts.append((cp1, sp1, z0))
verts.append((cp1, sp1, z1))
verts.append((cp0, sp0, z1))
verts2.append(verts)
col=plt.cm.Blues(0.4)
cols.append(col)
poly3 = Poly3DCollection(verts2, facecolor=cols ,edgecolor = "red" )
poly3.set_alpha(0.5)
ax.add_collection3d(poly3)
ax.set_xlabel('X')
ax.set_xlim3d(-3, 3)
ax.set_ylabel('Y')
ax.set_ylim3d(-3, 3)
ax.set_zlabel('Z')
ax.set_zlim3d(0, 300)
plt.axis('off')
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
plt.show()
Unfortunately, I don't think you can; From the documentation:
‘elev’ stores the elevation angle in the z plane. ‘azim’ stores the
azimuth angle in the x,y plane
So you've not actually got 3 degrees of freedom here, because you can only rotate in the xy plane, rather than around each axis independently.
It's a bit clearer if you display the axes:
I have some Fortran code which outputs the polar coordinates of a grid on the surface of a sphere in theta, phi format. It also outputs a value associated with each of these points (specifically meant to represent the voltage at that point on the sphere's surface).
Now I want to read this data into Python, plot a sphere, and then colour it according to the voltage data values. I know how to do this for a latitude-longitude grid, but my grid points are not ordered in any specific way.
The code I'm trying is as follows:
import matplotlib.pyplot as plt
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
import option_d
import numpy as np
# Create a sphere
r = 1.0
pi = np.pi
cos = np.cos
sin = np.sin
#Read in grid points
data = np.genfromtxt('grid.txt')
phi, theta = np.hsplit(data, 2)
#Convert grid points to cartesian
x = r*sin(phi)*cos(theta)
y = r*sin(phi)*sin(theta)
z = r*cos(phi)
#Import data from initial state
colorfunction = np.genfromtxt('sphere_init.txt')
print np.shape(colorfunction)
#Normalise the colour map to the initial data
newcm = option_d.test_cm
norm=colors.Normalize(vmin = -np.max(colorfunction), vmax = np.max(colorfunction), clip = False)
#Plot the surface
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(
x,y,z,rstride=1,cstride=1,cmap=newcm,facecolors=newcm(norm(colorfunction)))
#Set axes and display or save
ax.set_aspect("equal")
plt.tight_layout()
plt.show()
The file 'grid.txt' contains two columns, each 770 in length, representing the phi, theta coordinates of each point. The file 'sphere_init.txt' contains a single column of length 770, which are the corresponding data values. However, this does not work - it just throws error messages at me. Is it even possible to plot a sphere from disordered grid points? Any help much appreciated. Thanks.
Edit
Here is the error message:
Traceback (most recent call last):
File "sphere.py", line 43, in <module>
x,y,z,rstride=1,cstride=1, cmap=newcm,facecolors=newcm(norm(colorfunction)))
File "/usr/lib/pymodules/python2.7/mpl_toolkits/mplot3d/axes3d.py", line 1611, in plot_surface
colset.append(fcolors[rs][cs])
IndexError: index out of bounds
I believe I have solved my problem. I read in my irregular grid data, and then also create a regular latitude-longitude grid. I then interpolate from the irregular grid to the lat-long grid:
import matplotlib.pyplot as plt
import matplotlib.mlab as ml
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
import option_d
import numpy as np
import time
#Read in lebedev grid points
data = np.genfromtxt('grid.txt')
u, v = np.hsplit(data, 2)
phi, theta = u[:,0], v[:,0]
#Import data from initial state
colorfunction = np.genfromtxt('sphere_init.txt')
#Generate a lat-long grid to interpolate on
p = np.linspace(0,np.pi, 770)
t = np.linspace(-np.pi, np.pi, 770)
p, t = np.meshgrid(p, t)
#Interpolate using delaunay triangularization
zi = ml.griddata(phi, theta, colorfunction, p, t)
#Convert the lat-long grid points to cartesian
x = np.sin(p)*np.cos(t)
y = np.sin(p)*np.sin(t)
z = np.cos(p)
#Normalize the interpolated colourfunction
#Use fancy new colourmap
newcm = option_d.test_cm
norm=colors.Normalize(vmin = -np.max(zi), vmax = np.max(zi), clip = False)
#Plot the surface
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(
x,y,z,rstride=1,cstride=1, cmap=newcm,facecolors=newcm(norm(zi)))
#Display
ax.set_aspect("equal")
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
plt.tight_layout()
plt.show()
Edit
I have run into a new problem with this method. It causes a chunk to be missing from the back of my sphere:
Any ideas why?
I am trying to use python3 and matplotlib (version 1.4.0) to plot a scalar function defined on the surface of a sphere. I would like to have faces distributed relatively evenly over the sphere, so I am not using a meshgrid. This has led me to use plot_trisurf to plot my function. I have tested it with a trivial scalar function, and am having the problem that there are rendering artefacts along the edges of the faces:
The code I used to create the plot is below:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.tri as mtri
from scipy.spatial import ConvexHull
def points_on_sphere(N):
""" Generate N evenly distributed points on the unit sphere centered at
the origin. Uses the 'Golden Spiral'.
Code by Chris Colbert from the numpy-discussion list.
"""
phi = (1 + np.sqrt(5)) / 2 # the golden ratio
long_incr = 2*np.pi / phi # how much to increment the longitude
dz = 2.0 / float(N) # a unit sphere has diameter 2
bands = np.arange(N) # each band will have one point placed on it
z = bands * dz - 1 + (dz/2) # the height z of each band/point
r = np.sqrt(1 - z*z) # project onto xy-plane
az = bands * long_incr # azimuthal angle of point modulo 2 pi
x = r * np.cos(az)
y = r * np.sin(az)
return x, y, z
def average_g(triples):
return np.mean([triple[2] for triple in triples])
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X, Y, Z = points_on_sphere(2**12)
Triples = np.array(list(zip(X, Y, Z)))
hull = ConvexHull(Triples)
triangles = hull.simplices
colors = np.array([average_g([Triples[idx] for idx in triangle]) for
triangle in triangles])
collec = ax.plot_trisurf(mtri.Triangulation(X, Y, triangles),
Z, shade=False, cmap=plt.get_cmap('Blues'), array=colors,
edgecolors='none')
collec.autoscale()
plt.show()
This problem appears to have been discussed in this question, but I can't seem to figure out how to set the edgecolors to match the facecolors. The two things I've tried are setting edgecolors='face' and calling collec.set_edgecolors() with a variety of arguments, but those throw AttributeError: 'Poly3DCollection' object has no attribute '_facecolors2d'.
How am I supposed to set the edgecolor equal to the facecolor in a trisurf plot?
You can set antialiased argument of plot_trisurf() to False. Here is the result:
I want to generate a heat map with my 3D data.
I have been able to plot trisurf using this data.
Can some one help me generate a heat map? I saw the online tutorials but they all seem quite complex for 3D. I found one on this website 'generating heatmap with scatter point in matplotlib but that is having only 2D data.
My code to generate trisurf is
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
n_angles = 36
n_radii = 8
# An array of radii
# Does not include radius r=0, this is to eliminate duplicate points
radii = np.linspace(0.125, 1.0, n_radii)
# An array of angles
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
# Repeat all angles for each radius
angles = np.repeat(angles[...,np.newaxis], n_radii, axis=1)
# Convert polar (radii, angles) coords to cartesian (x, y) coords
# (0, 0) is added here. There are no duplicate points in the (x, y) plane
x,y,z =np.loadtxt('output/flash_KR_endowment_duration_3D.dat',delimiter='\t',usecols=(0,1,2),unpack=True)
#x,y,z =np.loadtxt('output/disk_KR_endowment_duration_3D.dat',delimiter='\t',usecols=(0,1,2),unpack=True)
fig = plt.figure()
ax = fig.gca(projection='3d')
#fig.suptitle(suptitle, fontsize=12, fontweight='bold')
#ax.set_title("Disk Kryder's Rate-Endowment-Duration Plot",fontsize=12)
ax.set_title("Flash Kryder's Rate-Endowment-Duration Plot",fontsize=12)
ax.set_xlabel("Kryder's rate")
ax.set_ylabel("Duration")
ax.set_zlabel("Endowment")
surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.2)
fig.colorbar(surf, shrink=.7, aspect=20)
plt.show()
Data is 3 column. say X,Y,Z. I have tried 3D scatter plot with color. But I am looking for heatmap.
If you only "want to use 3rd dimension for coloring", you can do it like this:
import pandas as pd
import numpy as np
import plotly.plotly as plotly
from plotly.graph_objs import Data, Heatmap
plotly.sign_in("username", "api_key") # this is annoying but you can get one after registering - free
# generate tridimentional data
pp = pd.Panel(np.random.rand(20, 20, 20))
# crunch (sum, average...) data along one axis
crunch = pp.sum(axis=0)
# now plot with plot.ly or matplotlib as you wish
data = Data([Heatmap(z=np.array(crunch))])
plotly.image.save_as(data, "filename.pdf")
Result - heatmap with 3rd variable of 3D data as colour:
Additionally, you can plot for each combination of axis with a loop:
## Plot
# for each axis, sum data along axis, plot heatmap
# dict is axis:[x,y,z], where z is a count of that variable
desc = {0 : ["ax1", "ax2", "ax3"], 1 : ["ax1", "ax2", "ax3"], 2 : ["ax1", "ax2", "ax3"]}
for axis in xrange(3):
# crunch (sum) data along one axis
crunch = pp.sum(axis=axis)
# now let's plot
data = Data([Heatmap(
z=np.array(crunch),
x=crunch.columns,
y=crunch.index)]
)
plotly.image.save_as(data,
"heatmap_{0}_vs_{1}_count_of_{2}".format(desc[axis][0], desc[axis][1], desc[axis][2])
)