Why are annotations creating white background space around plot? - python

I wanted to understand why when I annotate the plot it creates a white space around the plot, and how I can remove it.
I have tried the bbox_inches when saving the image and nothing changes. I have tried the subplots_adjust and it adjusts the size of the entire plot around including the white space. I have tried adjusting the margins to no improvement.
import matplotlib as mpl
import matplotlib.pyplot as plt
import plotly as py
from shapely.geometry import Point, Polygon
import geopandas as gpd
import colour
import matplotlib.colors as mcolors
from mpl_toolkits.axes_grid1 import make_axes_locatable
from mpl_toolkits.axes_grid1.colorbar import colorbar
fig1, ax1 = plt.subplots(1, 1)
sm = plt.cm.ScalarMappable(cmap='seismic_r', norm=custom_cmap)
sm.set_array([])
divider1 = make_axes_locatable(ax1)
cax1 = divider1.append_axes("top", size = "5%", pad = "9%")
state2020_data[state2020_data['state'].isin(['ALASKA','HAWAII','DC']) == False].plot(column = 'democrat_margin',
cmap = 'seismic_r', linewidth = 2, edgecolor = '0.2', norm = custom_cmap, ax = ax1)
cb_ax1 = fig1.axes[1]
for idx, row in state2020_data.iterrows():
ax1.annotate(text = row['state_po'], xy = row['coords'],
horizontalalignment = 'center', fontsize = 6)
cb1 = colorbar(sm, cax=cax1, orientation = 'horizontal')
cb_ax1.tick_params(labelsize = 12)
cb_ax1.set_title('Winning margin')
ax1.grid(False)
ax1.axis('tight')
ax1.set_axis_off()
Here is the image if I drop the annotation code:
Here is the image when I include the annotation code:
I'd be really grateful if someone could explain what's going on and how I can erase the additional space.
Thanks

Related

How to add a colorbar to a plt.bar chart?

I am trying to create a self updating chart that displays a horizontal line and color bars based on a y-axis value of interest. So bars might be colored red if they are definitely above this value (given a 95% confidence interval), blue if they are definitely below this value, or white if they contain this value. something similar to this:
The problem I have is I cant display the colorbar on my plot. I managed to color each bar based on a LinearSegmentedColormap and some conditions, but I cant manage to display this colorbar on my image.
This is my code:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from math import sqrt
import matplotlib.axes
from matplotlib import cm
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
from matplotlib.cm import ScalarMappable
np.random.seed(12345)
df = pd.DataFrame([np.random.normal(32000,200000,3650),
np.random.normal(43000,100000,3650),
np.random.normal(43500,140000,3650),
np.random.normal(48000,70000,3650)],
index=[1992,1993,1994,1995])
means = []
for i in df.index:
means.append(df.loc[i].mean())
std = []
for i in df.index:
std.append(df.loc[i].std())
# compute the 95% confidence intervals
conf = []
for i in range(len(means)):
margin = (1.96*std[i])/sqrt(len(df.columns))
conf.append(margin)
fig, axs = plt.subplots(1)
bars = plt.bar(df.index, means, yerr= conf, tick_label = df.index, capsize = 10)
#Setup the plot
yinterest = 43000
plt.gca().spines.get('top').set_visible(False)
plt.gca().spines.get('right').set_visible(False)
plt.axhline(yinterest, color = 'black', label = '4300')
#setting the y-interest tick
plt.draw()
labels = [w.get_text() for w in ax.get_yticklabels()]
locs=list(ax.get_yticks())
labels+=[str(yinterest)]
locs+=[float(yinterest)]
ax.set_yticklabels(labels)
ax.set_yticks(locs)
plt.draw()
#setting up the colormap
colormap = cm.get_cmap('RdBu', 10)
colores = []
for i in range(len(means)):
color = (yinterest-(means[i]-conf[i]))/((means[i]+conf[i])-(means[i]-conf[i]))
bars[i].set_color(colormap(color))
I am fairly new to python (or programming for that matter) and I have searched everywhere for a solution but to no avail. Any help would be appreciated.
Greetings.
The first hint is to use pandasonic methods to compute plot data
(much more concise):
means = df.mean(axis=1)
std = df.std(axis=1)
conf = (std * 1.96 / sqrt(df.shape[1]))
And to draw your plot, run:
yinterest = 39541
fig, ax = plt.subplots(figsize=(10,6))
ax.spines.get('top').set_visible(False)
ax.spines.get('right').set_visible(False)
colors = (yinterest - (means - conf)) / (2 * conf)
colormap = plt.cm.get_cmap('RdBu', 10)
plt.bar(df.index, means, yerr=conf, tick_label=df.index, capsize=10, color=colormap(colors))
cbar = plt.colorbar(plt.cm.ScalarMappable(cmap=colormap), orientation='horizontal')
cbar.set_label('Color', labelpad=5)
plt.axhline(yinterest, color='black', linestyle='--', linewidth=1)
plt.show()
One trick that allows to avoid colouring the bars after their
generation is that I compute colors, which are then converted to
a color map and passed to plt.bar.
To draw the color bar, use plt.colorbar.
I changed the value of yinterest to that included in your picture and got
something similar to your picture, but with a color bar:

Why don't colors change in matplotlib boxplot?

I make a boxplot with matplotlib:
import pandas as pd
import matplotlib.pyplot as plt
A = pd.DataFrame([54.183933149245775,98.14228839908178,97.56790596547185,81.28351460722497,116.36733517668105,93.64706288367272,107.68860349692736,109.65565349602194,88.58717530217115,54.87561132504807,137.89097514410435,116.90021701471281,121.41252555476005,102.68420408219474,107.32642696333856,
120.27307064490907,114.3674635060443,91.38936314166017,149.0476109186976,121.76625219213736,155.6027360469248,115.86331915425764,99.35036421024546,104.93804853361358,115.64286896238708,129.51583078514085,116.30239399660411,97.58582728510798,119.59975852978403,103.68594428632996], columns=['A'])
fig, ax = plt.subplots(1,1)
A.boxplot(grid=False, fontsize=12, notch=True,
flierprops = dict(markersize=10, markeredgecolor ='red', markerfacecolor='b'),
boxprops = dict(linewidth=2, color='red'))
fig.show()
The flier props will change the colors and marker size. However, for "boxprops", the linewidth can change but the color NEVER changes (here it stays blue). Does anybody know why? Also, where is the matplotlib documentation giving all the options for these properties?
You can do that by doing two things actually,
First, determine the return_type of your boxplot
Second, change the color of the boxes key like so:
Here, I will change the boxes into green
import pandas as pd
import matplotlib.pyplot as plt
A = pd.DataFrame([54.183933149245775,98.14228839908178,97.56790596547185,81.28351460722497,116.36733517668105,93.64706288367272,107.68860349692736,109.65565349602194,88.58717530217115,54.87561132504807,137.89097514410435,116.90021701471281,121.41252555476005,102.68420408219474,107.32642696333856,
120.27307064490907,114.3674635060443,91.38936314166017,149.0476109186976,121.76625219213736,155.6027360469248,115.86331915425764,99.35036421024546,104.93804853361358,115.64286896238708,129.51583078514085,116.30239399660411,97.58582728510798,119.59975852978403,103.68594428632996], columns=['A'])
fig, ax = plt.subplots(1,1)
bp = A.boxplot(grid=False, fontsize=12, notch=True,
flierprops = dict(markersize=10, markeredgecolor ='red', markerfacecolor='b'),
boxprops = dict(linewidth=2, color='red'),
return_type='dict') # add this argument
# set the color of the boxes to green
for item in bp['boxes']:
item.set_color('g')
plt.show()
And this will show the following graph:

Way to change only the width of marker in scatterplot but not height?

I want to make a scatterplot with marker type as rectange (not square), such that width is more than height. With the "s" I can control the overall size of the marker but it increases in both dimension.
I can not directly pass height and width as these are unknown properties of scatter.
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter(np.arange(1,6), np.random.normal(size=5), marker='s', s=16)
Try the following snippet.
import numpy as np
import matplotlib.pyplot as plt
width = 60
height = 30
verts = list(zip([-width,width,width,-width],[-height,-height,height,height]))
fig, ax = plt.subplots()
ax.scatter(np.arange(1,6), np.random.normal(size=5), marker=(verts,0),s=40)
Here, the argument s changes the size of the scatter. The drawn rectangle keeps the ratio width/height.
Output:
update
Since matplotlib 3.2x, use of (verts, 0) is depreciated. The working code should be changed to
fig, ax = plt.subplots()
ax.scatter(np.arange(1,6), np.random.normal(size=5), marker=verts, s=40)

How to obtain correct size for a second colorbar in matplotlib plot?

