def calc_posession_lattice(posessions, frames_tb, match_tb, subsample):
    # parity=1, home team is attacking right->left
    frames = []
    fig,ax = vis.plot_pitch(match_tb)
    fig1,ax1 = vis.plot_pitch(match_tb)
    # note all posessions **must** be the same team
    team = posessions[0].team
    for pos in posessions:
        assert pos.team==team
        frames = frames + frames_tb[pos.pos_start_fnum:pos.pos_end_fnum+1]
    form_Att = formation(team)
    form_Def = formation(team)
    for frame in frames[::subsample]:
        if posessions[0].team=='H':
            attacking_players = frame.team1_players
            defending_players = frame.team0_players
            parity = match_tb.period_parity[frame.period]
        else:
            attacking_players = frame.team0_players
            defending_players = frame.team1_players
            parity = match_tb.period_parity[frame.period]*-1
        form_Att.add_latice( lattice('A',parity,frame.timestamp,attacking_players,[1,31]) )
        form_Def.add_latice( lattice('D',parity*-1,frame.period,frame.timestamp,defending_players,[1,31]) )
    print (form_Att.pids)
    print (form_Def.pids
    form_Def.calc_average_lattice)(match_tb)
    form_Att.calc_average_lattice(match_tb)
    offsets = calc_offset_between_formations(form_Def,form_Att,calc='offside',parity=parity)
    form_Def.plot_formation(factor=100,figax=(fig,ax),lt='bo')
    form_Att.plot_formation(factor=100,offsets=offsets,figax=(fig1,ax1),lt='ro')
    return form_Def,form_Att
Beispiel #2
0
def plot_defensive_actions(team,all_matches,include_tackles=True,include_intercept=True):
    match_OPTA = all_matches[0]
    # tackles won and retained posession
    team_events = []
    for match in all_matches:
        if match.hometeam.teamname==team:
            team_events += match.hometeam.events
        elif match.awayteam.teamname==team:
            team_events += match.awayteam.events
    tackle_won = [e for e in team_events if e.type_id == 7 and e.outcome==1 and 167 not in e.qual_id_list]
    # interceptions
    intercepts = [e for e in team_events if e.type_id == 8]
    fig,ax = vis.plot_pitch(all_matches[0])
    xfact = match_OPTA.fPitchXSizeMeters*100
    yfact = match_OPTA.fPitchYSizeMeters*100
    count = 0
    # tackles
    if include_tackles:
        for tackle in tackle_won:
            ax.plot(tackle.x*xfact,tackle.y*yfact,'rd',alpha=0.6,markersize=10,label=count)
            count +=1
    if include_intercept:
        for intercept in intercepts:
            ax.plot(intercept.x*xfact,intercept.y*yfact,'rs',alpha=0.6,markersize=10,label=count)
            count +=1
def plot_all_passes(match_OPTA):
    fig, ax = vis.plot_pitch(match_OPTA)
    home_passes = [e for e in match_OPTA.hometeam.events if e.is_pass]
    away_passes = [e for e in match_OPTA.awayteam.events if e.is_pass]
    xfact = match_OPTA.fPitchXSizeMeters * 100
    yfact = match_OPTA.fPitchYSizeMeters * 100

    for p in home_passes:
        ax.arrow(p.pass_start[0] * xfact,
                 p.pass_start[1] * yfact,
                 (p.pass_end[0] - p.pass_start[0]) * xfact,
                 (p.pass_end[1] - p.pass_start[1]) * yfact,
                 color='b',
                 length_includes_head=True,
                 head_width=0.08 * xfact,
                 head_length=0.00002 * yfact)

    for p in away_passes:
        ax.arrow(p.pass_start[0] * xfact,
                 p.pass_start[1] * yfact,
                 (p.pass_end[0] - p.pass_start[0]) * xfact,
                 (p.pass_end[1] - p.pass_start[1]) * yfact,
                 color='r',
                 length_includes_head=True,
                 head_width=0.08 * xfact,
                 head_length=0.00002 * yfact)

    match_string = '%s %d vs %d %s' % (
        match_OPTA.hometeam.teamname, match_OPTA.homegoals,
        match_OPTA.awaygoals, match_OPTA.awayteam.teamname)

    plt.title(match_string, fontsize=16, y=1.0)
    plt.waitforbuttonpress(0)

    return fig, ax
