How to assign a specific color to a specific set of values when generating a heatmap when using sns.heatmap? - colors

In a project, I am attempting to assign colors to specific value ranges when plotting a heatmap using sns.heatmap. The goal is to get all values between 0-50 colored green, 50-100 colored yellow, 100-150 colored orange, 150-200 colored red, and 200 and above colored purple. Here is what has been tried so far:
# To create a custom color palette for the heatmap.
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
def create_color(r, g, b):
return [r/256, g/256, b/256]
# RGB codes for green, yellow, orange, red, purple, respectively
def my_colors():
return LinearSegmentedColormap.from_list("", [
create_color(0, 255, 0), create_color(255,255,0), create_color(255,165,0),
create_color(255, 0, 0), create_color(143,0,255)])
# creating a colormap
colormap = my_colors()
#defining "plt"
import matplotlib.pyplot as plt
# Figure plotting
fig = plt.figure(figsize=(12,12))
# Plot heatmap with 'cmap=colormap' colors.
r = sns.heatmap(data, cmap=colormap, annot=True, vmin=0, vmax=200, linewidths=0.5, cbar_kws={"shrink": 0.8},
palette={'Value' <= 50:'(0, 255, 0)',
51 <= 'Value' <=100:'(255, 255, 0)',
101 <= 'Value' <= 150:'(255,165,0)',
151 <= 'Value' <= 200:'(255, 0, 0)',
201 <= 'Value' <= 300:'(143,0,255)',
301 <= 'Value' <= 500:'(128,0,0)'})
plt.xlabel('x')
plt.ylabel('y')
r.set_title("Heatmap")
However, <= cannot be used to relate a str and an int. Any idea how to assign one color to a range of values in this way? or is there an easier way?

Related

Reorder/reshape NP array as image

I can grab the colours of an image with
import re
from PIL import Image
import numpy as np
docName = "pal.png"
img = Image.open(docName).convert("RGB")
# Make into Numpy array
npArr = np.array(img)
# Arrange all pixels into a tall column of 3 RGB values and find unique rows (colours)
colours, counts = np.unique(npArr.reshape(-1,3), axis=0, return_counts=1)
# Change to string
npStr = np.array2string(colours, separator = ", ")
pal = re.sub(r"\s?\[|\]\,|]]", "", npStr)
print(pal)
Using a small 4 colour sample image
We have four colours:
51, 51, 51
179, 198, 15
255, 204, 0
255, 255, 255
Trouble is NP re-orders them in order of brightness. I want to preserve the order as reading it from top left to bottom right.
I need them in this order:
51, 51, 51 # near black
255, 255, 255 # white
255, 204, 0 # yellow
179, 198, 15 # green
Can that be easily done with NumPy?
I don't know exactly what the image is, but you could use the return_index=True parameter in np.unique. This way you get the indices of the first occurrences for corresponding colours in colours. If you then sort these indices, you can index colours from your image to get the unique colours while preserving the order.
colours, idx, counts = np.unique(
npArr.reshape(-1,3), axis=0, return_index=True, return_counts=True
)
print(npArr.reshape(-1,3)[np.sort(idx)])

Convert manually CIE LChab values to RGB

I want display sRGB values based on CIE LHab values, i didn't really know the topic aroud color theory but here is my code, i use colour library.
Did i miss something?
#Use Illuminant d65
d65 = [0.31382,0.33100]
# Maximun lightness of 100
lightess = 100
# Maximun chroma of 90
chroma = 90
# Create primary hue
hue = np.arange(0,360,45)
# Create np array
primary_rgb = np.array([[lightess,chroma, x] for x in hue])
# Convert to CIE L*a*b
primary_lab = colour.LCHab_to_Lab(primary_rgb)
# Convert to XYZ
primary_xyz = colour.Lab_to_XYZ(primary_lab)
# Convert to sRGB color
primary_rgb = colour.XYZ_to_sRGB(primary_xyz,d65,'Bradford')
# Denormalize values
primary_rgb*255
Output out of range with negative values...
array([[ 409.91335532, 170.93938038, 260.71868158],
[ 393.03002494, 198.83037084, 134.96104706],
[ 300.27298956, 250.59731666, 58.49528246],
[ 157.31758891, 283.79165255, 123.85945153],
[-1256.38350547, 296.51665099, 254.2577884 ],
[-2417.70063864, 292.21019209, 380.58920247],
[ -374.81508589, 264.85047515, 434.59056034],
[ 315.68646752, 211.99574857, 383.26874897]])
I want a correct ouput
The problem here is that you are constructing a hue sweep that covers a significant portion of the CIE Lab space, doing so, some of the colours, i.e. the negative ones, will be outside sRGB gamut:
import colour
import numpy as np
D65 = colour.CCS_ILLUMINANTS["CIE 1964 10 Degree Standard Observer"]["D65"]
hue = np.arange(0, 360, 45)
LCHab = colour.utilities.tstack([np.full(hue.shape, 100), np.full(hue.shape, 90), hue])
Lab = colour.LCHab_to_Lab(LCHab)
XYZ = colour.Lab_to_XYZ(Lab, D65)
sRGB = (
colour.cctf_encoding(
np.clip(colour.XYZ_to_sRGB(XYZ, apply_cctf_encoding=False), 0, 1)
)
* 255
)
print(sRGB)
figure, axes = colour.plotting.plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS(
"sRGB", diagram_opacity=0.25, standalone=False
)
uv = colour.Luv_to_uv(colour.XYZ_to_Luv(XYZ, D65))
axes.scatter(uv[..., 0], uv[..., 1])
colour.plotting.render()

