I want to define a colour palette to use in a geopandas map. I want to fade between two colors, RGB 0-0-90 and RGB 126-193-61.
I've checked out this page: https://matplotlib.org/stable/tutorials/colors/colormap-manipulation.html
but I don't understand how I can use customized colours based on that information.
fig, ax = plt.subplots(1, figsize=(10, 16))
matplotlib.rcParams["figure.dpi"] = 100
ax.axis('off')
ax.set_title('TITLE', fontdict={'fontsize': '16', 'fontweight' : '3'})
ax.annotate('Källa: Datalagret', xy=(0.7, .05), xycoords='figure fraction', fontsize=11, color='#555555')
sm = plt.cm.ScalarMappable(cmap='GnBu', norm=plt.Normalize(vmin=vmin, vmax=vmax))
fig.colorbar(sm, orientation="horizontal", fraction=0.036, pad=0.015, aspect = 30)
geo_df1.plot(edgecolor='black', column=variable, cmap='GnBu', linewidth=0.2, ax=ax)
# I'm using GnBu right now, wish to change this to a custom palette.
To create a custom colormap from 2 given colors, ListedColormap can be used. Here is an example code.
import matplotlib
import matplotlib.cm as cm
#from matplotlib.colors import Normalize
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# Required colors: from_RGB(0-0-90) to_RGB(126-193-61)
RGB1 = [0,0,90] # dark blue
RGB2 = [126,193,61] # pale green
N = 256 #number of discrete levels
vals = np.ones((N,4))
vals[:, 0] = np.linspace(RGB1[0]/256, RGB2[0]/256, N)
vals[:, 1] = np.linspace(RGB1[1]/256, RGB2[1]/256, N)
vals[:, 2] = np.linspace(RGB1[2]/256, RGB2[2]/256, N)
# finally, create the required colormap that ranges from
# -- dark blue to pale green
my_cmp = ListedColormap(vals)
# test plot using random data
fig, ax = plt.subplots(figsize=(4, 4))
np.random.seed(1470)
arrdata = 3 + 2.5 * np.random.randn(20, 20)
minv = np.min(arrdata)
maxv = np.max(arrdata)
psm = ax.pcolormesh(arrdata, cmap=my_cmp, rasterized=True, vmin=minv, vmax=maxv)
fig.colorbar(psm, ax=ax)
plt.show()
Related
Essentially, I want to make my discrete, binary, colorbar in python matplotlib/seaborn to have custom spacing, so that one color takes up more of the color bar than the other.
I am using seaborn heatmap to plot some binary data I have. Each row contains p different items which were labeled by my binary classifier. Four out of eleven rows belong to Class1 and the other 7 belong to Class0. I would like to have the colorbar help illustrate that breakdown, so that 4/11 of the colorbar is colored the same as Class1.
# make colormap
yellow = (249/255, 231/255, 85/255)
blue = (62/255,11/255, 81/255)
color_list = [yellow, blue]
cmap = ListedColormap(color_list)
# plot data
h = sns.heatmap(binary_preds, cmap=cmap, cbar_kws = dict(use_gridspec=False,location="left"))
for i in range(len(binary_preds) + 1):
h.axhline(i, color='white', lw=5)
colorbar = h.collections[0].colorbar
colorbar.set_ticks([.25,.75])
colorbar.set_ticklabels(['Class0', 'Class1'])
## code I would like:
# colorbar.set_spacing([0.37, 63])
Resulting colorbar:
How I'd like it to be (manually adjusted colorbar spacing):
The following approach uses a BoundaryNorm and proportional spacing for the colorbar:
from matplotlib import pyplot as plt
from matplotlib.colors import BoundaryNorm, ListedColormap
import numpy as np
import seaborn as sns
# make colormap
yellow = (249 / 255, 231 / 255, 85 / 255)
blue = (62 / 255, 11 / 255, 81 / 255)
color_list = [yellow, blue]
cmap = ListedColormap(color_list)
# define a boundary norm
proportion_class0 = 0.67 # proportion for class0
norm = BoundaryNorm([0, proportion_class0, 1], 2)
binary_preds = np.random.choice([False, True], size=(10, 15), p=[proportion_class0, 1 - proportion_class0])
ax = sns.heatmap(binary_preds, cmap=cmap, norm=norm,
cbar_kws=dict(use_gridspec=False, location="left", spacing="proportional"))
for i in range(len(binary_preds) + 1):
ax.axhline(i, color='white', lw=5)
colorbar = ax.collections[0].colorbar
colorbar.set_ticks([proportion_class0 / 2, (1 + proportion_class0) / 2])
colorbar.set_ticklabels(['Class0', 'Class1'])
plt.show()
0
I want to make a colormap used in the attached image.
img = imread('/path/Screenshot 2022-04-12 at 2.14.16 PM.png')
colors_from_img = img[:, 0, :]
my_cmap = LinearSegmentedColormap.from_list('my_cmap', colors_from_img, N=651)
y = random_sample((100, 100))
imshow(y, cmap=my_cmap);plt.colorbar().png')
Looking forward to your inputs
You just have to transpose the strategy linked in my comment from vertical to horizontal. To avoid random guessing, you analyze first the image dimensions, then guesstimate what level the horizontal line should be (ca 66/3) and what the step for the color bars is (ca 616/11). Finally, you have to normalize the image for the range -0.3 ... 0.5 and tell matplotlib that values above and below should also be considered (extend="both"). This leads us to:
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, BoundaryNorm
import numpy as np
img = plt.imread('test.png')
#analyze image dimensions
#print(img.shape)
#>>> (66, 616, 4)
colors_from_img = img[22, 60::56, :]
#generate color map
my_cmap = LinearSegmentedColormap.from_list("my_cmap", colors_from_img, N=len(colors_from_img))
#normalize with boundaries
my_norm = BoundaryNorm(np.linspace(-0.3, 0.5, 9), my_cmap.N, extend="both")
y = 2*np.random.random_sample((20, 20))-1
plt.imshow(y, cmap=my_cmap, norm=my_norm)
plt.colorbar()
plt.show()
Sample output:
If you want only the color bar to be an image, I would suggest the tutorial in the formula for an example. I have set the color names similar to the colors in your image, but you can change them to whatever colors you like.
import matplotlib.pyplot as plt
import matplotlib as mpl
fig, ax = plt.subplots(figsize=(6, 0.5))
fig.subplots_adjust(bottom=0.5)
c = ['darkblue', 'lightblue', 'aquamarine', 'green', 'lime', 'yellow','orange','red']
cmap = (mpl.colors.ListedColormap(c)
.with_extremes(over='purple', under='white'))
bounds = [-0.3,-0.2,-0.1,0,0.1,0.2,0.3,0.4,0.5]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig.colorbar(
mpl.cm.ScalarMappable(cmap=cmap, norm=norm),
cax=ax,
boundaries=[-10] + bounds + [10],
extend='both',
extendfrac='auto',
ticks=bounds,
spacing='uniform',
orientation='horizontal',
#label='Custom extension lengths, some other units',
)
fig.savefig('my_colorbar.png')
plt.show()
I'm trying to color a Voronoi object according to the number of neighbors it has. I have created a list of colors according to this number, which ranges from 4 to 7. I then set the array of the PatchCollection to the set of neighbor numbers. This technically works, however, it's selecting some really ugly colors and the colorbar on the side is continuous while it should be discrete. I would prefer to make it so that <= 4 neighbors is blue, 5 neighbors is green, 6 neighbors is grey, >=7 neighbors is red. Any ideas on how to resolve these issues?
Code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from scipy.spatial import Voronoi
import curved_analysis as ca
from matplotlib import patches
from matplotlib.collections import PatchCollection
def vor_plot(particles):
vor = Voronoi(particles[0,:,:2])
trial_ridges = vor.ridge_vertices
line_info = []
for first, last in trial_ridges:
if -1 not in (first, last):
line_info.append([vor.vertices[first], vor.vertices[last]])
vor_poly = Voronoi(particles[0,:,:2])
regions = vor_poly.regions
real_regions = []
for inner_list in regions:
if -1 not in inner_list:
real_regions.append(inner_list)
real_regions.remove([])
fig, ax = plt.subplots()
vor_poly = []
colors = []
for gon in real_regions:
xy = vor.vertices[gon]
vor_poly.append(patches.Polygon(xy))
colors.append(xy.shape[0])
lc = LineCollection(line_info, color='k', lw=0.5)
ax.add_collection(lc)
ax.scatter(vor.points[:,0], vor.points[:,1], s = 3)
ax.set_xlim([vor.points[:,0].min()-5, vor.points[:,0].max()+5])
ax.set_ylim([vor.points[:,1].min()-5, vor.points[:,1].max()+5])
colors = np.array(colors)
p = PatchCollection(vor_poly, alpha=0.3)
p.set_array(colors)
fig.colorbar(p, ax=ax)
ax.add_collection(p)
plt.show()
if __name__ == "__main__":
particles = ca.read_xyz("flat.xyz")
vor_plot(particles)
You can create a ListedColormap listing the desired colors. To decide which number maps to which color, a norm can be used, fixing 4 for the first color and 7 for the last. Both the colormap and norm need to be assigned to the PatchCollection. To position the tick labels, one can divide the range of 4 colored cells into 9 equally-spaced positions and take the ones at odd indexes.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection, PatchCollection
from scipy.spatial import Voronoi
from matplotlib import patches
from matplotlib.colors import ListedColormap
particles = np.random.rand(1, 20, 2) * 100
vor = Voronoi(particles[0, :, :2])
trial_ridges = vor.ridge_vertices
line_info = []
for first, last in trial_ridges:
if -1 not in (first, last):
line_info.append([vor.vertices[first], vor.vertices[last]])
vor_poly = Voronoi(particles[0, :, :2])
regions = vor_poly.regions
real_regions = []
for inner_list in regions:
if -1 not in inner_list:
real_regions.append(inner_list)
real_regions.remove([])
fig, ax = plt.subplots()
vor_poly = []
colors = []
for gon in real_regions:
xy = vor.vertices[gon]
vor_poly.append(patches.Polygon(xy))
colors.append(xy.shape[0])
lc = LineCollection(line_info, color='k', lw=0.5)
ax.add_collection(lc)
ax.scatter(vor.points[:, 0], vor.points[:, 1], s=3)
ax.set_xlim([vor.points[:, 0].min() - 5, vor.points[:, 0].max() + 5])
ax.set_ylim([vor.points[:, 1].min() - 5, vor.points[:, 1].max() + 5])
cmap = ListedColormap(['dodgerblue', 'limegreen', 'grey', 'crimson'])
colors = np.array(colors)
p = PatchCollection(vor_poly, alpha=0.3, cmap=cmap, norm=plt.Normalize(4, 7))
p.set_array(colors)
ax.add_collection(p)
cbar = fig.colorbar(p, ticks=np.linspace(4, 7, 9)[1::2], ax=ax)
cbar.ax.set_yticklabels(['≤ 4', '5', '6', '≥ 7'])
plt.show()
How to adjust label location relate to key?I reclassified the data and displayed a discrete corbar which looks like multi-handles legend. Actually ,I couldn't find any parameters about the location of labels(text or numbers).The default setting is keys in left while label in right. Could I change the position? such as labels under keys or above. My purpose is to show the legend as follows (label under key and no space between keys:
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
data = np.random.randint(8, size=(100,100))
cmap = plt.cm.get_cmap('PiYG', 8)
plt.pcolormesh(data,cmap = cmap,alpha = 0.75)
# Set borders in the interval [0, 1]
bound = np.linspace(0, 1, 9)
# Preparing borders for the legend
bound_prep = np.round(bound * 7, 2)
# Creating 8 Patch instances
plt.legend([mpatches.Patch(color=cmap(b)) for b in bound[:-1]],
['{}'.format(bound_prep[i]) for i in range(8)],
bbox_to_anchor=(0,-0.25,1,0.2),ncol=len(bound))
It seems that there is no parameters to adjust location of labels.
import matplotlib.pyplot as plt
import numpy as np
data = np.random.randint(8, size=(100,100))
cmap = plt.cm.get_cmap('PiYG', 8)
fig, ax = plt.subplots()
pcm = ax.pcolormesh(data,cmap = cmap,alpha = 0.75, vmin=0, vmax=8)
fig.colorbar(pcm, ax=ax)
plt.show()
I have tried this and got the result as in the image:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list("", ["red","grey","green"])
df = pd.read_csv('t.csv', header=0)
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax = ax1.twiny()
# Scatter plot of positive points, coloured blue (C0)
ax.scatter(np.argwhere(df['real'] > 0), df.loc[df['real'] > 0, 'real'], color='C2')
# Scatter plot of negative points, coloured red (C3)
ax.scatter(np.argwhere(df['real'] < 0), df.loc[df['real'] < 0, 'real'], color='C3')
# Scatter neutral values in grey (C7)
ax.scatter(np.argwhere(df['real'] == 0), df.loc[df['real'] == 0, 'real'], color='C7')
ax.set_ylim([df['real'].min(), df['real'].max()])
index = len(df.index)
ymin = df['prediction'].min()
ymax= df['prediction'].max()
ax1.imshow([np.arange(index),df['prediction']],cmap=cmap,
extent=(0,index-1,ymin, ymax), alpha=0.8)
plt.show()
Image:
I was expecting one output where the color is placed according to the figure. I am getting green color and no reds or greys.
I want to get the image or contours spread as the values are. How I can do that? See the following image, something similar:
Please let me know how I can achieve this. The data I used is here: t.csv
For a live version, have a look at Tensorflow Playground
There are essentially 2 tasks required in a solution like this:
Plot the heatmap as the background;
Plot the scatter data;
Output:
Source code:
import numpy as np
import matplotlib.pyplot as plt
###
# Plot heatmap in the background
###
# Setting up input values
x = np.arange(-6.0, 6.0, 0.1)
y = np.arange(-6.0, 6.0, 0.1)
X, Y = np.meshgrid(x, y)
# plot heatmap colorspace in the background
fig, ax = plt.subplots(nrows=1)
im = ax.imshow(X, cmap=plt.cm.get_cmap('RdBu'), extent=(-6, 6, -6, 6), interpolation='bilinear')
cax = fig.add_axes([0.21, 0.95, 0.6, 0.03]) # [left, bottom, width, height]
fig.colorbar(im, cax=cax, orientation='horizontal') # add colorbar at the top
###
# Plot data as scatter
###
# generate the points
num_samples = 150
theta = np.linspace(0, 2 * np.pi, num_samples)
# generate inner points
circle_r = 2
r = circle_r * np.random.rand(num_samples)
inner_x, inner_y = r * np.cos(theta), r * np.sin(theta)
# generate outter points
circle_r = 4
r = circle_r + np.random.rand(num_samples)
outter_x, outter_y = r * np.cos(theta), r * np.sin(theta)
# plot data
ax.scatter(inner_x, inner_y, s=30, marker='o', color='royalblue', edgecolors='white', linewidths=0.8)
ax.scatter(outter_x, outter_y, s=30, marker='o', color='crimson', edgecolors='white', linewidths=0.8)
ax.set_ylim([-6,6])
ax.set_xlim([-6,6])
plt.show()
To keep things simple, I kept the colorbar range (-6, 6) to match the data range.
I'm sure this code can be changed to suit your specific needs. Good luck!
Here is a possible solution.
A few notes and questions:
What are the 'prediction' values in your data file? They do not seem to correlate with the values in the 'real' column.
Why do you create a second axis? What is represented on the bottom X-axis in your plot? I removed the second axis and labelled the remaining axes (index and real).
When you slice a pandas DataFrame, the index comes with it. You don't need to create a separate index (argwhere and arange(index) in your code). I simplified the first part of the code, where scatterplots are produced.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list("", ["red","grey","green"])
df = pd.read_csv('t.csv', header=0)
print(df)
fig = plt.figure()
ax = fig.add_subplot(111)
# Data limits
xmin = 0
xmax = df.shape[0]
ymin = df['real'].min()
ymax = df['real'].max()
# Scatter plots
gt0 = df.loc[df['real'] > 0, 'real']
lt0 = df.loc[df['real'] < 0, 'real']
eq0 = df.loc[df['real'] == 0, 'real']
ax.scatter(gt0.index, gt0.values, edgecolor='white', color='C2')
ax.scatter(lt0.index, lt0.values, edgecolor='white', color='C3')
ax.scatter(eq0.index, eq0.values, edgecolor='white', color='C7')
ax.set_ylim((ymin, ymax))
ax.set_xlabel('index')
ax.set_ylabel('real')
# We want 0 to be in the middle of the colourbar,
# because gray is defined as df['real'] == 0
if abs(ymax) > abs(ymin):
lim = abs(ymax)
else:
lim = abs(ymin)
# Create a gradient that runs from -lim to lim in N number of steps,
# where N is the number of colour steps in the cmap.
grad = np.arange(-lim, lim, 2*lim/cmap.N)
# Arrays plotted with imshow must be 2D arrays. In this case it will be
# 1 pixel wide and N pixels tall. Set the aspect ratio to auto so that
# each pixel is stretched out to the full width of the frame.
grad = np.expand_dims(grad, axis=1)
im = ax.imshow(grad, cmap=cmap, aspect='auto', alpha=1, origin='bottom',
extent=(xmin, xmax, -lim, lim))
fig.colorbar(im, label='real')
plt.show()
This gives the following result: