31

I would like to know how to make matplotlib's scatter function colour points by a third variable.

Questions gnuplot linecolor variable in matplotlib? and Matplotlib scatterplot; colour as a function of a third variable posed similar queries, however, the answers to those questions don't address my issue: the use of c=arraywhichspecifiespointcolour in the scatter function only sets the fill colour, not the edge colour. This means that the use of c=arr... fails when using markersymbol='+', for instance (because that marker has no fill, only edges). I want points to be coloured by a third variable reliably, regardless of which symbol is used.

Is there a way to achieve this with Matplotlib's scatter function?

Community
  • 1
  • 1
aaron_python_dude
  • 321
  • 1
  • 3
  • 6

1 Answers1

35

This works for me, using matplotlib 1.1:

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(10)
y = np.sin(x)

plt.scatter(x, y, marker='+', s=150, linewidths=4, c=y, cmap=plt.cm.coolwarm)
plt.show()

Result:

enter image description here

Alternatively, for n points, make an array of RGB color values with shape (n, 3), and assign it to the edgecolors keyword argument of scatter():

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 20, 100)
y = np.sin(x)
z = x + 20 * y

scaled_z = (z - z.min()) / z.ptp()
colors = plt.cm.coolwarm(scaled_z)

plt.scatter(x, y, marker='+', edgecolors=colors, s=150, linewidths=4)
plt.show()

Result: enter image description here

That example gets the RGBA values by scaling the z values to the range [0,1], and calling the colormap plt.cm.coolwarm with the scaled values. When called this way, a matplotlib colormap returns an array of RGBA values, with each row giving the color of the corresponding input value. For example:

>>> t = np.linspace(0, 1, 5)
>>> t
array([ 0.  ,  0.25,  0.5 ,  0.75,  1.  ])
>>> plt.cm.coolwarm(t) 
array([[ 0.2298,  0.2987,  0.7537,  1.    ],
       [ 0.5543,  0.6901,  0.9955,  1.    ],
       [ 0.8674,  0.8644,  0.8626,  1.    ],
       [ 0.9567,  0.598 ,  0.4773,  1.    ],
       [ 0.7057,  0.0156,  0.1502,  1.    ]])
Warren Weckesser
  • 110,654
  • 19
  • 194
  • 214
  • Thanks Warren for your answer. Unfortunately, your first example results in black pluses for me. I don't know why. mpl.__version__ gives 1.0.1, if that's relevant. I might have to try passing 'edgecolors' a list of rgbs, but that means I have to create those from the 'z' values and the colourmap manually, right? – aaron_python_dude Oct 19 '12 at 02:45
  • I'm using version 1.1. There were some changes to the marker code between 1.0 and 1.1, so that could be problem. I've updated the second example to show one way of mapping 'z' values to colors (but I haven't tried that in matplotlib 1.0). – Warren Weckesser Oct 19 '12 at 03:24
  • Hi Warren. Just to follow up on this, when I use a newer (1.1) version of matplotlib, I do indeed get coloured points with exactly the same code. Issue considered solved. Thanks! – aaron_python_dude Nov 13 '12 at 09:32
  • If you are using `Python 2.7` remember to use: `from __future__ import division` or the `colors` array will be all zeroes (due to rounding down) – mjp Feb 13 '17 at 17:32