I am trying to plot a 3 dimensional plot in matplotlib. I have to plot Frequency vs Amplitude Distribution for four (or multiple) Radii in a single 3D plot. I was looking at PolyCollection command available in matplotlib.collections and I also went through the example but I do not know how to use the existing data to arrive at the plot.
The dimensions of the quantities that I have are,
Frequency : 4000 x 4,
Amplitude : 4000 x 4,
Radius : 4
I would like to plot something like,
With X axis being Frequencies, Y axis being Radius, and Z axis being Amplitudes. How do I go about solving this problem?
PolyCollection expects a sequence of vertices, which matches your desired data pretty well. You don't provide any example data, so I'll make some up for illustration (my dimension of 200 would be your 4000 .... although I might consider a different plot than this if you have so many data points):
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection
from mpl_toolkits.mplot3d import axes3d
import numpy as np
# These will be (200, 4), (200, 4), and (4)
freq_data = np.linspace(0,300,200)[:,None] * np.ones(4)[None,:]
amp_data = np.random.rand(200*4).reshape((200,4))
rad_data = np.linspace(0,2,4)
verts = []
for irad in range(len(rad_data)):
# I'm adding a zero amplitude at the beginning and the end to get a nice
# flat bottom on the polygons
xs = np.concatenate([[freq_data[0,irad]], freq_data[:,irad], [freq_data[-1,irad]]])
ys = np.concatenate([[0],amp_data[:,irad],[0]])
verts.append(list(zip(xs, ys)))
poly = PolyCollection(verts, facecolors = ['r', 'g', 'c', 'y'])
poly.set_alpha(0.7)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# The zdir keyword makes it plot the "z" vertex dimension (radius)
# along the y axis. The zs keyword sets each polygon at the
# correct radius value.
ax.add_collection3d(poly, zs=rad_data, zdir='y')
ax.set_xlim3d(freq_data.min(), freq_data.max())
ax.set_xlabel('Frequency')
ax.set_ylim3d(rad_data.min(), rad_data.max())
ax.set_ylabel('Radius')
ax.set_zlim3d(amp_data.min(), amp_data.max())
ax.set_zlabel('Amplitude')
plt.show()
Most of this is straight from the example you mention, I just made it clear where your particular datasets would lie. This yields this plot:
Related
I'm using a line symmetry detector for a project I've found on github and it makes use of matplotlib's hexbin plot to identify coordinates to find the symmetric line.
Unfortunately, this method is very manual and requires the user to identify the x and y coordinates through the generated plot, and then input the values into the program again.
Is there a way to return the x and y values where the region is hottest in the hexbin plot?
For reference, this is the generated hexbin plot. The coordinates I am looking for is roughly x=153.0 y=1.535
plt.hexbin returns a PolyCollection object, which has the X and Y positions and their values, see here. You can access them with get_offsets() and get_array(). Here's an example:
# Figure from https://www.geeksforgeeks.org/matplotlib-pyplot-hexbin-function-in-python/
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(19680801)
n = 100000
x = np.random.standard_normal(n)
y = 12 * np.random.standard_normal(n)
polycollection = plt.hexbin(x, y, gridsize = 50, cmap ='Greens')
# New part
max_ = polycollection.get_array().max()
max_pos = polycollection.get_array().argmax()
pos_x, pos_y = polycollection.get_offsets()[max_pos]
plt.text(pos_x, pos_y, max_, color='w')
This is the resulting plot:
TL/DR: How to use Wedge() in polar coordinates?
I'm generating a 2D histogram plot in polar coordinates (r, theta). At various values of r there can be different numbers of theta values (to preserve equal area sized bins). To draw the color coded bins I'm currently using pcolormesh() calls for each radial ring. This works ok, but near the center of the plot where there may be only 3 bins (each 120 degrees "wide" in theta space), pcolormesh() draws triangles that don't "sweep" out full arc (just connecting the two outer arc points with a straight line).
I've found a workaround using ax.bar() call, one for each radial ring and passing in arrays of theta values (each bin rendering as an individual bar). But when doing 90 rings with 3 to 360 theta bins in each, it's incredibly slow (minutes).
I tried using Wedge() patches, but can't get them to render correctly in the polar projection. Here is sample code showing both approaches:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Wedge
from matplotlib.collections import PatchCollection
# Theta coordinates in degrees
theta1=45
theta2=80
# Radius coordinates
r1 = 0.4
r2 = 0.5
# Plot using bar()
fig, ax = plt.subplots(figsize=[6,6], subplot_kw={'projection': 'polar'})
theta_mid = np.deg2rad((theta1 + theta2)/2)
theta_width = np.deg2rad(theta2 - theta1)
height = r2 - r1
ax.bar(x=theta_mid, height = height, width=theta_width, bottom=r1)
ax.set_rlim(0, 1)
plt.savefig('bar.png')
# Plot using Wedge()
fig, ax = plt.subplots(figsize=[6,6], subplot_kw={'projection': 'polar'})
patches = []
patches.append( Wedge(center=(0, 0), r = r1, theta1=theta1, theta2=theta2, width = r2-r1, color='blue'))
p = PatchCollection(patches)
ax.add_collection(p)
ax.set_rlim(0, 1)
plt.savefig('wedge.png')
The outputs of each are:
Bar
Wedge
I've tried using radians for the wedge (because polar plots usually want their angle values in radians). That didn't help.
Am I missing something in how I'm using the Wedge? If I add thousands of Wedges to my Patch collection should I have any expectation it will be faster than bar()?
Thinking this was an actual bug, I opened this issue https://github.com/matplotlib/matplotlib/issues/22717 on matplotlib where one of the maintainers nicely pointed out that I should be using Rectangle() instead of Wedge().
The solution they provided is
from matplotlib.patches import Rectangle
fig, ax = plt.subplots(figsize=[6,6], subplot_kw={'projection': 'polar'})
p = PatchCollection([Rectangle((np.deg2rad(theta1), r1), theta_width, height, color='blue')])
ax.add_collection(p)
ax.set_rlim(0, 1)
plt.savefig('wedge.png')
I read a waveform from an oscilloscope. The waveform is divided into 10 segments as a function of time. I want to plot the complete waveform, one segment above (or under) another, 'with a vertical offset', so to speak. Additionally, a color map is necessary to show the signal intensity. I've only been able to get the following plot:
As you can see, all the curves are superimposed, which is unacceptable. One could add an offset to the y data but this is not how I would like to do it. Surely there is a much neater way of plotting my data? I've tried a few things to solve this issue using pylab but I am not even sure how to proceed and if this is the right way to go.
Any help will be appreciated.
import readTrc #helps read binary data from an oscilloscope
import matplotlib.pyplot as plt
fName = r"...trc"
datX, datY, m = readTrc.readTrc(fName)
segments = m['SUBARRAY_COUNT'] #number of segments
x, y = [], []
for i in range(segments+1):
x.append(datX[segments*i:segments*(i+1)])
y.append(datY[segments*i:segments*(i+1)])
plt.plot(x,y)
plt.show()
A plot with a vertical offset sounds like a frequency trail.
Here's one approach that does just adjust the y value.
Frequency Trail in MatPlotLib
The same plot has also been coined a joyplot/ridgeline plot. Seaborn has an implementation that creates a series of plots (FacetGrid), and then adjusts the offset between them for a similar effect.
https://seaborn.pydata.org/examples/kde_joyplot.html
An example using a line plot might look like:
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
segments = 10
points_per_segment = 100
#your data preparation will vary
x = np.tile(np.arange(points_per_segment), segments)
z = np.floor(np.arange(points_per_segment * segments)/points_per_segment)
y = np.sin(x * (1 + z))
df = pd.DataFrame({'x': x, 'y': y, 'z': z})
pal = sns.color_palette()
g = sns.FacetGrid(df, row="z", hue="z", aspect=15, height=.5, palette=pal)
g.map(plt.plot, 'x', 'y')
g.map(plt.axhline, y=0, lw=2, clip_on=False)
# Set the subplots to overlap
g.fig.subplots_adjust(hspace=-.00)
g.set_titles("")
g.set(yticks=[])
g.despine(bottom=True, left=True)
plt.show()
Out:
I have 2 lists tab_x (containe the values of x) and tab_z (containe the values of z) which have the same length and a value of y.
I want to plot a 3D curve which is colored by the value of z. I know it's can be plotted as a 2D plot but I want to plot a few of these plot with different values of y to compare so I need it to be 3D.
My tab_z also containe negatives values
I've found the code to color the curve by time (index) in this question but I don't know how to transforme this code to get it work in my case.
Thanks for the help.
I add my code to be more specific:
fig8 = plt.figure()
ax8 = fig8.gca(projection = '3d')
tab_y=[]
for i in range (0,len(tab_x)):
tab_y.append(y)
ax8.plot(tab_x, tab_y, tab_z)
I have this for now
I've tried this code
for i in range (0,len(tab_t)):
ax8.plot(tab_x[i:i+2], tab_y[i:i+2], tab_z[i:i+2],color=plt.cm.rainbow(255*tab_z[i]/max(tab_z)))
A total failure:
Your second attempt almost has it. The only change is that the input to the colormap cm.jet() needs to be on the range of 0 to 1. You can scale your z values to fit this range with Normalize.
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import colors
fig = plt.figure()
ax = fig.gca(projection='3d')
N = 100
y = np.ones((N,1))
x = np.arange(1,N + 1)
z = 5*np.sin(x/5.)
cn = colors.Normalize(min(z), max(z)) # creates a Normalize object for these z values
for i in xrange(N-1):
ax.plot(x[i:i+2], y[i:i+2], z[i:i+2], color=plt.cm.jet(cn(z[i])))
plt.show()
I am trying to create a barb vector plot in matplotlib and map some colors to specific magnitudes: for example, to have vectors with magnitudes between 10 and 20 plotted as blue, and between 20 and 30 as rgb(0,15,40), and so on. The documentation for the barbs and quiver functions (they are similar) mentions the C input arg:
barb(X, Y, U, V, C, **kw)
Arguments:
X, Y:
The x and y coordinates of the barb locations (default is head of barb; see pivot kwarg)
U, V:
Give the x and y components of the barb shaft
C:
An optional array used to map colors to the barbs
However, this is very vague, and after searching all over Google, I am no closer to understanding how to use this color array in specific ways. I managed to discover that by setting C equal to the array of vector magnitudes and specifying the "cmap" kwarg, it will map the barbs to the specified colormap, as in the example code below. However, this is not what I want. I want to control the colors of specific groups of magnitudes. Any help would be appreciated.
Example code:
from matplotlib import pyplot as plt
from numpy import arange,meshgrid,sqrt
u,v = arange(-50,51,10),arange(-50,51,10)
u,v = meshgrid(u,v)
x,y = u,v
C = sqrt(u**2 + v**2)
plt.barbs(x,y,u,v,C,cmap=plt.cm.jet)
plt.show()
Resulting plot image link: (sorry can't post images directly yet)
http://i49.tinypic.com/xombmc.jpg
You can get it by discretizing the map.
import matplotlib as mpl
import pyplot as plt
from numpy import arange,meshgrid,sqrt
u,v = arange(-50,51,10),arange(-50,51,10)
u,v = meshgrid(u,v)
x,y = u,v
C = sqrt(u**2 + v**2)
cmap=plt.cm.jet
bounds = [10, 20, 40, 60]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
img=plt.barbs(x,y,u,v,C,cmap=cmap,norm=norm)
plt.colorbar(img, cmap=cmap, norm=norm, boundaries=bounds, ticks=bounds)
plt.show()
sx = 0
ex = 135
sy = 0
ey = 234
plt.barbs(x[sx:ex:5, sy:ey:5], y[sx:ex:5, sy:ey:5],
u[sx:ex:5, sy:ey:5], v[sx:ex:5, sy:ey:5],
u[sx:ex:5, sy:ey:5], cmap='coolwarm',
linewidth=1)
try this for "different color barbs w.r.t wind speed"