How to change spines linewidth in 3D plot? - python

When you have a 2D plot in matplolib you can change the line width of spines (the containing box) as follows:
fig, ax = plt.subplots()
ax.plot([1,2,3])
spines = ax.spines
[i.set_linewidth(5) for i in spines.values()]
Figure with thick spines:
However this same methodology does not work for 3D plots.
How could I change the axes line thickness for a 3D plot?

You can do this using the following code. I adapted the idea of accessing the axes in 3D from this answer. If you want to change the thickness of all the grid lines as well, refer to this answer by ImportanceOfBeingErnest
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for axis in [ax.w_xaxis, ax.w_yaxis, ax.w_zaxis]:
axis.line.set_linewidth(5)

Related

How to use twinx and still get square plot

How do I show a plot with twin axes such that the aspect of the top and right axes are 'equal'. For example, the following code will produce a square plot
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.plot([0,1],[0,1])
But this changes as soon as you use the twinx function.
ax2 = ax.twinx()
ax2.set_ylim([0,2])
ax3 = ax.twiny()
ax3.set_xlim([0,2])
Using set_aspect('equal') on ax2 and ax3 seems to force it the the aspect of ax, but set_aspect(0.5) doesn't seem to change anything either.
Put simply, I would like the plot to be square, the bottom and left axes to run from 0 to 1 and the top and right axes to run from 0 to 2.
Can you set the aspect between two twined axes? I've tried stacking the axes:
ax3 = ax2.twiny()
ax3.set_aspect('equal')
I've also tried using the adjustable keyword in set_aspect:
ax.set_aspect('equal', adjustable:'box-forced')
The closest I can get is:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal', adjustable='box-forced')
ax.plot([0,1],[0,1])
ax2=ax.twinx()
ax3 = ax2.twiny()
ax3.set_aspect(1, adjustable='box-forced')
ax2.set_ylim([0,2])
ax3.set_xlim([0,2])
ax.set_xlim([0,1])
ax.set_ylim([0,1])
Which produces:
I would like to remove the extra space to the right and left of the plot
It seems overly complicated to use two different twin axes to get two independent set of axes. If the aim is to create one square plot with one axis on each side of the plot, you may use two axes, both at the same position but with different scales. Both can then be set to have equal aspect ratios.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.plot([0,1],[0,1])
ax2 = fig.add_axes(ax.get_position())
ax2.set_facecolor("None")
ax2.set_aspect('equal')
ax2.plot([2,0],[0,2], color="red")
ax2.tick_params(bottom=0, top=1, left=0, right=1,
labelbottom=0, labeltop=1, labelleft=0, labelright=1)
plt.show()

matplotlib: reduce axes width in subplots

I have a matplotlib bar chart, which bars are colored according to some rules through a colormap. I need a colorbar on the right of the main axes, so I added a new axes with
fig, (ax, ax_cbar) = plt.subplots(1,2)
and managed to draw my color bar in the ax_bar axes, while I have my data displayed in the ax axes. Now I need to reduce the width of the ax_bar, because it looks like this:
How can I do?
Using subplots will always divide your figure equally. You can manually divide up your figure in a number of ways. My preferred method is using subplot2grid.
In this example, we are setting the figure to have 1 row and 10 columns. We then set ax to be the start at row,column = (0,0) and have a width of 9 columns. Then set ax_cbar to start at (0,9) and has by default a width of 1 column.
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
num_columns = 10
ax = plt.subplot2grid((1,num_columns), (0,0), colspan=num_columns-1)
ax_cbar = plt.subplot2grid((1,num_columns), (0,num_columns-1))
The ususal way to add a colorbar is by simply putting it next to the axes:
fig.colorbar(sm)
where fig is the figure and sm is the scalar mappable to which the colormap refers. In the case of the bars, you need to create this ScalarMappable yourself. Apart from that there is no need for complex creation of multiple axes.
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
fig , ax = plt.subplots()
x = [0,1,2,3]
y = np.array([34,40,38,50])*1e3
norm = matplotlib.colors.Normalize(30e3, 60e3)
ax.bar(x,y, color=plt.cm.plasma_r(norm(y)) )
ax.axhline(4.2e4, color="gray")
ax.text(0.02, 4.2e4, "42000", va='center', ha="left", bbox=dict(facecolor="w",alpha=1),
transform=ax.get_yaxis_transform())
sm = plt.cm.ScalarMappable(cmap=plt.cm.plasma_r, norm=norm)
sm.set_array([])
fig.colorbar(sm)
plt.show()
If you do want to create a special axes for the colorbar yourself, the easiest method would be to set the width already inside the call to subplots:
fig , (ax, cax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios" : [10,1]})
and later put the colorbar to the cax axes,
fig.colorbar(sm, cax=cax)
Note that the following questions have been asked for this homework assignment already:
Point picker event_handler drawing line and displaying coordinates in matplotlib
Matplotlib's widget to select y-axis value and change barplot
Display y axis value horizontal line drawn In bar chart
How to change colors automatically once a parameter is changed
Interactively Re-color Bars in Matplotlib Bar Chart using Confidence Intervals

Matplotlib subplot: imshow + plot

I want to create a plot that looks like the image below. There are two unique plots in the figure. img1 was generated using plt.imshow(), while img2 was generated using plt.plot(). The code I used to generate each of the plots is provided below
plt.clf()
plt.imshow(my_matrix)
plt.savefig("mymatrix.png")
plt.clf()
plt.plot(x,y,'o-')
plt.savefig("myplot.png")
The matrix used in img1 is 64x64. The same range for img2's x-axis (x=range(64)). Ideally, the x-axes of the two img2's align with the axes of img1.
It is important to note that the final image is reminiscent of seaborn's jointplot(), but the marginal subplots (img2) in the image below do not show distribution plots.
You can use the make_axes_locatable functionality of the mpl_toolkits.axes_grid1 to create shared axes along both directions of the central imshow plot.
Here is an example:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np; np.random.seed(0)
Z = np.random.poisson(lam=6, size=(64,64))
x = np.mean(Z, axis=0)
y = np.mean(Z, axis=1)
fig, ax = plt.subplots()
ax.imshow(Z)
# create new axes on the right and on the top of the current axes.
divider = make_axes_locatable(ax)
axtop = divider.append_axes("top", size=1.2, pad=0.3, sharex=ax)
axright = divider.append_axes("right", size=1.2, pad=0.4, sharey=ax)
#plot to the new axes
axtop.plot(np.arange(len(x)), x, marker="o", ms=1, mfc="k", mec="k")
axright.plot(y, np.arange(len(y)), marker="o", ms=1, mfc="k", mec="k")
#adjust margins
axright.margins(y=0)
axtop.margins(x=0)
plt.tight_layout()
plt.show()

mplot3D fill_between extends over axis limits

I have questions related to creating a simple lineplot in Python with mplot3D where the area under the plot is filled. I am using Python 2.7.5 on RedHatEnterprise 7.2, matplotlib 1.2.0 and numpy 1.7.2.
Using the code below, I am able to generate a line plot. This is displayed as expected with the beginning / end of the plot set by the limits of the imported data set.
I am then trying to fill the area between the line plot and -0.1 using the answer given by Bart from Plotting a series of 2D plots projected in 3D in a perspectival way. This works, however, the filled area is continued beyond the limits of the data set. This is also the case when running the example from the link.
This screen shot shows the plot generated with filled area extending beyond the set axis limits.
How do I achieve that the filled area is only the range of the data set or the axis limits whichever is smaller?
How do I add a legend for those plots onto the figure?
Code as follows:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
x,y = genfromtxt("data.dat",unpack=True)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
ax.plot(x,y,1,zdir="y",label="line plot")
ax.legend()
ax.set_xlim3d(852.353,852.359)
ax.set_zlim3d(-0.1,5)
ax.set_ylim3d(0,2)
ax.get_xaxis().get_major_formatter().set_useOffset(False)
plt.show()
I don't know how to put fill_between working the way you want it to, but I can provide an alternative using a 3D polygon:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection # New import
#x,y = genfromtxt("data.dat",unpack=True)
# Generated some random data
w = 3
x,y = np.arange(100), np.random.randint(0,100+w,100)
y = np.array([y[i-w:i+w].mean() for i in range(3,100+w)])
z = np.zeros(x.shape)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
verts = [(x[i],z[i],y[i]) for i in range(len(x))] + [(x.max(),0,0),(x.min(),0,0)]
ax.add_collection3d(Poly3DCollection([verts],color='orange')) # Add a polygon instead of fill_between
ax.plot(x,z,y,label="line plot")
ax.legend()
ax.set_ylim(-1,1)
plt.show()
The code above generates some random data. Builds vertices from it and plots a polygon with those vertices. This will give you the plot you wish (but does not use fill_between). The result is:

modifying colorbar in matplotlib and seaborn

I'm trying to save an image I generated using seaborn. The image is 4x4 confusion matrix ('confmat' np.array). I learned that when I save the image in vector format, certain viewers have issues resulting in white lines on colorbar, quoting from matplotlib reference:
It is known that some vector graphics viewer (svg and pdf) renders
white gaps between segments of the colorbar. This is due to bugs in
the viewers not matplotlib. As a workaround the colorbar can be
rendered with overlapping segments:
cbar = colorbar()
cbar.solids.set_edgecolor("face")
draw()
However, I have trouble doing what is suggested.
Here is what I did:
import seaborn as sns
import matplotlib.pyplot as plt
cmap=plt.cm.Blues
fig, ax = plt.subplots()
ax = sns.heatmap(confmat, annot=True, cmap=cmap)
ax.set_title('title')
ax.tick_params(
axis='both', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom='off', # ticks along the bottom edge are off
top='off', # ticks along the top edge are off
labelbottom='off', # labels along the bottom edge are off
labelleft='off',
right='off')
fig.savefig('confusion_matrix.svg', format='svg')
I tried to get colorbar using
cbar = ax.colorbar()
But get an error AttributeError: 'AxesSubplot' object has no attribute 'colorbar'.
I searched for solution and found a few questions here that suggest using plt.imshow() to get the colorbar object, but I'm completely confused about what I'm doing by now.
Can someone suggest, and if possible, explain why, the solution for implementing what matplotlib documentation has offered for colorbar?
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
x = np.random.randn(10, 10)
f, ax = plt.subplots()
sns.heatmap(x)
cbar_ax = f.axes[-1]
cbar_solids = cbar_ax.collections[0]
cbar_solids.set_edgecolor("face")
f.savefig("heatmap.svg")
Changing a colorbar with cb.solid.set_edgecolor("face") as suggested in the matplotlib docs appears to be a bit of a hack to ensure there are no white lines between elements on the colorbar. I think seaborn is designed assuming you should be able to do everything you need by passing kwargs (cbar_kws in heatmap). For example, you can pass cb_kwargs to the sns.heatmap function cbar_kws={"drawedges": "False"} but unfortunately this doesn't fix the problem.
As the Seaborn Heatmap only returns an axis handle on which the heatplot and the colorbar are plotted, you don't have direct access to the mappable object, cbar in the source code. As a result you can't apply this hack.
One solution is to just plot this using pcolormesh and colorbar. I think seaborn actually redefines matplotlib styles so should look the same,
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
cmap=plt.cm.Blues
fig, ax = plt.subplots()
confmat = np.random.rand(4, 4)
cb = ax.pcolormesh(confmat, cmap=cmap)
ax.set_title('title')
ax.tick_params(
axis='both', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom='off', # ticks along the bottom edge are off
top='off', # ticks along the top edge are off
labelbottom='off', # labels along the bottom edge are off
labelleft='off',
right='off')
cbar = plt.colorbar(cb)
cbar.solids.set_edgecolor("face")
plt.draw()
fig.savefig('confusion_matrix.svg', format='svg')
The result for me looks to be rid of the white lines when you zoom in.

Categories