I'm drawing a polygon in matplotlib and exporting it in .svg so I can use it in eg. Inkscape. I know you can specify the image size in inches, but I'd like to have a line with length "1" to be mapped to a line with length 1 inch when opened in Inkscape.
import matplotlib.pyplot as plt
plt.close('all')
f,ax = plt.subplots(figsize=(3,3))
plt.plot([0,1],[0,0])
ax.axis('off')
plt.gca().set_position([0, 0, 1, 1])
ax.set_aspect('equal')
plt.savefig("line.svg")
It is unclear what ax.set_aspect('equal') would do here. So I'd remove that.
Next, what you need to do is to have the axis coordinate system synchronized to the figure size.
At this point it's unclear from the question what exactly you are trying to do.
Create a 3 inch figure with a 3 inch long line
Let the coordinate system go from 0 to 1, remove any margin inside the axes.
import matplotlib.pyplot as plt
f,ax = plt.subplots(figsize=(3,3))
plt.plot([0,1],[0,0])
ax.axis('off')
plt.gca().set_position([0, 0, 1, 1])
ax.margins(0)
plt.savefig("line.svg")
or let the coordinate system go from 0 to 3, make the line 3 units long.
import matplotlib.pyplot as plt
f,ax = plt.subplots(figsize=(3,3))
plt.plot([0,3],[0,0])
ax.axis('off')
plt.gca().set_position([0, 0, 1, 1])
ax.set_xlim(0,3)
plt.savefig("line.svg")
Create a 3 inch figure with a 1 inch long line
Let the coordinate system go from 0 to 1, create a line of 1/3 units in length
import matplotlib.pyplot as plt
f,ax = plt.subplots(figsize=(3,3))
plt.plot([0,1/.3],[0,0])
ax.axis('off')
plt.gca().set_position([0, 0, 1, 1])
ax.set_xlim(0,1)
plt.savefig("line.svg")
or let the coordinate system go from 0 to 3 and create a line of 1 unit length.
import matplotlib.pyplot as plt
f,ax = plt.subplots(figsize=(3,3))
plt.plot([0,1],[0,0])
ax.axis('off')
plt.gca().set_position([0, 0, 1, 1])
ax.set_xlim(0,3)
plt.savefig("line.svg")
This last approach seems to be the most intuitive one, but since it's not too clear from the question how the size in inches of the figure should relate to the axes units, I provided all possible solutions.
Related
I would like to create sth like the following graph in matplotlib:
I have x = [0, 1, ..., 10], and for each x I have values from range [0, 60]. Lets say that the black line is the quantile of values for a given i from range x. For selected i I want to add horizontally histogram (with parameter density = True) like in the picture with the possibility to control the width of this histogram (in the picture it goes from 2 to 5 but I would like to set fixed width). How can I do that?
Yes, this is relatively straightforward with inset_axes:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
x = np.random.randn(100)
ax.plot(x)
ylim = ax.get_ylim()
histax = ax.inset_axes([0.3, 0, 0.2, 1], transform=ax.transAxes)
histax.hist(x, orientation='horizontal', alpha=0.5 )
histax.set_facecolor('none')
histax.set_ylim(ylim)
plt.show()
You will probably want to clean up the axes etc, but that is the general idea.
I'm trying to create a radial plot with a logrithmic scale on the r-axis, but the tick labels for the theta are coming up inside the plot.
import numpy as np
from matplotlib import pyplot as plt
np.random.seed(1)
r = 10**(1 + 2*np.random.rand(36))
theta = 2 * np.pi * np.linspace(0, 1, 37)
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
ax.plot(theta, r)
# We need to reset the minimum r-limit to avoid log(0)
ax.set_rlim(0.1, None)
ax.set_rscale('log')
plt.show()
The theta ticks are inside the figure, which doesn't look so bad here, but are hidden for e.g. a pcolormesh plot - for comparison, if I comment out the set_rlim and set_rscale lines, we get the following with the desired location for the ticks. (For anyone using dark mode, the image background is transparent so the ticks might not show inline.)
I've tried looking at the ax.get_xticklabels but the y-position (equivalently the r-position) is 0.
[Text(0.0, 0, '0°'),
Text(0.7853981633974483, 0, '45°'),
Text(1.5707963267948966, 0, '90°'),
Text(2.356194490192345, 0, '135°'),
Text(3.141592653589793, 0, '180°'),
Text(3.9269908169872414, 0, '225°'),
Text(4.71238898038469, 0, '270°'),
Text(5.497787143782138, 0, '315°')]
Interestingly, if you increate the upper rlim (e.g. ax.set_rlim([0.1, 1e5])) the ticks move right to the edge of the figure.
You can use Axes.tick_params() to set the pad distance between the ticks and labels:
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
ax.plot(theta, r)
ax.set_rmin(0.1)
ax.set_rscale('log')
ax.tick_params(pad=35)
I am trying to make use the polar plot projection to make a radar chart. I would like to know how to put only one grid line in bold (while the others should remain standard).
For my specific case, I would like to highlight the gridline associated to the ytick "0".
from matplotlib import pyplot as plt
import pandas as pd
import numpy as np
#Variables
sespi = pd.read_csv("country_progress.csv")
labels = sespi.country
progress = sespi.progress
angles=np.linspace(0, 2*np.pi, len(labels), endpoint=False)
#Concatenation to close the plots
progress=np.concatenate((progress,[progress[0]]))
angles=np.concatenate((angles,[angles[0]]))
#Polar plot
fig=plt.figure()
ax = fig.add_subplot(111, polar=True)
ax.plot(angles, progress, '.--', linewidth=1, c="g")
#ax.fill(angles, progress, alpha=0.25)
ax.set_thetagrids(angles * 180/np.pi, labels)
ax.set_yticklabels([-200,-150,-100,-50,0,50,100,150,200])
#ax.set_title()
ax.grid(True)
plt.show()
The gridlines of a plot are Line2D objects. Therefore you can't make it bold. What you can do (as shown, in part, in the other answer) is to increase the linewidth and change the colour but rather than plot a new line you can do this to the specified gridline.
You first need to find the index of the y tick labels which you want to change:
y_tick_labels = [-100,-10,0,10]
ind = y_tick_labels.index(0) # find index of value 0
You can then get a list of the gridlines using gridlines = ax.yaxis.get_gridlines(). Then use the index you found previously on this list to change the properties of the correct gridline.
Using the example from the gallery as a basis, a full example is shown below:
r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
ax = plt.subplot(111, projection='polar')
ax.set_rmax(2)
ax.set_rticks([0.5, 1, 1.5, 2]) # less radial ticks
ax.set_rlabel_position(-22.5) # get radial labels away from plotted line
ax.grid(True)
y_tick_labels = [-100, -10, 0, 10]
ax.set_yticklabels(y_tick_labels)
ind = y_tick_labels.index(0) # find index of value 0
gridlines = ax.yaxis.get_gridlines()
gridlines[ind].set_color("k")
gridlines[ind].set_linewidth(2.5)
plt.show()
Which gives:
It is just a trick, but I guess you could just plot a circle and change its linewidth and color to whatever could be bold for you.
For example:
import matplotlib.pyplot as plt
import numpy as np
Yline = 0
Npoints = 300
angles = np.linspace(0,360,Npoints)*np.pi/180
line = 0*angles + Yline
ax = plt.subplot(111, projection='polar')
plt.plot(angles, line, color = 'k', linewidth = 3)
plt.ylim([-1,1])
plt.grid(True)
plt.show()
In this piece of code, I plot a line using plt.plot between any point of the two vectors angles and line. The former is actually all the angles between 0 and 2*np.pi. The latter is constant, and equal to the 'height' you want to plot that line Yline.
I suggest you try to decrease and increase Npoints while having a look to the documentaion of np.linspace() in order to understand your problem with the roundness of the circle.
I want to mark a line over two aligned subplots. Therefore, I use matplotlib.patches.ConnectionPatch as suggested in other answers. It worked already in other examples, but here for the second time, the line just is cut off at the second plot area.
How do I assure that the ConnectionPatch is plotted in the front?
I tried playing around with zorder, but did not find a solution yet.
from matplotlib.patches import ConnectionPatch
import matplotlib.pyplot as plt
xes=[-2, 0, 2]
field=[0, -10, 0]
potential=[-20, 0, 20]
fig, axs = plt.subplots(2, 1, sharex=True)
axs[0].plot(xes, field)
axs[1].plot(xes, potential)
# line over both plots
_, ytop = axs[0].get_ylim()
ybot, _ = axs[1].get_ylim()
n_p_border = ConnectionPatch(xyA=(0., ytop), xyB=(0., ybot),
coordsA='data', coordsB='data',
axesA=axs[0], axesB=axs[1], lw=3)
print(n_p_border)
axs[0].add_artist(n_p_border)
You would need to inverse the role of the two axes. This is also shown in Drawing lines between two plots in Matplotlib.
from matplotlib.patches import ConnectionPatch
import matplotlib.pyplot as plt
xes=[-2, 0, 2]
field=[0, -10, 0]
potential=[-20, 0, 20]
fig, axs = plt.subplots(2, 1, sharex=True)
axs[0].plot(xes, field)
axs[1].plot(xes, potential)
# line over both plots
_, ytop = axs[0].get_ylim()
ybot, _ = axs[1].get_ylim()
n_p_border = ConnectionPatch(xyA=(0., ybot), xyB=(0., ytop),
coordsA='data', coordsB='data',
axesA=axs[1], axesB=axs[0], lw=3)
axs[1].add_artist(n_p_border)
plt.show()
I'm currently working in a plot in which I show to datas combined.
I plot them with the following code:
plt.figure()
# Data 1
data = plt.cm.binary(data1)
data[..., 3] = 1.0 * (data1 > 0.0)
fig = plt.imshow(data, interpolation='nearest', cmap='binary', vmin=0, vmax=1, extent=(-4, 4, -4, 4))
# Plotting just the nonzero values of data2
x = numpy.linspace(-4, 4, 11)
y = numpy.linspace(-4, 4, 11)
data2_x = numpy.nonzero(data2)[0]
data2_y = numpy.nonzero(data2)[1]
pts = plt.scatter(x[data2_x], y[data2_y], marker='s', c=data2[data2_x, data2_y])
And this gives me this plot:
As can be seen in the image, my background and foreground squares are not aligned.
Both of then have the same dimension (20 x 20). I would like to have a way, if its possible, to align center with center, or corner with corner, but to have some kind of alignment.
In some grid cells it seems that I have right bottom corner alignment, in others left bottom corner alignment and in others no alignment at all, with degrades the visualization.
Any help would be appreciated.
Thank you.
As tcaswell says, your problem may be easiest to solve by defining the extent keyword for imshow.
If you give the extent keyword, the outermost pixel edges will be at the extents. For example:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(np.random.random((8, 10)), extent=(2, 6, -1, 1), interpolation='nearest', aspect='auto')
Now it is easy to calculate the center of each pixel. In X direction:
interpixel distance is (6-2) / 10 = 0.4 pixels
center of the leftmost pixel is half a pixel away from the left edge, 2 + .4/2 = 2.2
Similarly, the Y centers are at -.875 + n * 0.25.
So, by tuning the extent you can get your pixel centers wherever you want them.
An example with 20x20 data:
import matplotlib.pyplot as plt
import numpy
# create the data to be shown with "scatter"
yvec, xvec = np.meshgrid(np.linspace(-4.75, 4.75, 20), np.linspace(-4.75, 4.75, 20))
sc_data = random.random((20,20))
# create the data to be shown with "imshow" (20 pixels)
im_data = random.random((20,20))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(im_data, extent=[-5,5,-5,5], interpolation='nearest', cmap=plt.cm.gray)
ax.scatter(xvec, yvec, 100*sc_data)
Notice that here the inter-pixel distance is the same for both scatter (if you have a look at xvec, all pixels are 0.5 units apart) and imshow (as the image is stretched from -5 to +5 and has 20 pixels, the pixels are .5 units apart).
here is a code where there is no alignment problem.
import matplotlib.pyplot as plt
import numpy
data1 = numpy.random.rand(10, 10)
data2 = numpy.random.rand(10, 10)
data2[data2 < 0.4] = 0.0
plt.figure()
# Plotting data1
fig = plt.imshow(data1, interpolation='nearest', cmap='binary', vmin=0.0, vmax=1.0)
# Plotting data2
data2_x = numpy.nonzero(data2)[0]
data2_y = numpy.nonzero(data2)[1]
pts = plt.scatter(data2_x, data2_y, marker='s', c=data2[data2_x, data2_y])
plt.show()
which gives a perfectly aligned combined plots:
Thus the use of additional options in your code might be the reason of the non-alignment of the combined plots.