I wish to create a "split plot", i.e. to use different colormaps in the left and the right half of my plot. Accordingly I will need two different colorbars. Unfortunately I have to set the position of the second colorbar by hand and modify everytime a label or title is included. Is there a way to automatise that?
I wondered if I could extract the rect parameter of the following minimal example from the right colorbar. That would help me as I only had shift it a bit. Any other (/better) idea is also welcome.
At the moment, whenever I change the labels or title a bit the manually set position of the left colorbar has to be modified again. This is very annoying. I include a running minimal example and a the output it produces:
import matplotlib as mpl
params = {
'xtick.direction' : 'out',
'ytick.direction' : 'out',
'text.usetex' : True,
}
mpl.rcParams.update(params)
mpl.rcParams.update({'figure.autolayout': True})
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
extent_arr1 = [-1,0, -1,1]
extent_arr2 = [ 0,1, -1,1]
M = 501
# define test-data
data_arr1 = np.zeros((M, M))
data_arr2 = np.ones((M, M))
# define figure
fig = plt.figure()
ax = fig.add_subplot(111)
# left plot:
image1 = ax.imshow( data_arr1, cmap='jet', interpolation='bilinear', extent=extent_arr1, \
origin='lower')
plt.title("Minimal example")
cbar1 = plt.colorbar(image1)
# right plot:
image2 = ax.imshow( data_arr2, cmap='gnuplot', interpolation='bilinear', extent=extent_arr2, \
origin='lower')
# define axes-labels:
plt.xlabel(r"$x$")
plt.ylabel(r"$y$")
# define colour-bar at left side:
rect_loc = [0.0, 0.08, 0.03, 0.88] # define position ---> how to automatise this?
cax2 = fig.add_axes(rect_loc) # left | bottom | width | height
cbar2 = plt.colorbar(image2, cax=cax2)
cbar2.ax.yaxis.set_ticks_position('left')
# set limits:
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
plt.show()
output:
Thanks in advance!
There are of course several ways to create a colorbar axes and put it next to a plot. I would recommend reading those questions:
positioning the colorbar
Matplotlib 2 Subplots, 1 Colorbar
Many of those concepts can be extended to a second colorbar. The solution I would personally prefer is the following, which uses an axes divider. The advantage is that the colorbar keeps the size of the axes.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np; np.random.seed(1)
plt.rcParams.update({'figure.autolayout': True})
fig, ax = plt.subplots(figsize=(6,4))
im = ax.imshow(np.random.rand(10,10), extent=[-1,0,0,1], cmap="RdYlGn")
im2 = ax.imshow(np.random.rand(10,10), extent=[0,1,0,1], cmap="magma")
ax.set_xlabel("x label")
ax.set_ylabel("y label")
ax.set_xlim(-1,1)
ax.set_ylim(0,1)
divider = make_axes_locatable(ax)
cax = divider.new_horizontal(size="5%", pad=0.2)
fig.add_axes(cax)
fig.colorbar(im2, cax=cax)
cax2 = divider.new_horizontal(size="5%", pad=0.7, pack_start=True)
fig.add_axes(cax2)
cb2 = fig.colorbar(im, cax=cax2)
cb2.ax.yaxis.set_ticks_position('left')
plt.show()

issues with PyPlot color bar tick marks with images

I'm trying to plot an image with a superimposed fitted curve, but for now I'll just provide an example of an image.
I have been following this example ( http://matplotlib.org/examples/pylab_examples/colorbar_tick_labelling_demo.html ), but when I try to replace the gaussian noise with an image the color bar tick marks don't display correctly (i.e they are all smashed down at the left end).
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
from matplotlib import cm
from numpy.random import randn
fig, ax = plt.subplots()
data = np.clip(randn(250, 250), -1, 1)
#data = Image.open('testTop.tif')
cax = ax.imshow(data, interpolation='nearest', cmap=cm.afmhot)
ax.set_title('colorBar fun')
cbar = fig.colorbar(cax, ticks=[-1, 0, 1], orientation='horizontal')
cbar.ax.set_xticklabels(['Low', 'Medium', 'High'])# horizontal colorbar
plt.show()
#data = np.clip(randn(250, 250), -1, 1)
data = Image.open('testTop.tif')
Is there something intrinsic about displaying images that skews the colorbar, or is there something else obvious that I'm missing?
The colorbar axis only goes from -1 to 1 in the example with the random nose because the data ranges from -1 to 1. The tif image your using probably has a different range of values. You can get the minimum and maximum values of the data you're plotting and use that to set the color bar ticks. Here's an example that will work with both the random data and an image:
import matplotlib.cbook as cbook
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
from matplotlib import cm
from numpy.random import randn
# Load sample image
image_file = cbook.get_sample_data('grace_hopper.png')
data = plt.imread(image_file)
data = data[:,:,0] # Take only one channel for a grey scale image.
#data = np.clip(randn(250, 250), -1, 1)
fig, ax = plt.subplots()
cax = ax.imshow(data, interpolation='nearest', cmap=cm.afmhot)
ax.set_title('colorBar fun')
dataMin = np.amin(data)
dataMax = np.amax(data)
mid = ((dataMax - dataMin) / 2) + dataMin
cbar = fig.colorbar(cax, ticks=[dataMin, mid, dataMax], orientation='horizontal')
cbar.ax.set_xticklabels(['Low', 'Medium', 'High'])# horizontal colorbar
plt.show()

Categories

Resources