• Stars
    star
    178
  • Rank 214,989 (Top 5 %)
  • Language
    R
  • License
    Other
  • Created over 7 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Plot soccer event data in R/ggplot2

ggsoccer

CRAN_Status_Badge CRAN_Version_Badge lifecycle R-CMD-check

Overview

ggsoccer provides a functions for plotting soccer event data in R/ggplot2.

Installation

ggsoccer is available via CRAN:

install.packages("ggsoccer")

Alternatively, you can download the development version from github like so:

# install.packages("remotes")
remotes::install_github("torvaney/ggsoccer")

Usage

library(ggplot2)
library(ggsoccer)

ggplot() +
  annotate_pitch() +
  theme_pitch()

The following example uses ggsoccer to solve a realistic problem: plotting a set of passes onto a soccer pitch.

pass_data <- data.frame(x = c(24, 18, 64, 78, 53),
                        y = c(43, 55, 88, 18, 44),
                        x2 = c(34, 44, 81, 85, 64),
                        y2 = c(40, 62, 89, 44, 28))

ggplot(pass_data) +
  annotate_pitch() +
  geom_segment(aes(x = x, y = y, xend = x2, yend = y2),
               arrow = arrow(length = unit(0.25, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label() +
  ggtitle("Simple passmap", 
          "ggsoccer example")

Because ggsoccer is implemented as ggplot layers, plots can be customised with standard ggplot functions and layers.

Here is a different example, plotting shots on a green pitch.

By default, ggsoccer will display the whole pitch. To display a subsection of the pitch, set the plot limits as you would with any other ggplot2 plot. Here, we use the xlim and ylim arguments to coord_flip.

coord_flip reverses the orientation of the points, so we must also reverse the y-axis to ensure that the orientation remains correct (that is, shots from the left hand side appear on the left, and right-sided shots appear on the right).

You can do this with either scale_y_reverse or by reversing the order of the limits in coord_flip’s ylim argument.

If you don’t correct (i.e. reverse) the y axis orientation, the penalty box arcs will appear inside the box!

shots <- data.frame(x = c(90, 85, 82, 78, 83, 74, 94, 91),
                    y = c(43, 40, 52, 56, 44, 71, 60, 54))

ggplot(shots) +
  annotate_pitch(colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_point(aes(x = x, y = y),
             colour = "yellow",
             size = 4) +
  theme_pitch() +
  theme(panel.background = element_rect(fill = "springgreen4")) +
  coord_flip(xlim = c(49, 101)) +
  scale_y_reverse() +
  ggtitle("Simple shotmap",
          "ggsoccer example")

Data providers

ggsoccer defaults to Opta’s 100x100 coordinate system. However, different data providers may use alternative coordinates.

ggsoccer provides support for a few data providers out of the box:

  • StatsPerform/Opta (pitch_statsperform, default)
  • Statsbomb (pitch_statsbomb)
  • Wyscout (pitch_wyscout)
  • Tracab (make_pitch_tracab())

ggsoccer also provides an interface for any custom coordinate system.

Statsbomb

# ggsoccer enables you to rescale coordinates from one data provider to another, too
to_statsbomb <- rescale_coordinates(from = pitch_opta, to = pitch_statsbomb)

passes_rescaled <- data.frame(x  = to_statsbomb$x(pass_data$x),
                              y  = to_statsbomb$y(pass_data$y),
                              x2 = to_statsbomb$x(pass_data$x2),
                              y2 = to_statsbomb$y(pass_data$y2))

ggplot(passes_rescaled) +
  annotate_pitch(dimensions = pitch_statsbomb) +
  geom_segment(aes(x = x, y = y, xend = x2, yend = y2),
               colour = "coral",
               arrow = arrow(length = unit(0.25, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(x_label = 60) +
  ggtitle("Simple passmap", 
          "Statsbomb co-ordinates")

Custom data

To plot data for a dataset not provided, ggsoccer requires a pitch specification. This is a list containing the required pitch dimensions like so:

pitch_custom <- list(
  length = 150,
  width = 100,
  penalty_box_length = 25,
  penalty_box_width = 60,
  six_yard_box_length = 8,
  six_yard_box_width = 26,
  penalty_spot_distance = 16,
  goal_width = 12,
  origin_x = -50,
  origin_y = -75
)

ggplot() +
  annotate_pitch(dimensions = pitch_custom) +
  theme_pitch()

Goals

ggsoccer allows you to customise your goals markings by supplying a function to the goals argument of annotate_pitch:

ggplot() +
  annotate_pitch(fill = "steelblue4", colour = "white", goals = goals_line) +
  theme_pitch() +
  theme(panel.background = element_rect(fill = "steelblue4"))

ggplot() +
  annotate_pitch(goals = goals_strip, fill = "lightgray") +
  theme_pitch()

This argument takes a function (or one-sided formula). You can use the supplied functions, or create your own goal markings function. The goals argument also supports using one-sided formulas as lambda functions (see rlang::as_function).

Custom goals functions must accept the arguments used by annotate_pitch: colour, fill, dimensions, linewidth, alpha, and linetype. Additional arguments can also be added.

goals_custom <- function(colour, fill, dimensions, ...) {
  goals_strip(colour, fill, dimensions, lineend = "square", linewidth = 3.5)
}

ggplot() +
  annotate_pitch(
    goals = goals_custom, 
    fill = "lightgray"
  ) +
  theme_pitch()

See help(goals_box) for the full list of available functions.

The idea for having multiple goal markings was taken and adapted from the fc.rstats package.

Further customisation

You can also alter the style of pitch markings with linewidth, alpha, and linetype:

ggplot() +
  annotate_pitch(
    colour = "white", 
    linewidth = 1.5, 
    linetype = "12", 
    alpha = 0.2, 
    goals = goals_line
  ) +
  theme_pitch() +
  theme(panel.background = element_rect(fill = "steelblue"))

Other options

There are other packages that offer alternative pitch plotting options. Depending on your use case, you may want to check these out too:

Python

There are a couple of pitch plotting options for matplotlib, too:

More Repositories

1

regista

An R package for soccer modelling
R
85
star
2

fpl-optimiser

Optimise FPL squads
Python
58
star
3

statsbombapi

An extendable Statsbomb API wrapper for data-pipelines
Python
46
star
4

mezzala

Models for estimating football (soccer) team-strength
Python
35
star
5

soccerstan

Reproduction of football models in Stan
Python
35
star
6

understat-db

Create a database of ⚽️ data from understat.com
Python
25
star
7

elm-soccer-tracker

Track xy coordinates of events on a soccer pitch
Elm
24
star
8

footballdatr

A minimal R package to fetch data from football-data.co.uk
R
23
star
9

wyscoutapi

wyscoutapi is an extremely basic API client for the Wyscout API (v2 & v3) for Python
Python
17
star
10

wingback

Backtesting team-strength models
Python
16
star
11

moneyline-team-strengths

Code, analysis, and data for estimating team strengths from odds data
R
12
star
12

flow-solver

Solving the "Flow Free" game (mobile app) with Clojure and SAT
Clojure
10
star
13

motd-extractor

Extract highlights from BBC's match of the day
Jupyter Notebook
9
star
14

gamblers-dice

A m̶i̶s̶t̶a̶k̶e̶ Python port of xori/gamblers-dice
Python
8
star
15

karlis-ntzoufras-reproduction

Reproduction of "Bayesian modelling of football outcomes" in Stan
Jupyter Notebook
6
star
16

torvaney.github.io

Personal website
HTML
5
star
17

reprex-clj

Reproducible examples for clojure
Clojure
4
star
18

nebra-shot-maps

Python
3
star
19

t9-optimised

Optimising the T9 keyboard layout
Python
3
star
20

alfred-timer

A timer for Alfred on MacOS
Python
2
star
21

terminal-snake

Toy project with haskell
Haskell
2
star
22

duck-debugger

Talk to the duck (ELIZA style)
Elm
2
star
23

football-data

R functions used in conjunction with football-data.co.uk csv files
R
1
star
24

clojure-soccer

(Incomplete) Implementations of soccer models in clojure
Clojure
1
star
25

langtons-ant

An implementation of Langton's ant in elm
Elm
1
star
26

wolf-and-hare

A 2-player implementation of the game by Martin Weidner.
Elm
1
star
27

chainplot

A(nother) rubbish wrapper for matplotlib
Python
1
star
28

simple-csv

A simple csv editor
Elm
1
star
29

elm-chaos-game

Elm
1
star
30

fully-automated-luxury-commentary

Python
1
star
31

image-xg

Jupyter Notebook
1
star