How to draw a line over a contour plot joining specific points in a range of values - matplotlib

I am working with georeference temperature data. I am trying to draw some lines joining specific data points in a particular data range. For example, I want to join all data values having temperature in between 4-6 degrees and overlay it on a filled contour plots.
I was trying to achieve this using the contour lines but contour lines are isolines like they join the with same values but I wanted to join the points in a range of values and overlay over each subplots.
dataset={}
for i in range(1991,2000):
yearly = dt.TEMP.sel(TIME=slice(str(i)+'-12-01',str(i+1)+'-02-28'),
ST_OCEAN=5.0).mean(dim='TIME',keep_attrs=True)
dataset[i]=[]
dataset[i].append(yearly)
fig, ax = plt.subplots(nrows=3, ncols=3, figsize=(18,15), dpi=200, sharex=True, sharey=True,
subplot_kw={'projection': ccrs.PlateCarree()},
gridspec_kw={'hspace': 0.0 , 'wspace': 0.1})
ax = ax.flatten()
L1 = [1.5,1.8,2,3,4,5,6,7,9,11]
for i,year in enumerate(dataset):
data = dataset[year]
'''contours = ax[i].contour(dt.lon, dt.lat, data[0], colors='black')
ax[i].clabel(contours, inline=True, fontsize=3)'''
cf = ax[i].contourf(dt.lon, dt.lat , data[0], 50,
transform=ccrs.PlateCarree(), cmap='seismic')
ax[i].title.set_text('Variation of Temperature at 5m depth in year:{}'.format(year))
ax[i].coastlines()
ax[i].plot(dt.lon,dt.lat,ab)
gl = ax[i].gridlines(crs=ccrs.PlateCarree(), draw_labels=True,linewidth=0,)
gl.top_labels = False
gl.bottom_labels = True
gl.right_labels = False
plt.tight_layout()
cb_ax=fig.add_axes([1.02,.2,0.02,0.6])
plt.colorbar(cf,cax=cb_ax)
#plt.savefig('test.jpg', bbox_inches='tight',dpi=300)

Related

Tick labels in between colors (discrete colorer)

Hi I want to put the ticklabels between colors (center of the intervals), and the figure is plotted by discrete colors. But the min value is not 0. How can I write the code to do that?
I used following code to do that, but what I got is wrong...
n_clusters = len(cbar_tick_label)
tick_locs = (np.arange(n_clusters)+0.5)*(n_clusters-1)/(n_clusters)
cbar.set_ticks(tick_locs)
cbar.set_ticklabels(cbar_tick_label)
This code is from question: Discrete Color Bar with Tick labels in between colors. But it does not work when the min value of data is not zero.
Thanks!
Suppose there are N (e.g. 6) clusters. If you subdivide the range from the lowest number (e.g. 5) to the highest number (e.g. 10) into N equal parts, there will be a tick at every border between color cells. Subdividing into 2*N+1 equal parts, will also have a tick in the center of each color cell. Now, skipping every other of these 2*N+1 ticks will leave us with only the cell centers. So, np.linspace(5, 10, 6*2+1) are the ticks for borders and centers; taking np.linspace(5, 10, 6*2+1)[1::2] will be only the centers.
import numpy as np
import matplotlib.pyplot as plt
x, y = np.random.rand(2, 100)
c = np.random.randint(5, 11, x.shape)
n_clusters = c.max() - c.min() + 1
fig, ax = plt.subplots()
cmap = plt.get_cmap('inferno_r', n_clusters)
scat = ax.scatter(x, y, c=c, cmap=cmap)
cbar = plt.colorbar(scat)
tick_locs = np.linspace(c.min(), c.max(), 2 * n_clusters + 1)[1::2]
cbar_tick_label = np.arange(c.min(), c.max() + 1)
cbar.set_ticks(tick_locs)
cbar.set_ticklabels(cbar_tick_label)
plt.show()

How to generate several legends for single plot matplotlib

I was making a plot of f(x,y,z) and wanted this to be displayed in a 2D-plane. To avoid cluttering my legend i decided to have different linestyles for y, different colors for z and place the two in two separate legends. I couldn't find out how to do this even after a lot of digging, so I'm posting the solution i came up with here :) If anyone has more elegant solutions I'm all ears :)
Basically the solution was to make three plots, set two of them to have size (0,0) and place those two where i wanted the legends. It feels like an ugly way to do it, but it gave a nice plot and i didn't find any other way :) The resulting plot looks like this:
def plot_alt(style = 'log'):
cmap = cm.get_cmap('inferno')
color_scale = 1.2 #Variable to get colors from a certain part of the colormap
#Making grids for delta T and average concentration
D_T_axis = -np.logspace(np.log10(400), np.log10(1), 7)
C_bar_list = np.linspace(5,10,4)
ST_list = np.logspace(-3,-1,100)
# f(x,y,z)
DC_func = lambda C_bar, ST, DT: 2*C_bar * (1 - np.exp(ST*DT))/(1 + np.exp(ST*DT))
#Some different linestyles
styles = ['-', '--', '-.', ':']
fig, ax = plt.subplots(1,3, figsize = (10,5))
plt.sca(ax[0])
for i, C_bar in enumerate(C_bar_list): #See plot_c_rel_av_DT() for 'enumerate'
for j, DT in enumerate(D_T_axis):
plt.plot(ST_list, DC_func(C_bar, ST_list, DT), color = cmap(np.log10(-DT)/(color_scale*np.log10(-D_T_axis[0]))),
linestyle = styles[i])
# Generating separate legends by plotting lines in the two other subplots
# Basically: to get two separate legends i make two plots, place them where i want the legends
# and set their size to zero, then display their legends.
plt.sca(ax[1]) #Set current axes to ax[1]
for i, C_bar in enumerate(C_bar_list):
# Plotting the different linestyles
plt.plot(C_bar_list, linestyle = styles[i], color = 'black', label = str(round(C_bar, 2)))
plt.sca(ax[2])
for DT in D_T_axis:
#plotting the different colors
plt.plot(D_T_axis, color = cmap(np.log10(-DT)/(color_scale*np.log10(-D_T_axis[0]))), label = str(int(-DT)))
#Placing legend
#This is where i move and scale the three plots to make one plot and two legends
box0 = ax[0].get_position() #box0 is an object that contains the position and dimentions of the ax[0] subplot
box2 = ax[2].get_position()
ax[0].set_position([box0.x0, box0.y0, box2.x0 + 0.4*box2.width, box0.height])
box0 = ax[0].get_position()
ax[1].set_position([box0.x0 + box0.width, box0.y0 + box0.height + 0.015, 0,0])
ax[1].set_axis_off()
ax[2].set_position([box0.x0 + box0.width ,box0.y0 + box0.height - 0.25, 0,0])
ax[2].set_axis_off()
#Displaying plot
plt.sca(ax[0])
plt.xscale('log')
plt.xlim(0.001, 0.1)
plt.ylim(0, 5)
plt.xlabel(r'$S_T$')
plt.ylabel(r'$\Delta C$')
ax[1].legend(title = r'$\langle c \rangle$ [mol/L]',
bbox_to_anchor = (1,1), loc = 'upper left')
ax[2].legend(title = r'$-\Delta T$ [K]', bbox_to_anchor = (1,1), loc = 'upper left')
#Suptitle is the title of the figure. You can also have titles for the individual subplots
plt.suptitle('Steady state concentration gradient as a function of Soret-coefficient\n'
'for different temperature gradients and total concentrations')

Standard Plot size in Python-matplotlib

I am generating multiple plots using matplotlib.patches.rect depending on the requirements. Some cases 2 rectangles are plotted sometimes 4 rectangles. But the visualisation size differs depending on the numbers of such rectangles although the dimensions of rectangles remains the same. Here in my case every rectangle has fixed shape (1200X230).
Below is the entire working code:
sampledata = {'Layer':[1,2,3,4,5,6], 'Type':[1,1,2,2,2,2]}
ip0 = pd.DataFrame(sampledata, columns=['Layer','Type'])
for i in range(ip0['Type'].nunique()):
fig = plt.figure()
ax = fig.add_subplot(111)
ip = ip0[ip0['Type']== i+1]
b = i+1
ax.grid(linestyle='--',linewidth = '0.3', color = 'black')
for i in range(ip['Layer'].nunique()):
y_pos = (i*300)
r = matplotlib.patches.Rectangle(xy=(0, y_pos), width=1201,height=233,
facecolor = None, edgecolor = 'red', linewidth=1.2, fill = False)
ax.text(-230, y_pos+175, 'Layer-{}'.format(i),
color='g',rotation='vertical', fontsize= (36/ip['Layer'].nunique()))
ax.add_patch(r)
plt.xlim([0, 1500])
plt.ylim([0, (ip['Layer'].nunique()*300)])
plt.savefig(f'image_bin_{b}.jpeg',bbox_inches='tight', dpi =
1600,transparent=True)
I have attached pictures of 2 cases one where there 2 rectangles and one 4. Please help me making them look similar since the actual dimensions are equal.

Multicolored graph based on data frame values

Im plotting chart based on the data frame as below., I want to show the graph line in different colour based on the column Condition. Im trying the following code but it shows only one colour throughout the graph.
df = pd.DataFrame(dict(
Day=pd.date_range('2018-01-01', periods = 60, freq='D'),
Utilisation = np.random.rand(60) * 100))
df = df.astype(dtype= {"Utilisation":"int64"})
df['Condition'] = np.where(df.Utilisation < 10, 'Winter',
np.where(df.Utilisation < 30, 'Summer', 'Spring'))
condition_map = {'Winter': 'r', 'Summer': 'k', 'Spring': 'b'}
df[['Utilisation','Day']].set_index('Day').plot(figsize=(10,4), rot=90,
color=df.Condition.map(condition_map))
So, I assume you want a graph for each condition.
I would use groupby to separate the data.
# Color setting
season_color = {'Winter': 'r', 'Summer': 'k', 'Spring': 'b'}
# Create figure and axes
f, ax = plt.subplots(figsize = (10, 4))
# Loop over and plot each group of data
for cond, data in df.groupby('Condition'):
ax.plot(data.Day, data.Utilisation, color = season_color[cond], label = cond)
# Fix datelabels
f.autofmt_xdate()
f.legend()
f.show()
If you truly want the date ticks to be rotated 90 degrees, use autofmt_xdate(rotation = 90)
Update:
If you want to plot everything in a single line it's a bit trickier since a line only can have one color associated to it.
You could plot a line between each point and split a line if it crosses a "color boundary", or check out this pyplot example: multicolored line
Another possibility is to plot a lot of scatter points between each point and create a own colormap that represents your color boundaries.
To create a colormap (and norm) I use from_levels_and_colors
import matplotlib.colors
colors = ['#00BEC5', '#a0c483', '#F9746A']
boundaries = [0, 10, 30, 100]
cm, nrm = matplotlib.colors.from_levels_and_colors(boundaries, colors)
To connect each point with next you could shift the dataframe, but here I just zip the original df with a sliced version
from itertools import islice
f, ax = plt.subplots(figsize = (10,4))
for (i,d0), (i,d1) in zip(df.iterrows(), islice(df.iterrows(), 1, None)):
d_range = pd.date_range(d0.Day, d1.Day, freq = 'h')
y_val = np.linspace(d0.Utilisation, d1.Utilisation, d_range.size)
ax.scatter(d_range, y_val, c = y_val, cmap = cm, norm = nrm)
f.autofmt_xdate()
f.show()

Matplotlib: impose personalized colorbar

background story:
I am producing two plots. One plot is a standard scatterplot with color map. The other plot contains multiple histograms, each histogram has a different color that is mapped to the colormap of the previous plot. I now want to show the colormap of the previous plot in this new plot. Here is a snippet of code:
#plot 1
f1, ax1 = plt.subplots()
sc = ax1.scatter(x, y, c=z)
cb = f1.colorbar(sc)
f1.show()
#plot 2
f2, ax2 = plt.subplots()
h1 = ax2.hist(x1, color=cb.to_rgba(val1))
h2 = ax2.hist(x2, color=cb.to_rgba(val2))
h3 = ax2.hist(x3, color=cb.to_rgba(val3))
Now I would like to add cb (from the f1 plot) to this new plot. Something like f2.set_colorbar(cb) does not exists.
Question
Given a plot generated with f2, ax2 = plt.subplots() and given a colorbar, how can I force matplotlib to insert a given colorbar in this plot? Note that the colorbar does not need to be related to the data inside the plot
You may simply add another colorbar to the second figure based on the PathCollection of the first figure's scatter plot.
f1, ax1 = plt.subplots()
sc = ax1.scatter(x, y, c=z)
cb = f1.colorbar(sc)
f1.show()
#plot 2
f2, ax2 = plt.subplots()
cb2 = f2.colorbar(sc)