Adding colorbar to scatter plot - python

I am currently working with some geospatial data and I want to add a colorbar next to the map. I used the following code to overlay a baseline map with a scatter plot. However, I cannot get the correct colorbar next to it. Can anyone find the reason for such behaviour?
cm = plt.cm.get_cmap('RdYlBu') # define a colormap
fig,ax = plt.subplots(figsize=(15, 15))
city_map = df.plot(alpha=0.2, ax=ax) # plot a city contour
ctx.add_basemap(city_map) # plot a city map
points = gdf_new.plot(c=gdf_new['Score'], cmap=cm, ax=ax) # scatter plot
fig.colorbar(ax.collections[1], ax=ax) # current solution
pandas column gdf_new['Score'] contains values from 0 to 1 used for points coloring.
If I replace the last line with below, I get:
for plt.colorbar() - RuntimeError: No mappable was found to use for colorbar creation. First define a mappable such as an image (with imshow) or a contour set (with contourf)
for plt.colorbar(point) - AttributeError: 'AxesSubplot' object has no attribute 'get_array'
My current solution is to add:
fig.colorbar(ax.collections[1], ax=ax) - it displays the correct color bar but with a wrong numbers' range of 0-200 instead 0-1.
How to fix it?

Related

Adding secondary y labels to heatmap in matplotlib [duplicate]

I'm trying to plot a two-dimensional array in matplotlib using imshow(), and overlay it with a scatterplot on a second y axis.
oneDim = np.array([0.5,1,2.5,3.7])
twoDim = np.random.rand(8,4)
plt.figure()
ax1 = plt.gca()
ax1.imshow(twoDim, cmap='Purples', interpolation='nearest')
ax1.set_xticks(np.arange(0,twoDim.shape[1],1))
ax1.set_yticks(np.arange(0,twoDim.shape[0],1))
ax1.set_yticklabels(np.arange(0,twoDim.shape[0],1))
ax1.grid()
#This is the line that causes problems
ax2 = ax1.twinx()
#That's not really part of the problem (it seems)
oneDimX = oneDim.shape[0]
oneDimY = 4
ax2.plot(np.arange(0,oneDimX,1),oneDim)
ax2.set_yticks(np.arange(0,oneDimY+1,1))
ax2.set_yticklabels(np.arange(0,oneDimY+1,1))
If I only run everything up to the last line, I get my array fully visualised:
However, if I add a second y axis (ax2=ax1.twinx()) as preparation for the scatterplot, it changes to this incomplete rendering:
What's the problem? I've left a few lines in the code above describing the addition of the scatterplot, although it doesn't seem to be part of the issue.
Following the GitHub discussion which Thomas Kuehn has pointed at, the issue has been fixed few days ago. In the absence of a readily available built, here's a fix using the aspect='auto' property. In order to get nice regular boxes, I adjusted the figure x/y using the array dimensions. The axis autoscale feature has been used to remove some additional white border.
oneDim = np.array([0.5,1,2.5,3.7])
twoDim = np.random.rand(8,4)
plt.figure(figsize=(twoDim.shape[1]/2,twoDim.shape[0]/2))
ax1 = plt.gca()
ax1.imshow(twoDim, cmap='Purples', interpolation='nearest', aspect='auto')
ax1.set_xticks(np.arange(0,twoDim.shape[1],1))
ax1.set_yticks(np.arange(0,twoDim.shape[0],1))
ax1.set_yticklabels(np.arange(0,twoDim.shape[0],1))
ax1.grid()
ax2 = ax1.twinx()
#Required to remove some white border
ax1.autoscale(False)
ax2.autoscale(False)
Result:

Problem updating imshow in loop and colormap after loop

