I have a vertical bar plot in matplotlib and would like the color of the bars to vary based on the values in an array. I know that in plot.scatter() this can be done with cmap=''. But i can't seem to find the same functionality with plot.bar(). Any advice? is the figure in question.
fig, season = plt.subplots()
# show the phenology season
season.set_ylabel('GPP 20th percent yearly max')
season.tick_params('y', colors = 'blue', labelsize =24)
season.bar(x = pheno['SRO_SoS'], height= pheno['SRO_20th'], width =
pheno['SRO_DateDelta'], zorder=1, color = 'wheat', align =
'edge')
season.set_ylim(0,5)
temp = season.twinx()
temp.plot(df_w.index, df_w['TA_F'],color = 'red', label = 'Tempurature',
linewidth = 2)
# set x-label
temp.set_xlabel('Date')
temp.tick_params('x', labelsize =24)
# set primary y label
temp.set_ylabel('Tempurature (C)')
temp.tick_params('y', colors = 'red', labelsize =24)
# set x-axis limits as the min and max of the series
temp.set_xlim(date2num([df_w.index.min(), df_w.index.max()]))
temp.xaxis.set_major_formatter(mdates.DateFormatter('%d-%m-%Y'))
temp.xaxis.set_major_locator(mdates.YearLocator(1, month=1, day=1))
temp.set_ylim(2,30)
temp.grid(True)
plt.show()
You can give an array to the color argument in your season.bar(). That should work.
If you want to give discrete colors, here is an example.
fig, season = plt.subplots()
clist = ['red', 'blue', 'green']
season.bar(x = range(1, 11), height= range(10, 30, 2), width = 0.8, zorder=1, color = clist, align = 'edge')
Discrete colors - Output plot
For using cmap continuous colors, here is another example.
fig, season = plt.subplots()
my_cmap = plt.get_cmap("viridis")
colorrange = [0,0.25,0.5,0.75,1.0] #Range should be in range of 0 to 1
season.bar(x = range(1, 11), height= range(10, 30, 2), width = 0.8, zorder=1, color=my_cmap(colorrange), align = 'edge')
Continuous colors - Output plot
Related
I'm having trouble with a plot I'm trying to make in matplotlib. I'm including the current version below (note the partially visible legend in the top right). I have two questions:
How do I get all of the subplots to shrink inward (i.e. their right boundaries to move leftward) to make room for the legend?
I would like group these axes into pairs by adding vertical space between each pair, i.e. if we are counting axes from 0 then I would like to have extra space between axes 1 and 2 as well as between 3 and 4. It seems like there should be a way to do this using subfigures or nested gridspecs, but subplots was the only way I could see to implement a shared x axis.
Thanks in advance.
Here is the code that I am using to generate it:
fig, axs = plt.subplots(6, 1, sharex = True, figsize = (8,8))
ts = [9, 38, 47]
for i in range(len(ts)):
t = ts[i]
ax = axs[2*i]
twin = axs[2*i+1]
bed_profile = dye_test.bed_profile_vs_t[t]
head_top_BC = head_top_BC_vs_t[t]
x_interp = np.linspace(x_axis.min(), x_axis.max(), 1000)
head_fn = interp1d(x_axis, head_top_BC)
h_interp = head_fn(x_interp)
peak_locs, peak_props = find_peaks(h_interp, prominence = 0.01)
trough_locs, trough_props = find_peaks(-h_interp, prominence = 0.01)
ax.plot(x_interp, h_interp)
ax.scatter(x_interp[peak_locs], h_interp[peak_locs], fc = 'limegreen', ec = 'black', marker = 'o', zorder = 3)
ax.scatter(x_interp[trough_locs], h_interp[trough_locs], fc = 'magenta', ec = 'black', marker = 's', zorder = 3)
ax.set_ylim(-0.072, 0.1)
twin.plot(bed_profile[:,0], bed_profile[:,1], color = 'darkgray')
twin.set_ylim(16, 19.25)
l = fig.axes[0].legend(artists, ["Head", "Head Peaks", "Head Troughs", "Bed Profile"], loc = 'upper left', bbox_to_anchor = (1.1, 1))
How to choose the font size for text annotations inside the bars of the bar graph with the condition:
Text will completely cover the rectangular bar area.
Please go through the diagram and code for better clarity about the problem.
So, the requirement is only : font size should be relative to bars in the bar graphs
Code
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
# Plot styles
mpl.style.use("ggplot")
# data
fruits = pd.Series(index = ["Apples", "Oranges", "Watermelon"], data = [324,518, 258])
# Bar graph for Fruits
# figure
plt.figure(figsize = (7,5))
# bar graph
fruits.plot(kind = "bar", color = ["red", "orange", "green"], alpha = 0.6, width = 0.5, )
# percentage of each fruit type
categories = list(fruits.index)
categories_percent = [100*(value/fruits.sum()) for value in fruits ]
# categories annotations coordinates
ax = plt.gca() # get current axes
rects = ax.patches # rectangles axes of bars in the graph
# annotations
for i in range(len(categories)):
plt.annotate(f"{categories[i]} - {categories_percent[i] : 0.2f}%",
xy = (rects[i].get_x() + rects[i].get_width()/2,
rects[i].get_y() + (ax.get_yticks()[1] - ax.get_yticks()[0])*.2),
fontsize = [20,28,12][i], # Chosen by hit and trial for adjustment
color = "white",
ha = "center",
rotation = 90,
)
plt.ylabel("# Counts", fontsize = 15,)
plt.title("Distribution of Fruits", fontsize = 25, fontname = "Monospace", alpha = .6)
plt.xticks([])
plt.tight_layout(rect=[0, 0, 1, 1])
plt.show()
How to deal with this line of code fontsize = [20,28,12][i], # Chosen by hit and trial for adjustment to adjust the font size dynamically with respect to bar area?
Updating the existing annotation with an adjustable fontsize
From a logical perspective figure sizes' y acts as a scaling factor for height.
Think .get_height as a relative height of the figure.
The actual height is the y scaling factor multiplied with .get_height.
About including breadth, we can include relative breadth which is just .get_width (not get_width*x), however it would just act as a constant, since it's relative width.
We can't include actual width because the font would adjusted unproportionally for y axis.
x,y=15,15
plt.figure(figsize = (x,y))
for i in range(len(categories)):
txt="{} - {: 0.2f} %".format(categories[i],categories_percent[i])
plt.annotate(txt,
xy = (rects[i].get_x() + rects[i].get_width()/2,
rects[i].get_y() + (ax.get_yticks()[1] - ax.get_yticks()[0])*.2),
fontsize = (rects[i].get_height())*y*.2/len(txt), # Chosen by hit and trial for adjustment
color = "white",
ha = "center",
rotation = 90,
)
The entire code can be written more cleanly as follows
# data
fruits = pd.Series(index = ["Apples", "Oranges", "Watermelon"], data=[324,518, 258])
# calculate percent
per = fruits.div(fruits.sum()).mul(100).round(2)
# bar graph
y = 5
ax = fruits.plot(kind="bar", color=["red", "orange", "green"], alpha=0.6, width=0.5, figsize=(7, y), rot=0)
labels = [f'{fruit} - {per[fruit]}%' for fruit in fruits.index]
# annotations:
for label, p in zip(labels, ax.patches):
left, bottom, width, height = p.get_bbox().bounds
fs = height * y * 0.18 / len(label)
ax.annotate(label, xy=(left+width/2, bottom+height/2), ha='center', va='center', rotation=90, fontsize=fs)
plt.ylabel("# Counts", fontsize=15,)
plt.title("Distribution of Fruits", fontsize=25, fontname="Monospace", alpha=.6)
plt.xticks([])
plt.tight_layout(rect=[0, 0, 1, 1])
plt.show()
For figsize=(15,15):
For figsize=(8,8):
For figsize=(7,5):
I want to add minor ticks on the y axis. I tried several methods but they all seem to not work. The minor ticks just won't show up.
Here is my code.
sns.set_style('whitegrid')
dd.plot(x='Date', y='Min', lw = 4, figsize=(50,30), color = 'red')
plt.grid(color = 'grey')
dd['P05'].plot(lw = 2)
dd['P10'].plot(lw = 2)
dd['P25'].plot(lw = 2)
dd['P50'].plot(lw = 2)
dd['P75'].plot(lw = 2)
dd['P90'].plot(lw = 2)
dd['P95'].plot(lw = 2)
dd['Max'].plot(lw = 4, color = 'black')
plt.xticks(np.arange(0,366, step=31), list(monthDict.values()),\
fontsize = 35, fontname = 'Consolas')
plt.yticks(fontsize = 35)
plt.ylabel('Daily Average Stage (ft NGVD29)', fontsize = 35, \
labelpad = 25,fontname = 'Consolas', color = 'black')
plt.xlabel('') #remove xlabel
plt.legend(prop={'family':'Consolas', 'size':35})
# plt.legend(['5th Pecrentile', '10th percentile', '25th percentile', \
# '50th percentile', '75th percentile', '90th percentile', \
# '95th percentile', '99th percentile'])
plt.title('Cyclic Analysis at {}-H'.format(x),\
fontsize = 55, fontname='Consolas', color = 'black')
ax.minorticks_on()
ax.tick_params(axis='y', which='minor', bottom=False)
I would like to get both the major and minor y ticks at the end of the plot
You can try this
ax.yaxis.get_ticklocs(minor=True)
ax.minorticks_on()
If you want to disable the minor ticks on the x-axis, you can do
ax.xaxis.set_tick_params(which='minor', bottom=False)
Based on #spinup's answer: how to turn on minor ticks only on y axis matplotlib
I am trying to plot a depth map using Basemap in python. The contour and pcolormesh are working, but them when I add meridians, parallels and scale is returning a blank image.
I have tried to plot one by one, excluding meridians and paralles, and adding just scale, but returns a blank map and it is the same with the others. I used the same code before and it was working...
import netCDF4 as nc
from netCDF4 import Dataset
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib import ticker
grid = nc.Dataset('remo_grd.nc', mode='r')
h = grid.variables['h'][:]
h = h.astype(int)
h=-h
lon= grid.variables['lon_rho'][:]
lat= grid.variables['lat_rho'][:]
latmin = np.min(lat)
latmax= np.max(lat)
lonmax= np.max(lon)
lonmin= np.min(lon)
fig = plt.figure(1, figsize = (7,5.4), dpi = 100)
ax = fig.add_subplot(111)
m = Basemap(projection='merc', llcrnrlon=lonmin-0.2, urcrnrlon=lonmax+0.2, llcrnrlat=latmin-0.2, urcrnrlat=latmax+0.2, lat_ts=0, resolution='i')
xi, yi = m(lon,lat)
m.ax = ax
cs= m.pcolormesh(xi, yi, np.squeeze(h), shading = 'flat', zorder = 2)
levels = [-1000, -200]
a = m.contour(xi, yi, np.squeeze(h), levels, colors = 'black', linestyles = 'solid', linewidth= 1.5, extend = 'both', zorder = 3 )
plt.clabel(a, inline=2, fontsize= 10, linewidth= 1.0, fmt = '%.f', zorder= 4)
ax.text(0.5, -0.07, 'Longitude', transform=ax.transAxes, ha='center', va='center', fontsize = '10')
ax.text(-0.15, 0.5, 'Latitude', transform=ax.transAxes, ha= 'center', va='center', rotation='vertical', fontsize = '10')
m.drawcoastlines(linewidth=1.5, color = '0.1',zorder=5)
m.fillcontinents(color=('gray'),zorder=5 )
m.drawstates(linewidth = 0.5, zorder = 7)
m.drawmapboundary(color = 'black', zorder = 8, linewidth =1.2)
m.drawparallels(np.arange(int(latmin),int(latmax),3),labels=[1,0,0,0], linewidth=0.0, zorder =0)
m.drawmeridians(np.arange(int(lonmin),int(lonmax),3),labels=[0,0,0,1], linewidth=0.0)
cbar = plt.colorbar(cs, shrink=0.97, extend = 'both')
cbar.set_ticks([-10, -250, -500, -750, -1000, -1250, -1500, -1750, -2000, -2250, -2500])
cbar.set_ticklabels([-10, -250, -500, -750, -1000, -1250, -1500, -1750, -2000, -2250, -2500])
cbar.set_label('Meters (m)' , size = 10, labelpad = 20, rotation = 270)
ax = cbar.ax.tick_params(labelsize = 9)
titulo='Depth'
plt.title(titulo, va='bottom', fontsize='12')
#plot scale
dref=200
# Coordinates
lat0=m.llcrnrlat+0.9
lon0=m.llcrnrlon+1.9
#Tricked distance to provide to the the function
distance=dref/np.cos(lat0*np.pi/180.)
# Due to the bug, the function will draw a bar of length dref
scale=m.drawmapscale(lon0,lat0,lon0,lat0,distance, barstyle='fancy', units='km', labelstyle='simple',fillcolor1='w', fillcolor2='#555555', fontcolor='#555555', zorder = 8)
#Modify the labels with dref instead of distance
scale[12].set_text(dref/2)
scale[13].set_text(dref)
plt.show()
I've solved the problem! I was setting a specific order to plot each one of the details using the function zorder, so I was overlapping the data
I've got two pandas series I would like to plot on the same axis, using a twinx. Here is a picture of what it looks like:
fig,(ax1,ax2,ax3,ax4,ax5) = plt.subplots(nrows = 5,ncols = 1, figsize = (8,13))
plt.subplots_adjust(hspace = 0.5)
ax1_1 = ax1.twinx()
df[["Var"]].plot(ax = ax1, label = 'Variance')
df[['w2']].plot(ax = ax1_1, color = 'g', label = '$w$')
ax1.locator_params('y',nbins = 5)
ax1_1.locator_params('y', nbins = 5)
ax1.set_ylabel('AC', labelpad = 10)
ax1_1.set_ylabel('w', labelpad = 10)
ax1.legend(loc = 'upper left')
ax1_1.legend()
I'd like to edit the x axis ticks, but using ax1.set_xticks() and ax1.set_xticklabels() doesn't seem to work. Furthermore, why are there are no x ticks after I execute the code found above? Shouldn't the ticks be the index of the Series?