slash89mf slash89mf - 7 months ago 405
Python Question

Density map (heatmaps) in matplotlib

I have a list of coordinates:

y,x
445.92,483.156
78.273,321.512
417.311,204.304
62.047,235.216
87.24,532.1
150.863,378.184
79.981,474.14
258.894,87.74
56.496,222.336
85.105,454.176
80.408,411.672
90.656,433.568
378.027,441.296
433.964,290.6
453.606,317.648
383.578,115.432
128.232,312.496
116.276,93.536
94.072,222.336
52.226,327.308
321.663,187.56
392.972,279.008


I would like to plot a density map (or heat map) based on these points, using matplotlib. I saw that
pcolor
and
pcolormesh
can be useful. But how can i apply them to my data?
I want to plot them on top of a png image (soccer pitch) to have something like this:
enter image description here

This is my code:

import matplotlib.pyplot as plt
import matplotlib
import numpy as np
from scipy.stats import gaussian_kde

im = plt.imread('statszone_football_pitch.png')
implot = plt.imshow(im, aspect='auto')
xlim, ylim = plt.xlim(), plt.ylim()

dpi=90

fig = plt.figure(figsize=(620/dpi, 579/dpi), dpi=dpi)
ax = fig.add_axes([0, 0, 1, 1], frame_on=False)
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
ax.imshow(im, interpolation='none')

y, x = np.genfromtxt('coordinate_tackle_success.csv', delimiter=',', unpack=True)
#plt.plot(x,y, "o")
plt.xlim(xlim)
plt.ylim(ylim)

Z = np.exp(-((x-1)**2+y**2))

# Plot the density map using nearest-neighbor interpolation
plt.pcolormesh(x,y,Z)

plt.savefig('tackles.png')


This give me an error:

Traceback (most recent call last):
File "tackles.py", line 27, in <module>
plt.pcolormesh(x,y,Z)
File "/usr/local/lib/python2.7/site-packages/matplotlib/pyplot.py", line 3093, in pcolormesh
ret = ax.pcolormesh(*args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/matplotlib/__init__.py", line 1812, in inner
return func(ax, *args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/matplotlib/axes/_axes.py", line 5395, in pcolormesh
X, Y, C = self._pcolorargs('pcolormesh', *args, allmatch=allmatch)
File "/usr/local/lib/python2.7/site-packages/matplotlib/axes/_axes.py", line 4995, in _pcolorargs
numRows, numCols = C.shape
ValueError: need more than 1 value to unpack

Answer

This will hopefully get you started on the right track, but I would definitely recommend reading the docs for pcolor and pcolormesh.

You have commented # Plot the density map using nearest-neighbor interpolation, but since Z is a 1D array, you don't have any 2D density data for a density map. Density maps are most easily created through the use of np.histogram2d as I'll show below using your data.

Z, xedges, yedges = np.histogram2d(x, y)

Z is now a 2D array that has information about the distribution of your x, y coordinates. This distribution can be plotted with pcolormesh like so

plt.pcolormesh(xedges, yedges, Z.T)

Sort of a ways to go before you obtain an image like the one you posted, but it should explain your error and help get you on the right track.

Update: For nicer, smoother density maps

Assuming you have two 1D arrays, x and y you can use a kernel density estimate to obtain much nicer heatmaps in the following way [reference],

from scipy.stats.kde import gaussian_kde

k = gaussian_kde(np.vstack([x, y]))
xi, yi = np.mgrid[x.min():x.max():x.size**0.5*1j,y.min():y.max():y.size**0.5*1j]
zi = k(np.vstack([xi.flatten(), yi.flatten()]))

Now you can plot the Gaussian KDE with either pcolormesh or contourf depending on what kind of effect/aesthetics you're after

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(7,8))
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)

# alpha=0.5 will make the plots semitransparent
ax1.pcolormesh(xi, yi, zi.reshape(xi.shape), alpha=0.5)
ax2.contourf(xi, yi, zi.reshape(xi.shape), alpha=0.5)

ax1.set_xlim(x.min(), x.max())
ax1.set_ylim(y.min(), y.max())
ax2.set_xlim(x.min(), x.max())
ax2.set_ylim(y.min(), y.max())

# you can also overlay your soccer field
im = plt.imread('soccerPitch.jpg')
ax1.imshow(im, extent=[x.min(), x.max(), y.min(), y.max()], aspect='auto')
ax2.imshow(im, extent=[x.min(), x.max(), y.min(), y.max()], aspect='auto')

I get this image:

enter image description here

Comments