I assume this is a simple fix. I'm trying to replace integer values in a 3d bar chart in matplotlib with string names, and the last one inexplicably won't show:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(11,8.5))
ax = fig.add_subplot(111, projection='3d')
for c, z in zip([qwer for qwer in ['r', 'g', 'b', 'y']*20][:len(sss.keys())], sss.keys()):
xs = np.array(sss[z].keys())
ys = np.array([sss[z][key] for key in sss[z]])
cs = [c] * len(xs)
ax.bar(xs, ys, zs=z, zdir='y', color=cs, alpha=0.8)
ax.set_xlabel('Second Interval')
ax.set_ylabel('First Interval')
ax.set_zlabel('Frequency')
ax.set_xticklabels(['skip down','step down','stay','step up','skip up'])
ax.set_yticklabels(['','skip down','','step down','','stay','','step up','','skip up'])
ax.set_title('Proportional Frequency of S/S/S Intervals by Previous Interval')
plt.show()
sss is a dictionary with other dictionaries as values.
Unfortunately I can't post images, but essentially the chart looks all good and all the string labels on the x and y axes work except 'skip up' along the y axis. It's right at the corner between the y and z axes and just doesn't appear.
What's up?
Related
I am trying to plot a series of curves on the same graph with individual markers:
Each curve has one colour
Each data point has its own markers
For this, I created 3 lists of lists: x_data, y_data and markers. Using np.array(), x_data and y_data can be plotted properly as different curves (with individual colours).
However, np.array() cannot be used with the attribute marker and I do not know how to pass markers to ax.plot().
Does someone know how to attribute individual markers?
MWE
import matplotlib.pyplot as plt
import numpy as np
x_data=[[1,2,3,4,5],[5,10,3,8,6]]
y_data=[[5,10,3,8,6],[1,2,3,4,5]]
markers=[["o","+","D","+","D"],["D","o","o","D","+"]]
fig, ax = plt.subplots()
for n in range(0,len(x_data)):
ax.plot(np.array(x_data[n]), np.array(y_data[n]),linewidth=1,marker=np.array(markers))
plt.show()
One option is an inner loop to scatter plot each marker individually:
import matplotlib.pyplot as plt
import numpy as np
x_data=[[1,2,3,4,5],[5,10,3,8,6]]
y_data=[[5,10,3,8,6],[1,2,3,4,5]]
markers=[["o","+","D","+","D"],["D","o","o","D","+"]]
fig, ax = plt.subplots()
for xs, ys, markers in zip(x_data, y_data, markers):
line = ax.plot(xs, ys, linewidth=1)
colour = line[0].get_color()
for x, y, marker in zip(xs, ys, markers):
ax.scatter(x, y, marker=marker, color=colour)
plt.show()
Output:
I have found code to project 2d bar plots or scatter plots into 3d. For example, with the following code:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111, projection='3d')
nbins = 50
for i, c, z in zip([0,1,2],['r', 'g', 'b', 'y'], [30, 20, 10, 0]):
ys = np.random.normal(loc=10, scale=10, size=2000)
hist, bins = np.histogram(ys, bins=nbins)
xs = (bins[:-1] + bins[1:])/2
ax.bar(xs, hist,zs=z, zdir='y', color=c, ec=c, alpha=0.8)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
However, I want to project other 2d plots into 3d, for example, boxplot. When I modified the code above to boxplot, I cannot use arguments including "zs=z" and "zdir='y'" to set apart 2d plots at different positions. What should I do to make boxplots into figure above? Thanks!
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-1,5)
y = 6 - np.square(x-1)
fig, ax = plt.subplots()
ax.plot(x, y, 'b')
ax.scatter(x, y, color='m', zorder=10)
ax.set_xlabel('x')
ax.set_ylabel('y')
This creates the following:
This function is increasing for all values of x < 1 and increasing for all values of x > 1. Is there a simple way that I can put the text "Increasing" like an x label but centered below the x ticks of 0 and 1, "Decreasing" like an x label but centered below 3, and move the "x" xlabel lower such that it has a lower vertical position than "Increasing" and "Decreasing"? I'd rather not do this with ax.text() unless I absolutely have to.
Maybe use text? I have tried changing the labels but this seems cumbersome. Unfortunately you have to set the text coordinates "manually". Note that you can use newline in the ticks to move them down.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-1,5)
y = 6 - np.square(x-1)
fig, ax = plt.subplots()
ax.text(0.5, -4.6, 'Increasing', ha="center")
ax.text(3, -4.6, 'Decreasing', ha="center")
ax.plot(x, y, 'b')
ax.scatter(x, y, color='m', zorder=10)
ax.set_xlabel('\nx')
ax.set_ylabel('y')
which produces
I'm fairly new to scatter plots and python in general. I am trying to plot a third variable against an x and a y, however, I'm not quite sure how to about specifying that argument? So I would have X values which are ints, y values which are also ints and then on the graph itself I want the model scores to show. Is there any way to do this sort of thing?
Thank you.
You can use color to plot a third value. Here is a very minimal example :
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
x = np.random.rand(100)
y = np.random.rand(100)
z = np.random.rand(100)
plt.scatter(x,y, c=z, s=5, cmap=cm.hsv)
cbar= plt.colorbar()
plt.show()
Edit
You could also use the size of markers, their transparency, hue or rgb values to depict even more information. Here is an example with marker size, alpha level and color on a perceptually uniform colormap.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cmx
x = np.random.rand(100)
y = np.random.rand(100)
z = np.random.rand(100)
t = np.random.rand(100)
w = np.random.rand(100)
fig, ax = plt.subplots(1, 1)
cmap = plt.get_cmap('plasma')
cNorm = colors.Normalize(vmin=0, vmax=max(z))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cmap)
for i in range(100):
ax.scatter(x[i],y[i], c=scalarMap.to_rgba(z[i]), s=t[i]*100, cmap=cmx.plasma, alpha=w[i], edgecolor='none')
scalarMap.set_array([])
fig.colorbar(scalarMap,ax=ax)
for a in [0.1, 0.5, 0.9]:
ax.scatter([], [], c='k', alpha=0.5, s=a*100, label=str(a), edgecolors='none')
l1 = ax.legend(scatterpoints=1, frameon=True, loc='lower left' ,markerscale=1)
for b in [0.25, 0.5, 0.75]:
ax.scatter([], [], c='k', alpha=b, s=50, label=str(b), edgecolors='none')
ax.legend(scatterpoints=1, frameon=True, loc='lower right' ,markerscale=1)
fig.show()
At face value, that question doesn't really make sense because a conventional scatterplot has only two axes, and of course you can't plot points with three dimensions (x, y and accuracy).
However, there are alternative ways to do so.
Use colours
import numpy as np
from matplotlib import pyplot as plt
x = np.random.rand(200)
y = np.random.rand(200)
fig, ax = plt.subplots(figsize=(5, 5))
ax.scatter(x, y, c=(x + y), cmap='RdPu')
scatter takes a c argument, which can be a numeric value, as well as a cmap argument, which can be a string referencing a colormap.
The colormap object translates the numbers provided in c into points along a colour mapping, which you can think of as a gradient bar.
Use 3D axes
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(5, 5))
ax = Axes3D(fig)
ax.scatter(x, y, (x + y))
This turns your 3rd dimension, accuracy, into an ordinary spatial dimension.
Use size of the markers
Very similar to the color option in the first part, you can change the size of the scatter markers (given you have some idea about the scale of the values). So based on the first example, you can also do;
import numpy as np
from matplotlib import pyplot as plt
x = np.random.rand(200)
y = np.random.rand(200)
fig, ax = plt.subplots(figsize=(5, 5))
ax.scatter(x, y, c='k', s=5*(x + y), cmap='RdPu')
scatter takes also the s argument, that changes the size of the markers.
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')