Apparently available on free transfer for either Udinese or us in January. 24 year old centre back. https://sportwitness.co.uk/pozzo-fa...free-agent-could-sign-for-watford-or-udinese/
Wonder why his contract was terminated between transfer windows. Seems odd as he can’t play for over 3 months? Gives Udinese time to assess whether he’s good enough to keep or bad enough to send to us.
Former France youth international. Seemed to play fairly regularly for RB Salzburg in recent seasons. Seems a bit odd that he was released and I'd have thought he'd probably be out of reach for us and rather a signing for Udinese. But who knows, maybe Porteous' recent displays have got us worried and already planning an urgent upgrade? Any stats for his last season @reids ? fbref doesn't seem to cover the Austrian Bundesliga.
I'm sure it was just his agent drumming up business but on the usual rumour-touting sites he was linked with Man Utd, Villa, West Ham etc only this summer. I can't imagine he'd be keen to come here unless we somehow got promoted.
I always tend to look at the shape of these rather than the scales and actual values. I mean do all of these radars have 0.27 as the max "OBV" stat (whatever that is !??) or is 0.27 actually pretty crap and good players are 0.75 or something....???
Yeah, all templates for each position will have the same values listed at the same points - so every radar for that position is comparable. He's actually got 0.31 OBV which goes off the scale - which puts him in the top 2% of all CBs. OBV stands for on-ball value and it basically quantifies every single action each player makes to give a value as to whether that action is likely to make his team more/less likely to score a goal. This probably explains it better than I could: They basically take a bunch of stats that are useful in that players position and compares them against every other player in the world who also plays in that position. The more colours = the better that player is in that particular statistic. A nice way to try and learn from is Haaland (as well as most people knowing what he does pretty well!). You can see some stats are very coloured and others have practically nothing in them. So from this we can see that: - He takes a LOT of shots (as it's coloured in pretty much at the max!) compared to most strikers - He presses quite a lot (as the pressures + pressures regains stats are very coloured in) - He's not a very good dribbler (or doesn't try to dribble that much) as the "successful dribbles" stat is basically nothing
Dunno whether you like/play chess but to simplify it for myself, I think of OBV as like a chess calculator/analyser - that tells you how each move (compared the positions of all the other pieces on the board) likely affects your chance of winning, then just replace chess with football and winning with scoring!
99.9% sure you won't be able to read this anyway, but how hard is it, exactly, to comprehend that the further out the colour goes towards the edge of the circle at each point, the stronger the player is at that particular attribute?
Wait till @reids starts writing his own software in R. His stats will be through the roof with graphs, diagrams and colour coded spread sheets/databases. Sofascore won't be able to touch it!
If I ever get time to get past anything else than installing it that being said, I've been given some code in Python that I have to edit each week that queries SB data to produce automated reports so I am getting a bit more comfortable in that regard!
You got that from a load of jumbled Python Code? (no offense I cannot stand Python scripts ) Very impressive! Thankfully I never have to handle databases (but I know how to write one if I had to) in my work but I do query them via a third party Ada library that uses a bind package from SQL. When you get a grasp of R you will understand how efficient it really is in not only presenting and displaying data, but hiding it from other files, sorting it and maintaining it 1000% better than Python ever will.
I don't know what you mean - it's easy! Code: team="Tenerife" #must be same spelling as appears in StatsBomb IQ competition=42 #StatsBomb's competition id season=317 #StatsBomb's season id league="La Liga 2" #this is for the viz title, call it what you like season_title="2024/25" #you can change any of the colours on the data viz should you wish to do so bgcolor="white" textc="black" white="white" sbred='#e21017' lightgrey="#d9d9d9" darkgrey='#9A9A9A' linec=darkgrey cmaplist = [white, darkgrey, sbred] pip install statsbombpy pip install mplsoccer from statsbombpy import sb import pandas as pd import numpy as np from mplsoccer import VerticalPitch, add_image import matplotlib.pyplot as plt from matplotlib.colors import LinearSegmentedColormap import matplotlib.patheffects as path_effects import matplotlib.font_manager as font_manager from scipy.ndimage import gaussian_filter from matplotlib import patches import warnings warnings.filterwarnings("ignore") #Choosing matches from competitions and season matches = sb.matches(competition_id= competition, season_id=season, creds={"user": email, "passwd": password}) team_matches = matches[(matches['home_team'] == team)|(matches['away_team'] == team)] team_matches = team_matches[team_matches["match_status"]=="available"] team_matches = team_matches.sort_values(by=['match_date']) list_matches = team_matches.match_id.tolist() #load events from specific matches (faster than pulling a whole competition season) events = [] for n in list_matches: match_events = sb.events(match_id = n,creds={"user": email, "passwd": password}) events.append(match_events) events=pd.concat(events) #filtering by specific event -> corners corners = events[events["pass_type"] == "Corner"] ### parse relevant columns to get start and end point of actions corners[['x', 'y']] = corners['location'].apply(pd.Series) corners[['pass_end_x', 'pass_end_y']] = corners['pass_end_location'].apply(pd.Series) #filling blank cells with short corner technique corners['pass_technique'] = corners['pass_technique'].fillna("Short") #filter dataframe to get only corners by focus team focus_corners=corners[(corners['team'] == team)] #identify corners by backpost, frontpost and penspot conditions=[((abs(focus_corners['y']-focus_corners['pass_end_y'])) > 43) & (focus_corners['pass_technique']!="Short")] values=[1] focus_corners['backpost']=np.select(conditions, values) conditions=[((abs(focus_corners['y']-focus_corners['pass_end_y'])) < 37 ) & (focus_corners['pass_technique']!="Short")] values=[1] focus_corners['frontpost']=np.select(conditions, values) conditions=[(focus_corners['backpost']==0) & (focus_corners['frontpost']==0) & (focus_corners['pass_technique']!="Short")] values=[1] focus_corners['penspot']=np.select(conditions, values) #separating by left and right corners in own team and also in opposition team left_corners=focus_corners[(focus_corners['y']<=40) & (focus_corners['x']>=60)] right_corners=focus_corners[(focus_corners['y']>=40) & (focus_corners['x']>=60)] def get_corner_stats (data): #get total corners total=len(data) # shot, front, back short=len(data[(data['pass_technique']=='Short')])*100 short=round(short/total,1) backpost=len(data[(data['backpost']==1)])*100 backpost=round(backpost/total,1) frontpost=len(data[(data['frontpost']==1)])*100 frontpost=round(frontpost/total,1) penspot=len(data[(data['penspot']==1)])*100 penspot=round(penspot/total,1) #six-yard, deeper sixyard=len(data[(data['pass_end_x']>=114)&(data['pass_technique']!='Short')])*100 sixyard=round(sixyard/total,1) deeper=len(data[(data['pass_end_x']<114)&(data['pass_technique']!='Short')])*100 deeper=round(deeper/total,1) goals=len(data[(data['pass_goal_assist']==True)]) shots_df=data[(data['pass_shot_assist']==True)] shots=len(shots_df) total_corners=len(data) shot_ids=shots_df.pass_assisted_shot_id.tolist() if len(shot_ids)>0: xg_df = [] for n in shot_ids: xg_shot = events[(events['id']==n)] xg_df.append(xg_shot) xg_df=pd.concat(xg_df) xg=round(xg_df.shot_statsbomb_xg.sum(),2) else: xg=0 if data.y.mean()>=40: side="right" else: side="left" return side, total, short, backpost, frontpost, penspot, sixyard, deeper, goals, shots, total_corners, xg #define function for removing axis on subplots def customise_plot(ax): for n in ["right","top","left","bottom"]: ax.spines[n].set_visible(False) ax.patch.set_facecolor(bgcolor) ax.tick_params(bottom=False,top=False,labelbottom=False) #create bar chart def plot_bar (data,groupby,taker_or_target,ax): target = data.groupby([groupby]).size() target=target.reset_index() target.rename(columns={target.columns[1]: taker_or_target }, inplace = True) target=target.sort_values(taker_or_target, ascending=False) target = target.head(5) target=target.sort_values(taker_or_target) bars = target[groupby] h = target[taker_or_target] y_pos = np.arange(len(bars)) ax.barh(y_pos, h, color=sbred, edgecolor=darkgrey) # Create names on the x-axis ax.set_yticks(y_pos) ax.set_yticklabels(bars,color=textc,size=24) customise_plot(ax) text=target[taker_or_target].astype(int) for i, v in enumerate(text): ax.text(v*0.9, i-0.09, str(v), color=textc, fontsize=28,fontweight="bold",ha='center') #plotting the data def plot_corners (data,axA,axB,axC): #calculate data points side, total, short, backpost, frontpost, penspot, sixyard, deeper, goals, shots, total_corners, xg = get_corner_stats (data) #create a new dataframe that doesn't include short corners non_short_df=data.loc[(data['pass_technique']!='Short')] #create heatmap of end point of crosses bin_statistic=pitch.bin_statistic(non_short_df['pass_end_x'], non_short_df['pass_end_y'], statistic='count', bins=(40,30)) bin_statistic['statistic'] = gaussian_filter(bin_statistic['statistic'], 1) pitch.heatmap(bin_statistic, edgecolors=bgcolor, cmap=cmap, ax=axA,alpha=0.75) if side=="left": x1_label=26.9 #front post x2_label=52.9 #back post x3_label=79.9 #6-yard/deeper x4_label=3 #short corner x4_circle=0 #start point total=len(data) inswinger=len(data.loc[(data['pass_technique']=="Inswinging")]) inswinger=int(inswinger/total*100) lw_i=inswinger/10 outswinger=len(data.loc[(data['pass_technique']=="Outswinging")]) outswinger=int(outswinger/total*100) lw_o=outswinger/10 #inswinger style = "Simple, tail_width=0.5, head_width=16, head_length=12" a1 = patches.FancyArrowPatch((x4_circle,120), (25,105), connectionstyle="arc3,rad=.2", color=darkgrey,arrowstyle=style, lw=lw_i,zorder=5,alpha=1) axA.add_patch(a1) #outswinger a2 = patches.FancyArrowPatch((x4_circle,120), (25,110), connectionstyle="arc3,rad=-0.28", color=darkgrey,arrowstyle=style, lw=lw_o,zorder=5,alpha=1) axA.add_patch(a2) axA.text(s=f"Inswinger: {inswinger}%",x=x4_circle-5,y=105,size=26,color=darkgrey,ha="center",fontweight='bold') axA.text(s=f"Outswinger: {outswinger}%",x=x4_circle-5,y=125,size=26,color=darkgrey,ha="center",fontweight='bold') elif side=="right": x1_label=52.9 x2_label=26.9 x3_label=3 x4_label=79.9 x4_circle=80 total=len(data) inswinger=len(data.loc[(data['pass_technique']=="Inswinging")]) inswinger=int(inswinger/total*100) lw_i=inswinger/10 outswinger=len(data.loc[(data['pass_technique']=="Outswinging")]) outswinger=int(outswinger/total*100) lw_o=outswinger/10 #outswinger style = "Simple, tail_width=0.5, head_width=16, head_length=12" a1 = patches.FancyArrowPatch((x4_circle,120), (55,110), connectionstyle="arc3,rad=.2", color=darkgrey,arrowstyle=style, lw=lw_o,zorder=5,alpha=1) axA.add_patch(a1) #inswinger a2 = patches.FancyArrowPatch((x4_circle,120), (55,105), connectionstyle="arc3,rad=-0.28", color=darkgrey,arrowstyle=style, lw=lw_i,zorder=5,alpha=1) axA.add_patch(a2) axA.text(s=f"Inswinger: {inswinger}%",x=x4_circle+5,y=105,size=26,color=darkgrey,ha="center",fontweight='bold') axA.text(s=f"Outswinger: {outswinger}%",x=x4_circle+5,y=125,size=26,color=darkgrey,ha="center",fontweight='bold') #add circle to highlight which side the corner is taken from a_circle = plt.Circle((x4_circle, 120), 3, zorder=5, fill=False,edgecolor=sbred,lw=5,ls="-") axA.add_artist(a_circle) #plot data relating to target areas axA.text(s=f"Total corners: {total}",x=40,y=128,size=32,color=textc,ha='center') axA.text(s=f"Front post: {frontpost}%",x=x1_label,y=63,size=28,color=textc,rotation=270,ha="center",path_effects=path_eff) axA.text(s=f"Middle: {penspot}%",x=40,y=63,size=28,color=textc,rotation=270,ha="center",path_effects=path_eff) axA.text(s=f"Back post: {backpost}%",x=x2_label,y=63,size=28,color=textc,rotation=270,ha="center",path_effects=path_eff) axA.axvline(x=37, ymin=0.5, ymax=0.875,color=darkgrey, lw=3, linestyle='--') axA.axvline(x=43, ymin=0.5, ymax=0.875,color=darkgrey, lw=3, linestyle='--') axA.text(s=f"6 yard box: {sixyard}%",x=x3_label,y=115,size=28,color=textc,ha="center",path_effects=path_eff) axA.text(s=f"Deeper: {deeper}%",x=x3_label,y=107,size=28,color=textc,ha="center",path_effects=path_eff) axA.text(s=f"Short\ncorners: {short}%",x=x4_label,y=92.5,size=24,color=textc,ha="center",path_effects=path_eff) axA.text(s=f"Goals: {goals} | Shots: {shots} | xG: {xg}",x=40,y=122,size=30,color=sbred,ha="center") #BAR CHART for first contacts plot_bar (non_short_df,'pass_recipient','target',axB) #BAR CHART for corner kick takers plot_bar (data,'player','corners_taken',axC) #set dimensions of the pitch pitch = VerticalPitch(half=True,pitch_color=bgcolor, line_color=linec,line_zorder=1,pad_top=10) cmap = LinearSegmentedColormap.from_list("", cmaplist) path_eff = [path_effects.Stroke(linewidth=2, foreground=darkgrey), path_effects.Normal()] #set figure size and dimensions fig = plt.figure(figsize=(40,30),constrained_layout=True) gs = fig.add_gridspec(nrows=3,ncols=4) fig.patch.set_facecolor(bgcolor) #plot results for left side corners ax1 = fig.add_subplot(gs[0,1:3]) ax1.set_title(label="Corners from left",x=0.5,y=1.04,size=30,color=textc,ha='center',fontweight='bold') pitch.draw(ax=ax1) ax2 = fig.add_subplot(gs[0,3]) ax2.set_title(label="Top 5 - Intended targets\ncontact from left\n(short corners excluded)",x=0.5,y=1.02,size=26,color=textc,ha='center',fontweight='bold') ax3 = fig.add_subplot(gs[0,0]) ax3.set_title(label="Top 5 - Corner takers from left",x=0.5,y=1.02,size=26,color=textc,ha='center',fontweight='bold') plot_corners (left_corners,ax1,ax2,ax3) #plot results for right side corners ax4 = fig.add_subplot(gs[1,1:3]) ax4.set_title(label="Corners from right",x=0.5,y=1.04,size=30,color=textc,ha='center',fontweight='bold') pitch.draw(ax=ax4) ax5 = fig.add_subplot(gs[1,3]) ax5.set_title(label="Top 5 - Intended targets\ncontact from right\n(short corners excluded)",x=0.5,y=1.02,size=26,color=textc,ha='center',fontweight='bold') ax6 = fig.add_subplot(gs[1,0]) ax6.set_title(label="Top 5 - Corner takers from right",x=0.5,y=1.02,size=26,color=textc,ha='center',fontweight='bold') plot_corners (right_corners,ax4,ax5,ax6) #set titles fig.text(s=f"{team} Corners | {league} {season_title}", #ha='center' x=0.02, y =1.1, fontsize=54,color=textc,fontweight='bold') fig.text(s="Goals, Shots and xG data refer to first point of contact from corner.\nFor the purposes of this data visualisation a short corner is any corner not tagged as outswinging, inswinging\nor straight - this may include corners that are ‘clipped’ to the edge of the box, or otherwise.", #ha='center' x=0.02, y =1.03, fontsize=36,color=sbred) Hmm, maybe not
My point stands on how confusing Python is to read via scripting. I do appreciate the consistency of the code flow, that is a plus. However I hate the way arrays are designed like that. I mean it's difficult from looking at the code to understand what the array can hold, what is does hold, does it have any limits and most importantly, does it handle overloading or 'buffer overflows' in a way the programmer can act/react to them. This might all be handled under-the-hood via language specification (I don't use Python so I am guessing) but surely implementing a form of exception handling to avoid memory leaks would be a good idea.
Will sign for us and then, in a remarkable move, be sold to Udinese for £25m, then loaned back to us.
He's an semi-active member of this community. Sometimes comes out of Hibernation to protect his son from any fault.
Did a bit of digging cos none of it made sense. Apparently there's been a fair few issues. He was highly aware of PL interest in the final months of the season so was (apparently) quite "unprofessional" in those last couple of months. They sent him to train with their 2nd team in the Austrian 2nd tier as a result. Apparently failed medicals at both Hoffenheim and West Ham over the summer, so with his contract expiring next year (and foreign clubs able to negotiate that free deal in January) I reckon they'd had enough and knew any chance of getting a fee for him was unlikely so negotiated his exit. Basically a peak Pozzo signing: Free? Check. Bit of a ****? Check. Injury issues? Check.