Search code examples
pythonpandasbokeh

Bokeh - plotting fewer items than number of items passed


THe following dataframe df_shots_on_the_pitch:

                 ShotSide  Close             Position          PositionXY  
ShotsDetailID                                                              
447            the centre  close    the boxthe centre   {'y': 34, 'x': 5}  
801                   N/A  close   outside the boxN/A  {'y': 32, 'x': 24}  
1353                  N/A  close  very close rangeN/A   {'y': 39, 'x': 3}  
3220                  N/A  close  very close rangeN/A   {'y': 39, 'x': 3}  
4680           the centre  close    the boxthe centre   {'y': 34, 'x': 5}  
4682                  N/A  close  very close rangeN/A   {'y': 39, 'x': 3}  
5463           the centre  close    the boxthe centre   {'y': 34, 'x': 5}  
5905           the centre  close    the boxthe centre   {'y': 34, 'x': 5}  

is feeding bokeh like so:

def plot_goals(df_shots_on_the_pitch):
    #Getting events data positions
    df_player_events = df_shots_on_the_pitch[df_shots_on_the_pitch['ShotOutcome']=='Goal']

    #Pitch with events
    pitch_player = plot_events(df_player_events, 'Goals', 'red') # <---- HERE

    grid = bokeh.layouts.grid(
        children=[
                [pitch_player],
                ],
        sizing_mode="stretch_width",
        )

return bokeh.models.Panel(child=grid, title="Goals")

and so:

def plot_events(df_player_events, event_name, plot_color):

    player_event_x = [(player_event.get('x')*104)/100. for player_event in df_player_events['PositionXY']]
    player_event_y = [(player_event.get('y')*68)/100. for player_event in df_player_events['PositionXY']]
    ##### ----->  player_x and player_y plot 8 elements here

    p = draw_pitch(figure_width=450, figure_height=295, pitch_width=104, pitch_height=68, line_color="grey", pitch_color="white")
    
    p.circle(player_event_x, player_event_y, fill_color=plot_color, line_width=1, line_color="grey", fill_alpha=0.2, size=8)
    
    player_stats = bokeh.models.Label(x=70, y=190, x_units='screen', y_units='screen',
                    text=str(len(player_event_x)) + " " + event_name, text_font_size= '20px', render_mode='css',
                    background_fill_color=plot_color, background_fill_alpha=0.3)
    
    p.add_layout(player_stats)

    return p

finally, this function draws the pitch:

def draw_pitch(figure_width=700, figure_height=350, 
               pitch_width=104, pitch_height=68, 
               pitch_color = "#B3DE69", line_color="grey"):

    p = figure(width=figure_width, height=figure_height, toolbar_location="below")
    
    #Empty pitch
    p.rect(x=pitch_width/2., y=pitch_height/2., 
           width=pitch_width, height=pitch_height, 
           fill_color=pitch_color, line_width=2, line_color=line_color)

    #Penalty Area Left
    p.circle(16.5, pitch_height/2., size=50, 
             fill_color=pitch_color, line_width=2, line_color=line_color)
    ##Big rectangle
    p.rect(x=16.5/2., y=pitch_height/2., 
           width=16.5, height=40.3, 
           fill_color=pitch_color, line_width=2, line_color=line_color)
    ##Small rectangle
    p.rect(x=5.5/2., y=pitch_height/2., 
           width=5.5, height=18.3, 
           fill_color=pitch_color, line_width=2, line_color=line_color)
    ##Goal post
    p.rect(x=0, y=pitch_height/2., 
           width=0.5, height=7.3, 
           fill_color=line_color, line_width=2, line_color=line_color)
    ##Penalty spot
    p.circle(11, pitch_height/2., size=2, 
             fill_color=line_color, line_width=2, line_color=line_color)
    
    #Penalty Area right
    p.circle((pitch_width-16.5), pitch_height/2., size=50, 
             fill_color=pitch_color, line_width=2, line_color=line_color)
    p.rect(x=pitch_width-(16.5/2.), y=pitch_height/2., width=16.5, height=40.3, 
           fill_color=pitch_color, line_width=2, line_color=line_color)
    ##Small rectangle
    p.rect(x=pitch_width-(5.5/2.), y=pitch_height/2., 
           width=5.5, height=18.3, 
           fill_color=pitch_color, line_width=2, line_color=line_color)
    ##Goal post
    p.rect(x=pitch_width, y=pitch_height/2., 
           width=0.5, height=7.3, 
           fill_color=line_color, line_width=2, line_color=line_color)
    ##Penalty spot
    p.circle((pitch_width-11), pitch_height/2., size=2, 
             fill_color=line_color, line_width=2, line_color=line_color)
    
    #middle of pitch
    p.circle(pitch_width/2., y=pitch_height/2., size=100, fill_color=pitch_color, line_width=2, line_color=line_color)
    p.circle(pitch_width/2., y=pitch_height/2., size=2, fill_color=line_color, line_width=2, line_color=line_color)
    p.line([pitch_width/2., pitch_width/2.], [0, pitch_height], line_width=2, line_color=line_color)
    
    return p

everything put together, plots the following:

enter image description here


But it always plot 3 goals, when it should plot, as shown by dataframe, 8 goals.

What am I missing here?


Solution

  • All of the points are getting plotted. Some of them have exactly the same coordinates, so they are plotted directly on top of one another. This can be seen in the data, but also the deeper shade of red in the "two" points near the goal—that is actually the alpha transparency "stacking up".