matplotlib: get axis ratio of plot - python

I need to produce scatter plots for several 2D data sets automatically.
By default the aspect ratio is set ax.set_aspect(aspect='equal'), which most of the times works because the x,y values are distributed more or less in a squared region.
Sometimes though, I encounter a data set that, when plotted with the equal ratio, looks like this:
i.e.: too narrow in a given axis. For the above image, the axis are approximately 1:8.
In such a case, an aspect ratio of ax.set_aspect(aspect='auto') would result in a much better plot:
Now, I don't want to set aspect='auto' as my default for all data sets because using aspect='equal' is actually the correct way of displaying such a scatter plot.
I need to fall back to using ax.set_aspect(aspect='auto') only for cases such as the one above.
The question: is there a way to know before hand if the aspect ratio of a plot will be too narrow if aspect='equal' is used? Like getting the actual aspect ratio of the plotted data set.
This way, based on such a number, I can adjust the aspect ratio to something more sane looking (i.e.: auto or some other aspect ratio) instead of 'equal'.

Something like this ought to do,
aspect = (max(x) - min(x)) / (max(y) - min(y))

The axes method get_data_ratio gives the aspect ratio of the bounds of your data as displayed.¹
ax.get_data_ratio()
for example:
M = 4.0
ax.set_aspect('equal' if 1/M < ax.get_data_ratio() < M else 'auto')
¹This is the reciprocal of #farenorth's answer when the axes are zoomed right around the data, i.e., when max(y) == max(ax.get_ylim()) since it is calculated using the ranges in ax.get_ybound and ax.get_xbound.

Related

Is there a way I can align the histogram with the function plot in this graph?

Here's a plot I currently have: (using Python)
The darkorange curve is my function, generated from
plt.plot(x,Yt,color = 'darkorange')
while the histogram comes from
plt.bar(dic.keys(), dic.values(), width=np.abs((rang2-rang1)/N), color='lightcoral')
From this graph we can see they are not quite aligned at the bottom (where both of them should be 0), I'm wondering is there a way I can make them aligned? Thanks!!
you might need to play around with the offset number below
offset = 0.01
Yt = [y-offset for y in Yt]
plt.plot(x,Yt,color = 'darkorange')
note that if you want to only offset outside the peak range of the function (the spiky part in the middle) you would need a non-constant offset.

Why error bars in log-scale matplotlib bar plot are lopsided?

I'm trying to plot some bar plots, where each y-value is averaged over some series. Consequently, I'm also trying to add the error bars (standard deviations) for each bar.
The magnitudes generally seem right, even in log scale, but for several of the bars, the error bar drops down (- direction) almost indefinitely, while the + direction error is the right magnitude. I don't think its just the log scaling, but any input is greatly appreciated. Here is a link to the plot
I've checked and the + direction error bars are correct, just not sure why/how they are are dropping down to the x-axis occasionally. Below is a simplified example.
y = [99.79999999999997, 0.11701249999999999, 0.00011250000000000004, 0.013393750000000001,0.007743750000000001,
0.01, 0.033906250000000006, 0.0009687500000000002, 0.04187500000000001, 0.0218, 0.0018062499999999997, 0.0005187500000000001]
std =[0.013662601021279521, 0.1500170651403811, 3.4156502553198664e-05, 0.001310709095617076,0.0006239324215543433,
0.0, 0.0021671698133741164,0.0018750000000000001, 0.005302515126491074,0.007984401459512583,0.0006297817082132506,4.0311288741492725e-05]
plt.figure() # Powder plot
plt.bar(np.arange(len(y)), y, yerr=std)
plt.yscale('log')
'key_list' is just a list of strings that will become the x-tick labels. 'width' is the bar offset to fit in pairs. 'cm' and 'kk' are just dictionaries of lists. This honestly seems like a rendering issue, but am mostly curious if any of you have encountered this.
Like mentioned in the comment, it is because your std is larger than y (for example std[1] > y[1], hence the log scale goes banana. You can fix this by introduce a small tolerance to the lower std:
tor = 1e-9
lower_std = [a - tor if a<b else b for a,b in zip(y,std)]
plt.figure()
plt.bar(np.arange(len(y)), y, yerr=(lower_std,std))
plt.yscale('log')
plt.show()
Output:
You should look at the relative error rather than trying to plot the standard deviation, or any other measure of variability.
To illustrate this with an example:
In your linear space, you will have x +/- delta_x to display.
Projected into your logarithmic space, this becomes: log(x) +/- log(delta_x). But remember that log(x) - log(y) = log(x/y).
Hence, your non-symmetric error bar, for example. If you learn more about relative error, you will find an appropriate symmetric error bar.
Enjoy your learning :)

Plot negative values on a log scale

I am doing some analysis to calculate the value of log_10(x) which is a negative number. I am now trying to plot these values, however, since the range of the answers is very large I would like to use a logarithmic scale for this. If I simply use plt.yscale('log') I get a message telling me UserWarning: Data has no positive values, and therefore cannot be log-scaled. I also cannot supply the values of x to plt.plot as the result of log_10(x) is so large and negative that the answer of x**(log_10(x)) is simply 0.
What might be the most straightforward way of plotting this data?
You can use
plt.yscale('symlog')
to set the scale to a symmetic log scale. This means that it will scale logarithmically to both sides of 0. Only using the negative part of the symlog scale would work just fine.
Two alternatives to ImportanceOfBeingErnest's solution:
Plot -log_10(x) on a semilog y axis and set the y-label to display negative units
Plot -log_10(-log_10(x)) on a linear scale
However, in all cases (including the solution proposed by ImportanceOfBeingErnest), the interpretation is not straightforward since you are displaying or calculating the log of a log.
Finally, in order to return the value for x, you need to calculate 10**(log_10(x)) not x**(log_10(x))

Set specific aspect ratio for narrow matrix - Matplotlib

I have a 13x1340 matrix that I usually plot correctly without the need to specify an aspect ratio.
However, I would now like to tweak that aspect ratio so that two matrices whose 13 rows correspond to different scales are plotted as rectangles of equal length but different height, proportionally to the corresponding axis scale.
I have tried to use the get_aspect() method to obtain the numerical value that is being used, but it returns 'auto'. I have tried to guess the value and found that it is close to 4.5/(1340*180), which looks like a completely absurd value to me. I expected it to be something closer to 13/1340, but perhaps I don't quite understand how aspect ratios are calculated.
Setting the aspect ratio to 1 gives me an incredibly thin figure, with the proper vertical size. As the value decreases, the figure becomes longer in length, until it reaches ~ 4.5/(1340*180). After that, it starts losing height while keeping a fixed length.
Figure size is set to 3 inches high by 7 inches large, and the dpi is set to 300 on the savefig() method.
The get_data_ratio() method returns a value slightly larger than 13*1340, although it is clear that this value is not the aspect ratio used do construct the figure.

Boxplot on distance Data - set Box manually to values

I have a bunch of 2d points and angles. To visualise the amount of movement i wanted to use a boxplot and plot the difference to the mean of the points.
I sucessfully visualised the angle jitter using python and matplotlib in the following boxplot:
Now i want to do the same for my position Data. After computing the euclidean distance all the data is positive, so a naive boxplot will give wrong results. For an Example see the boxplot at the bottom, points that are exactly on the mean have a distance of zero and are now outliers.
So my Question is:
How can i set the bottom end of the box and the whiskers manually onto zero?
If i should take another approach like a bar chart please tell me (i would like to use the same style though)
Edit:
It looks similar to the following plot at the moment (This a plot of the distance the angle have from their mean).
As you can see the boxplot does't cover the zero. That is correct for the data, but not for the meaning behind it! Zero is perfect (since it represents a points that was exactly in the middle of the angles) but it is not included in the boxplot.
I found out it has already been asked before in this question on SO. While not as exact duplicate, the other question contains the answer!
In matplotlib 1.4 will probably be a faster way to do it, but for now the answer in the other thread seems to be the best way to go.
Edit:
Well it turned out that i couldn't use their approach since i have plt.boxplot(data, patch_artist=True) to get all the other fancy stuff.
So i had to resort to the following ugly final solution:
N = 12 #number of my plots
upperBoxPoints= []
for d in data:
upperBoxPoints.append(np.percentile(d, 75))
w = 0.5 # i had to tune the width by hand
ind = range(0,N) #compute the correct placement from number and width
ind = [x + 0.5+(w/2) for x in ind]
for i in range(N):
rect = ax.bar(ind[i], menMeans[i], w, color=color[i], edgecolor='gray', linewidth=2, zorder=10)
# ind[i] position
# menMeans[i] hight of box
# w width
# color=color[i] as you can see i have a complex color scheme, use '#AAAAAAA' for colors, html names won't work
# edgecolor='gray' just like the other one
# linewidth=2 dito
# zorder=2 IMPORTANT you have to use at least 2 to draw it over the other stuff (but not to high or it is over your horizontal orientation lines
And the final result:

Categories