This question already has answers here:
Removing axes margins in 3D plot
(2 answers)
Closed 4 years ago.
I'm having an issue with setting limits for my 3d plots in matplotlib; I'm finding that no matter how I set my limits for the x,y, and z axes, the plotting routine for 3dplots adds an extra buffer.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.axes.set_xlim3d(left=0, right=10)
ax.axes.set_ylim3d(bottom=0, top=10)
ax.axes.set_zlim3d(bottom=0, top=10)
plt.show()
This produces the following plot:
As you can see, the limits are supposed to be at x, y, z = {0, 10} however the 3D plotting always adds a little bit of a buffer to each edge. Does anyone know a way to turn this effect off?
I've also used plt.xlims(); and ax.axes.set_xlims() but they produce the same effect.
I think this is deliberate (see e.g. this), if you try plotting ax.axes.set_xlim3d(left=0.000001, right=9.9999999) then you get no 0 or 10 displayed on your figure.
Even making the numbers as arbitrarily close as possible doesn't work, e.g.
eps = 1e-16
ax.axes.set_xlim3d(left=0.-eps, right=10+eps)
ax.axes.set_ylim3d(bottom=0.-eps, top=10+eps)
ax.axes.set_zlim3d(bottom=0.-eps, top=10+eps)
The best solution I've found is to set the ticks manually and then slightly scale so the overlap is hidden.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.set_xticks([0,2,4,6,8,10])
ax.set_yticks([0,2,4,6,8,10])
ax.set_zticks([0,2,4,6,8,10])
ax.axes.set_xlim3d(left=0.2, right=9.8)
ax.axes.set_ylim3d(bottom=0.2, top=9.8)
ax.axes.set_zlim3d(bottom=0.2, top=9.8)
plt.show()
This gives,
This is pretty hacky but could be made more general (and I always end up setting ticks manually for publication quality figures). Alternatively, it may be better to turn off the lowest grid line or hide the grid...
Related
This question already has an answer here:
Inconsistent figsize resizing in matplotlib
(1 answer)
Closed 3 years ago.
I am trying to generate a bar chart with lots of bars. If I keep the figsize at defaults, the data is squeezed together and the plot is unusable.
I have the following code snippet to reproduce my problem:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(1)
ax = fig.add_subplot(111)
N=100
# Example data
labels = [chr(x) for x in range(N)]
y_pos = np.arange(len(labels))
performance = 3 + 10 * np.random.rand(len(labels))
error = np.random.rand(len(labels))
ax.barh(y_pos, performance, xerr=error, align='center')
ax.set_yticks(y_pos)
ax.set_yticklabels(labels)
ax.set_xlabel('Performance')
ax.set_title('How fast do you want to go today?')
plt.savefig('a.png', bbox_inches='tight')
plt.show()
If I manually set the height of the figure (for example figsize=(8,N*0.2)), the plotted data looks nice, but there is an annoying vertical whitespace before the firs bar and after the last one.
Is there any way to automatically size the plot properly?
One thing I've used is
plt.tight_layout()
it generates less whitespace for subplots, but may work with just 1 plot.
Here's more info:
https://matplotlib.org/users/tight_layout_guide.html
Another thing that may work is aspect auto when showing the plot.
plt.imshow(X, aspect='auto')
Yet another solution is 'scaled' axis
plt.axis('scaled') #this line fits your images to screen
Also if you mean the overall plot size,I generally just pick a generic size that fits, say 15 x 10 on a laptop screen, or 30x20 on a monitor. Guess and test.
Is there a simple way to animate a scatterplot in matplotlib, in a similar way to which the plot is created?
I know currently I can do this to create the plot:
scatter = ax.scatter([x values], [y values], [z values])
However, every example I find online uses numpy functions to generate its data rather than something external like three lists, leaving me with much difficulty understanding how the data is modified in the method which updates the animation.
Is it possible to give matplotlib an entirely new set of data to plot for each frame? (every point of data will change anyway)
Note: in case there are special considerations for this situation, this is a 3D plot.
The easiest way to animate is to plot in interactive mode, as a minimal(ish) examples with lists,
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plt.show()
for i in range(1000):
x =[1,2,3,4,5,6,7,8,9,10]
y =[5,6+i%10,2,3,13,4,1,2,4-i%10,8]
z =[2+(i%10)*2.,3,3,3,5,7,9+i%10,11,9+i%10,10]
ax.scatter(x, y, z, marker='o')
ax.set_xlim([0.,10.])
ax.set_ylim([0.,20.])
ax.set_zlim([0.,20.])
plt.pause(0.01)
plt.cla()
A reason to plot using numpy arrays instead of lists is the data is stored as a contiguous block and this speeds up plots (it's easy to convert to an array with xn = np.array(x)). The main reason most examples will use various numpy functions is that it is just easier to provide a self contained demonstration with animation in matplotlib requiring a function which adjusts the collection object. For a great example of a minimum scatter plot animation, see the second example of this.
This question already has answers here:
How to show minor tick labels on log-scale with Matplotlib
(2 answers)
Closed 7 years ago.
Upon adding the line plt.yscale('log') to my simple plotting script
import numpy as np
residuals = np.loadtxt('res_jacobi.txt', skiprows=1)
import matplotlib.pyplot as plt
fig = plt.figure()
steps = np.arange(0, len(residuals), 1)
plt.plot(steps, residuals, label='$S$')
plt.xlabel("Step",fontsize=20)
plt.ylabel("$S$",fontsize=20)
plt.ylim(0.95 * min(residuals), 1.05 * max(residuals))
plt.yscale('log')
plt.savefig('jacobi-res.pdf', bbox_inches='tight', transparent=True)
the y labels disappear.
I'm sure there is simple fix for this but searching did not turn one up. Any help would be much appreciated.
The normal behavior for matplotlib is to only label major tick marks in log-scaling --- which are even orders of magnitude, e.g. {0.1, 1.0}. Your values are all between those. You can:
rescale your axes to larger bounds,
plt.gca().set_ylim(0.1, 1.0)
label the tick-marks manually,
plt.gca().yaxis.set_minor_formatter(FormatStrFormatter("%.2f"))
semilogy works for me.
Change:
plt.plot(steps, residuals, label='$S$')
Into:
plt.semilogy(steps, residuals, label='$S$')
Remove plt.yscale('log')
So I'm trying to draw two subplots in the same figure that share the x axis. However, I cannot get it to draw the last minor xtick. I have no idea from where this behaviour comes, but I managed to reproduce it with random data.
The system used is python2.7 and matplotlib v1.2.1
So here goes my minimal error-reproducing example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from matplotlib.ticker import MaxNLocator
xdat = np.linspace(0,6.6,endpoint=True)
ydat1 = np.random.rand(50)*500
ydat2 = np.random.rand(50)*4
fig = plt.figure(figsize=(6,8), dpi=72)
gs = gridspec.GridSpec(2,1, height_ratios=[3,1])
fig.subplots_adjust(hspace=0.0)
ax1 = plt.subplot(gs[0])
ax1.plot(xdat, ydat1)
ax1.set_xlim(0,6.6)
ax1.set_xticks(range(0,8,1))
ax1.minorticks_on()
[label.set_visible(False) for label in ax1.get_xticklabels() ] # Make tick labels invisible
ax2 = plt.subplot(gs[1], sharex=ax1)
ax2.plot(xdat, ydat2, 'r-')
ax2.yaxis.set_major_locator(MaxNLocator(nbins=5, steps=[1,2,4,5,10], symmetric=False, prune='upper'))
plt.show()
I got the following image:
I have no idea whether I found a bug or if there is an easy way to alleviate this problem (i.e. update matplotlib).
Haven't been able to find where the bug comes from yet, but version 1.3.1 has the same behavior.
A work around would be to set the minor ticks manually, by adding a ax2.xaxis.set_ticks(np.hstack((ax2.xaxis.get_ticklocs(minor=True), 6.4)), minor=True), where 6.4 is the last minor tick.
Or you can force the xlim to be slightly larger than the default and the last tick will come out. ax2.set_xlim((0,6.6)). The default is (0.0, 6.5999999999999996).
I guess it can be considered as a bug.
In the following code snippet:
import numpy as np
import pandas as pd
import pandas.rpy.common as com
import matplotlib.pyplot as plt
mtcars = com.load_data("mtcars")
df = mtcars.groupby(["cyl"]).apply(lambda x: pd.Series([x["cyl"].count(), np.mean(x["wt"])], index=["n", "wt"])).reset_index()
plt.plot(df["n"], range(len(df["cyl"])), "o")
plt.yticks(range(len(df["cyl"])), df["cyl"])
plt.show()
This code outputs the dot plot graph, but the result looks quite awful, since both the xticks and yticks don't have enough space, that it's quite difficult to notice both 4 and 8 of the cyl variable output its values in the graph.
So how can I plot it with enough space in advance, much like you can do it without any hassles in R/ggplot2?
For your information, both of this code and this doesn't work in my case. Anyone knows the reason? And do I have to bother to creating such subplots in the first place? Is it impossible to automatically adjust the ticks with response to the input values?
I can't quite tell what you're asking...
Are you asking why the ticks aren't automatically positioned or are you asking how to add "padding" around the inside edges of the plot?
If it's the former, it's because you've manually set the tick locations with yticks. This overrides the automatic tick locator.
If it's the latter, use ax.margins(some_percentage) (where some_percentage is between 0 and 1, e.g. 0.05 is 5%) to add "padding" to the data limits before they're autoscaled.
As an example of the latter, by default, the data limits can be autoscaled such that a point can lie on the boundaries of the plot. E.g.:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(10), 'ro')
plt.show()
If you want to avoid this, use ax.margins (or equivalently, plt.margins) to specify a percentage of padding to be added to the data limits before autoscaling takes place.
E.g.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(10), 'ro')
ax.margins(0.04) # 4% padding, similar to R.
plt.show()