mplsoccer绘制利物浦传球网络球员位置反转问题求助
问题:利物浦传球网络图球员位置左右颠倒

各位好:
如您所见,这是利物浦某场比赛的传球网络图。对比实际阵容后发现,Trent Alexander-Arnold和Mohamed Salah本应出现在右侧,但图中却在左侧。想请教我哪里出错了?是合并传球者与接球者的环节?分组环节?还是传球绘制环节?
以下是我的代码:
import numpy as np import pandas as pd from mplsoccer.pitch import Pitch from mplsoccer.pitch import VerticalPitch from matplotlib.colors import to_rgba from mplsoccer import Pitch, FontManager import arabic_reshaper from bidi.algorithm import get_display #set options to display the mxa_rows and max columns pd.set_option('display.max_rows', None) pd.set_option('display.max_columns', None) #read in csv dataframe = pd.read_csv('LIVERPOOLvsWOLVES.csv') #Select only the relevant columns dataframe = dataframe [["event/eventId","event/timeMin","event/timeSec","event/playerName", "event/playerId","event/outcome","event/x","event/y","event/contestantId", "event/qualifier/0/qualifierId","event/qualifier/0/value", "event/qualifier/1/qualifierId","event/qualifier/1/value", "event/qualifier/2/qualifierId","event/qualifier/2/value", "event/qualifier/3/qualifierId","event/qualifier/3/value", "event/qualifier/4/qualifierId","event/qualifier/4/value", "event/qualifier/5/qualifierId","event/qualifier/5/value", "event/qualifier/6/qualifierId","event/qualifier/6/value", "event/qualifier/7/qualifierId","event/qualifier/7/value", "event/qualifier/8/qualifierId","event/qualifier/8/value", ]] #Sort now only the wanted team in this case liverpool dataframe = dataframe[dataframe['event/contestantId']== 'c8h9bw1l82s06h77xxrelzhur'] #Sort now only the successful passes outcome 1 dataframe = dataframe[dataframe['event/outcome']==1] #rename 22 columns dataframe.rename({'event/qualifier/0/qualifierId':'eventqualifier0qualifierId'},axis='columns',inplace=True) + 21 columns #Add two extra columns dataframe["endX"]="" dataframe["endY"]="" #fill the endX and the endY dataframe = pd.DataFrame(dataframe) #loop through all rows and get the x_end and y_end def get_final_values(row): for i in range(0,8): if row[f'eventqualifier{i}qualifierId'] == 140: row['endX'] = row[f'eventqualifier{i}value'] elif row[f'eventqualifier{i}qualifierId'] == 141: row['endY'] = row[f'eventqualifier{i}value'] return row dataframe = dataframe.apply(get_final_values,axis=1) #Add two extra columns dataframe['passer'] = dataframe['event/playerName'] dataframe['recipient'] = dataframe['event/playerName'].shift(-1) #1-remove now the dublicated rows for all the players df.drop(df[(df.score < 50) & (df.score > 20)].index) for player Stefan Bajčetić dataframe = dataframe.drop( dataframe[(dataframe.passer =='Stefan Bajčetić')&(dataframe.recipient =='Stefan Bajčetić')].index) #Remove the empty rows of endX and endY dataframe = dataframe.drop( dataframe[(dataframe.endX =='')& (dataframe.endY =='')].index) #filter the dataframe untill the first subtitutionOff dataframe = dataframe[ dataframe['event/timeMin']<75] #Filter now again only the needed columns dataframe = dataframe [["event/eventId","event/timeMin","event/timeSec","event/playerName", "event/playerId","event/outcome","x","y","event/contestantId", "endX","endY","passer","recipient" ]] #Rename some columns dataframe.rename({'event/playerName':'playerName'},axis='columns',inplace=True) dataframe.rename({'event/playerId':'playerId'},axis='columns',inplace=True) #Create the average location average_locations = dataframe.groupby('passer').agg({'x':['mean'],'y':['mean','count']}) average_locations.columns = ['x','y','count'] #find the number of passes between each player #pass_between = sorted_df_SuccessfullPasses[sorted_df_SuccessfullPasses['event/timeMin']<75] pass_between = dataframe.groupby(['passer','recipient'], as_index=False).playerId.count().reset_index() pass_between.rename({'playerId':'pass_count'},axis='columns',inplace=True) #Merge now the average location and pass_between pass_between = pass_between.merge(average_locations, left_on='passer', right_index=True) pass_between = pass_between.merge(average_locations, left_on='recipient', right_index=True,suffixes=['','_end']) MAX_LINE_WIDTH = 18 MAX_MARKER_SIZE = 3000 pass_between['width'] = (pass_between.pass_count / pass_between.pass_count.max() * MAX_LINE_WIDTH) average_locations['marker_size'] = (average_locations['count'] / average_locations['count'].max() * MAX_MARKER_SIZE) MIN_TRANSPARENCY = 0.3 color = np.array(to_rgba('white')) color = np.tile(color, (len(pass_between), 1)) c_transparency = pass_between.pass_count / pass_between.pass_count.max() c_transparency = (c_transparency * (1 - MIN_TRANSPARENCY)) + MIN_TRANSPARENCY color[:, 3] = c_transparency #Now plotting pitch = Pitch(pitch_type='statsbomb', pitch_color='black', line_color='#c7d5cc') fig, ax = pitch.draw(figsize=(16, 11), constrained_layout=True, tight_layout=False) fig.set_facecolor("#22312b") arrows = pitch.arrows(1.2*pass_between.x, .8*pass_between.y, 1.2*pass_between.x_end,.8*pass_between.y_end, ax=ax, width = 3, headwidth= 3, color='white', zorder =1, alpha = .5) arrows = pitch.lines(1.2*pass_between.x, .8*pass_between.y, 1.2*pass_between.x_end,.8*pass_between.y_end,lw=pass_between.width, color=color, zorder=1, ax=ax) nodes = pitch.scatter(1.2*average_locations.x,.8*average_locations.y, s = average_locations.marker_size, color = 'red', edgecolors = 'black', linewidth = 2.5, alpha = 1, zorder = 1, ax=ax) for index, row in average_locations.iterrows(): pitch.annotate(row.name, xy=(1.2*row.x, .8*row.y), c='white', va='center', ha='center', size=16, weight='bold', ax=ax)
问题诊断与解决
问题出在绘制环节的坐标错误缩放,你手动对x、y坐标进行了1.2*和.8*的缩放操作,这既扭曲了球场比例,又导致球员位置偏移颠倒。此外代码中还有一处语法错误需要修复:
1. 修复坐标缩放问题
移除所有手动缩放坐标的代码,mplsoccer的Pitch类会自动适配StatsBomb的0-100坐标系。修改后的绘制代码如下:
# 移除手动缩放,直接使用原始坐标 arrows = pitch.arrows(pass_between.x, pass_between.y, pass_between.x_end, pass_between.y_end, ax=ax, width=3, headwidth=3, color='white', zorder=1, alpha=.5) arrows = pitch.lines(pass_between.x, pass_between.y, pass_between.x_end, pass_between.y_end, lw=pass_between.width, color=color, zorder=1, ax=ax) nodes = pitch.scatter(average_locations.x, average_locations.y, s=average_locations.marker_size, color='red', edgecolors='black', linewidth=2.5, alpha=1, zorder=1, ax=ax) for index, row in average_locations.iterrows(): pitch.annotate(row.name, xy=(row.x, row.y), c='white', va='center', ha='center', size=16, weight='bold', ax=ax)
如果修改后位置仍颠倒,说明数据是从对手视角记录的,创建Pitch时添加reverse=True翻转视角:
pitch = Pitch(pitch_type='statsbomb', pitch_color='black', line_color='#c7d5cc', reverse=True)
2. 修复列重命名语法错误
你代码中dataframe.rename(...) + 21 columns是无效语法,替换为批量重命名逻辑:
# 批量重命名qualifier相关列 for i in range(9): dataframe.rename({f'event/qualifier/{i}/qualifierId': f'eventqualifier{i}qualifierId', f'event/qualifier/{i}/value': f'eventqualifier{i}value'}, axis='columns', inplace=True)
这一步修复后,后续获取传球终点坐标的逻辑才能正常工作。
内容的提问来源于stack exchange,提问作者WilliamAshoti
相关产品推荐
相关产品推荐

