Determine plot size with grid Matplotlib - python

I'm plotting some data using plt.scatter(), and I want to change the size of the plot which is it on, however the only results which come up when you search 'change plot size' are things to do with changing the figure's size, which I am not looking to do.
To visualise my issue, I have a reproducible example where I'm trying to plot 4 points on a 10x10 grid, however the size of the scatter plot is determined by the data not the grid
The two graphs above demonstrate my problem, I am trying to plot the four points on the left graph on the 10x10 grid seen on the right graph. I have added in a datapoint at (10, 10) to show this.
My code is currently:
x = [1, 2, 3, 4]
y = [1, 2, 3, 4]
fig = plt.figure()
ax = fig.gca()
ax.set_xticks(np.arange(0, 11, 1))
ax.set_yticks(np.arange(0, 11, 1))
plt.grid()
plt.scatter(x, y)
Which produces the left graph.

IIUC:
x = [1, 2, 3, 4]
y = [1, 2, 3, 4]
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 10)
plt.grid()
plt.scatter(x, y)
Output:

Just change the limits of x and y axes:
plt.xlim(0,11)
plt.ylim(0,11)

Related

Prevent axes from cutting off dots in matplotlib scatter plots

import matplotlib.pyplot as plt
x = [1, 2, 3, 4]
y = [0, 1, 7, 2]
plt.scatter(x, y, color='red')
plt.title('number of iterations')
plt.xlim([1, 4])
plt.ylim([1, 8])
If one was to plot this data, the dots on the axes are partially cut off. Is there a way to prevent this (i.e. can the dots be plotted on top of the axes)?
Setting the clip_on attribute to False allows you to go beyond the axes, but by default the axes will be on top. For example, the script
x = [1, 2, 3, 4]
y = [0, 1, 7, 2]
plt.scatter(x, y, color="red", clip_on=False)
plt.title('number of iterations')
plt.xlim([1, 4])
plt.ylim([1, 8])
Yields the following.
Note that the axes "cut through" the dots. If you want the dots to go on top of the axes/labels, you need to change the default zorder. For example, the script
x = [1, 2, 3, 4]
y = [0, 1, 7, 2]
plt.scatter(x, y, color="red", clip_on=False, zorder = 10)
plt.title('number of iterations')
plt.xlim([1, 4])
plt.ylim([1, 8])
yields
Note: any zorder value 3 or greater will work here.
Set clip_on to False:
plt.scatter(x, y, color='red', clip_on=False)

Matplotlib: How to add another X-axis where the ticks correspond to the points on the graph with custom labels

I want to plot data obtained from a script.
My code is something like this:
f, ax = plt.subplots()
a = [10, 20, 30] # N Total Items
b = [1, 5, 10] # Time (s)
c = [2, 6, 7] # N items of lenght 1
ax.plot(a, b, '-m^')
ax2 = ax.twiny()
ax2.set_xticks(ax.get_xticks())
ax2.set_xticklabels(c)
ax.set_xlabel("N Total Items")
ax.set_ylabel("Time (s)")
ax2.set_xlabel("N items of lenght 1")
plot
I want the C values to represent ticks that correspond to each of the points drawn on the graph. So, "2" would correspond to point (10, 1), "6" to point (20, 5), etc.
Is there any way to achieve this with matplotlib? Thank you in advance.
This is one way to do so by aligning the axis limits and then replacing the tick-labels
ax.plot(a, b, '-m^')
ax2 = ax.twiny()
ax2.set_xlim(ax.get_xlim())
ax2.set_xticks(a)
ax2.set_xticklabels(c)

Matplotlib pyplot: Is it possible to have exponential axis?

i need "the opposite" of loglog for my pyplot chart.
How can i achive exponential axis?
Data looks like:
x = [1, 2, 4]
y = [1, 2, 4]
z = [2, 2, 2]
quadranten = plt.figure()
s = [20*4**n for n in z]
fig, ax = plt.subplots()
ax.axis([1, 5, 1, 5])
ax.loglog() <-- opposite function?
xstart, xend = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(xstart, xend, 0.712123))
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
ystart, yend = ax.get_ylim()
ax.yaxis.set_ticks(np.arange(ystart, yend, 0.712123))
ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
plt.xlabel('x')
plt.ylabel('y')
plt.scatter(x,y,s=s)
plt.show()
The goal is the x axis to have evenly sized steps: 0, 1, 2, 4, 8,...
Same for the y axis with evenly sized steps getting exponentially bigger by a factor (for example two): 0, 1, 2, 4, 8, ...
Is this possible?
Something like this:
loglog takes arguments basex and basey that control the base of the logarithms:
loglog(arange(1,100), arange(1,100)**2, basex=2, basey=2)

How to add counts of points as a label in a sparse scatter plot

I have sparse scatter plot to visualize the comparison of predicted vs actual values. The range of the values are 1-4 and there are no decimal points.
I have tried plotly so far with hte following code (but I can also use a matplotlib solution):
my_scatter = go.Scatter(
x = y_actual, y = y_pred, mode = 'markers',
marker = dict(color = 'rgb(240, 189, 89)', opacity=0.5)
)
This prints the graph nicely (see below). I use opacity to see the density at each point. I.e. if two points lie on top of each other, the point will be shown in darker color. However, this is not explanatory enough. Is it possible to add the counts at each point as a label? There are some overlaps at certain intersections. I want to display how many points intersects. Can this be done automatically using matplotlib or plotly?
This answer uses matplotlib.
To answer the initial question first: You need to find out how often the data produces a point at a given coordinate to be able to annotate the points. If all values are integers this can easily be done using a 2d histogram. Out of the hstogram one would then select only those bins where the count value is nonzero and annotate the respective values in a loop:
x = [3, 0, 1, 2, 2, 0, 1, 3, 3, 3, 4, 1, 4, 3, 0]
y = [1, 0, 4, 3, 2, 1, 4, 0, 3, 0, 4, 2, 3, 3, 1]
import matplotlib.pyplot as plt
import numpy as np
x = np.array(x)
y = np.array(y)
hist, xbins,ybins = np.histogram2d(y,x, bins=range(6))
X,Y = np.meshgrid(xbins[:-1], ybins[:-1])
X = X[hist != 0]; Y = Y[hist != 0]
Z = hist[hist != 0]
fig, ax = plt.subplots()
ax.scatter(x,y, s=49, alpha=0.4)
for i in range(len(Z)):
ax.annotate(str(int(Z[i])), xy=(X[i],Y[i]), xytext=(4,0),
textcoords="offset points" )
plt.show()
You may then decide not to plot all points but the result from the histogramming which offers the chance to change the color and size of the scatter points,
ax.scatter(X,Y, s=(Z*20)**1.4, c = Z/Z.max(), cmap="winter_r", alpha=0.4)
Since all values are integers, you may also opt for an image plot,
fig, ax = plt.subplots()
ax.imshow(hist, cmap="PuRd")
for i in range(len(Z)):
ax.annotate(str(int(Z[i])), xy=(X[i],Y[i]), xytext=(0,0), color="w",
ha="center", va="center", textcoords="offset points" )
Without the necesity to calculate the number of occurances, another option is to use a hexbin plot. This gives slightly inaccurate positions of the dots, du to the hexagonal binning, but I still wanted to mention this option.
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
x = np.array(x)
y = np.array(y)
fig, ax = plt.subplots()
cmap = plt.cm.PuRd
cmaplist = [cmap(i) for i in range(cmap.N)]
cmaplist[0] = (1.0,1.0,1.0,1.0)
cmap = matplotlib.colors.LinearSegmentedColormap.from_list('mcm',cmaplist, cmap.N)
ax.hexbin(x,y, gridsize=20, cmap=cmap, linewidth=0 )
plt.show()

looking for marker text option for pyplot.plot()

I am looking for a way to insert numbers or text into markers. There is nothing in the matplotlib.pyplot.plot(*args, **kwargs) documentation about that.
The default zoom level places markers on the edge, hence reducing the available space on which to inscribe text.
import matplotlib.pyplot as plt
x = [1, 2, 3, 4 ,5]
y = [1, 4, 9, 6, 10]
plt.plot(x, y, 'ro',markersize=23)
plt.show()
As jkalden suggested, annotate would solve your problem. The function's xy-argument let you position the text so that you can place it on the marker's position.
About your "zoom" problem, matplotlib will by default stretch the frame between the smallest and largest values you are plotting. It results in your outer markers having their centers on the very edge of the figure, and only half of the markers are visible. To override the default x- and y-limits you can use set_xlim and set_ylim. Here an offset is define to let you control the marginal space.
import matplotlib.pyplot as plt
x = [1, 2, 3, 4 ,5]
y = [1, 4, 9, 6, 10]
fig, ax = plt.subplots()
# instanciate a figure and ax object
# annotate is a method that belongs to axes
ax.plot(x, y, 'ro',markersize=23)
## controls the extent of the plot.
offset = 1.0
ax.set_xlim(min(x)-offset, max(x)+ offset)
ax.set_ylim(min(y)-offset, max(y)+ offset)
# loop through each x,y pair
for i,j in zip(x,y):
corr = -0.05 # adds a little correction to put annotation in marker's centrum
ax.annotate(str(j), xy=(i + corr, j + corr))
plt.show()
Here is how it looks:
This is a revision of #snake_charmer 's method. I used alignment options (instead of manual offsets) to center the text on the dot, and other options for color, size and boldness (weight) of the text.
import matplotlib.pyplot as plt
x = [1, 2, 3, 4 ,5]
y = [1, 4, 9, 6, 10]
fig, ax = plt.subplots()
# instantiate a figure and ax object
# annotate is a method that belongs to axes
ax.plot(x, y, 'ro',markersize=23)
## controls the extent of the plot.
offset = 1.0
ax.set_xlim(min(x)-offset, max(x)+ offset)
ax.set_ylim(min(y)-offset, max(y)+ offset)
# loop through each x,y pair
for i,j in zip(x,y):
ax.annotate(str(j), xy=(i, j), color='white',
fontsize="large", weight='heavy',
horizontalalignment='center',
verticalalignment='center')
plt.show()
You can do this using MathText.
Here are the instructions from matplotlib.org
fig, ax = plt.subplots()
fig.subplots_adjust(left=0.4)
marker_style.update(mec="None", markersize=15)
markers = ["$1$", r"$\frac{1}{2}$", "$f$", "$\u266B$", r"$\mathcal{A}$"]
for y, marker in enumerate(markers):
# Escape dollars so that the text is written "as is", not as mathtext.
ax.text(-0.5, y, repr(marker).replace("$", r"\$"), **text_style)
ax.plot(y * points, marker=marker, **marker_style)
format_axes(ax)
fig.suptitle('mathtext markers', fontsize=14)
plt.show()

Categories