Issue saving matplotlib plot with artist - matplotlib

I used the following code to generate The wind rose shares an axis with the artists which are Wedge patches.
fov = Wedge((0.51,0.43), 0.497, 174.5, 202.5, lw=1.5, facecolor="grey", edgecolor ='black', transform=ax.transAxes, alpha=0.2)
fov1 = Wedge((0.51,0.43), 0.497, 174.5, 202.5, lw=1.5, fill=None, edgecolor ='black', transform=ax.transAxes, alpha=1)
plt.hist([0, 1])
plt.close()
ax=WindroseAxes.from_ax()
ax.grid(linestyle="dashed", color="grey", zorder=0)
ax.bar(df['dir'], df['w_speed'],normed=True, opening=1, cmap = cm.magma_r, edgecolor='black', linewidth=0.5, bins=spd_bins, nsector=36, zorder= 3)
ax.set_legend(loc=(-0.12, 0.75), labels=spd_labels)
ax.set_yticks(np.arange(1, 12, step=3))
ax.set_yticklabels(np.arange(1, 12, step=3))
ax.add_artist(fov)
ax.add_artist(fov1)
plt.savefig(fname)
plt.show()
The plot includes an artists which gets moved when I save the figure in either of .pdf, .jpeg, .png, .eps formats.

Related

How to make the plot's shape round?

I have created a plot, which is working just fine.
But I really want to change its shape to a circle.
This is my current plotting code:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5,5), dpi=300)
ax = fig.add_axes([0,0,1,1])
ax.plot(30, 80, marker="o", markersize=20, markeredgecolor="#ed6033", markerfacecolor="#ed6033")
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
ax.set_facecolor('#8cc9e2')
ax.margins(0.1)
plt.setp(ax.get_xticklabels()[4], visible=False)
plt.xlim(10, 90)
plt.ylim(10, 90)
plt.grid(color='white')
plt.show()
and this is the output I get:
eventually, this is my desired output:
You can clip the path of artists including the background patch using the path of another artist.
Add this snippet before the plt.show() call:
clip_path = plt.Circle(
(0.5, 0.5), 0.5, transform=ax.transAxes, # circle coordinates defined in axes fractions
fill=None, linewidth=0 # makes circle invisible
)
ax.add_patch(clip_path)
ax.patch.set_clip_path(clip_path)

No Plotting all values of X in xaxis and why there is the background grey?

I would like to get a plot with less data on xaxis. I have this very simple script. I put a 'range' for xaxis. Furthemore I would like that my background was white with contours black and not grey (see figure). How can I do?
import matplotlib.pyplot as plt
plt.figure()
# Increase the plot size and font size.
plt.rcParams["figure.figsize"] = (60,30)
plt.xticks(fontsize=40)
plt.yticks(fontsize=40)
plt.grid(True, color='gray', linestyle='dashed', linewidth=0.5, axis='y')
# Plot the learning curve.
plt.plot(df_stats['Training Loss'], color='b', marker='.', linestyle='solid', mec='b', markersize=24, markerfacecolor='white', label="Training", linewidth=7)
plt.plot(df_stats['Valid. Loss'], color='g', marker='.', linestyle='solid', mec='b', markersize=24, markerfacecolor='white',label="Validation", linewidth=7)
# Label the plot.
plt.title("Training & Validation Loss",fontsize=60)
plt.xlabel("Epoch", fontsize=52)
plt.ylabel("Loss", fontsize=52)
plt.legend(fontsize=50)
plt.xticks(list(range(1, 72)))
plt.show()
To set the X-axis ticks to a lower frequency, you will need to change the xticks to a lower frequency. One way to do this using numpy.arange().
Regarding the background color, the default is white. But, if for reason it is not, you can set it to white using the plot and axis facecolor() to white explicitly.
The code below is the updated version with these changes. Note that I used some dummy data to demonstrate the same.
Code
df_stats= pd.DataFrame(columns=['Training Loss', 'Valid. Loss'])
df_stats['Training Loss'] = list(range(1,72))
df_stats['Valid. Loss'] = df_stats['Training Loss'] * 2.1
import matplotlib.pyplot as plt
plt.figure()
# Background color of outer area
plt.figure(facecolor='white')
# Background color of the plot area
ax = plt.axes()
ax.set_facecolor("white")
# Increase the plot size and font size.
plt.rcParams["figure.figsize"] = (60,30)
plt.xticks(fontsize=40)
plt.yticks(fontsize=40)
plt.grid(True, color='gray', linestyle='dashed', linewidth=0.5, axis='y')
# Plot the learning curve.
plt.plot(df_stats['Training Loss'], color='b', marker='.', linestyle='solid', mec='b', markersize=24, markerfacecolor='white', label="Training", linewidth=7)
plt.plot(df_stats['Valid. Loss'], color='g', marker='.', linestyle='solid', mec='b', markersize=24, markerfacecolor='white',label="Validation", linewidth=7)
# Label the plot.
plt.title("Training & Validation Loss",fontsize=60)
plt.xlabel("Epoch", fontsize=52)
plt.ylabel("Loss", fontsize=52)
plt.legend(fontsize=50)
plt.xticks(list(np.arange(1, 72, 9)))
plt.show()
Output plot

How to add a legend for a GeoAxes that adds a Cartopy shapely feature?