def Hungarian_Cost(F1, F2, match_tb, metric='L2', squeeze=[1.], plot=False):
    # calculates cost of player assignments between any two roles (L1 or L2 norm) in each formation
    # this process tells us how to match a player in one formation observation to a player in another to minimize the overal formation comparison cost
    # uses the Kuhn-Munkres algorithm
    pids1 = np.array(F1.pids)
    pids2 = np.array(F2.pids)
    n1 = len(pids1)
    n2 = len(pids2)
    if len(squeeze) > 1:  # marginalise over the squeeze paramter
        cost = []
        for s in squeeze:
            C = np.zeros(shape=(n1, n2))
            for i in range(n1):
                pid1 = pids1[i]
                for j in np.arange(n2):
                    pid2 = pids2[j]
                    C[i, j] = Cost_Metric(F1,
                                          F2,
                                          pid1,
                                          pid2,
                                          metric=metric,
                                          s=s)
            row_ind, col_ind = linear_sum_assignment(C)
            cost.append((s, C[row_ind, col_ind].sum(), col_ind))
        cost = sorted(cost, key=lambda x: x[1])
        col_ind_min = cost[0][2]
        cost_min = cost[0][1]
        squeeze_min = cost[0][0]
        # print squeeze_min
        pmatch = pids2[col_ind_min]
    else:  # just use a single squeeze parameter. Obviously can simplify this.
        squeeze_min = squeeze[0]
        C = np.zeros(shape=(n1, n2))
        for i in range(n1):
            pid1 = pids1[i]
            for j in range(n2):
                pid2 = pids2[j]
                C[i, j] = Cost_Metric(F1,
                                      F2,
                                      pid1,
                                      pid2,
                                      metric=metric,
                                      s=squeeze_min)
        row_ind, col_ind = linear_sum_assignment(C)
        cost_min = C[row_ind, col_ind].sum()
        pmatch = pids2[col_ind]
    if plot:
        fig, ax = vis.plot_pitch(match_tb)
        F1.plot_formation(factor=100, figax=(fig, ax), lt='ro')
        F2.plot_formation(factor=100, figax=(fig, ax), lt='bo')
        for i in range(n1):
            pid1 = pids1[i]
            pid2 = pmatch[i]
            ax.plot(
                [100 * F1.nodes[pid1].x, squeeze_min * 100 * F2.nodes[pid2].x],
                [100 * F1.nodes[pid1].y, squeeze_min * 100 * F2.nodes[pid2].y],
                'k')
    return cost_min, pids1, pmatch, squeeze_min
Beispiel #5
0
def plot_all_passes(events,match_tb,window=(0,200),figax = None, flip=1.0,color='blue',alpha=0.3):
    if figax is None:
        fig,ax = vis.plot_pitch(match_tb)
    else:
        fig,ax = figax
    xfact = match_tb.fPitchXSizeMeters*100*flip
    yfact = match_tb.fPitchYSizeMeters*100*flip
    all_pts = []
    for e in events:
        if e.type_id in [1,2] and e.time>=window[0] and e.time<window[1] and 6 not in e.qual_id_list: # pass event
            pass_end_x = e.qualifiers[e.qual_id_list.index(140)].value/100. - 0.5
            pass_end_y = e.qualifiers[e.qual_id_list.index(141)].value/100. - 0.5
            x = np.array([e.x,pass_end_x])*xfact
            y = np.array([e.y,pass_end_y])*yfact
            #ax.plot(x,y,'r',alpha=0.6)
            p = ax.annotate('', xy=(x[1], y[1]), xytext=(x[0], y[0]),arrowprops=dict(arrowstyle="-|>",linewidth=1,color=color, alpha=alpha) )
            all_pts.append(p)
    return fig,ax,all_pts
Beispiel #6
0
def plot_all_shots(match_OPTA,plotly=False,twindow=(0,200),figax=None,pt='o'):
    symbols = lambda x: 'd' if x=='Goal' else pt
    if figax is None:
        fig,ax = vis.plot_pitch(match_OPTA)
    else:
        fig,ax = figax
    homeshots = [e for e in match_OPTA.hometeam.events if e.is_shot and e.time>=twindow[0] and e.time<twindow[1]]
    awayshots = [e for e in match_OPTA.awayteam.events if e.is_shot and e.time>=twindow[0] and e.time<twindow[1]]
    xfact = match_OPTA.fPitchXSizeMeters*100
    yfact = match_OPTA.fPitchYSizeMeters*100
    descriptors = {}
    count = 0
    for shot in homeshots:
        descriptors[str(count)] = shot.shot_descriptor
        ax.plot(shot.x*xfact,shot.y*yfact,'r'+symbols(shot.description),alpha=0.6,markersize=20*np.sqrt(shot.expG_caley),label=count)
        count += 1
    for shot in awayshots:
        descriptors[str(count)] = shot.shot_descriptor
        ax.plot(-1*shot.x*xfact,-1*shot.y*yfact,'b'+symbols(shot.description),alpha=0.6,markersize=20*np.sqrt(shot.expG_caley),label=count)
        count += 1
    home_xG = np.sum([s.expG_caley2 for s in homeshots])
    away_xG = np.sum([s.expG_caley2 for s in awayshots])
    match_string = '%s %d (%1.1f) vs (%1.1f) %d %s' % (match_OPTA.hometeam.teamname,match_OPTA.homegoals,home_xG,away_xG,match_OPTA.awaygoals,match_OPTA.awayteam.teamname)
    #ax.text(-75*len(match_string),match_OPTA.fPitchYSizeMeters*50+500,match_string,fontsize=20)
    
    if plotly:
        plt.title( match_string, fontsize=20, y=0.95 )
        plotly_fig = tls.mpl_to_plotly( fig )
        for d in plotly_fig.data:
            if d['name'] in descriptors.keys():
                d['text'] = descriptors[d['name']]
                d['hoverinfo'] = 'text'
            else:
                d['name'] = ""
                d['hoverinfo'] = 'name'
                
        plotly_fig['layout']['titlefont'].update({'color':'black', 'size':20, 'family':'monospace'})
        plotly_fig['layout']['xaxis'].update({'ticks':'','showticklabels':False})
        plotly_fig['layout']['yaxis'].update({'ticks':'','showticklabels':False})
        #url = py.plot(plotly_fig, filename = 'Aalborg-match-analysis')
        return plotly_fig
    else:
        plt.title( match_string, fontsize=16, y=1.0 )
        return fig,ax
def plot_defensive_actions(team,
                           all_matches,
                           include_tackles=True,
                           include_intercept=True):
    match_OPTA = all_matches[0]
    # tackles won and retained posession
    team_events = []
    for match in all_matches:
        if match.hometeam.teamname == team:
            team_events += match.hometeam.events
        elif match.awayteam.teamname == team:
            team_events += match.awayteam.events
    tackle_won = [
        e for e in team_events
        if e.type_id == 7 and e.outcome == 1 and 167 not in e.qual_id_list
    ]
    # interceptions
    intercepts = [e for e in team_events if e.type_id == 8]
    fig, ax = vis.plot_pitch(all_matches[0])
    xfact = match_OPTA.fPitchXSizeMeters * 100
    yfact = match_OPTA.fPitchYSizeMeters * 100
    count = 0
    # tackles
    if include_tackles:
        for tackle in tackle_won:
            ax.plot(tackle.x * xfact,
                    tackle.y * yfact,
                    'rd',
                    alpha=0.6,
                    markersize=10,
                    label=count)
            count += 1
    if include_intercept:
        for intercept in intercepts:
            ax.plot(intercept.x * xfact,
                    intercept.y * yfact,
                    'rs',
                    alpha=0.6,
                    markersize=10,
                    label=count)
            count += 1
    #match_string = '%s %d (%1.1f) vs (%1.1f) %d %s' % (match_OPTA.hometeam.teamname,match_OPTA.homegoals,home_xG,away_xG,match_OPTA.awaygoals,match_OPTA.awayteam.teamname)
    #plt.title( match_string, fontsize=16, y=1.0 )
    plt.waitforbuttonpress(0)
Beispiel #8
0
# plot positions and velocities of the ball in each direction
fig,axes = plt.subplots(3,1,figsize=(8,8))
fig2,axes2 = plt.subplots(3,1,figsize=(8,8))
for i,l in zip([0,1,2],['x','y','z']):
    axes[i].plot(timestamps,ball_positions_xyz[:,i],'r')
    axes2[i].plot(timestamps,ball_velocities_xyz[:,i],'r')
    axes[i].set_ylabel(l + '-position (m)')
    axes2[i].set_ylabel(l + '-velocity (m/s)')
# add x-axis labels
axes[2].set_xlabel('time (mins)')
axes2[2].set_xlabel('time (mins)')


# EXAMPLE: make a plot of a player 2's position (home team) over the first half
# this is a bit clumsy
fig,ax = vis.plot_pitch(match_tb) # plot pitch
px = np.array( [f.pos_x for f in team1_players[2].frame_targets] )
py = np.array( [f.pos_y for f in team1_players[2].frame_targets] )
t = np.array(  team1_players[2].frame_timestamps )
flast = vis.find_framenum_at_timestamp(frames_tb,match_tb,2,0) # first frame of second half
ax.plot( px[0:flast],py[0:flast],'r.')

# EXAMPLE: make a timeseries plot of player 2's velocity (x and y components) and speed over the first half
vx = np.array( [f.vx for f in team1_players[2].frame_targets] )
vy = np.array( [f.vy for f in team1_players[2].frame_targets] )
fig,ax = plt.subplots()
ax.plot( t[0:flast], vx[0:flast], 'r' )
ax.plot( t[0:flast], vy[0:flast], 'b' )

def calc_formation_by_period(period_length,min_period_length,posessions,frames_tb,match_tb,
                                subsample,excludeH,excludeA,plotfig=False,deleteLattices=False):
    # aggregates individual posessions into windoes of at least min_period_length and at most period_length
    # then calculates attackoing (team in possession) and defensive (team out of possession) formations in each possession window
    # posessions is a list of possessions
    # excludeH/excludeA are player ids to exclude (the goalkeepers)
    # subsample reduces framerate to speed up calculation
    # note all posessions **must** be the same team
    team = posessions[0].team # the team that has possession in the first possession of the list (Home or away)
    # group posessions into chunks
    period_posessions = [] # each element corresponds to a list of frames for the aggregated possessions
    count = 0.0  # this is a counter that tracks how much playing time we have in each aggregated possession window
    frames = []
    # set of player ids that appear in the first frame of the match
    pids_H = set( frames_tb[0].team1_jersey_nums_in_frame )
    pids_A = set( frames_tb[0].team0_jersey_nums_in_frame )
    for pos in posessions:
        assert pos.team==team # make sure that we haven't switched team
        # has there been a substituion?
        sub = pids_H != set( frames_tb[pos.pos_start_fnum].team1_jersey_nums_in_frame ) or pids_A != set( frames_tb[pos.pos_start_fnum].team0_jersey_nums_in_frame )
        if sub: # there was a substituion
            #print "sub", count, frames_tb[pos.pos_start_fnum].timestamp, frames_tb[pos.pos_end_fnum].timestamp
            period_posessions.append(frames) # end possession window
            frames = frames_tb[pos.pos_start_fnum:pos.pos_end_fnum+1] # start new possession window
            count = pos.pos_duration
            pids_H = set( frames_tb[pos.pos_start_fnum].team1_jersey_nums_in_frame )
            pids_A = set( frames_tb[pos.pos_start_fnum].team0_jersey_nums_in_frame )
        else:
            frames = frames + frames_tb[pos.pos_start_fnum:pos.pos_end_fnum+1]
            if count >= period_length: # if we have enough time in aggregated possessions, start a new possession window
                #print count
                period_posessions.append(frames)
                frames = []
                count = 0
            else:
                count += pos.pos_duration
    #  now we have a list of aggregated possession windows. Remove those that are too short (because of substitutions)
    period_posessions = [p for p in period_posessions if len(p)>min_period_length*float(match_tb.iFrameRateFps)]
    attacking_formations = []
    defensive_formations = []
    # now measure the attacking and defensive formations in each possession window
    for period_posession in period_posessions:
        form_Att = formation(team) # 'team' here is technically the team in possession, so the same in both cases
        form_Def = formation(team)
        period_start = period_posession[0].period
        for frame in period_posession[::subsample]: # subsampling to speed this up
            if team=='H':
                attacking_players = frame.team1_players
                defending_players = frame.team0_players
                parity = match_tb.period_parity[frame.period] # deals with left-right play direction
                excludeAtt = excludeH # these are the player ids to exclude (goalkeepers)
                excludeDef = excludeA
            else:
                attacking_players = frame.team0_players
                defending_players = frame.team1_players
                parity = match_tb.period_parity[frame.period]*-1
                excludeAtt = excludeA
                excludeDef = excludeH
            form_Att.add_latice( lattice('A',parity,frame.timestamp,attacking_players,excludeAtt ) ) # always make this an attacking 'A' formation, so that the team shoots from right->left
            form_Def.add_latice( lattice('A',parity*-1,frame.timestamp,defending_players,excludeDef) )
        form_Def.calc_average_lattice(match_tb)
        form_Att.calc_average_lattice(match_tb)
        offsets = calc_offset_between_formations(form_Def,form_Att,calc='offside',parity=parity)
        if plotfig:
            fig,ax = vis.plot_pitch(match_tb)
            fig1,ax1 = vis.plot_pitch(match_tb)
            form_Def.plot_formation(factor=100,figax=(fig,ax),lt='bo')
            form_Att.plot_formation(factor=100,offsets=offsets,figax=(fig1,ax1),lt='ro')
        if deleteLattices: # reduce memory usage
            form_Def.delete_lattices()
            form_Att.delete_lattices()
        attacking_formations.append(form_Att)
        defensive_formations.append(form_Def)
    return attacking_formations,defensive_formations
def ball_movie(match_OPTA,
               relative_positioning=True,
               team="home",
               weighting="regular"):
    """
    Displays estimated player positions and animates ball movement throughout game. Highlights passes and
    ball losses.
    TODO: substitutions (for now color ball differently when passed to a sub)

    Args:
        match_OPTA (OPTAmatch)
        relative_positioning (bool): whether the players should be displayed with relative position or average
        team (string): "home" or "away"
        weighting (string): type of network positioning to display
    """

    fig, ax = vis.plot_pitch(match_OPTA)
    team_object = match_OPTA.hometeam if team == "home" else match_OPTA.awayteam

    events_raw = [
        e for e in team_object.events
        if e.is_pass or e.is_shot or e.is_substitution
    ]

    mapped_players = get_player_positions(
        match_OPTA,
        relative_positioning=relative_positioning,
        team=team,
        weighting=weighting)

    for player in mapped_players:
        shrink_factor = 0.25
        fig, ax, pt = utils.plot_bivariate_normal([player.x, player.y],
                                                  player.cov *
                                                  shrink_factor**2,
                                                  figax=(fig, ax))
        ax.annotate(team_object.player_map[player.id].lastname,
                    (player.x, player.y))

    # display ball as black circle being passed
    # turn ball into red x when lost
    # turn ball blue when passed to a sub
    # turn ball into diamond on shot attempt (black for attempt, green for success)
    last_pass = None
    ball = None
    pause_time = 0.1
    shot_pause_factor = 10
    pass_lines = []
    last_location = None
    for e in events_raw:
        if ball:
            ball.remove()
            ball = None
        player = team_object.player_map[e.player_id]

        if last_location:
            new_pass = ax.arrow(last_location[0],
                                last_location[1],
                                player.x - last_location[0],
                                player.y - last_location[1],
                                length_includes_head=True)
            pass_lines.append(new_pass)

        last_location = (player.x, player.y)

        if e.is_pass:
            ball = ax.plot(player.x, player.y, color='black', marker='o')[0]
            plt.pause(pause_time)

            if e.outcome != 1:
                ball.remove()
                ball = ax.plot(player.x, player.y, color='red', marker='X')[0]
                plt.pause(pause_time)

                for l in pass_lines:
                    l.remove()
                    pass_lines = []
                    last_location = None

        elif e.is_shot:
            ball = ax.plot(player.x, player.y, color='black', marker='o')[0]
            plt.pause(pause_time)

            color = 'black'
            if e.outcome == 1:
                color = 'green'
            ball.remove()
            ball = ax.plot(player.x, player.y, color=color, marker='D')[0]
            plt.pause(pause_time * shot_pause_factor)

            for l in pass_lines:
                l.remove()
                pass_lines = []
                last_location = None

    plt.waitforbuttonpress(0)
def plot_passing_network(match_OPTA,
                         weighting="regular",
                         team="home",
                         relative_positioning=True,
                         display_passes=True,
                         wait=True):
    """
    Plot the passing networks of the entire match, displaying player movement as bivariate normal
    ellipses and arrow weights directly corresponding to the number of passes executed

    TODO:
    - account for subs
    - allow different weighting schema

    Args:
        match_OPTA (OPTAmatch): match OPTA information

    Kwargs:
        weighting (string): type of pass network. Choices include:
            regular - all passes included and weighted
            offensive - weight offensive passes but not defensive
            defensive - weight defensive passes but not offensive
            lateral - weight passes with minimal progression in either direction on the field
            forwards - weight passes in the offensive direction
            backwards - weight passes in the defensive direction
        relative_positioning (bool): if True, player positions on the diagram should be relative
            in terms of formation rather than exact position average
    """

    fig, ax = vis.plot_pitch(match_OPTA)

    team_object = match_OPTA.hometeam if team == "home" else match_OPTA.awayteam

    # some passes may be completed and followed by a shot instead of another pass
    events_raw = [
        e for e in team_object.events
        if e.is_pass or e.is_shot or e.is_substitution
    ]
    xfact = match_OPTA.fPitchXSizeMeters * 100
    yfact = match_OPTA.fPitchYSizeMeters * 100

    mapped_players = get_player_positions(
        match_OPTA,
        relative_positioning=relative_positioning,
        team=team,
        weighting=weighting)

    for player in mapped_players:
        shrink_factor = 0.25
        fig, ax, pt = utils.plot_bivariate_normal([player.x, player.y],
                                                  player.cov *
                                                  shrink_factor**2,
                                                  figax=(fig, ax))
        ax.plot([player.x, player.y])
        ax.annotate(
            # team_object.player_map[player.id].lastname,
            player.id,
            (player.x, player.y))

    if display_passes:
        # Plot network of passes with arrows
        max_passes = 0
        for player in team_object.players:
            player_max_passes = 0
            if player.pass_destinations.values():
                player_max_passes = float(
                    sorted(player.pass_destinations.values())[-1])
            if player_max_passes > max_passes:
                max_passes = player_max_passes

        for player in team_object.players:
            # if player in exclude_players:
            if player not in mapped_players:
                continue

            for dest_player_id, num_passes in player.pass_destinations.items():
                dest_player = team_object.player_map[dest_player_id]
                if dest_player not in mapped_players:
                    continue

                # right to left is orange and left to right is red to differentiate directions
                color = 'red'
                if dest_player.x > player.x:
                    color = 'orange'

                max_width = 3

                arrow = patches.FancyArrowPatch(
                    (player.x, player.y),
                    (dest_player.x, dest_player.y),
                    connectionstyle="arc3,rad=.1",
                    color=color,
                    arrowstyle=
                    'Simple,tail_width=0.5,head_width=4,head_length=8',
                    # linewidth=num_passes*0.8,
                    linewidth=max_width * (num_passes / max_passes))
                ax.add_artist(arrow)

    match_string = '%s %d vs %d %s' % (
        match_OPTA.hometeam.teamname, match_OPTA.homegoals,
        match_OPTA.awaygoals, match_OPTA.awayteam.teamname)
    plt.title(match_string, fontsize=16, y=1.0)

    if wait:
        plt.waitforbuttonpress(0)

    return fig, ax
vis.plot_frame(frame,
               match,
               units=1.0,
               include_player_velocities=False,
               include_ball_velocities=False)

# plot trajectories of attacking players in 3 second window around the corner, starting one second before the corner is taken

# first get the frames from 1 second before the corner to 2 seconds after
corner_frames = frames[corner_frame_number - 25:corner_frame_number + 2 *
                       25]  # factor of 25 because there are 25 frames/second

penalty_area_edge = match.fPitchXSizeMeters / 2. - 16.38  # 16.38 is 18 yards in meters
penalty_area_width = 40.04  # 40.04 is 44 yards in meters

fig, ax = vis.plot_pitch(match)
vis.plot_frame(corner_frames[0],
               match,
               units=1.0,
               include_player_velocities=False,
               include_ball_velocities=False,
               figax=(fig, ax))

for cf in corner_frames:
    if corners.iloc[0]['Team'] == hometeam:
        players = cf.team1_players  # home team players
        pcolor = 'r.'  # for plots
        direction_of_play = match.period_parity[
            frame.
            period] * -1  # 1 means home team is playing right->left, -1 means left-right (but switch sign for this example)
    else: