I have been following a tutorial on plotting F1 data over a circuit, color coded with the fastf1 library.
I wanted to add some extra's to the script to utilize the official team colors.
It works but the end result shows the colormap with the circuit covering the n bins 100.
In the picture above I used the same colormap as in the tutorial 'winter' so there is most certainly something wrong in my code.
However, the original tutorial gets a cleaner end result with only the circuit showing like this:
the tutorial in question uses a default colormap from matplotlib 'winter'. To get the team colors working I had to create a custom colormap from the 2 colors that are fetched from api.
Let's get into the code, I have tried so much and searched everywhere without success...
The custom colormap is build with this sequence of code I got from the matplotlib docs.
# Create custom colormap
teamcolor1 = to_rgb('{}'.format(team1_color))
teamcolor2 = to_rgb('{}'.format(team2_color))
colors = [teamcolor1, teamcolor2]
n_bins = [3, 6, 10, 100]
cmap_name = 'colors'
fig, axs = plt.subplots(2, 2, figsize=(6, 9))
fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05)
x = np.arange(0, np.pi, 0.1)
y = np.arange(0, 2 * np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.cos(X) * np.sin(Y) * 10
for n_bin, ax in zip(n_bins, axs.ravel()):
colormap = LinearSegmentedColormap.from_list(cmap_name, colors, N=n_bin)
im = ax.imshow(Z, interpolation='nearest', origin='lower', cmap=colormap)
ax.set_title("N bins: %s" % n_bin)
fig.colorbar(im, ax=ax)
cm.register_cmap(cmap_name, colormap)
I register the colormap to easily call it later in the script with get_cmap.
The eventual plotting of the circuit is done in this piece of code:
x = np.array(telemetry['X'].values)
y = np.array(telemetry['Y'].values)
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
fastest_driver_array = telemetry['Fastest_driver_int'].to_numpy().astype(float)
cmap = cm.get_cmap('winter', 2)
lc_comp = LineCollection(segments, norm=plt.Normalize(1, cmap.N+1), cmap=cmap)
lc_comp.set_array(fastest_driver_array)
lc_comp.set_linewidth(5)
plt.rcParams['figure.figsize'] = [18, 10]
plt.gca().add_collection(lc_comp)
plt.axis('equal')
plt.tick_params(labelleft=False, left=False, labelbottom=False, bottom=False)
cbar = plt.colorbar(mappable=lc_comp, boundaries=np.arange(1, 4))
cbar.set_ticks(np.arange(1.5, 9.5))
cbar.set_ticklabels(['{}'.format(driver1), '{}'.format(driver2)])
plt.savefig(
'{}_'.format(year) + '{}_'.format(driver1) + '{}_'.format(driver2) + '{}_'.format(circuit) + '{}.png'.format(
session), dpi=300)
plt.show()
This is where I think things go wrong, but I'm unsure of what is going wrong. I guess it has to do with how I use the colormap. But everything I changed broke the whole script.
As I don't have a lot of experience with matplotlib, it's getting very complicated.
As I don't want this question to be overly long the whole code can be read here:
https://gist.github.com/platinaCoder/7b5be22405f2003bd577189692a2b36b
Instead of creating a whole custome cmap, I got rid of this piece of code:
# Create custom colormap
teamcolor1 = to_rgb('{}'.format(team1_color))
teamcolor2 = to_rgb('{}'.format(team2_color))
colors = [teamcolor1, teamcolor2]
n_bins = [3, 6, 10, 100]
cmap_name = 'colors'
fig, axs = plt.subplots(2, 2, figsize=(6, 9))
fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05)
x = np.arange(0, np.pi, 0.1)
y = np.arange(0, 2 * np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.cos(X) * np.sin(Y) * 10
for n_bin, ax in zip(n_bins, axs.ravel()):
colormap = LinearSegmentedColormap.from_list(cmap_name, colors, N=n_bin)
im = ax.imshow(Z, interpolation='nearest', origin='lower', cmap=colormap)
ax.set_title("N bins: %s" % n_bin)
fig.colorbar(im, ax=ax)
cm.register_cmap(cmap_name, colormap)
and replaced cmap = cm.get_cmap('colors', 2) with cmap = cm.colors.ListedColormap(['{}'.format(team1_color), '{}'.format(team2_color)])
Related
In the attached image, only one small dot is seen, representing the point source, but I want to be able to see the entire concentration spread across the map.
cmap = "jet"
fig = plt.figure()
proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
cs = ax.contourf(
lon,
lat,
depo,
transform=ccrs.PlateCarree(),
cmap=cmap,
vmin=depo.min(),
vmax=depo.max(),
extend="max",
)
cbar = fig.colorbar(cs, shrink=0.8)
ax.coastlines()
ax.set_title("Conc (ng/m2) 2020-01-28 18.00")
ax.set_xticks([-40, 0, 40])
ax.set_xlabel("longitude [degE]", visible="true")
ax.set_yticks([10, 40, 70])
ax.set_ylabel("latitude [degN]", visible="true")
The problem is that you are scaling the color map with vmax to the largest occuring value in your dataset. Try something like vmax=0.1 * depo.max(). Sometimes, the mean value is also useful, e.g. vmax=1.5 * depo.mean() but that depends on the distribution of the values. Also the colors can be easiest specified with the levels parameter:
Here is a small example that you can use as starting point
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
x = np.linspace(-2, 2, 51) + 1
y = np.linspace(-2, 2, 51) - 0.5
xx, yy = np.meshgrid(x, y)
zz = 1 / (xx ** 2 + yy ** 2)
norm=mpl.colors.Normalize(vmin=-0.5, vmax=1))
cs = plt.contourf(xx, yy, zz, cmap='RdBu', levels=np.linspace(-0.5, 1, 10), extend='both')
cbar = plt.colorbar(cs, shrink=0.8)
I have run an MCMC chain for parameter estimation and have obtained accepted parameter values. I have 3 parameters and about 300 000 accepted values for each parameter.
I would now like to do a contour plot (doable) but in a 3-choose-2 triangular matrix type (a very specific requirement) Please see the attached photo contour-plot. The image shows some unrelated contour-plots from a paper but I want to have a similar type of plot for my parameters.
In total, I will have 6 plots: 3 single parameter histograms (like the top plot in each column in the image) and 3-choose-2 = 3 contour plots (as the lower triangle). Again, I need it to look as much as possible like the image.
How to achieve this on Python?
Update:
I have been able to write the below code which gives me a plot as my-plot-here.
However, I need an exact / as best as possible match with the type of figure 1. i.e. I need my xticks, yticks to show inside and not outside, the spaces between the figues to go away, a better way to show the left vertical plot labels (I'm currently using set_ylabel), the outer crooked contour level to go away, and have detailed (long-short) ticks along the x-axis of the pdfs.
def plot_histogram_fig(param, nbins, subplot_index, subplot_title):
counts, bins = np.histogram(param, bins = nbins)
plotcounts = np.insert(counts, -1, counts[-1])
bincentres = (bins[:-1] + bins[1:])/2
ax = fig.add_subplot(3, 3, subplot_index)
#ax.step(bins, plotcounts, where='post', c='y')
ax.plot(bincentres, counts, 'b')
#ax.plot([bins[np.argmax(counts)], bins[np.argmax(counts)]], [0, np.max(counts)], 'y')
ax.set_yticks([])
return [ax, counts, bincentres]
def plot_contour_fig(p1, p2, nbins, subplot_index):
H, xedges, yedges = np.histogram2d(p1, p2, bins = nbins)
Z = H.T
#Z_gauss = scipy.ndimage.gaussian_filter(Z, sigma = 0.8, order = 0) #filtering
X, Y = np.meshgrid(xedges[:-1], yedges[:-1])
ax = fig.add_subplot(3, 3, subplot_index)
im = ax.contour(X, Y, Z, levels = 6)
#plt.colorbar(im, ax = ax)
ax.clabel(im, inline=True, fontsize=4)
return [ax, H, xedges, yedges]
nbins = 50
fig = plt.figure(figsize = (10, 6))
#Histograms
ax1 = plot_histogram_fig(all_alphas, nbins, 1, subplot_title = 'alpha')
ax1[0].set_xticks([])
ax1[0].set_ylabel('alpha')
ax5 = plot_histogram_fig(all_betas, nbins, 5, subplot_title = 'beta')
ax5[0].set_xticks([])
ax9 = plot_histogram_fig(all_gammas, nbins, 9, subplot_title = 'gamma')
ax9[0].set_title('gamma', y = -0.5)
#Contours
ax4 = plot_contour_fig(all_alphas, all_betas, nbins, 4)
ax4[0].set_xticklabels([])
ax4[0].set_ylabel('beta')
ax7 = plot_contour_fig(all_alphas, all_gammas, nbins, 7)
ax7[0].set_title('alpha', y = -0.5)
ax7[0].set_ylabel('gamma')
ax8 = plot_contour_fig(all_betas, all_gammas, nbins, 8)
ax8[0].set_yticklabels([])
ax8[0].set_title('beta', y = -0.5)
plt.show()
all_alphas, all_betas, all_gammas are 1d numpy arrays storing the accepted parameter values.
Here is my code (adapted from here):
df_1 = pd.DataFrame({'Cells' : np.arange(0,100), 'Delta_7' : np.random.rand(100,), 'Delta_10' : np.random.rand(100,), 'Delta_14' : np.random.rand(100,)}, columns = ['Cells','Delta_7', 'Delta_10', 'Delta_14'])
#figure
fig, ax1 = plt.subplots()
fig.set_size_inches(13, 10)
#c sequence
c = df_1['Delta_7']
#plot
plt.scatter(np.full((len(df_1), 1), 1), df_1['Delta_7'] , s = 50, c=c, cmap = 'viridis')
plt.scatter(np.full((len(df_1), 1), 2), df_1['Delta_10'] , s = 50, c=c, cmap = 'viridis')
plt.scatter(np.full((len(df_1), 1), 3), df_1['Delta_14'] , s = 50, c=c, cmap = 'viridis')
cbar = plt.colorbar()
I would like to make a beautiful jitterplot (like on R or seaborn) with matplotlib. The thing is that I would like to give each cell a color based on its 'Delta_7' value. And this color would be kept when plotting 'Delta_10' and 'Delta_14', that I didn't manage to do with seaborn.
Please, could you let me know if you have any clue (python package, coding tricks …)?
Kindly,
The positions of the dots can be obtained from the list returned by scatter. These positions can be jittered, for example only in the x-direction. Possibly the range of the x-axis needs to be extended a bit to show every displaced dot.
Here is some code to start experimenting:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
def jitter_dots(dots):
offsets = dots.get_offsets()
jittered_offsets = offsets
# only jitter in the x-direction
jittered_offsets[:, 0] += np.random.uniform(-0.3, 0.3, offsets.shape[0])
dots.set_offsets(jittered_offsets)
df_1 = pd.DataFrame({'Cells': np.arange(0, 100),
'Delta_7': np.random.rand(100),
'Delta_10': np.random.rand(100),
'Delta_14': np.random.rand(100)})
fig, ax1 = plt.subplots()
columns = df_1.columns[1:]
c = df_1['Delta_7']
for i, column in enumerate(columns):
dots = plt.scatter(np.full((len(df_1), 1), i), df_1[column], s=50, c=c, cmap='plasma')
jitter_dots(dots)
plt.xticks(range(len(columns)), columns)
xmin, xmax = plt.xlim()
plt.xlim(xmin - 0.3, xmax + 0.3) # make some room to show the jittered dots
cbar = plt.colorbar()
plt.show()
An image is worth a thousand words :
https://www.harrisgeospatial.com/docs/html/images/colorbars.png
I want to obtain the same color bar than the one on the right with matplotlib.
Default behavior use the same color for "upper"/"lower" and adjacent cell...
Thank you for your help!
Here is the code I have:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
fig, ax = plt.subplots(1, 1, figsize=(8, 8))
# even bounds gives a contour-like effect
bounds = np.linspace(-1, 1, 10)
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
pcm = ax.pcolormesh(X, Y, Z,
norm=norm,
cmap='RdBu_r')
fig.colorbar(pcm, ax=ax, extend='both', orientation='vertical')
In order to have the "over"/"under"-color of a colormap take the first/last color of that map but still be different from the last color inside the colormapped range you can get one more color from a colormap than you have boundaries in the BoundaryNorm and use the first and last color as the respective colors for the "over"/"under"-color.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
fig, ax = plt.subplots(1, 1, figsize=(8, 8))
# even bounds gives a contour-like effect
bounds = np.linspace(-1, 1, 11)
# get one more color than bounds from colormap
colors = plt.get_cmap('RdBu_r')(np.linspace(0,1,len(bounds)+1))
# create colormap without the outmost colors
cmap = mcolors.ListedColormap(colors[1:-1])
# set upper/lower color
cmap.set_over(colors[-1])
cmap.set_under(colors[0])
# create norm from bounds
norm = mcolors.BoundaryNorm(boundaries=bounds, ncolors=len(bounds)-1)
pcm = ax.pcolormesh(X, Y, Z, norm=norm, cmap=cmap)
fig.colorbar(pcm, ax=ax, extend='both', orientation='vertical')
plt.show()
As suggested in my comment you can change the color map with
pcm = ax.pcolormesh(X, Y, Z, norm=norm, cmap='rainbow_r')
That gives:
You can define your own color map as shown here: Create own colormap using matplotlib and plot color scale
I have an patch collection that I'd like to display a color map for. Because of some manipulations I do on top of the colormap, it's not possible for me to define it using a matplotlib.colorbar instance. At least not as far as I can tell; doing so strips some manipulations I do with my colors that blank out patches lacking data:
cmap = matplotlib.cm.YlOrRd
colors = [cmap(n) if pd.notnull(n) else [1,1,1,1]
for n in plt.Normalize(0, 1)([nullity for _, nullity in squares])]
# Now we draw.
for i, ((min_x, max_x, min_y, max_y), _) in enumerate(squares):
square = shapely.geometry.Polygon([[min_x, min_y], [max_x, min_y],
[max_x, max_y], [min_x, max_y]])
ax0.add_patch(descartes.PolygonPatch(square, fc=colors[i],
ec='white', alpha=1, zorder=4))
So I define a matplotlib.colorbar.ColorbarBase instance instead, which works:
matplotlib.colorbar.ColorbarBase(ax1, cmap=cmap, orientation='vertical',
norm=matplotlib.colors.Normalize(vmin=0, vmax=1))
Which results in e.g.:
The problem I have is that I want to reduce the size of this colorbar (specifically, the shrink it down to a specific vertical size, say, 500 pixels), but I don't see any obvious way of doing this. If I had a colorbar instance, I could adjust this easily using its axis property arguments, but ColorbarBase lacks these.
For further reference:
The example my implementation is based on.
The source code in question (warning: lengthy).
The size and shape is defined with the axis. This is a snippet from code I have where I group 2 plots together and add a colorbar at the top independently. I played with the values in that add_axes instance until I got a size that worked for me:
cax = fig.add_axes([0.125, 0.925, 0.775, 0.0725]) #has to be as a list - starts with x, y coordinates for start and then width and height in % of figure width
norm = mpl.colors.Normalize(vmin = low_val, vmax = high_val)
mpl.colorbar.ColorbarBase(cax, cmap = self.cmap, norm = norm, orientation = 'horizontal')
The question may be a bit old, but I found another solution that can be of help for anyone who is not willing to manually create a colorbar axes for the ColorbarBase class.
The solution below uses the matplotlib.colorbar.make_axes class to create a dependent sub_axes from the given axes. That sub_axes can then be supplied for the ColorbarBase class for the colorbar creation.
The code is derived from the matplotlib code example describe in here
Here is a snippet code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import matplotlib.colorbar as mcbar
from matplotlib import ticker
import matplotlib.colors as mcolors
# Make some illustrative fake data:
x = np.arange(0, np.pi, 0.1)
y = np.arange(0, 2 * np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.cos(X) * np.sin(Y) * 10
colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] # R -> G -> B
n_bins = [3, 6, 10, 100] # Discretizes the interpolation into bins
cmap_name = 'my_list'
fig, axs = plt.subplots(2, 2, figsize=(9, 7))
fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05)
for n_bin, ax in zip(n_bins, axs.ravel()):
# Create the colormap
cm = LinearSegmentedColormap.from_list(cmap_name, colors, N=n_bin)
# Fewer bins will result in "coarser" colomap interpolation
im = ax.imshow(Z, interpolation='nearest', origin='lower', cmap=cm)
ax.set_title("N bins: %s" % n_bin)
cax, cbar_kwds = mcbar.make_axes(ax, location = 'right',
fraction=0.15, shrink=0.5, aspect=20)
cbar = mcbar.ColorbarBase(cax, cmap=cm,
norm=mcolors.Normalize(clip=False),
alpha=None,
values=None,
boundaries=None,
orientation='vertical', ticklocation='auto', extend='both',
ticks=n_bins,
format=ticker.FormatStrFormatter('%.2f'),
drawedges=False,
filled=True,
extendfrac=None,
extendrect=False, label='my label')
if n_bin <= 10:
cbar.locator = ticker.MaxNLocator(n_bin)
cbar.update_ticks()
else:
cbar.locator = ticker.MaxNLocator(5)
cbar.update_ticks()
fig.show()