Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib Colormap with two parameter

I am trying to represent two variables x and y on a map using colours.

So far the best I can acheive is using color for one variable and transparancy for another.

plt.Polygon(shape.points, fill=False, color=cmap(y), alpha=x)

Is it possible to create a 2D colormap in matplotlib that takes two variables?

like image 641
hangc Avatar asked Jan 31 '17 20:01

hangc


2 Answers

There is a Javascript plugin for 2D colormaps available here.

A quick translation of this to Python could be something like this:

# Based on: https://github.com/dominikjaeckle/Color2D

import matplotlib.pyplot as plt
import numpy as np

COLORMAP_FILE = "./bremm.png"


class ColorMap2D:
    def __init__(self, filename=None):
        self._colormap_file = filename or COLORMAP_FILE
        self._img = plt.imread(self._colormap_file)

        self._width = len(self._img[0])
        self._height = len(self._img)

        self._range_x = (0, 1)
        self._range_y = (0, 1)

    def _scale(self, u: float, u_min: float, u_max: float) -> float:
        return ((u + 1) - (u_min + 1)) / ((u_max + 1) - (u_min + 1))

    def _scale_x(self, x: float) -> int:
        val = self._scale(x, self._range_x[0], self._range_x[1])
        return int(val * (self._width - 1))

    def _scale_y(self, y: float) -> int:
        val = self._scale(y, self._range_y[0], self._range_y[1])
        return int(val * (self._height - 1))

    def __call__(self, X):
        assert len(X.shape) == 2

        self._range_x = (X[:, 0].min(), X[:, 0].max())
        self._range_y = (X[:, 1].min(), X[:, 1].max())

        output = np.zeros((X.shape[0], 3))
        for i in range(X.shape[0]):
            x, y = X[i, :]
            xp = self._scale_x(x)
            yp = self._scale_y(y)
            output[i, :] = self._img[xp, yp]
        return output

which expects the bremm.png file in the same directory.

This can be used in the same way as any other Matplotlib colormap:

cmap = ColorMap2D()
colors = cmap(array)

where array is an N x 2 matrix of (x, y) coordinates.

like image 31
GjjvdBurg Avatar answered Jan 01 '23 01:01

GjjvdBurg


In general colormaps in matplotlib are 1D, i.e. they map a scalar to a color. In order to obtain a 2D colormap one would need to somehow invent a mapping of two scalars to a color. While this is in principle possible, it's not as convenient as the usual colormaps.

An example is below, where we map two parameters to the red and blue RGB color channel respectively and thereby create a 2D colormap.

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()
ax.set_aspect("equal")

x = [1,1,2,2,3,3] # xdata
y = [1,2,3,1,2,3] # ydata
p1 = [0.2,0.4,.6,.8,.6,.1] # parameter 1
p2 = [0.4,0.5,.7,.1,.3,.7] # parameter 2

# create a very simple colormap, 
#  mapping parameter 1 to the red channel and 
#          parameter 2 to the blue channel
cmap = lambda p1,p2 : (p1, 0, p2)

# put shapes at positions (x[i], y[i]) and colorize them with our
# cmap according to their respective parameters
for i in range(len(x)):
    circle = plt.Circle((x[i], y[i]), 0.5, color=cmap(p1[i],p2[i]))
    ax.add_artist(circle)
    tx="p1: {}\np2: {}".format(p1[i],p2[i]) # create a label
    ax.text(x[i], y[i], tx, ha="center", color="w", va="center")

ax.set_xlim(0,4)
ax.set_ylim(0,4)
ax.set_xlabel("x")
ax.set_ylabel("y")

# create the legend:

plt.subplots_adjust(left=0.1, right=0.65, top=0.85)
cax = fig.add_axes([0.7,0.55,0.3,0.3])
cp1 = np.linspace(0,1)
cp2 = np.linspace(0,1)
Cp1, Cp2 = np.meshgrid(cp1,cp2)
C0 = np.zeros_like(Cp1)
# make RGB image, p1 to red channel, p2 to blue channel
Legend = np.dstack((Cp1, C0, Cp2))
# parameters range between 0 and 1
cax.imshow(Legend, origin="lower", extent=[0,1,0,1])
cax.set_xlabel("p1")
cax.set_ylabel("p2")
cax.set_title("2D cmap legend", fontsize=10)

plt.show()

enter image description here

This scheme can of course be extended to other (more complicated) colormappings and also to imshow or pcolormesh plots. Also, the use of the HSV colorscheme may be beneficial compared to the RGB, so matplotlib.colors.hsv_to_rgb(hsv) might be helpful.

like image 70
ImportanceOfBeingErnest Avatar answered Jan 01 '23 00:01

ImportanceOfBeingErnest