Как наложить тепловую карту моря на фигуру matplotlib

Я пытаюсь наложить тепловую карту поверх фигуры matplotlib футбольного поля.

Это изображение шага matplotlib, созданного блоком кода ниже:

Как наложить тепловую карту моря на фигуру matplotlib


import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.patches as plt_p
import numpy as np

def draw_pitch(ax):
    # size of the pitch is 120, 80
    #Create figure

    #Pitch Outline & Centre Line
    plt.plot([0,0],[0,80], color = "black")
    plt.plot([0,120],[80,80], color = "black")
    plt.plot([120,120],[80,0], color = "black")
    plt.plot([120,0],[0,0], color = "black")
    plt.plot([60,60],[0,80], color = "black")

    #Left Penalty Area
    plt.plot([14.6,14.6],[57.8,22.2],color = "black")
    plt.plot([0,14.6],[57.8,57.8],color = "black")
    plt.plot([0,14.6],[22.2,22.2],color = "black")

    #Right Penalty Area
    plt.plot([120,105.4],[57.8,57.8],color = "black")
    plt.plot([105.4,105.4],[57.8,22.5],color = "black")
    plt.plot([120, 105.4],[22.5,22.5],color = "black")

    #Left 6-yard Box
    plt.plot([0,4.9],[48,48],color = "black")
    plt.plot([4.9,4.9],[48,32],color = "black")
    plt.plot([0,4.9],[32,32],color = "black")

    #Right 6-yard Box
    plt.plot([120,115.1],[48,48],color = "black")
    plt.plot([115.1,115.1],[48,32],color = "black")
    plt.plot([120,115.1],[32,32],color = "black")

    #Prepare Circles
    centreCircle = plt.Circle((60,40),8.1,color = "black",fill=False)
    centreSpot = plt.Circle((60,40),0.71,color = "black")
    leftPenSpot = plt.Circle((9.7,40),0.71,color = "black")
    rightPenSpot = plt.Circle((110.3,40),0.71,color = "black")

    #Draw Circles
    ax.add_patch(centreCircle)
    ax.add_patch(centreSpot)
    ax.add_patch(leftPenSpot)
    ax.add_patch(rightPenSpot)

    #Prepare Arcs
    # arguments for arc
    # x, y coordinate of centerpoint of arc
    # width, height as arc might not be circle, but oval
    # angle: degree of rotation of the shape, anti-clockwise
    # theta1, theta2, start and end location of arc in degree
    leftArc = plt_p.Arc((9.7,40),height=16.2,width=16.2,angle=0,theta1=310,theta2=50,color = "black")
    rightArc = plt_p.Arc((110.3,40),height=16.2,width=16.2,angle=0,theta1=130,theta2=230,color = "black")

    #Draw Arcs
    ax.add_patch(leftArc)
    ax.add_patch(rightArc)

fig=plt.figure()
fig.set_size_inches(7, 5)
ax=fig.add_subplot(1,1,1)
draw_pitch(ax)
plt.axis('off')
plt.show()

Как было рекомендовано в предыдущих постах, я попытался передать аргумент топора в функцию sns.heatmap() и изменить альфа-канал, чтобы повысить прозрачность тепловой карты. Однако тепловая карта по-прежнему покрывает всю фигуру, и футбольное поле не видно.

При запуске приведенного ниже кода я получаю следующий результат:

Как наложить тепловую карту моря на фигуру matplotlib

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.patches as plt_p
import numpy as np

#DUMMY DATA 
df_test = pd.DataFrame(np.array([[43.2, 22.4, 0], [-5.1,-53.2,1], [33.5,-19.2,0],
                                 [23.2, 32.4, 1], [-5.3,-53.2,1], [33.5,-69.2,0],
                                 [53.2, -42.4, 0], [-5.4,-53.2,0], [-3.5,-39.2,0],
                                 [63.2, 62.4, 1], [-52,-53.2,0], [37.5,-11.2,1],
                                 [113.2, 72.4, 0], [-34.2,-53.2,0], [42.5,-119.2,1]]),
                  columns=['x', 'y', 'outcome'])

#CREATES THE HEATMAP OVERLAY ON THE FOOTBALL PITCH
def pass_comp_map(df):
    df['x_bands'] = pd.qcut(df['x'],4,labels=False)
    df['y_bands'] = pd.qcut(df['y'],3,labels=False)
    df_pass = df[['x_bands','y_bands','outcome']]
    df_sum = df_pass.groupby(['x_bands','y_bands'], as_index=False).sum() # get total number of completed passes 
    df_count = df_pass.groupby(['x_bands','y_bands'], as_index=False).count() #get total number passes
    df_agg = pd.merge(df_sum, df_count['outcome'].to_frame(), how ='left',left_index=True,right_index=True)
    df_agg['pass_comp'] = df_agg['outcome_x'] / df_agg['outcome_y']
    data = df_agg[['x_bands','y_bands','pass_comp']]
    data_pivot = data.pivot_table(index='y_bands', columns='x_bands', values='pass_comp')
    data_pivot = data_pivot.fillna(0)

    #OVERLAY FIGIURE CREATED HERE
    fig=plt.figure()
    fig.set_size_inches(7, 5)
    ax=fig.add_subplot(1,1,1)
    draw_pitch(ax)
    plt.axis('off')
    sns.heatmap(data_pivot,cbar=False, xticklabels=False, yticklabels=False,annot=True,alpha = 0.5,ax=ax)
    plt.show()

pass_comp_map(df_test)

Как сделать так, чтобы тепловая карта накладывалась на футбольное поле, но футбольное поле все равно было видно?

Я бы посоветовал вам ознакомиться с различными уровнями абстракции, на которых вы можете взаимодействовать с matplotlib (например, здесь). Когда вы хотите работать с наложениями, очень важно понимать и иметь полный контроль над осями в вашей фигуре.

Florian Drawitsch 06.02.2019 14:05

Короче говоря: не используйте тепловую карту Seaborn. Вместо этого используйте imshow или pcolormesh от matplotlib. Они позволяют определить масштабы сюжета. Затем выберите тот же масштаб, что и ваше футбольное поле.

ImportanceOfBeingErnest 06.02.2019 16:27
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
5 045
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Взгляните на свои масштабы, поле находится в совершенно другом масштабе, чем тепловая карта. Если вы уменьшите масштаб, вы увидите что-то похожее на это:

Уменьшить масштаб

Спасибо, а как лучше масштабировать? Я попытался изменить plt.ylim() и plt.xlim(), что сделало тепловую карту больше, но я все еще не мог видеть футбольное поле Тепловая карта

James 06.02.2019 15:16

Я думаю, что проще всего было бы изменить вашу подачу так, чтобы вместо [0-120, 0-80] они стали [0-4, 0-3]. Я не знаю, как масштабировать тепловую карту моря, поскольку она отображается в виде категорий. Поэтому вам, возможно, придется рассмотреть другие способы создания тепловой карты (например, pcolormesh).

Simon Rogers 06.02.2019 16:26
Ответ принят как подходящий

Как показано в Ответ @simon-rogers, у вас есть проблема масштаба между вашим рисунком поля и вашей тепловой картой.

Тепловая карта Seaborn будет нарисована на оси с ограничениями: [0-количество столбцов] по x и [0-количество строк] по y. Таким образом, в вашем примере кадра данных результирующий график имеет размер 4x3, а ваш шаг - 120x80.

Решение состоит в том, чтобы нарисовать поле в масштабе 4x3. А еще лучше нарисуйте поле с параметризованными размерами, чтобы оно соответствовало тепловым картам различных размеров.

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

def draw_pitch(ax, width=120, height=80):
    # size of the pitch is width, height
    #Create figure

    #Pitch Outline & Centre Line
    plt.plot([0,0],[0,height], color = "black")
    plt.plot([0,width],[height,height], color = "black")
    plt.plot([width,width],[height,0], color = "black")
    plt.plot([width,0],[0,0], color = "black")
    plt.plot([width/2,width/2],[0,height], color = "black")

    #Left Penalty Area
    plt.plot([width*0.12,width*0.12],[height*0.72,height*0.28],color = "black")
    plt.plot([0,width*0.12],[height*0.72,height*0.72],color = "black")
    plt.plot([0,width*0.12],[height*0.28,height*0.28],color = "black")

    #Right Penalty Area
    plt.plot([width,width*0.88],[height*0.72,height*0.72],color = "black")
    plt.plot([width*0.88,width*0.88],[height*0.72,height*0.28],color = "black")
    plt.plot([width, width*0.88],[height*0.28,height*0.28],color = "black")

    #Left 6-yard Box
    plt.plot([0,width*0.04],[height*0.6,height*0.6],color = "black")
    plt.plot([width*0.04,width*0.04],[height*0.6,height*0.4],color = "black")
    plt.plot([0,width*0.04],[height*0.4,height*0.4],color = "black")

    #Right 6-yard Box
    plt.plot([width,width*0.96],[height*0.6,height*0.6],color = "black")
    plt.plot([width*0.96,width*0.96],[height*0.6,height*0.4],color = "black")
    plt.plot([width,width*0.96],[height*0.4,height*0.4],color = "black")

    #Prepare Circles
    centreCircle = plt.Circle((width/2,40),8.1,color = "black",fill=False)
    centreSpot = plt.Circle((width/2,40),0.71,color = "black")
    leftPenSpot = plt.Circle((9.7,40),0.71,color = "black")
    rightPenSpot = plt.Circle((110.3,40),0.71,color = "black")

    #Draw Circles
    ax.add_patch(centreCircle)
    ax.add_patch(centreSpot)
    ax.add_patch(leftPenSpot)
    ax.add_patch(rightPenSpot)

    #Prepare Arcs
    # arguments for arc
    # x, y coordinate of centerpoint of arc
    # width, height as arc might not be circle, but oval
    # angle: degree of rotation of the shape, anti-clockwise
    # theta1, theta2, start and end location of arc in degree
    leftArc = plt_p.Arc((9.7,40),height=16.2,width=16.2,angle=0,theta1=310,theta2=50,color = "black")
    rightArc = plt_p.Arc((110.3,40),height=16.2,width=16.2,angle=0,theta1=130,theta2=230,color = "black")

    #Draw Arcs
    ax.add_patch(leftArc)
    ax.add_patch(rightArc)

Зачем подгонять шаг под тепловую карту, а не наоборот? Мне кажется гораздо более естественным; иначе, если у вас будет больше данных, шаг будет расти?!

ImportanceOfBeingErnest 06.02.2019 16:37

Было бы легче точно. Я не смог найти способ указать протяженность тепловой карты с помощью Seaborn, поэтому вместо этого я решил масштабировать шаг. Суть в том, чтобы сохранить правильный размер шага для фрейма данных, но это хорошо работает только в том случае, если тепловая карта имеет соотношение 4/3, иначе все будет выглядеть странно.

Diziet Asahi 06.02.2019 16:41

Как уже отмечалось, я бы рекомендовал не использовать карту seaborn.heatmap, потому что практически невозможно масштабировать ее до размера поля.

Если вы больше не будете вносить изменения в свой код, просто замените строку sns.heatmap(...) на

ax.imshow(data_pivot.values, zorder=0, aspect = "auto", extent=(0,120,0,80), 
          cmap=sns.cubehelix_palette(light=1, as_cmap=True))

Сюжет уже похож

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

scale = np.array([120,80])
ax.imshow(data_pivot.values, zorder=0, aspect = "auto", extent=(0,scale[0],0,scale[1]), 
          cmap=sns.cubehelix_palette(light=1, as_cmap=True), origin = "lower")
offs = np.array([scale[0]/data_pivot.values.shape[1], scale[1]/data_pivot.values.shape[0]])
for pos, val in np.ndenumerate(data_pivot.values):
    ax.annotate(f"{val:.2f}", xy=np.array(pos)[::-1]*offs+offs/2, ha = "center", va = "center")
ax.invert_yaxis()

Другие вопросы по теме