Сгенерируйте тепловую карту в MatPlotLib, используя набор данных рассеяния

У меня есть набор точек данных X, Y (около 10k), которые легко построить в виде графика рассеяния, но я хотел бы представить его как тепловую карту.

Я просмотрел примеры в MatPlotLib, и все они, кажется, уже начали с значений ячейки тепловой карты, чтобы сгенерировать изображение.

Есть ли метод, который преобразует связку x, y, все разные, в тепловую карту (где зоны с более высокой частотой x, y будут "теплее" )?

Ответ 1

Если вы не хотите шестиугольников, вы можете использовать функцию numpy histogram2d:

import numpy as np
import numpy.random
import matplotlib.pyplot as plt

# Generate some test data
x = np.random.randn(8873)
y = np.random.randn(8873)

heatmap, xedges, yedges = np.histogram2d(x, y, bins=50)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]

plt.clf()
plt.imshow(heatmap.T, extent=extent, origin='lower')
plt.show()

Это делает 50х50 тепловой карты. Если вы хотите, скажем, 512x384, вы можете поместить bins=(512, 384) в вызов histogram2d.

Пример: Matplotlib heat map example

Ответ 2

В лексиконе Matplotlib, я думаю, вам нужен график hexbin.

Если вы не знакомы с этим типом сюжета, это просто двумерная гистограмма, в которой плоскость xy тесселируется регулярной сеткой шестиугольников.

Итак, из гистограммы вы можете просто подсчитать количество точек, попадающих в каждый шестиугольник, дискретизировать область построения в виде набора окон, назначить каждую точку одному из этих окон; наконец, сопоставьте окна с массивом цветов, и у вас есть диаграмма hexbin.

Хотя менее широко используемые, чем, например, круги или квадраты, эти шестиугольники являются лучшим выбором для геометрии контейнера для биннинга, является интуитивным:

  • шестиугольники имеют симметрию ближайшего соседа (например, квадратные ячейки не имеют, например, расстояние от точки на квадратной границе до точки внутри этого квадрата не везде равно) и

  • hexagon - самый высокий n-многоугольник, который дает регулярную плоскость тесселяции (т.е. вы можете безопасно переделать свой кухонный пол с гексагональной плитки, потому что у вас не будет пустоты между плитами, когда вы закончите - не верно для всех других более высоких n, n >= 7, многоугольники).

(Matplotlib использует термин hexbin plot, так что (AFAIK) все построение библиотек для R, но я не знаю, если это общепринятый термин для графиков этого типа, хотя я подозреваю, что он, вероятно, указывает на то, что гексбин является коротким для гексагонального биннинга, что описывает важный шаг при подготовке данных для отображения.)


from matplotlib import pyplot as PLT
from matplotlib import cm as CM
from matplotlib import mlab as ML
import numpy as NP

n = 1e5
x = y = NP.linspace(-5, 5, 100)
X, Y = NP.meshgrid(x, y)
Z1 = ML.bivariate_normal(X, Y, 2, 2, 0, 0)
Z2 = ML.bivariate_normal(X, Y, 4, 1, 1, 1)
ZD = Z2 - Z1
x = X.ravel()
y = Y.ravel()
z = ZD.ravel()
gridsize=30
PLT.subplot(111)

# if 'bins=None', then color of each hexagon corresponds directly to its count
# 'C' is optional--it maps values to x-y coordinates; if 'C' is None (default) then 
# the result is a pure 2D histogram 

PLT.hexbin(x, y, C=z, gridsize=gridsize, cmap=CM.jet, bins=None)
PLT.axis([x.min(), x.max(), y.min(), y.max()])

cb = PLT.colorbar()
cb.set_label('mean value')
PLT.show()   

enter image description here

Ответ 3

Вместо того, чтобы использовать np.hist2d, который вообще производит довольно уродливые гистограммы, я хотел бы переработать py-sphviewer, пакет python для рендеринга частиц, используя адаптивное сглаживание ядра и которое можно легко установить из pip (см. документацию по веб-странице). Рассмотрим следующий код, основанный на примере:

import numpy as np
import numpy.random
import matplotlib.pyplot as plt
import sphviewer as sph

def myplot(x, y, nb=32, xsize=500, ysize=500):   
    xmin = np.min(x)
    xmax = np.max(x)
    ymin = np.min(y)
    ymax = np.max(y)

    x0 = (xmin+xmax)/2.
    y0 = (ymin+ymax)/2.

    pos = np.zeros([3, len(x)])
    pos[0,:] = x
    pos[1,:] = y
    w = np.ones(len(x))

    P = sph.Particles(pos, w, nb=nb)
    S = sph.Scene(P)
    S.update_camera(r='infinity', x=x0, y=y0, z=0, 
                    xsize=xsize, ysize=ysize)
    R = sph.Render(S)
    R.set_logscale()
    img = R.get_image()
    extent = R.get_extent()
    for i, j in zip(xrange(4), [x0,x0,y0,y0]):
        extent[i] += j
    print extent
    return img, extent

fig = plt.figure(1, figsize=(10,10))
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222)
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224)


# Generate some test data
x = np.random.randn(1000)
y = np.random.randn(1000)

#Plotting a regular scatter plot
ax1.plot(x,y,'k.', markersize=5)
ax1.set_xlim(-3,3)
ax1.set_ylim(-3,3)

heatmap_16, extent_16 = myplot(x,y, nb=16)
heatmap_32, extent_32 = myplot(x,y, nb=32)
heatmap_64, extent_64 = myplot(x,y, nb=64)

ax2.imshow(heatmap_16, extent=extent_16, origin='lower', aspect='auto')
ax2.set_title("Smoothing over 16 neighbors")

ax3.imshow(heatmap_32, extent=extent_32, origin='lower', aspect='auto')
ax3.set_title("Smoothing over 32 neighbors")

#Make the heatmap using a smoothing over 64 neighbors
ax4.imshow(heatmap_64, extent=extent_64, origin='lower', aspect='auto')
ax4.set_title("Smoothing over 64 neighbors")

plt.show()

который создает следующее изображение:

введите описание изображения здесь

Как вы видите, изображения выглядят довольно красиво, и мы можем идентифицировать на нем различные подструктуры. Эти изображения построены, распределяя заданный вес для каждой точки внутри определенной области, определяемой длиной сглаживания, которая по очереди задается расстоянием до ближайшего соседа nb (для примеров я выбрал 16, 32 и 64). Таким образом, области с более высокой плотностью обычно распределены по более мелким областям по сравнению с областями с более низкой плотностью.

Функция myplot - это очень простая функция, которую я написал, чтобы дать x, y данным py-sphviewer делать магию.

Ответ 4

Если вы используете 1.2.x

import numpy as np
import matplotlib.pyplot as plt

x = np.random.randn(100000)
y = np.random.randn(100000)
plt.hist2d(x,y,bins=100)
plt.show()

gaussian_2d_heat_map

Ответ 5

Изменение: Для лучшего приближения ответа Алехандро, см. Ниже.

Я знаю, что это старый вопрос, но я хотел добавить кое-что в Alewandro anwser: если вы хотите получить хорошее сглаженное изображение без использования py-sphviewer, вы можете вместо этого использовать np.histogram2d и применить np.histogram2d фильтр (из scipy.ndimage.filters) к тепловая карта:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from scipy.ndimage.filters import gaussian_filter


def myplot(x, y, s, bins=1000):
    heatmap, xedges, yedges = np.histogram2d(x, y, bins=bins)
    heatmap = gaussian_filter(heatmap, sigma=s)

    extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
    return heatmap.T, extent


fig, axs = plt.subplots(2, 2)

# Generate some test data
x = np.random.randn(1000)
y = np.random.randn(1000)

sigmas = [0, 16, 32, 64]

for ax, s in zip(axs.flatten(), sigmas):
    if s == 0:
        ax.plot(x, y, 'k.', markersize=5)
        ax.set_title("Scatter plot")
    else:
        img, extent = myplot(x, y, s)
        ax.imshow(img, extent=extent, origin='lower', cmap=cm.jet)
        ax.set_title("Smoothing with  $\sigma$ = %d" % s)

plt.show()

Производит:

Output images

Диаграмма рассеивания и s = 16, нанесенные поверх друг друга для Агапе Гальо (нажмите для лучшего просмотра):

On top of eachother


Одно из различий, которое я заметил с моим подходом гауссовского фильтра и подходом Алехандро, было то, что его метод показывает локальные структуры намного лучше, чем мой. Поэтому я реализовал простой метод ближайшего соседа на уровне пикселей. Этот метод рассчитывает для каждого пикселя обратную сумму расстояний n ближайших точек в данных. Этот метод с высоким разрешением довольно затратен в вычислительном отношении, и я думаю, что есть более быстрый способ, поэтому дайте мне знать, если у вас есть какие-либо улучшения. Во всяком случае, здесь код:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm


def data_coord2view_coord(p, vlen, pmin, pmax):
    dp = pmax - pmin
    dv = (p - pmin) / dp * vlen
    return dv


def nearest_neighbours(xs, ys, reso, n_neighbours):
    im = np.zeros([reso, reso])
    extent = [np.min(xs), np.max(xs), np.min(ys), np.max(ys)]

    xv = data_coord2view_coord(xs, reso, extent[0], extent[1])
    yv = data_coord2view_coord(ys, reso, extent[2], extent[3])
    for x in range(reso):
        for y in range(reso):
            xp = (xv - x)
            yp = (yv - y)

            d = np.sqrt(xp**2 + yp**2)

            im[y][x] = 1 / np.sum(d[np.argpartition(d.ravel(), n_neighbours)[:n_neighbours]])

    return im, extent


n = 1000
xs = np.random.randn(n)
ys = np.random.randn(n)
resolution = 250

fig, axes = plt.subplots(2, 2)

for ax, neighbours in zip(axes.flatten(), [0, 16, 32, 64]):
    if neighbours == 0:
        ax.plot(xs, ys, 'k.', markersize=2)
        ax.set_aspect('equal')
        ax.set_title("Scatter Plot")
    else:
        im, extent = nearest_neighbours(xs, ys, resolution, neighbours)
        ax.imshow(im, origin='lower', extent=extent, cmap=cm.jet)
        ax.set_title("Smoothing over %d neighbours" % neighbours)
        ax.set_xlim(extent[0], extent[1])
        ax.set_ylim(extent[2], extent[3])
plt.show()

Результат:

Nearest Neighbour Smoothing

Ответ 6

Теперь у Seaborn есть функция функция совместного использования, которая должна работать здесь хорошо:

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

# Generate some test data
x = np.random.randn(8873)
y = np.random.randn(8873)

sns.jointplot(x=x, y=y, kind='hex')
plt.show()

demo image

Ответ 7

и начальный вопрос был... как преобразовать значения рассеяния в значения сетки, правильно? histogram2d подсчитывает частоту на ячейку, однако, если у вас есть другие данные на ячейку, а не только частота, вам потребуется дополнительная работа.

x = data_x # between -10 and 4, log-gamma of an svc
y = data_y # between -4 and 11, log-C of an svc
z = data_z #between 0 and 0.78, f1-values from a difficult dataset

Итак, у меня есть набор данных с Z-результатами для координат X и Y. Тем не менее, я вычислял несколько баллов за пределами области интереса (большие пробелы) и кучи точек в небольшой области интересов.

Да здесь это становится сложнее, но и веселее. Некоторые библиотеки (извините):

from matplotlib import pyplot as plt
from matplotlib import cm
import numpy as np
from scipy.interpolate import griddata

pyplot - мой графический движок сегодня, см - это набор цветных карт с некоторым выбором для начинающих. numpy для вычислений и griddata для привязки значений к фиксированной сетке.

Последнее важно, особенно потому, что частота точек xy неравномерно распределена в моих данных. Во-первых, давайте начнем с некоторых границ, соответствующих моим данным, и произвольного размера сетки. Исходные данные имеют точки данных за пределами этих границ x и y.

#determine grid boundaries
gridsize = 500
x_min = -8
x_max = 2.5
y_min = -2
y_max = 7

Таким образом, мы определили сетку с 500 пикселями между минимальными и максимальными значениями x и y.

По моим данным, в области с большим интересом имеется более 500 значений; тогда как в области с низким процентом в общей сетке нет даже 200 значений; между графическими границами x_min и x_max еще меньше.

Поэтому для получения хорошей картины задача состоит в том, чтобы получить среднее значение для высоких значений процентов и заполнить пробелы в другом месте.

Теперь я определяю свою сетку. Для каждой пары xx-yy я хочу иметь цвет.

xx = np.linspace(x_min, x_max, gridsize) # array of x values
yy = np.linspace(y_min, y_max, gridsize) # array of y values
grid = np.array(np.meshgrid(xx, yy.T))
grid = grid.reshape(2, grid.shape[1]*grid.shape[2]).T

Почему странная форма? scipy.griddata хочет форму (n, D).

Griddata вычисляет одно значение для каждой точки сетки по предопределенному методу. Я выбираю "ближайший" - пустые точки сетки будут заполнены значениями из ближайшего соседа. Это выглядит так, как будто области с меньшей информацией имеют более крупные ячейки (даже если это не так). Можно было бы интерполировать "линейный", тогда области с меньшей информацией выглядят менее резкими. Дело вкуса, действительно.

points = np.array([x, y]).T # because griddata wants it that way
z_grid2 = griddata(points, z, grid, method='nearest')
# you get a 1D vector as result. Reshape to picture format!
z_grid2 = z_grid2.reshape(xx.shape[0], yy.shape[0])

И хоп, мы передаем matplotlib для отображения сюжета

fig = plt.figure(1, figsize=(10, 10))
ax1 = fig.add_subplot(111)
ax1.imshow(z_grid2, extent=[x_min, x_max,y_min, y_max,  ],
            origin='lower', cmap=cm.magma)
ax1.set_title("SVC: empty spots filled by nearest neighbours")
ax1.set_xlabel('log gamma')
ax1.set_ylabel('log C')
plt.show()

Вокруг заостренной части V-Shape вы видите, что я много вычислял во время поиска сладкого пятна, тогда как менее интересные части почти везде имеют более низкое разрешение.

Heatmap of a SVC in high resolution

Ответ 8

Сделайте 2-мерный массив, соответствующий ячейкам вашего окончательного изображения, называемый say heatmap_cells и создайте его как все нули.

Выберите два коэффициента масштабирования, которые определяют разницу между каждым элементом массива в реальных единицах, для каждого измерения, например x_scale и y_scale. Выберите их так, чтобы все ваши точки данных попадали в пределы массива тепловой карты.

Для каждого исходного datapoint с x_value и y_value:

heatmap_cells[floor(x_value/x_scale),floor(y_value/y_scale)]+=1

Ответ 9

Очень похоже на ответ @Piti, но использует 1 вызов вместо 2 для генерации баллов:

import numpy as np
import matplotlib.pyplot as plt

pts = 1000000
mean = [0.0, 0.0]
cov = [[1.0,0.0],[0.0,1.0]]

x,y = np.random.multivariate_normal(mean, cov, pts).T
plt.hist2d(x, y, bins=50, cmap=plt.cm.jet)
plt.show()

Выход:

2d_gaussian_heatmap

Ответ 10

Боюсь, я немного опоздал на вечеринку, но у меня был похожий вопрос некоторое время назад. Принятый ответ (@ptomato) помог мне, но я также хотел бы опубликовать его, если он кому-нибудь пригодится.


''' I wanted to create a heatmap resembling a football pitch which would show the different actions performed '''

import numpy as np
import matplotlib.pyplot as plt
import random

#fixing random state for reproducibility
np.random.seed(1234324)

fig = plt.figure(12)
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)

#Ratio of the pitch with respect to UEFA standards 
hmap= np.full((6, 10), 0)
#print(hmap)

xlist = np.random.uniform(low=0.0, high=100.0, size=(20))
ylist = np.random.uniform(low=0.0, high =100.0, size =(20))

#UEFA Pitch Standards are 105m x 68m
xlist = (xlist/100)*10.5
ylist = (ylist/100)*6.5

ax1.scatter(xlist,ylist)

#int of the co-ordinates to populate the array
xlist_int = xlist.astype (int)
ylist_int = ylist.astype (int)

#print(xlist_int, ylist_int)

for i, j in zip(xlist_int, ylist_int):
    #this populates the array according to the x,y co-ordinate values it encounters 
    hmap[j][i]= hmap[j][i] + 1   

#Reversing the rows is necessary 
hmap = hmap[::-1]

#print(hmap)
im = ax2.imshow(hmap)


Вот результат enter image description here

Ответ 11

enter image description here

Здесь я заработал 1 миллион очков с тремя категориями (красный, зеленый и синий). Здесь ссылка на хранилище, если вы хотите попробовать эту функцию. Github Repo

histplot(
    X,
    Y,
    labels,
    bins=2000,
    range=((-3,3),(-3,3)),
    normalize_each_label=True,
    colors = [
        [1,0,0],
        [0,1,0],
        [0,0,1]],
    gain=50)