Related
Question
How can I plot the following scenario, just like shown in the attached image? This is for the purpose of visualising frequency allocation in a network
Scenario
I have a range of frequency values in a list-tuple like so, where the 1st value is the centre frequency, 2nd is total width, 3rd is guard band:
frequencies = [('195.71250000', '59.00000000', '2.50000000'), ('195.78750000', '59.00000000', '2.50000000'), ('195.86250000', '59.00000000', '2.50000000')]
and the range of these values are:
range = [('191.32500000', '196.12500000')]
Note: These are dummy values, the actual data is much larger but follows the same general structure
There are several ways to create this plot. One way is to use ax.vlines to plot the dashed lines for the frequencies and to use ax.bar for the rectangles representing the frequency ranges.
Here is an example where the frequencies are occupied at regular intervals within the range you have given (boundaries included) but with widths of randomly varying size. No guards are computed seeing as they should be automatically apparent thanks to the position of the frequencies and the widths, as far as I understand.
Also, the widths are much smaller compared to the sample data you have provided, else the bars will be very wide and will all overlap with one another, which would look very different from the image you have shared.
import numpy as np # v 1.19.2
import matplotlib.pyplot as plt # v 3.3.2
# Create sample dataset
rng = np.random.default_rng(seed=1) # random number generator
frequencies = np.arange(191.325, 196.125, step=0.3)
widths = rng.uniform(0.05, 0.25, size=frequencies.size)
# Create figure with single Axes and loop through frequencies and widths to plot
# vertical dashed lines for the frequencies and bars for the widths
fig, ax = plt.subplots(figsize=(10,3))
for freq, width in zip(frequencies, widths):
ax.vlines(x=freq, ymin=0, ymax=10, colors='tab:blue', linestyle='--', zorder=1)
ax.bar(x=freq, height=6, width=width, color='tab:blue', zorder=2)
# Additional formatting
ax.set_xlabel('Frequency (THZ)', labelpad=15, size=12)
ax.set_xticks(frequencies[::2])
ax.yaxis.set_visible(False)
for spine in ['top', 'left', 'right']:
ax.spines[spine].set_visible(False)
plt.show()
Im trying to scatter a single (square) marker such that it fills the whole figure (no more, no less).
As for simplification, I'm creating a figure such that x- and y- axes both go from -0.5 to 0.5. That is, the plotting area is the unit square, centred at the origin.
The marker now shall be scattered at the origin. What size should it be so that it occupies exactly the unit square?
I looked at this Finding the right marker size for a scatter plot and this pyplot scatter plot marker size but couldn't get it right so far.
This is what I tried:
fig, ax = plt.subplots(figsize=(4,4));
ax.set_aspect('equal');
ax.set_xlim(-0.5, 0.5);
ax.set_ylim(-0.5, 0.5);
figsize = fig.get_size_inches()[0]
dpi = fig.dpi
print(f'figsize = {int(figsize)}')
print(f'dpi = {int(dpi)}')
print(f'figure is {int(figsize*dpi)} x {int(figsize*dpi)} pixels\n')
print(f'setting the marker size to be {int(figsize*dpi)}**2 = {int((figsize*dpi)**2)}')
ax.scatter(0, 0, s=(figsize*dpi)**2, marker='s');
It turns out that the marker (blue area) does fill the unit square but it is actually filling way more than that. After manually trying different sizes, the right value seems to be around 46000 (opposed to the 82944 suggested at the second post).
You will need to apply the aspect, then get the axes width and transform it to display space (or transform the axes position first, then get its width). This can be used to calculate the width of the axes in units of points.
The square of that number is the markersize of the scatter if it shall be as large as the axes.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(4,4))
ax.set_xlim(-0.5, 0.5)
ax.set_ylim(-0.5, 0.5)
ax.set_aspect('equal')
ax.apply_aspect()
s = ax.get_position().transformed(fig.transFigure).width*72/fig.dpi
ax.scatter(0, 0, s=s**2, marker='s');
plt.show()
I'm using matplotlib to look at how wins are distributed based on betting odds for the MLB. The issue is that because betting odds are either >= 100 or <= -100, there's a big gap in the middle of my histogram.
Is there any way to exclude certain bins (specifically anything between -100 and 100) so that the bars of the chart flow more smoothly?
Link to current histogram
Here's the code I have right now:
num_bins = 20
fig, ax = plt.subplots()
n, bins, patches = ax.hist(winner_odds_df['WinnerOdds'], num_bins,
range=range_of_winner_odds)
ax.set_xlabel('Betting Odds')
ax.set_ylabel('Win Frequency')
ax.set_title('Histogram of Favorite Win Frequency Based on Betting Odds (2018)')
fig.tight_layout()
plt.show()
You could break your chart's x-axis as explained here, by plotting on two different axes that are made to visually look like one plot. The essential part, rewritten to apply to the x-axis instead of the y-axis, is:
f, (axl, axr) = plt.subplots(1, 2, sharey=True)
# plot the same data on both axes
axl.hist(winner_odds_df['WinnerOdds'], num_bins)
axr.hist(winner_odds_df['WinnerOdds'], num_bins)
# zoom-in / limit the view to different portions of the data
axl.set_xlim(-500, -100) # outliers only
axr.set_xlim(100, 500) # most of the data
# hide the spines between axl and axr
axl.spines['right'].set_visible(False)
axr.spines['left'].set_visible(False)
axr.yaxis.tick_right()
# How much space to leave between plots
plt.subplots_adjust(wspace=0.15)
See the linked document for how to polish this by adding diagonal break lines. The basic version produced by the code above then looks like this:
I have an array of arrays with format [2000][200,3] that I am creating an animated scatter plot of. 2000 is the number of frames and the interior arrays have format [length, [x,y,inten]] which are the points to scatter.
So for an example a single frame will look like:
Array[0]=np.array([x_1,y_1,I_1],[x_2,y_2,I_2],...,[x_200,y_200,I_200])
So we have 2000 frames of 200 points each. These points are arbitrarily truncated every 200 and are actually sequential. So I can feasibly reshape the array into:
Array=np.array(np.array([x_1,y_1,I_1],[x_2,y_2,I_2],...,[x_400000,y_400000,I_400000])
Which is no problem for me. I know how to do this.
My question is how can I animate a scatter plot that adaptively moves through the points instead of displaying 200 point bins? The code below allows me to plot an animated scatter plot with frames (1-200,201-400,401-600,etc) but the result is not very smooth to the eye. Ideally I would like something that updates at every point or at least every 10 points so for example frames (1-200,2-201,3-202,etc) or (1-200,11-210,21-200,etc)
numframes=len(Array)
plt.ion()
fig, ax = plt.subplots()
norm = plt.Normalize(Array[:][:,2].min(), Array[:][:,2].max())
sc = ax.scatter(Array[0][:,0], Array[0][:,1], c=Array[0][:,2], cmap=cm.hot, s=5)
plt.xlim(-40,40)
plt.ylim(0,200)
plt.draw()
for i in range(numframes):
sc.set_offsets(np.c_[Array[i][:,0], Array[i][:,1]])
sc.set_array(Array[i][:,2])
print(i)
plt.pause(0.1)
plt.ioff()
plt.show()
The code below steps continuously through my array of points with a given step size and window of 200 instead of discretely binning every 200.
stepsize=10
NewArray=np.ravel(Array)
NewArray.reshape(2000*200,3)
plt.ion()
fig, ax = plt.subplots()
norm = plt.normalize(NewArray[:,2].min(), NewArray[:,2].max())
sc = ax.scatter(NewArray[0:200,0], NewArray[0:200,1], c=NewArray[0:200,2], cmap=cm.jet, s=5)
plt.xlim(-40,40)
plt.ylim(0,200)
plt.draw()
for i in range(len(NewArray//stepsize)-200):
sc.set_offsets(np.c_[NewArray[(i*stepsize):(i*stepsize)+200,0],\
NewArray[(i*stepsize):(i*stepsize)+200,1]])
sc.set_array(NewArray[(i*stepsize):(i*stepsize)+200,2])
plt.pause(0.1)
plt.ioff()
plt.show()
Original Post
I need to make several subplots with different sizes.
I have simulation areas of the size (x y) 35x6µm to 39x2µm and I want to plot them in one figure. All subplots have the same x-ticklabels (there is a grid line every 5µm on the x-axis).
When I plot the subplots into one figure, then the graphs with the small x-area are streched, so that the x-figuresize is completely used. Therefore, the x-gridlines do not match together anymore.
How can I achieve that the subplots aren't streched anymore and are aligned on the left?
Edit: Here is some code:
size=array([[3983,229],[3933,350],[3854,454],[3750,533],[3500,600]], dtype=np.float)
resolution=array([[1024,256],[1024,320],[1024,448],[1024,512],[1024,640]], dtype=np.float)
aspect_ratios=(resolution[:,0]/resolution[:,1])*(size[:,1]/size[:,0])
number_of_graphs=len(data)
fig, ax=plt.subplots(nrows=number_of_graphs, sharex=xshare)
fig.set_size_inches(12,figheight)
for i in range(number_of_graphs):
temp=np.rot90(np.loadtxt(path+'/'+data[i]))
img=ax[i].imshow(temp,
interpolation="none",
cmap=mapping,
norm=specific_norm,
aspect=aspect_ratios[i]
)
ax[i].set_adjustable('box-forced')
#Here I have to set some ticks and labels....
ax[i].xaxis.set_ticks(np.arange(0,int(size[i,0]),stepwidth_width)*resolution[i,0]/size[i,0])
ax[i].set_xticklabels((np.arange(0, int(size[i,0]), stepwidth_width)))
ax[i].yaxis.set_ticks(np.arange(0,int(size[i,1]),stepwidth_height)*resolution[i,1]/size[i,1])
ax[i].set_yticklabels((np.arange(0, int(size[i,1]), stepwidth_height)))
ax[i].set_title(str(mag[i]))
grid(True)
savefig(path+'/'+name+'all.pdf', bbox_inches='tight', pad_inches=0.05) #saves graph
Here are some examples:
If I plot different matrices in a for loop, the iPhython generates an output which is pretty much what I want. The y-distande between each subplot is constant, and the size of each figure is correct. You can see, that the x-labels match to each other:
When I plot the matrices in one figure using subplots, then this is not the case: The x-ticks do not fit together, and every subplot has the same size on the canvas (which means, that for thin subplots there is more white space reservated on the canvas...).
I simply want the first result from iPython in one output file using subplots.
Using GridSpec
After the community told me to use GridSpec to determine the size of my subplots directly I wrote a code for automatic plotting:
size=array([[3983,229],[3933,350],[3854,454],[3750,533],[3500,600]], dtype=np.float)
#total size of the figure
total_height=int(sum(size[:,1]))
total_width=int(size.max())
#determines steps of ticks
stepwidth_width=500
stepwidth_height=200
fig, ax=plt.subplots(nrows=len(size))
fig.set_size_inches(size.max()/300., total_height/200)
gs = GridSpec(total_height, total_width)
gs.update(left=0, right=0.91, hspace=0.2)
height=0
for i in range (len(size)):
ax[i] = plt.subplot(gs[int(height):int(height+size[i,1]), 0:int(size[i,0])])
temp=np.rot90(np.loadtxt(path+'/'+FFTs[i]))
img=ax[i].imshow(temp,
interpolation="none",
vmin=-100,
vmax=+100,
aspect=aspect_ratios[i],
)
#Some rescaling
ax[i].xaxis.set_ticks(np.arange(0,int(size[i,0]),stepwidth_width)*resolution[i,0]/size[i,0])
ax[i].set_xticklabels((np.arange(0, int(size[i,0]), stepwidth_width)))
ax[i].yaxis.set_ticks(np.arange(0,int(size[i,1]),stepwidth_height)*resolution[i,1]/size[i,1])
ax[i].set_yticklabels((np.arange(0, int(size[i,1]), stepwidth_height)))
ax[i].axvline(antenna[i]) #at the antenna position a vertical line is plotted
grid(True)
#colorbar
cbaxes = fig.add_axes([0.93, 0.2, 0.01, 0.6]) #[left, bottom, width, height]
cbar = plt.colorbar(img, cax = cbaxes, orientation='vertical')
tick_locator = ticker.MaxNLocator(nbins=3)
cbar.locator = tick_locator
cbar.ax.yaxis.set_major_locator(matplotlib.ticker.AutoLocator())
cbar.set_label('Intensity',
#fontsize=12
)
cbar.update_ticks()
height=height+size[i,1]
plt.show()
And here is the result....
Do you have any ideas?
What about using matplotlib.gridspec.GridSpec? Docs.
You could try something like
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
gs = GridSpec(8, 39)
ax1 = plt.subplot(gs[:6, :35])
ax2 = plt.subplot(gs[6:, :])
data1 = np.random.rand(6, 35)
data2 = np.random.rand(2, 39)
ax1.imshow(data1)
ax2.imshow(data2)
plt.show()