How to use rgb value to add color in pandas.scatter_matrix?

I want to add color in pd.scatter_matrix, the rgb value like that,
val_rgb = [[127 80 34]
[130 89 34]
[170 133 75]
...]
I once use them in scatter3D them like that,
for i in range(0, len(df)):
ax1.scatter3D(
df[i,0],
df[i,1],
df[i,2],
s = 2,
marker='o',
c = '#%02x%02x%02x' % tuple(val_rgb[i])
)
However, in scatter_matrix, I find it only can add c='red' , Is there more accurate to adjust the color of each point?
PS(I also find adding color by label sns.pairplot(df), but also didn't find how to add color accurately...)
You can specify a color using RGB format using a tuple of float values between 0 and 1. Thus simply divide the RGB values by 255:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
N = 500
data = pd.DataFrame(np.random.randn(N, 4), columns=['A','B','C','D'])
colors = np.random.randint(256, size=(N, 3)) # random colors
pd.plotting.scatter_matrix(data, alpha=.2, color=colors / 255)

Misunderstanding in a Matplotlib program

I was working on the code "Discrete distribution as horizontal bar chart", found here LINK, using Matplotlib 3.1.1
I've been circling around the question for a while, but I still can't figure it out: what's the meaning of the instruction: category_colors = plt.get_cmap('RdYlGn')(np.linspace(0.15, 0.85, data.shape[1])) ?
As np.linspace(0.15, 0.85, data.shape[1]) resolves to array([0.15 , 0.325, 0.5 , 0.675, 0.85 ]), I first thought that the program was using the colormap RdYlGn (supposed to go from color=0.0 to color=1.0) and was then taking the 5 specific colors located at point 0.15, etc., 0.85
But, printing category_colors resolves to a (5, 4) array:
array([[0.89888504, 0.30549789, 0.20676663, 1. ],
[0.99315648, 0.73233372, 0.42237601, 1. ],
[0.99707805, 0.9987697 , 0.74502115, 1. ],
[0.70196078, 0.87297193, 0.44867359, 1. ],
[0.24805844, 0.66720492, 0.3502499 , 1. ]])
I don't understand what these numbers refer to ???
plt.get_cmap('RdYlGn') returns a function which maps a number between 0 and 1 to a corresponding color, where 0 gets mapped to red, 0.5 to yellow and 1 to green. Often, this function gets the name cmap = plt.get_cmap('RdYlGn'). Then cmap(0) (which is the same as plt.get_cmap('RdYlGn')(0)) would be the rbga-value (0.6470588235294118, 0.0, 0.14901960784313725, 1.0) for (red, green, blue, alpha). In hexadecimal, this color would be #a50026.
By numpy's broadcasting magic, cmap(np.array([0.15 , 0.325, 0.5 , 0.675, 0.85 ])) gets the same result as np.array([cmap(0.15), cmap(0.325), ..., cmap(0.85)]). (In other words, many numpy functions applied to an array return an array of that function applied to the individual elements.)
So, the first row of category_colors = cmap(np.linspace(0.15, 0.85, 5)) will be the rgba-values of the color corresponding to value 0.15, or 0.89888504, 0.30549789, 0.20676663, 1.. This is a color with 90% red, 31% green and 21% blue (and alpha=1 for complete opaque), so quite reddish. The next row are the rgba values corresponding to 0.325, and so on.
Here is some code to illustrate the concepts:
import matplotlib.pyplot as plt
from matplotlib.colors import to_hex # convert a color to hexadecimal format
from matplotlib.cm import ScalarMappable # needed to create a custom colorbar
import numpy as np
cmap = plt.get_cmap('RdYlGn')
color_values = np.linspace(0.15, 0.85, 5)
category_colors = cmap(color_values)
plt.barh(color_values, 1, height=0.15, color=category_colors)
plt.yticks(color_values)
plt.colorbar(ScalarMappable(cmap=cmap), ticks=color_values)
plt.ylim(0, 1)
plt.xlim(0, 1.1)
plt.xticks([])
for val, color in zip(color_values, category_colors):
r, g, b, a = color
plt.text(0.1, val, f'r:{r:0.2f} g:{g:0.2f} b:{b:0.2f} a:{a:0.1f}\nhex:{to_hex(color)}', va='center')
plt.show()
PS: You might also want to read about norms, which map an arbitrary range to the range 0,1 to be used by colormaps.

Why do assigned RGB values get changed automatically?

First, consider this code:
from PIL import Image
im = Image.open("best_tt.jpg")
im2 = Image.new("RGB", im.size, (255,255,255))
b = 200
for i in range(im.size[0]):
for j in range(im.size[1]):
rgb = im.getpixel((i,j))
if rgb[0] <= b and rgb[1] <= b and rgb[2] <= b:
im2.putpixel((i,j), (0,0,0))
else:
im2.putpixel((i,j), (0, rgb[1], rgb[2]))
im2.save("tmp.jpg")
What I am doing is simply removing the RED component from each pixel (other than black pixels: the if statement checks for pixels that look black). In other words, I'm converting the given image to a yellow scale (since G+B = Y).
In that way, every pixel should have an RGB value like (0, G, B).
However, certain pixels of the new image returned values like:
(1, 255, 203)
(3, 205, 243)
(16, 242, 47)
though some had the red component as 0.
What causes this arbitrary adjustment of the RGB values?
The save() function will determine the type as a jpeg, which has a default compression quality of 75. The way the file is encoded and compressed can end up changing values after the fact.
See the PIL documentation for save() below:
https://pillow.readthedocs.io/en/3.1.x/handbook/image-file-formats.html

Resources