I copied the code for adding legend via proxy artists from matplotlib's documentation but it doesn't work. I also tried the rest in matplotlib's legends guide but nothing works. I guess it's because the element is a shapely feature which ax.legend() somehow doesn't recognize.
Code
bounds = [116.9283371, 126.90534668, 4.58693981, 21.07014084]
stamen_terrain = cimgt.Stamen('terrain-background')
fault_line = ShapelyFeature(Reader('faultLines.shp').geometries(), ccrs.epsg(32651),
linewidth=1, edgecolor='black', facecolor='none') # geometry is multilinestring
fig = plt.figure(figsize=(15,10))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
ax.set_extent(bounds)
ax.add_image(stamen_terrain, 8)
a = ax.add_feature(fault_line, zorder=1, label='test')
ax.legend([a], loc='lower left', fancybox=True) #plt.legend() has the same result
plt.show()
Result
When copying the matplotlib example, you omitted the actual "proxy" artist line!
red_patch = mpatches.Patch(color='red', label='The red data')
plt.legend(handles=[red_patch])
That red_patch is the proxy artist. You have to create a dummy artist to pass to legend(). Your code as written is still passing the unrecognized Shapely feature.
It's tedious, but the relevant code would be something like:
fault_line = ShapelyFeature(Reader('faultLines.shp').geometries(), ccrs.epsg(32651), linewidth=1, edgecolor='black', facecolor='none')
ax.add_feature(fault_line, zorder=1)
# Now make a dummy object that looks as similar as possible
import matplotlib.patches as mpatches
proxy_artist = mpatches.Rectangle((0, 0), 1, 0.1, linewidth=1, edgecolor='black', facecolor='none')
# And manually add the labels here
ax.legend([proxy_artist], ['test'], loc='lower left', fancybox=True)
Here I just used a Rectangle, but depending on the feature, you can use various supported matplotlib "artists".

Errorbar plot transparency overlapping

In an errorbar matplotlib plot, the main line, the markers and the errorbars of a same color overlap each other on their countour when I use the alpha parameter. Although my goal was to have a transparency between the two different colors, but not within the same color, as if same color lines, markers and errorbars were only one object. Is that possible?
import matplotlib.pyplot as plt
import numpy as np
Time = np.array([1, 2, 3])
Green = np.array([3, 5, 9])
Blue = np.array([4, 7, 13])
Green_StDev = np.array([0.6, 0.6, 0.7])
Blue_StDev = np.array([0.5, 0.5, 0.6])
plt.errorbar(Time, Green, Green_StDev, marker='o', c='green', alpha=0.5)
plt.errorbar(Time, Blue, Blue_StDev, marker='o', c='blue', alpha=0.5)
plt.show()
Like the example below, but with transparency only between different color objects, differently of the example above.
I think you cannot draw them as one single object since they (marker and error bar) are drawn individually. However, to make it more 'aesthetic', you could redraw a non-transparent marker:
import matplotlib.pyplot as plt
import numpy as np
Time = np.array([1, 2, 3])
Green = np.array([3, 5, 9])
Blue = np.array([4, 7, 13])
Green_StDev = np.array([0.6, 0.6, 0.7])
Blue_StDev = np.array([0.5, 0.5, 0.6])
plt.errorbar(Time, Green, Green_StDev, marker='o', c='green', alpha=0.5)
# Add additional marker
plt.scatter(Time, Green,marker='o', c='green')
plt.errorbar(Time, Blue, Blue_StDev, marker='o', c='blue', alpha=0.5)
# Add additional marker
plt.scatter(Time, Blue, marker='o', c='blue')
plt.show()

How to increase padding (whitespace) for subplots when saving as png?

I have created a 4x2 subplot and I want to save the individual plots as separate png files. I am able to do this with the following code, however the tightbbox format is too tight and it makes it tough to read the title and x,y labels. Is there a way to increase the padding (whitespace) around each individual plot when using this tightbbox layout?
nrow = combined.shape[0]
ncol = combined.shape[1]
shape_row = int((nrow + 1)/2 if (nrow % 2) != 0 else nrow/2)
fig, axes = plt.subplots(shape_row, 2, figsize=(20,45))
fig.subplots_adjust(hspace=0.5, wspace=0.4)
plt.rcParams['savefig.facecolor'] = 'white'
axes = axes.ravel()
for i in range(nrow):
axes[i].bar(range(2), combined.iloc[i,:2].values, color='blue', label=label_1)
axes[i].plot(range(2,ncol-1), combined.iloc[i,2:-1], 'o-', color='orange', markersize=10, label=label_2)
axes[i].plot([-1, 7], [combined.iloc[i,-1]]*2, linestyle='--', color='red', label=label_3)
axes[i].plot([ncol-1,ncol-1],[0, combined.iloc[i,-1]], '--', color='red')
axes[i].set_title(combined.index[i], fontsize=26, fontweight='bold', pad=15)
axes[i].set_xlabel('')
axes[i].set_ylabel("(in local currency '000s)", fontsize=18, labelpad=10)
axes[i].set_xticks(range(ncol))
axes[i].set_xticklabels(combined.columns, rotation=45)
axes[i].tick_params(labelsize=18, pad=10)
axes[i].set_xlim([-.7, nrow-.97])
axes[i].margins(x=0, y=0.2)
blue_bar= mpatches.Patch(color='blue', label='aaa')
orange_line=mlines.Line2D(range(2,ncol-1), combined.iloc[i,2:-1], color='orange', linestyle='-', marker = 'o', markersize=10, label='bbb')
red_line=mlines.Line2D([-1, 7], [combined.iloc[i,-1]]*2, color='red', linestyle='--', label='ccc')
lgd = axes[i].legend(handles=[blue_bar, orange_line, red_line],
loc='upper right', bbox_to_anchor=(1,1), fontsize=18, shadow=True, borderpad=1)
bbox = axes[i].get_tightbbox(fig.canvas.get_renderer())
fig.savefig("subplot{}.png".format(i),
bbox_inches=bbox.transformed(fig.dpi_scale_trans.inverted()))