Is there any automatic way of manipulating legend in matplotlib to avoid overlapping data points and legend? I have lots of data points and fixed y axis range, can I instruct matplotlib move the legend to left or down if it's on datapoints. Thanks
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(1, 100)
y = -1/x
plt.plot(x,y, label='x and y', linewidth=30)
plt.legend()
plt.show()
You can manually position the legend to the position that you want it, the documentation can be found here.. You can also move it off the plotting area of the graph to avoid any chance of an overlap as shown in the code below:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(1, 100)
y = -1/x
plt.plot(x,y, label='x and y', linewidth=30)
#adjust the plot to allow the legend to fit nicely
plt.subplots_adjust(left=0.1,right = 0.75)
plt.legend(bbox_to_anchor=(1.01, 0.5), loc=2) # move the legend
plt.show()
The resulting image is shown below:
Note: If you plan on moving the legend outside of the plotting area then you may have to adjust your plots as I have done in the code above.
Related
By default matplotlib would position colorbar labels alongside the vertical colorbars. What is the best way to force the label to be on top of a colorbar? Currently my solution needs adjusting labelpad and y values depending on size of the label:
import numpy as np
import matplotlib.pylab as plt
dat = np.random.randn(10,10)
plt.imshow(dat, interpolation='none')
clb = plt.colorbar()
clb.set_label('label', labelpad=-40, y=1.05, rotation=0)
plt.show()
Is there a better, more generic way to do this?
You could set the title of the colorbar axis (which appears above the axis), rather than the label (which appears along the long axis). To access the colorbar's Axes, you can use clb.ax. You can then use set_title, in the same way you can for any other Axes instance.
For example:
import numpy as np
import matplotlib.pylab as plt
dat = np.random.randn(10,10)
plt.imshow(dat, interpolation='none')
clb = plt.colorbar()
clb.ax.set_title('This is a title')
plt.show()
Two questions:
-My y axis values have a space from the x axis, and I want it to be closen
-How do I add a color to everything, background ofthe image and graph.
Thank you
You want to change the xmin and ymin properties of the axis.
Note the the function is almost invisible near the origin.
import numpy as np
import matplotlib.pyplot as plt
# oh yes, y is quite flat about the origin
t = np.linspace(0, 1.57, 158)
y = t**5*np.cos(t)
fig, ax = plt.subplots(layout='constrained')
# change the colors of the graph and of the figure
fig.set_facecolor('k')
ax.set_facecolor('C3')
# change the colors of the text elements
plt.rc('axes', labelcolor='w')
plt.rc('xtick', color='w')
plt.rc('ytick', color='w')
plt.rc('text', color='w')
# plot y and label it properly
ax.plot(t, y, color='xkcd:pink', lw=2)
plt.xlabel('X', size='xx-large')
plt.ylabel('Y', size='x-large')
plt.title('As You Like It', color='xkcd:pink', size='xx-large')
###############################################################
# remove the despised space ###################################
plt.axis(xmin=0, ymin=0) ######################################
###############################################################
plt.show()
I'd suggest to remove plt.axis(...) and use plt.grid(1), obtaining the figure below.
I would like a representation consisting of a scatter plot and 2 histograms on the right and below the scatter plot
create. I have the following requirements:
1.) In the scatter plot, the apect ratio is equal so that the circle does not look like an ellipse.
2.) In the graphic, the subplots should be exactly as wide or high as the axes of the scatter plot.
This also works to a limited extent. However, I can't make the lower histogram as wide as the x axis of the scatter plot. How do I do that?
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import random
#create some demo data
x = [random.uniform(-2.0, 2.0) for i in range(100)]
y = [random.uniform(-2.0, 2.0) for i in range(100)]
#create figure
fig = plt.figure()
gs = gridspec.GridSpec(2, 2, width_ratios = [3, 1], height_ratios = [3, 1])
ax = plt.subplot(gs[0])
# Axis labels
plt.xlabel('pos error X [mm]')
plt.ylabel('pos error Y [mm]')
ax.grid(True)
ax.axhline(color="#000000")
ax.axvline(color="#000000")
ax.set_aspect('equal')
radius = 1.0
xc = radius*np.cos(np.linspace(0,np.pi*2))
yc = radius*np.sin(np.linspace(0,np.pi*2))
plt.plot(xc, yc, "k")
ax.scatter(x,y)
hist_x = plt.subplot(gs[1],sharey=ax)
hist_y = plt.subplot(gs[2],sharex=ax)
plt.tight_layout() #needed. without no xlabel visible
plt.show()
what i want is:
Many thanks for your help!
The easiest (but not necessarily most elegant) solution is to manually position the lower histogram after applying the tight layout:
ax_pos = ax.get_position()
hist_y_pos = hist_y.get_position()
hist_y.set_position((ax_pos.x0, hist_y_pos.y0, ax_pos.width, hist_y_pos.height))
This output was produced by matplotlib version 3.4.3. For your example output, you're obviously using a different version, as I get a much wider lower histogram than you.
(I retained the histogram names as in your example although I guess the lower one should be hist_x instead of hist_y).
I've got a map of the world on which I am iteratively plotting drought areas in a for-loop.
For reproducibility, data is here: https://data.humdata.org/dataset/global-droughts-events-1980-2001
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
import seaborn as sns
from IPython.display import clear_output
sns.set_theme(style='whitegrid')
dr_geometry = gpd.read_file('data/dr_events.shp')
world_geometry = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
for y in dr_geometry.year.unique():
clear_output(wait=True)
fig, ax = plt.subplots(1, 1, figsize=(15, 15))
world_geometry.plot(ax=ax)
dr_geometry[dr_geometry.year == y].plot(ax=ax, color='red', edgecolor='black', linewidth=0.1)
plt.show();
This is working fine, except that the y-axis shrinks or expands on each iteration by a small but very noticeable amount, resulting in a choppy animation. How can I eliminate this behavior?
Note: Setting the ylim explicitly does not change this. Also I have tried moving the subplots instantiation outside of the for-loop, but this results in empty outputs.
An iteration output:
ax.set_aspect('equal') prevents the shifting on my end:
for y in dr_geometry.year.unique():
clear_output(wait=True)
fig, ax = plt.subplots(1, 1, figsize=(15, 15))
world_geometry.plot(ax=ax)
dr_geometry[dr_geometry.year == y].plot(ax=ax, color='red', edgecolor='black', linewidth=0.1)
# set aspect ratio explicitly
ax.set_aspect('equal')
plt.show();
Thanks to #martinfleis for pointing out the reason for the shifting:
geopandas#1121 - Consider scaling y-axis for unprojected map plots
geopandas#1290 - ENH: scaling y-axis for plots in geographic CRS
.plot() now automatically determines whether GeoSeries (or GeoDataFrame) is in geographic or projected CRS and calculates aspect for geographic using 1/cos(s_y * pi/180) with s_y as the y coordinate of the mean of y-bounds of GeoSeries. This leads to better representation of the actual shapes than current hard-coded 'equal' aspect.
I`d like to have a multiline figure with a dataframe.
Original data is as following:
from numpy.random import randn
import numpy as np
df=pd.DataFrame()
df['Years']=range(1995,2013)
np.random.seed(0)
df['Goverment']=randn(len(df.Years))
df['Household']=randn(len(df.Years))
df['Corporate']=randn(len(df.Years))
print(df)
and I want to set the legend along fully the bound pf figure box. I referred to the answer of #Joe Kington but this problem hasn`t been solved.
For plotting:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10,6))
ax = plt.subplot(111)
ax.plot(df.Years, df.Government,ls='--',label='Goverment',color='black')
ax.plot(df.Years,df.Household,ls=':',label='Household',color='black')
ax.plot(df.Years,df.Corporate,ls='-',label='Corporate',color='black')
plt.xlabel("common X")
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.1,
box.width, box.height * 1])
# Put a legend below current axis
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.1),borderaxespad=1,mode='expand',ncol=3)
plt.show()
and the following is my result. Apparently the mode='expand' doesn`t work here.
My questions are:
1. Why the values on X axis are not integral but floats?
2. How to expand the legend box into one line instrad fully along the bound of box?
The ideal legend box should be:
The difference is indeed that you use the mode='expand'. Now this will tell the legend to expand in its bounding box. However the bounding box has no extent, it is a single point. The legend will hence expand inside a zero-width box and hence become shrunk to zero width itself.
The solution is to specify a bounding box with 4 coordinates (i.e. a true box). In principle this should also be explained in my answer to the linked question. So here we would use axes coordinates for the bbox_transform and make the box one unit in axes coordinates wide.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
fig.subplots_adjust(bottom=0.2, top=0.95)
ax = plt.subplot(111)
for i in range(5):
line, = ax.plot(x, i * x, label='$y = %ix$'%i)
# Put a legend below current axis
ax.legend(loc="upper center", mode='expand',
bbox_to_anchor=(0,-0.2,1,.1), bbox_transform=ax.transAxes,
fancybox=True, shadow=True, ncol=5)
plt.show()