I am desperately trying to add a "dark" border around this pie chart. I have tried the solutions described in plenty of questions here, but none turned out to add anything. You can find part of the attempts in the code:
import matplotlib.pyplot as plt
from cycler import cycler
plt.rc("axes", prop_cycle=cycler("color", ["darkgray", "gray", "lightgray"])
)
plt.rcParams["axes.edgecolor"] = "0.15"
plt.rcParams["axes.linewidth"] = 1.25
labels = ["lab1", "lab2"]
sizes = [2000, 3000]
def make_autopct(values):
def my_autopct(pct):
total = sum(values)
val = int(round(pct*total/100.0))
s = '{p:.2f}%({v:d}%)'.format(p=pct,v=val)
s = f"${val}_{{\\ {pct:.2f}\%}}$"
return s
return my_autopct
fig, ax = plt.subplots(figsize=(10, 3))
ax.pie(sizes, explode=(0,0.02), labels=labels, autopct=make_autopct(sizes))
ax.set_title("title")
ax.patch.set_edgecolor('black')
ax.patch.set_linewidth('1')
plt.savefig("title.png")
If I've understood your question right possible solution is the following:
# pip install matplotlib
import matplotlib.pyplot as plt
import numpy as np
# set chart style
plt.style.use('_mpl-gallery-nogrid')
# set data
x = [5, 2, 3, 4]
# set colors of segments
colors = plt.get_cmap('GnBu')(np.linspace(0.2, 0.7, len(x)))
# plot
fig, ax = plt.subplots()
ax.pie(x, colors=colors, radius=2,
wedgeprops={"linewidth": 2, "edgecolor": "black", 'antialiased': True}, # << HERE
frame=False, startangle=0, autopct='%.1f%%', pctdistance=0.6)
plt.show()
Below, three possibilities:
add a frame around pie patch:
ax.pie(sizes,
explode=(0,0.02),
labels=labels,
autopct=make_autopct(sizes),
frame=True)
add a border using axes coordinates (0, 0) to (1, 1) with fig.add_artist which draw on the fig object:
rect = pt.Rectangle((-0.1, -0.1), 1.2, 1.2,
fill=False, color="blue", lw=3, zorder=-1
transform=ax.transAxes)
fig.add_artist(rect)
add a border using fig coordinates (0, 0) to (1, 1) with fig.add_artist which draw on the fig object:
rect = pt.Rectangle((0.05, 0.05), .9, .9,
fill=False, ec="red", lw=1, zorder=-1,
transform=fig.transFigure)
fig.add_artist(rect)
Result:
Edit This matplotlib's transformations page explains the different coordinate systems
Related
I was trying to get a map projection using cartopy in python and it wasn't made so I'm trying to piece it together using to subplots with the following code:
fig = plt.figure(figsize =(25,13),facecolor='white')
gs = fig.add_gridspec(1,2,width_ratios=[4,2],height_ratios = [1], hspace=0.2,wspace=.0)
ax1=fig.add_subplot(gs[0,0],projection=ccrs.PlateCarree())
ax2=fig.add_subplot(gs[0,1],projection=ccrs.PlateCarree())
ax2.set_extent([-180,0,-90,90])
ax1.set_extent([-180,180,-90,90])
ax1.add_feature(cfeature.LAND, color = 'lightgray')
ax2.add_feature(cfeature.LAND, color = 'lightgray')
ax1.add_feature(cfeature.COASTLINE)
ax2.add_feature(cfeature.COASTLINE)
and I get the right projection I was looking for, however I am trying to remove the line between the two subplots and I keep getting issues, any suggestions?
Your question is a challenge as it is uncommon to plot a map with longitude extent greater than 360 degrees. What you have done is already a good achievement. What I will do just to finish your work.
Here is the code that produces the plot you need.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
#from shapely.geometry import Point, Polygon
import cartopy.feature as cfeature
import matplotlib.transforms as transforms
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
fig = plt.figure(figsize =(25,9.5), facecolor='white')
gs = fig.add_gridspec(1, 2, width_ratios=[4,2], height_ratios = [1], hspace=0.2, wspace=.0)
proj = ccrs.PlateCarree(central_longitude=0)
ax1=fig.add_subplot( gs[0,0], projection=proj )
ax2=fig.add_subplot( gs[0,1], projection=proj )
ax1.set_extent([-179.9, 180, -90, 90]) #Tricky, -180 not works!
ax2.set_extent([-179.9, 0, -90, 90])
ax1.add_feature(cfeature.LAND, color = 'lightgray')
ax2.add_feature(cfeature.LAND, color = 'lightgray')
ax1.add_feature(cfeature.COASTLINE)
ax2.add_feature(cfeature.COASTLINE)
# Set color of ax2's boundaries
# If set 'white' the gridline at that position will be gone!
ax2.outline_patch.set_edgecolor('lightgray') # set color to match other gridlines
# Draw 3 sides boundaries of ax2
# ------------------------------
# Define a `transformation`
# Signature: blended_transform_factory(x_transform, y_transform)
# the y coords of this transformation are data (as is = ax.transData)
# but the x coord are axes coordinate (0 to 1, ax.transAxes)
transAD = transforms.blended_transform_factory(ax2.transAxes, ax2.transData)
# Plot 3 lines around extents of ax2
# Color is intentionally set as 'red'
# You need to change it to 'black' for your work
ax2.plot([0.996, 0.996], [-90, 90], color='red', lw=2, transform=transAD)
ax2.plot([0.0, 0.996], [-90, -90], color='red', lw=2, transform=transAD)
ax2.plot([0.0, 0.996], [89.6, 89.6], color='red', lw=2, transform=transAD)
gl1 = ax1.gridlines(ccrs.PlateCarree(),
xlocs=range(-180,181,20),
ylocs=range(-90,90,10),
linestyle='-',
y_inline=False, x_inline=False,
color='b', alpha=0.6, linewidth=0.25, draw_labels=True)
gl1.xformatter = LONGITUDE_FORMATTER
gl1.yformatter = LATITUDE_FORMATTER
gl1.right_labels = False
gl2 = ax2.gridlines(ccrs.PlateCarree(),
xlocs=range(-180,180,20),
ylocs=range(-90,90,10),
linestyle='-',
y_inline=False, x_inline=False,
color='b', alpha=0.6, linewidth=0.25, draw_labels=True)
gl2.xformatter = LONGITUDE_FORMATTER
gl2.yformatter = LATITUDE_FORMATTER
gl2.left_labels = False
I'm trying to create an inset figure that has a different projection from the parent. The only issue I have at this point is the inset figures's tick labels are not legible because they are black and blend in with the plot behind it. I could change the color of the ticks and labels to white, but that does not help when the data in ax0 yields lighter colors. Here is the MWE:
import calipsoFunctions as cf
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import numpy as np
import pylab as pl
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, mark_inset, InsetPosition
x, y = np.arange(100), np.arange(200)
X, Y = np.meshgrid(x, y)
C = np.random.randint(0, 100, (200, 100))
fig = pl.figure(figsize=(6.5, 5.25))
gs0 = pl.GridSpec(3, 1)
gs0.update(left=0.08, right=0.925,
top=0.95, bottom=0.33,
hspace=0.10, wspace=0.0)
gs1 = pl.GridSpec(1, 2)
gs1.update(left=0.08, right=0.925,
top=0.225, bottom=0.05,
hspace=0.0, wspace=0.025)
# create primary axes
ax0 = pl.subplot(gs0[0])
ax1 = pl.subplot(gs0[1])
ax0.pcolormesh(X, Y, C, vmin=0, vmax=75)
ax1.pcolormesh(X, Y, C, vmin=0, vmax=75)
# add map plot (inset axis)
loc_box = [0.8, 0.55, 0.20, 0.45]
ax0_inset = fig.add_axes(loc_box,
projection=ccrs.PlateCarree(),
aspect="auto",
facecolor="w",
frameon=True)
lat_array = np.arange(-20, 20)
lon_array = np.arange(-10, 10, 0.5)
ax0_inset.plot(lat_array, lon_array, "k-", lw=1)
ip = InsetPosition(ax0, loc_box)
ax0_inset.set_axes_locator(ip)
ax0_inset.coastlines(resolution="10m", linewidth=0.25, color="k")
ax0_inset.add_feature(cfeature.LAND)
llat, ulat = lat_array.min(), lat_array.max()
llon, ulon = lon_array.min(), lon_array.max()
llat = np.round(llat / 10) * 10
ulat = np.round(ulat / 10) * 10
llon = np.round(llon / 5) * 5
ulon = np.round(ulon / 5) * 5
ax0_inset.set_yticks(np.arange(llat, ulat, 20), minor=False)
ax0_inset.set_yticks(np.arange(llat, ulat, 10), minor=True)
ax0_inset.set_yticklabels(np.arange(llat, ulat, 20),
fontsize=8)
ax0_inset.yaxis.set_major_formatter(LatitudeFormatter())
ax0_inset.set_xticks(np.arange(llon, ulon, 5), minor=False)
ax0_inset.set_xticks(np.arange(llon, ulon, 1), minor=True)
ax0_inset.set_xticklabels(np.arange(llon, ulon, 5),
fontsize=8,
rotation=45)
ax0_inset.xaxis.set_major_formatter(LongitudeFormatter())
ax0_inset.grid()
ax0_inset.tick_params(which="both",
axis="both",
direction="in",
labelsize=8)
fig.show()
Is there a way to change the background color of ax0_inset so that these tick labels are legible? I tried changing the face_color to "w", but that did not work. Ideally, I want the same behavior as ax0.figure.set_facecolor("w"), but for the ax0_inset axis. Is this doable?
Following #Mr. T's comment suggestion, a work-around solution could be:
# insert transparent (or opaque) rectangle around inset_axes plot
# to make axes labels more visible
# make buffer variable to control amount of buffer around inset_axes
buffer = 0.1 # fractional axes coordinates
# use ax inset tuple coords in loc_box to add rectangle patch
# [left, bottom, width, height] (fractional axes coordinates)
fig.add_patch(plt.Rectangle((
loc_box[0]-buffer, loc_box[1]-buffer),
loc_box[2]+buffer, loc_box[3]+buffer,
linestyle="-", edgecolor="k", facecolor="w",
linewidth=1, alpha=0.75, zorder=5,
transform=ax0.transAxes))
I'm using this code to plot my data in boxplot:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon
random_dists = ['Overlap', 'Non overlap', ]
Overlap= [6,6,5,1,3,4,4,3]
non_overlap= [1,2,6,6,1,3,3,3,3,3,5,2,2]
data = [
Overlap,
non_overlap
]
fig, ax1 = plt.subplots(figsize=(6, 6))
fig.canvas.set_window_title('A Boxplot Example')
fig.subplots_adjust(left=0.075, right=0.95, top=0.9, bottom=0.25)
# bp = ax1.boxplot(data, notch=0, sym='+', vert=1, whis=1.5)
bp = ax1.boxplot(data)
plt.setp(bp['boxes'], color='black')
plt.setp(bp['whiskers'], color='black')
plt.setp(bp['fliers'], color='red', marker='+')
# Add a horizontal grid to the plot, but make it very light in color
# so we can use it for reading data values but not be distracting
ax1.yaxis.grid(True, linestyle='-', which='major', color='lightgrey',
alpha=0.5)
# Hide these grid behind plot objects
ax1.set_axisbelow(True)
ax1.set_title('overlap and non_overlap against mRS')
# ax1.set_xlabel('Distribution')
# ax1.set_ylabel('Value')
# Now fill the boxes with desired colors
box_colors = ['darkkhaki', 'royalblue']
num_boxes = len(data)
medians = np.empty(num_boxes)
for i in range(num_boxes):
box = bp['boxes'][i]
boxX = []
boxY = []
for j in range(5):
boxX.append(box.get_xdata()[j])
boxY.append(box.get_ydata()[j])
box_coords = np.column_stack([boxX, boxY])
# Alternate between Dark Khaki and Royal Blue
ax1.add_patch(Polygon(box_coords, facecolor=box_colors[i % 2]))
# Now draw the median lines back over what we just filled in
med = bp['medians'][i]
medianX = []
medianY = []
for j in range(2):
medianX.append(med.get_xdata()[j])
medianY.append(med.get_ydata()[j])
ax1.plot(medianX, medianY, 'k')
medians[i] = medianY[0]
# Finally, overplot the sample averages, with horizontal alignment
# in the center of each box
ax1.plot(np.average(med.get_xdata()), np.average(data[i]),
color='w', marker='*', markeredgecolor='k')
# Set the axes ranges and axes labels
ax1.set_xlim(0.5, num_boxes + 0.5)
top = 10 #y-axis
bottom = 0 #y-axis
ax1.set_ylim(bottom, top)
ax1.set_xticklabels(np.repeat(random_dists, 1),
rotation=45, fontsize=8)
pos = np.arange(num_boxes) + 1
# Finally, add a basic legend
fig.text(0.80, 0.08, 'Overlap',
backgroundcolor=box_colors[0], color='black', weight='roman',
size='x-small')
fig.text(0.80, 0.045, 'Non overlap',
backgroundcolor=box_colors[1],
color='white', weight='roman', size='x-small')
fig.text(0.80, 0.015, '*', color='white', backgroundcolor='silver',
weight='roman', size='medium')
fig.text(0.815, 0.013, ' Average Value', color='black', weight='roman',
size='x-small')
plt.show()
What i need is overlap the data into it as a scatter plot just like the picture from this link
I really try hard to use the code on the link and try to search on overstack to find a solution but i'm not that good in coding, also i try using seaborn library but i always get an error that: 'list' object has no attribute 'get' and couldn't fix it
so please any one can help ()
The current version of plt.boxplot() allows plotting most of these elements standard.
Means will be drawn if showmeans is set to True. Its properties can be controlled via the meanprops dictionary. When setting patch_artist=True, instead of just the outline, a filled box will be drawn, boxprops controls how they look.
To draw the scatter plot on top, just call ax1.scatter. The x-positions can be jittered randomly via i + np.random.uniform(-0.4, 0.4). To force them on top of boxplot, their z-order can be changed.
As the fliers are also part of the scatter data, it probably makes sense to leave them out (showfliers=False).
To create a legend, you can collect handles to all desired elements and pass them to ax1.legend(). Note that your boxplots already get labels in the x-axis, so having them also in the legend might be a bit superfluous.
import matplotlib.pyplot as plt
import numpy as np
random_dist_names = ['Overlap', 'Non overlap']
overlap = [6, 6, 5, 1, 3, 4, 4, 3]
non_overlap = [1, 2, 6, 6, 1, 3, 3, 3, 3, 3, 5, 2, 2]
data = [overlap, non_overlap]
fig, ax1 = plt.subplots(figsize=(6, 6))
fig.canvas.set_window_title('A Boxplot Example')
fig.subplots_adjust(left=0.075, right=0.95, top=0.9, bottom=0.25)
box_colors = ['darkkhaki', 'royalblue']
scatter_colors = ['purple', 'crimson']
legend_handles = []
for i, (values, box_color, scatter_color) in enumerate(zip(data, box_colors, scatter_colors), start=1):
bp = ax1.boxplot(values, positions=[i], showmeans=True, patch_artist=True, showfliers=False,
boxprops={'edgecolor': 'black', 'facecolor': box_color},
whiskerprops={'color': 'black'}, # flierprops={'color': 'red', 'marker': '+'},
medianprops={'color': 'lime', 'linewidth': 2, 'linestyle': ':'},
meanprops={'markerfacecolor': 'w', 'marker': '*', 'markeredgecolor': 'k', 'markersize': 10})
if i == 1:
legend_handles.append(bp['means'][0])
legend_handles.append(bp['boxes'][0])
ax1.scatter(i + np.random.uniform(-0.4, 0.4, len(values)), values, color=scatter_color, alpha=0.5, zorder=3)
ax1.yaxis.grid(True, linestyle='-', which='major', color='lightgrey', alpha=0.5)
ax1.set_axisbelow(True)
ax1.set_title('overlap and non_overlap against mRS')
ax1.set_xlim(0.5, len(data) + 0.5)
ax1.set_ylim(ymin=0)
ax1.set_xticklabels(random_dist_names, rotation=0, fontsize=8)
ax1.legend(legend_handles, ['Mean'] + random_dist_names, bbox_to_anchor=[1, -0.1], loc='upper right')
plt.show()
Note that you have very few data points, and they all have integer values, which makes the red dots appear in horizontal lines.
PS: To create something similar with Seaborn, the data has to be organized more similar to a pandas dataframe. Such a dataframe would have one column with all the values, and one column with the category.
The legend can be created more automatically. To also get the means into the legend, a label has to be assigned to the mean via meanprops={..., 'label': 'Mean'}. Unfortunately, this creates one legend entry for every box. These can be skipped by first getting all the legend entries with ax.get_legend_handles_labels() and taking subarrays of the handles and labels.
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
random_dist_names = ['Overlap', 'Non overlap']
overlap = [6, 6, 5, 1, 3, 4, 4, 3]
non_overlap = [1, 2, 6, 6, 1, 3, 3, 3, 3, 3, 5, 2, 2]
data_names = np.repeat(random_dist_names, [len(overlap), len(non_overlap)])
data_values = np.concatenate([overlap, non_overlap])
ax = sns.boxplot(x=data_names, y=data_values, hue=data_names, palette=['darkkhaki', 'royalblue'],
dodge=False, showfliers=False, showmeans=True,
meanprops={'markerfacecolor': 'w', 'marker': '*', 'markeredgecolor': 'k', 'markersize': 10, 'label': 'Mean'})
sns.stripplot(x=data_names, y=data_values, color='red', alpha=0.4)
handles, labels = ax.get_legend_handles_labels()
skip_pos = len(random_dist_names) - 1
ax.legend(handles[skip_pos:], labels[skip_pos:], bbox_to_anchor=(1.02, -0.05), loc='upper right')
plt.tight_layout()
plt.show()
I'm trying to plot a big amount of curves in a stackplot with matplotlib, using python.
To read the graph, I need to show legends, but if I show it with the legend method, my graph is unreadable (because of the number of legends, and their size).
I have found that mplcursors could help me to do that with a popup in the graph itself. It works with "simple" plots, but not with a stackplot.
Here is the warning message with stackplots:
/usr/lib/python3.7/site-packages/mplcursors/_pick_info.py:141: UserWarning: Pick support for PolyCollection is missing.
warnings.warn(f"Pick support for {type(artist).__name__} is missing.")
And here is the code related to this error (it's only a proof of concept):
import matplotlib.pyplot as plt
import mplcursors
import numpy as np
data = np.outer(range(10), range(1, 5))
timestamp = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
tmp = list()
tmp.append(data[:, 0])
tmp.append(data[:, 1])
tmp.append(data[:, 2])
tmp.append(data[:, 3])
print(data)
print(tmp)
fig, ax = plt.subplots()
ax.stackplot(timestamp, tmp, labels=('curve1', 'line2', 'curvefever', 'whatever'))
ax.legend()
mplcursors.cursor()
cursor = mplcursors.cursor(hover=True)
#cursor.connect("add")
def on_add(sel):
print(sel)
label = sel.artist.get_label()
sel.annotation.set(text=label)
plt.show()
Do you have an idea of how to fix that, or do you know another way to do something like that ?
It is not clear why mplcursors doesn't accept a stackplot. But you can replicate the behavior with more primitive matplotlib functionality:
import matplotlib.pyplot as plt
import numpy as np
def update_annot(label, x, y):
annot.xy = (x, y)
annot.set_text(label)
def on_hover(event):
visible = annot.get_visible()
is_outside_of_stackplot = True
if event.inaxes == ax:
for coll, label in zip(stckplt, labels):
contained, _ = coll.contains(event)
if contained:
update_annot(label, event.x, event.y)
annot.set_visible(True)
is_outside_of_stackplot = False
if is_outside_of_stackplot and visible:
annot.set_visible(False)
fig.canvas.draw_idle()
data = np.random.randint(1, 5, size=(4, 40))
fig, ax = plt.subplots()
labels = ('curve1', 'line2', 'curvefever', 'whatever')
stckplt = ax.stackplot(range(data.shape[1]), data, labels=labels)
ax.autoscale(enable=True, axis='x', tight=True)
# ax.legend()
annot = ax.annotate("", xy=(0, 0), xycoords="figure pixels",
xytext=(20, 20), textcoords="offset points",
bbox=dict(boxstyle="round", fc="yellow", alpha=0.6),
arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
plt.connect('motion_notify_event', on_hover)
plt.show()
I am trying to plot the phasors of the voltage across the resistor, capacitor, and inductor in an series R-L-C circuit. I have done all of the calculations and I can get a decent plot with just the normal ax.plot(theta,r,....).
I would like to make the phasor vectors look like arrows. I have been trying to use ax.arrow(0,0,theta,magnitude) but it looks like a line still. The gist to the code that I have written is here : GIST
My image that I create is
I tried to follow the example that I found on this list because it is very similar to what I want to accomplish, it produces the following image:
When I run their code on my computer I get
I am on Xubuntu 14.04 and running matplotlib 1.3.1. I do see that the example I am using was using matplotlib 0.99 in 2009.
Any help would be much appreciated.
Arrow sizes were too big, this:
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
print "matplotlib.__version__ = ", matplotlib.__version__
print "matplotlib.get_backend() = ", matplotlib.get_backend()
# radar green, solid grid lines
plt.rc('grid', color='#316931', linewidth=1, linestyle='-')
plt.rc('xtick', labelsize=15)
plt.rc('ytick', labelsize=15)
# force square figure and square axes looks better for polar, IMO
width, height = matplotlib.rcParams['figure.figsize']
size = min(width, height)
# make a square figure
fig = plt.figure(figsize=(size, size))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, axisbg='#d5de9c')
r = np.arange(0, 3.0, 0.01)
theta = 2*np.pi*r
ax.plot(theta, r, color='#ee8d18', lw=3)
ax.set_rmax(2.0)
plt.grid(True)
ax.set_title("And there was much rejoicing!", fontsize=20)
#This is the line I added:
arr1 = plt.arrow(0, 0.5, 0, 1, alpha = 0.5, width = 0.015,
edgecolor = 'black', facecolor = 'green', lw = 2, zorder = 5)
# arrow at 45 degree
arr2 = plt.arrow(45/180.*np.pi, 0.5, 0, 1, alpha = 0.5, width = 0.015,
edgecolor = 'black', facecolor = 'green', lw = 2, zorder = 5)
plt.show()
Produces:
Better? :)