I have two imshow() problems that I suspect are closely related.
First, I can't figure out how to use set_data() to update an image I've created with imshow().
Second, I can't figure out why the colorbar I add to the imshow() plot after I'm done updating the plot doesn't match the colorbar I add to an imshow() plot of the same data that I create from scratch after I'm done taking data. The colorbar of the second plot appears to be correct.
Background.
I'm collecting measurement data in two nested loops, with each loop controlling one of the measurement conditions. I'm using pyplot.imshow() to plot my results, and I'm updating the imshow() plot every time I take data in the inner loop.
What I have works in terms of updating the imshow() plot but it seems to be getting increasingly slower as I add more loop iterations, so it's not scaling well. (The program I've included in with this post creates a plot that is eight rows high and six columns wide. A "real" plot might be 10x or 20x this size, in both dimensions.)
I think what I want to do is use the image's set_data() method but I can't figure out how. What I've tried either throws an error or doesn't appear to have any effect on the imshow() plot.
Once I'm done with the "take the data" loops, I add a colorbar to the imshow() plot I've been updating. However, the colorbar scale is obviously bogus.
In contrast, if I take create an entirely new imshow() plot, using the data I took in the loops, and then add a colorbar, the colorbar appears to be correct.
My hunch is the problem is with the vmin and vmax values associated with the imshow() plot I'm updating in the loops but I can't figure out how to fix it.
I've already looked at several related StackOverflow posts. For example:
update a figure made with imshow(), contour() and quiver()
Update matplotlib image in a function
How to update matplotlib's imshow() window interactively?
These have helped, in that they've pointed me to set_data() and given me solutions to some other
problems I had, but I still have the two problems I mentioned at the start.
Here's a simplified version of my code. Note that there are repeated zero values on the X and Y axes. This is on purpose.
I'm running Python 3.5.1, matplotlib 1.5.1, and numpy 1.10.4. (Yes, some of these are quite old. Corporate IT reasons.)
import numpy as np
import matplotlib.pyplot as plt
import random
import time
import warnings
warnings.filterwarnings("ignore", ".*GUI is implemented.*") # Filter out bogus matplotlib warning.
# Create the simulated data for plotting
v_max = 120
v_step_size = 40
h_max = 50
h_step_size = 25
scale = 8
v_points = np.arange(-1*abs(v_max), 0, abs(v_step_size))
v_points = np.append(v_points, [-0.0])
reversed_v_points = -1 * v_points[::-1] # Not just reverse order, but reversed sign
v_points = np.append(v_points, reversed_v_points)
h_points = np.arange(-1*abs(h_max), 0, abs(h_step_size))
h_points = np.append(h_points, [-0.0])
reversed_h_points = -1 * h_points[::-1] # Not just reverse order, but reversed sign
h_points = np.append(h_points, reversed_h_points)
h = 0 # Initialize
v = 0 # Initialize
plt.ion() # Turn on interactive mode.
fig, ax = plt.subplots() # So I have access to the figure and the axes of the plot.
# Initialize the data_points
data_points = np.zeros((v_points.size, h_points.size))
im = ax.imshow(data_points, cmap='hot', interpolation='nearest') # Specify the color map and interpolation
ax.set_title('Dummy title for initial plot')
# Set up the X-axis ticks and label them
ax.set_xticks(np.arange(len(h_points)))
ax.set_xticklabels(h_points)
ax.set_xlabel('Horizontal axis measurement values')
# Set up the Y-axis ticks and label them
ax.set_yticks(np.arange(len(v_points)))
ax.set_yticklabels(v_points)
ax.set_ylabel('Vertical axis measurement values')
plt.pause(0.0001) # In interactive mode, need a small delay to get the plot to appear
plt.show()
for v, v_value in enumerate(v_points):
for h, h_value in enumerate(h_points):
# Measurement goes here.
time.sleep(0.1) # Simulate the measurement delay.
measured_value = scale * random.uniform(0.0, 1.0) # Create simulated data
data_points[v][h] = measured_value # Update data_points with the simulated data
# Update the heat map with the latest point.
# - I *think* I want to use im.set_data() here, not ax.imshow(), but how?
ax.imshow(data_points, cmap='hot', interpolation='nearest') # Specify the color map and interpolation
plt.pause(0.0001) # In interactive mode, need a small delay to get the plot to appear
plt.draw()
# Create a colorbar
# - Except the colorbar here is wrong. It goes from -0.10 to +0.10 instead
# of matching the colorbar in the second imshow() plot, which goes from
# 0.0 to "scale". Why?
cbar = ax.figure.colorbar(im, ax=ax)
cbar.ax.set_ylabel('Default heatmap colorbar label')
plt.pause(0.0001) # In interactive mode, need a small delay to get the colorbar to appear
plt.show()
fig2, ax2 = plt.subplots() # So I have access to the figure and the axes of the plot.
im = ax2.imshow(data_points, cmap='hot', interpolation='nearest') # Specify the color map and interpolation
ax2.set_title('Dummy title for plot with pseudo-data')
# Set up the X-axis ticks and label them
ax2.set_xticks(np.arange(len(h_points)))
ax2.set_xticklabels(h_points)
ax2.set_xlabel('Horizontal axis measurement values')
# Set up the Y-axis ticks and label them
ax2.set_yticks(np.arange(len(v_points)))
ax2.set_yticklabels(v_points)
ax2.set_ylabel('Vertical axis measurement values')
# Create a colorbar
cbar = ax2.figure.colorbar(im, ax=ax2)
cbar.ax.set_ylabel('Default heatmap colorbar label')
plt.pause(0.0001) # In interactive mode, need a small delay to get the plot to appear
plt.show()
dummy = input("In interactive mode, press the Enter key when you're done with the plots.")
OK, I Googled some more. More importantly, I Googled smarter and figured out my own answer.
To update my plot inside my nested loops, I was using the command:
ax.imshow(data_points, cmap='hot', interpolation='nearest') # Specify the color map and interpolation
What I tried using to update my plot more efficiently was:
im.set_data(data_points)
What I should have used was:
im.set_data(data_points)
im.autoscale()
This updates the pixel scaling, which fixed both my "plot doesn't update" problem and my "colorbar has the wrong scale" problem.

