I'm trying to graph a the values of a variable vs time in a polar graph countour (see the image of what i'm trying to do).
The emotion_list is the score for detected emotion
import numpy as np
import matplotlib.pyplot as plt
emotion_list = [0.0,0.2,0.3,0.3,0.0,0.2,0.0]
rad_arr = np.radians(np.arange(0,360,7))
r_arr = emotion_list
def func(r, theta):
return r*np.sin(theta)
r, theta = np.meshgrid(r_arr, rad_arr)
print(r)
print(theta)
values = func(r, theta)
fig, ax = plt.subplots(subplot_kw=dict(projection = 'polar'))
ax.contourf(theta, r, values, cmap = 'Spectral_r')
But the graphic i got is not as expected.
The graph i would like to is something like this:
Graphic wanted
Thanks everyone for your help.
A contour plot will not work well with repeated values. Try this
import numpy as np
import matplotlib.pyplot as plt
emotion_list = [0.0,0.2,0.3]
rad_arr = np.radians(np.arange(0,360,len(emotion_list)))
r_arr = emotion_list
def func(r, theta):
return r*np.sin(theta)
r, theta = np.meshgrid(r_arr, rad_arr)
print(r)
print(theta)
values = func(r, theta)
fig, ax = plt.subplots(subplot_kw=dict(projection = 'polar'))
ax.contourf(theta, r, values, cmap = 'Spectral_r')
plt.show()
The repeated values in emotion_list were removed and the shape of the rad_arr array is now connected to the length of the emotion_list array.
Related
the value on y-axis does not change in my plot if I define my function outside ax.plot_wireframe().
It is the problem of my real function which longer.
import pandas
import numpy
from matplotlib import cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # <--- This is important for 3d plotting
a = numpy.linspace(1,10,10) # x axis
b = numpy.linspace(1,10,10) # y axis
R = a+b #model function, real function is longer
z = R
fig = plt.figure()
ax = plt.axes(projection='3d')
a,b = numpy.meshgrid(a,b)
#ax.plot_wireframe(a,b,a+b, color='b') #correct
#ax.plot_wireframe(a,b,z, color='b') #wrong
ax.plot_wireframe(a,b,R, color='b') #wrong
ax.set_title('surface');
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
Here is the result
Take a look at the matplotlib documentation regarding 3D wireframe plots. The x, y and z values need to be 2-dimensional. Explanations for this are given here or here. This is also the reason for the line
a,b = numpy.meshgrid(a,b)
in your code. It creates a 10x10 2D array for both 1D inputs. In the next lines you call the wireframe method with a+b for the z values. Hence, the z values are calculated in place and the result is again a 10x10 2D array. The reason why you get the "wrong" graph with the variables R or z is that they are calculated before a and b are turned into their respective 2D meshgrids. If you define R or z after the line containing numpy.meshgrid it works fine.
import pandas
import numpy
from matplotlib import cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # <--- This is important for 3d plotting
a = numpy.linspace(1,10,10) # x axis
b = numpy.linspace(1,10,10) # y axis
R = a+b #not working
z = R #not working
def f(x,y):
return x+y
fig = plt.figure()
ax = plt.axes(projection='3d')
a,b = numpy.meshgrid(a,b)
Z = f(a,b)#working
R = a+b #working
z = R #working
#ax.plot_wireframe(a,b,a+b, color='b') #
#ax.plot_wireframe(a,b,Z, color='b') #
ax.plot_wireframe(a,b,R, color='b') #
ax.set_title('surface');
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
So the short answer is: numpy.meshgrid changes your variables a and b and your are basically doing your calculations with different as and bs
Am trying to create a 3D plot in Python where I have an FFT on the XY plane and a parameter on the Z axis
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, ifft
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
from scipy.signal import blackman
Nmax=64 # length of logistic series and no of FFT bins
w = blackman(Nmax) # blackman window for FFT
def fourier_of_logmap(k, r): # returns kth element in spectrum for given r
x_0 = 0.5
x_n = x_0
n_0 = 0
n = n_0
x_values = []
while n < Nmax:
y = r*x_n*(1-x_n)
x_values.append(x_n)
n = n + 1
x_n = y
fourier = np.log(abs(fft(w*x_values)))
return fourier[k]
test = fourier_of_logmap(20,3.7) #test if function is working
k_values = np.linspace(0, Nmax-1, Nmax)
r_values = np.linspace(0,4,100)
K, R = np.meshgrid(k_values, r_values)
A = fourier_of_logmap(K, R)
'Plot 3d surface'
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(K, R, A, cmap=cm.coolwarm, linewidth=0, antialiased=False)
plt.show()
Testing the function with 'test' returns a float as desired. However when applied to (K, R) I get "ValueError: setting an array element with a sequence". Yet when I replace fourier_of_logmap with a simple function like K + R it runs fine. Why is this happening?
I have a 3d plot made using matplotlib. I now want to fill the vertical space between the drawn line and the x,y axis to highlight the height of the line on the z axis. On a 2d plot this would be done with fill_between but there does not seem to be anything similar for a 3d plot. Can anyone help?
here is my current code
from stravalib import Client
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
... code to get the data ....
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
zi = alt
x = df['x'].tolist()
y = df['y'].tolist()
ax.plot(x, y, zi, label='line')
ax.legend()
plt.show()
and the current plot
just to be clear I want a vertical fill to the x,y axis intersection NOT this...
You're right. It seems that there is no equivalent in 3D plot for the 2D plot function fill_between. The solution I propose is to convert your data in 3D polygons. Here is the corresponding code:
import math as mt
import matplotlib.pyplot as pl
import numpy as np
import random as rd
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
# Parameter (reference height)
h = 0.0
# Code to generate the data
n = 200
alpha = 0.75 * mt.pi
theta = [alpha + 2.0 * mt.pi * (float(k) / float(n)) for k in range(0, n + 1)]
xs = [1.0 * mt.cos(k) for k in theta]
ys = [1.0 * mt.sin(k) for k in theta]
zs = [abs(k - alpha - mt.pi) * rd.random() for k in theta]
# Code to convert data in 3D polygons
v = []
for k in range(0, len(xs) - 1):
x = [xs[k], xs[k+1], xs[k+1], xs[k]]
y = [ys[k], ys[k+1], ys[k+1], ys[k]]
z = [zs[k], zs[k+1], h, h]
#list is necessary in python 3/remove for python 2
v.append(list(zip(x, y, z)))
poly3dCollection = Poly3DCollection(v)
# Code to plot the 3D polygons
fig = pl.figure()
ax = Axes3D(fig)
ax.add_collection3d(poly3dCollection)
ax.set_xlim([min(xs), max(xs)])
ax.set_ylim([min(ys), max(ys)])
ax.set_zlim([min(zs), max(zs)])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
pl.show()
It produces the following figure:
I hope this will help you.
I am trying to plot simple function r = 3*sin(2*theta) using matplotlib:
import numpy as np
import matplotlib.pyplot as plt
theta = np.arange(0,2*np.pi,0.01)
r = 3.0*np.sin(2.0*theta)
ax = plt.subplot(111, projection='polar')
ax.plot(theta, r)
plt.show()
This is the result I get (it is not correct):
This is what I expect to see (wolfram alpha):
Am I missing something?
Thanks!
this patches the polar plot for neg r
import numpy as np
import matplotlib.pyplot as plt
theta = np.arange(0,2*np.pi,0.01)
r = 3.0*np.sin(2.0*theta)
theta = theta + (1 - np.sign(r))*np.pi/2 # add pi to points with negative r values
r = np.abs(r) # make all r values postive to fake out matplotlib
ax = plt.subplot(111, projection='polar')
ax.plot(theta, r)
plt.show()
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?