Increasing size of 3d surface plot with matplotlib - python

Picture of Plot
This should really not be this difficult. I am plotting a 3d surface plot from an array. The code looks like this:
z = arr
y = np.arange(len(z))
x = np.arange(len(z[0]))
(x ,y) = np.meshgrid(x,y)
plt.figure(figsize=(100,100))
ax.plot_surface(x,y,z, cmap=cm.coolwarm)
ax.set_xlabel("Bonus to AC")
ax.set_ylabel("Current AC")
ax.set_zlabel("Reduction in Damage")
plt.show()
It does not matter if I set the fig size to 10,10 or 1000,1000, the image still shows up the same size.
What kind of works is adding subplots,
ax = fig.add_subplot(211, projection='3d')
but this splits it up into one okay plot and one empty plot. Not sure how to use the subplots function.

you are referencing ax from a different figure than the one produced by plt.figure
you should instead use ax= fig.add_subplot after you assign fig= plt.figure as follows.
z = np.ones((100,100))
y = np.arange(len(z))
x = np.arange(len(z[0]))
(x ,y) = np.meshgrid(x,y)
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x,y,z)
ax.set_xlabel("Bonus to AC")
ax.set_ylabel("Current AC")
ax.set_zlabel("Reduction in Damage")
plt.show()
note i just swapped the z=np.ones((100,100)) in the first line so viewers can get this working.

Related

Plot straight line along y axes using 3D plot

I am trying to plot a straight line on the 3D surface (x,y,z) along the base of this plot from (1,3,0) to (1,6,0). The straight line along the x plane is not plotting and I can't seem to figure out what my error is. I have found a few similar questions to this but couldn't find where my mistake is.
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# points
z = np.repeat(0.1, 100)
x = np.repeat(1.0, 100)
y = np.linspace(start=3.0, stop=6.0, num=100)
# set axes limits
ax.set_xlim(6,3)
ax.set_ylim(0,1.1)
ax.set_zlim(0,1.75)
# plot
ax.plot(x, y, z, c='red',label=r'straight line at $x=1.0$')
plt.show()
I am holding x and z fixed while changing the y coordinates.
You have (likely accidentally) switched the x-axis and y-axis limits. Try this
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# points
z = np.repeat(0.1, 100)
x = np.repeat(1.0, 100)
y = np.linspace(start=3.0, stop=6.0, num=100)
# set axes limits
ax.set_xlim(0,1.1)
ax.set_ylim(6,3)
ax.set_zlim(0,1.75)
# plot
ax.plot(x, y, z, c='red',label=r'straight line at $x=1.0$')
plt.show()

Pyplot combine two subplot axes

I am currently trying to have two plots in one figure. I am stuck on this for a while now and I don't have any idea why it wouldn't work like I want it to. I have two functions, which return similar axes. The data comes from a csv file, where I get the frequency (y-axis) according to the size of specific objects (x-axis). I expect to have one figure displaying the plots on top of each other. However my plot only contains the legend to axs[1] and the data also only contains axs[1].
My code:
fig, axs = plt.subplots(2)
axs[0].plot(ax=return_some_ax())
axs[1].plot(ax=return_similar_ax())
plt.savefig('plot.png')
I hope that you can help me out :)
Thank you!
This is how you do it in general. You can substitute your functions in place of x and y in here if they return a list of those values:
x_data = list(range(10))
y = [x**2 for x in x_data]
y1 = [x+5 for x in x_data]
fig, [ax1, ax2] = plt.subplots(2)
ax1.plot(x_data, y, label = "quadratic", color = 'red')
ax2.plot(x_data, y1, label = "linear", color = 'blue')
ax1.legend()
ax2.legend()
plt.show()
plt.savefig('plot.png')
Here is the same code using two functions:
def function1(x):
return x**2
def function2(x):
return x+5
x_data = list(range(10))
fig, [ax1, ax2] = plt.subplots(2)
ax1.plot(x_data, [function1(x) for x in x_data], label = "quadratic", color = 'red')
ax2.plot(x_data, [function2(x) for x in x_data], label = "linear", color = 'blue')
ax1.legend()
ax2.legend()
plt.show()
plt.savefig('plot.png')

Matplotlib: categorical plot without strings and inversion of axes

Let's take this snippet of Python:
import matplotlib.pyplot as plt
x = [5,4,3,2,1,0]
x_strings = ['5','4','3','2','1','0']
y = [0,1,2,3,4,5]
plt.figure()
plt.subplot(311)
plt.plot(x, y, marker='o')
plt.subplot(312)
plt.plot(x_strings, y, marker='^', color='red')
plt.subplot(313)
plt.plot(x, y, marker='^', color='red')
plt.gca().invert_xaxis()
plt.show()
Which produces these three subplots:
In the top subplot the x values are automatically sorted increasingly despite their order in the given list. If I want to plot x vs. y exactly in the given order of x, then I have two possibilities:
1) Convert x values to strings and have a categorical plot -- that's the middle subplot.
2) Invert the x-axis -- that's the bottom subplot.
Question: is there any other way to do a sort of categorical plot, but without conversion of numbers into strings and without the inversion of the x-axis?
ADD-ON:
If I use set_xticklabels(list), then for some unclear reason the first element in the list is skipped (no matter if I refer to the x or to the x_strings list), and the resulting plot is also totally strange:
import matplotlib.pyplot as plt
x = [5,4,3,2,1,0]
x_strings = ['5','4','3','2','1','0']
y = [0,1,2,3,4,5]
fig, ax = plt.subplots()
ax.set_xticklabels(x)
ax.plot(x, y, marker='^', color='red')
plt.show()
Both attempted solutions seem possible. Alternatively, you can always mimic categorical plots by plotting integer numbers and setting the ticklabels to your liking.
import matplotlib.pyplot as plt
x = [5,4,3,2,1,0]
y = [0,1,2,3,4,5]
fig, ax = plt.subplots()
ax.plot(range(len(y)), y, marker='^', color='red')
ax.set_xticks(range(len(y)))
ax.set_xticklabels(x)
plt.show()
I have found another way to do it, without being anyhow categorical and without x-axis inversion!
ax = plt.subplot()
ax.set_xlim(x[0],x[-1], auto=True) # this line plays the trick
plt.plot(x, y, marker='^', color='red')

How to display all label values in matplotlib

I have two lists, when I plot with the following code, the x axis only shows up to 12 (max is 15). May I know how can I show all of the values in x list to the x axis? Thanks in advance.
x = [4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3]
y = [10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160]
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(np.arange(len(x)), y, 'o')
ax1.set_xticklabels(x)
plt.show()
If I set minor=True in the set_xticklabels function, it shows me all x=2,4,6,8,..,16... but I want ALL values.
P.S. My x axis is not sorted, should display as it shows.
The issue here is that the number of ticks -set automatically - isn’t the same as the number of points in your plot.
To resolve this, set the number of ticks:
ax1.set_xticks(np.arange(len(x)))
Before the ax1.set_xticklabels(x) call.
or better
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(ticker.MultipleLocator(1))
from other answers in SO
from matplotlib import ticker
import numpy as np
labels = [
"tench",
"English springer",
"cassette player",
"chain saw",
"church",
"French horn",
"garbage truck",
"gas pump",
"golf ball",
"parachute",
]
fig = plt.figure()
ax = fig.add_subplot(111)
plt.title('Confusion Matrix', fontsize=18)
data = np.random.random((10,10))
ax.matshow(data, cmap=plt.cm.Blues, alpha=0.7)
ax.set_xticklabels([''] + labels,rotation=90)
ax.set_yticklabels([''] + labels)
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(ticker.MultipleLocator(1))
for i in range(data.shape[0]):
for j in range(data.shape[1]):
ax.text(x=j, y=i,s=int(data[i, j]), va='center', ha='center', size='xx-small')
plt.xlabel('Predicted')
plt.ylabel('True')
plt.show()

Adding y=x to a matplotlib scatter plot if I haven't kept track of all the data points that went in

Here's some code that does scatter plot of a number of different series using matplotlib and then adds the line y=x:
import numpy as np, matplotlib.pyplot as plt, matplotlib.cm as cm, pylab
nseries = 10
colors = cm.rainbow(np.linspace(0, 1, nseries))
all_x = []
all_y = []
for i in range(nseries):
x = np.random.random(12)+i/10.0
y = np.random.random(12)+i/5.0
plt.scatter(x, y, color=colors[i])
all_x.extend(x)
all_y.extend(y)
# Could I somehow do the next part (add identity_line) if I haven't been keeping track of all the x and y values I've seen?
identity_line = np.linspace(max(min(all_x), min(all_y)),
min(max(all_x), max(all_y)))
plt.plot(identity_line, identity_line, color="black", linestyle="dashed", linewidth=3.0)
plt.show()
In order to achieve this I've had to keep track of all the x and y values that went into the scatter plot so that I know where identity_line should start and end. Is there a way I can get y=x to show up even if I don't have a list of all the points that I plotted? I would think that something in matplotlib can give me a list of all the points after the fact, but I haven't been able to figure out how to get that list.
You don't need to know anything about your data per se. You can get away with what your matplotlib Axes object will tell you about the data.
See below:
import numpy as np
import matplotlib.pyplot as plt
# random data
N = 37
x = np.random.normal(loc=3.5, scale=1.25, size=N)
y = np.random.normal(loc=3.4, scale=1.5, size=N)
c = x**2 + y**2
# now sort it just to make it look like it's related
x.sort()
y.sort()
fig, ax = plt.subplots()
ax.scatter(x, y, s=25, c=c, cmap=plt.cm.coolwarm, zorder=10)
Here's the good part:
lims = [
np.min([ax.get_xlim(), ax.get_ylim()]), # min of both axes
np.max([ax.get_xlim(), ax.get_ylim()]), # max of both axes
]
# now plot both limits against eachother
ax.plot(lims, lims, 'k-', alpha=0.75, zorder=0)
ax.set_aspect('equal')
ax.set_xlim(lims)
ax.set_ylim(lims)
fig.savefig('/Users/paul/Desktop/so.png', dpi=300)
Et voilà
In one line:
ax.plot([0,1],[0,1], transform=ax.transAxes)
No need to modify the xlim or ylim.
Starting with matplotlib 3.3 this has been made very simple with the axline method which only needs a point and a slope. To plot x=y:
ax.axline((0, 0), slope=1)
You don't need to look at your data to use this because the point you specify (i.e. here (0,0)) doesn't actually need to be in your data or plotting range.
If you set scalex and scaley to False, it saves a bit of bookkeeping. This is what I have been using lately to overlay y=x:
xpoints = ypoints = plt.xlim()
plt.plot(xpoints, ypoints, linestyle='--', color='k', lw=3, scalex=False, scaley=False)
or if you've got an axis:
xpoints = ypoints = ax.get_xlim()
ax.plot(xpoints, ypoints, linestyle='--', color='k', lw=3, scalex=False, scaley=False)
Of course, this won't give you a square aspect ratio. If you care about that, go with Paul H's solution.

Categories