Adding legend to scatterplot when color is encoded

I can't add legend to my scatterplot.
The problem that color is encoded by variable y which takes two values 0 or 1.
X comes from PCA method, I try to plot 2 principal components with different colors corresponding to different y.
I get error mesage "No handles with labels found to put in legend."
Tried different tutorial, but still cofused.
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(111)
plt.scatter(x_reduced[:,0], x_reduced[:,1],c=y, alpha=0.5)
plt.legend()
plt.show()
If you're using a newer version of Matplotlib (>=3.1), then you can add legends to a scatterplot following this answer: Scatterplot legends
Otherwise, a workaround is to do two separate calls to plt.scatter
# one scatter for y == 0
plt.scatter(x_reduced[y==0,0], x_reduced[y==0,1], alpha=0.5, label = "group1")
# another scatter for y == 1
plt.scatter(x_reduced[y==1,0], x_reduced[y==1,1], alpha=0.5, label = "group2")
# create legend for both
plt.legend()

Matplotlib: data being plotted over legend when using twinx

I'm trying to use Python and Matplotlib to plot a number of different data sets. I'm using twinx to have one data set plotted on the primary axis and another on the secondary axis. I would like to have two separate legends for these data sets.
In my current solution, the data from the secondary axis is being plotted over the top of the legend for the primary axis, while data from the primary axis is not being plotted over the secondary axis legend.
I have generated a simplified version based on the example here: http://matplotlib.org/users/legend_guide.html
Here is what I have so far:
import matplotlib.pyplot as plt
import pylab
fig, ax1 = plt.subplots()
fig.set_size_inches(18/1.5, 10/1.5)
ax2 = ax1.twinx()
ax1.plot([1,2,3], label="Line 1", linestyle='--')
ax2.plot([3,2,1], label="Line 2", linewidth=4)
ax1.legend(loc=2, borderaxespad=1.)
ax2.legend(loc=1, borderaxespad=1.)
pylab.savefig('test.png',bbox_inches='tight', dpi=300, facecolor='w', edgecolor='k')
With the result being the following plot:
As shown in the plot, the data from ax2 is being plotted over the ax1 legend and I would like the legend to be over the top of the data. What am I missing here?
Thanks for the help.
You could replace your legend setting lines with these:
ax1.legend(loc=1, borderaxespad=1.).set_zorder(2)
ax2.legend(loc=2, borderaxespad=1.).set_zorder(2)
And it should do the trick.
Note that locations have changed to correspond to the lines and there is .set_zorder() method applied after the legend is defined.
The higher integer in zorder the 'higher' layer it will be painted on.
The trick is to draw your first legend, remove it, and then redraw it on the second axis with add_artist():
legend_1 = ax1.legend(loc=2, borderaxespad=1.)
legend_1.remove()
ax2.legend(loc=1, borderaxespad=1.)
ax2.add_artist(legend_1)
Tribute to #ImportanceOfBeingErnest :
https://github.com/matplotlib/matplotlib/issues/3706#issuecomment-378407795

Label objects not found

Setup a graph using matplotlib which is working properly (see image below), but when I try to add a legend I get the following error: UserWarning: No labeled objects found. Use label='...' kwarg on indivial plots.
Here's the code I'm using to define the lines that I want in the legend and draw the legend:
#Moving average labels
smaLabel1 = str(SMA1)+'d SMA'
smaLabel2 = str(SMA2)+'d SMA'
smaLabel3 = str(SMA3)+'d SMA'
#Add SMAs to chart
ax1.plot(ind, avg1, '#5998ff', label=smaLabel1, linewidth=1)
ax1.plot(ind, avg2, '#ffbb82', label=smaLabel2, linewidth=1)
ax1.plot(ind, avg3, '#d689c4', label=smaLabel3, linewidth=1)
""" End SMA additions """
#Add legend
plt.legend()
I've checked the smaLabel variables, and all hold the correct strings. Anyone know why the labels aren't registering?
You must have plot the candle plots and the volume before plotting the SMA. The candle plot doesn't have any labeled object, when you call the plt.legend(), it tries to plot a label for every plot on the current axes. Therefore, you get this UserWarning: No labeled objects found. Use label='...' kwarg on indivial plots.
To solve it, I hope it is clear at this point, simply requires you to plot the SMA's very first, before the candle plot, and call the legend() right after that before any other plots being generated.

Categories