{"id":13711736,"url":"https://torvaney.github.io/ggsoccer/","last_synced_at":"2025-05-06T21:32:07.147Z","repository":{"id":40499413,"uuid":"84352345","full_name":"Torvaney/ggsoccer","owner":"Torvaney","description":"Plot soccer event data in R/ggplot2","archived":false,"fork":false,"pushed_at":"2024-10-07T21:02:59.000Z","size":23167,"stargazers_count":186,"open_issues_count":11,"forks_count":28,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-27T01:33:20.459Z","etag":null,"topics":["event-data","football","ggplot2","plot","r","rstats","soccer"],"latest_commit_sha":null,"homepage":"https://torvaney.github.io/ggsoccer/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Torvaney.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-08T18:18:47.000Z","updated_at":"2025-04-16T10:39:47.000Z","dependencies_parsed_at":"2024-06-12T16:36:25.996Z","dependency_job_id":"9cb8a081-132b-4ab7-8b1d-3efa9a61dc21","html_url":"https://github.com/Torvaney/ggsoccer","commit_stats":{"total_commits":136,"total_committers":7,"mean_commits":"19.428571428571427","dds":0.1470588235294118,"last_synced_commit":"4b8ad2e0078da247f71d142627003bb76254992f"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Torvaney%2Fggsoccer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Torvaney%2Fggsoccer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Torvaney%2Fggsoccer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Torvaney%2Fggsoccer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Torvaney","download_url":"https://codeload.github.com/Torvaney/ggsoccer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252772121,"owners_count":21801856,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["event-data","football","ggplot2","plot","r","rstats","soccer"],"created_at":"2024-08-02T23:01:11.124Z","updated_at":"2025-05-06T21:32:02.124Z","avatar_url":"https://github.com/Torvaney.png","language":"HTML","funding_links":[],"categories":["Plot layers"],"sub_categories":[],"readme":"---\noutput:\n  github_document:\n    html_preview: false\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, echo = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\"\n)\n```\n\n# ggsoccer  \u003cimg src=\"man/figures/logo.png\" width=\"160px\" align=\"right\" /\u003e \n\n\u003c!-- badges: start --\u003e\n[![CRAN\\_Status\\_Badge](http://www.r-pkg.org/badges/version/ggsoccer)](https://cran.r-project.org/package=ggsoccer)\n[![CRAN\\_Version\\_Badge](https://cranlogs.r-pkg.org/badges/ggsoccer?color=ff69b4)](https://cran.r-project.org/package=ggsoccer)\n[![lifecycle](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable)\n[![R-CMD-check](https://github.com/Torvaney/ggsoccer/workflows/R-CMD-check/badge.svg)](https://github.com/Torvaney/ggsoccer/actions)\n\u003c!-- badges: end --\u003e\n\n## Overview\n\nggsoccer provides a functions for plotting soccer event data in R/ggplot2.\n\n## Installation\n\nggsoccer is available via CRAN:\n\n```{r, eval = FALSE}\ninstall.packages(\"ggsoccer\")\n```\n\nAlternatively, you can download the development version from github like so:\n\n```{r, eval = FALSE}\n# install.packages(\"remotes\")\nremotes::install_github(\"torvaney/ggsoccer\")\n```\n\n## Usage\n\n```{r example_blank, message = FALSE}\nlibrary(ggplot2)\nlibrary(ggsoccer)\n\nggplot() +\n  annotate_pitch() +\n  theme_pitch()\n```\n\nThe following example uses ggsoccer to solve a realistic problem: plotting a set \nof passes onto a soccer pitch.\n\n```{r example_passes}\npass_data \u003c- data.frame(x = c(24, 18, 64, 78, 53),\n                        y = c(43, 55, 88, 18, 44),\n                        x2 = c(34, 44, 81, 85, 64),\n                        y2 = c(40, 62, 89, 44, 28))\n\nggplot(pass_data) +\n  annotate_pitch() +\n  geom_segment(aes(x = x, y = y, xend = x2, yend = y2),\n               arrow = arrow(length = unit(0.25, \"cm\"),\n                             type = \"closed\")) +\n  theme_pitch() +\n  direction_label() +\n  ggtitle(\"Simple passmap\", \n          \"ggsoccer example\")\n```\n\nBecause ggsoccer is implemented as ggplot layers, plots can be customised with\nstandard ggplot functions and layers. \n\nHere is a different example, plotting shots on a **green** pitch.\n\nBy default, ggsoccer will display the whole pitch. To display a \nsubsection of the pitch, set the plot limits as you would with any other\nggplot2 plot. Here, we use the `xlim` and `ylim` arguments to `coord_flip`.\n\n`coord_flip` reverses the orientation of the points, so we must also reverse the y-axis\nto ensure that the orientation remains correct (that is, shots from the left\nhand side appear on the left, and right-sided shots appear on the right).\n\nYou can do this with either `scale_y_reverse` or by reversing the\norder of the limits in `coord_flip`'s `ylim` argument.\n\nIf you don't correct (i.e. reverse) the y axis orientation, the penalty box arcs will appear \ninside the box!\n\n```{r example_shots}\nshots \u003c- data.frame(x = c(90, 85, 82, 78, 83, 74, 94, 91),\n                    y = c(43, 40, 52, 56, 44, 71, 60, 54))\n\nggplot(shots) +\n  annotate_pitch(colour = \"white\",\n                 fill   = \"springgreen4\",\n                 limits = FALSE) +\n  geom_point(aes(x = x, y = y),\n             colour = \"yellow\",\n             size = 4) +\n  theme_pitch() +\n  theme(panel.background = element_rect(fill = \"springgreen4\")) +\n  coord_flip(xlim = c(49, 101)) +\n  scale_y_reverse() +\n  ggtitle(\"Simple shotmap\",\n          \"ggsoccer example\")\n```\n\n### Data providers\n\nggsoccer defaults to Opta's 100x100 coordinate system. However, different data \nproviders may use alternative coordinates. \n\nggsoccer provides support for a few data providers out of the box:\n\n* StatsPerform/Opta (`pitch_statsperform`, default)\n* Statsbomb (`pitch_statsbomb`)\n* Wyscout (`pitch_wyscout`)\n* Tracab (`make_pitch_tracab()`)\n\nggsoccer also provides an interface for any custom coordinate system.\n\n#### Statsbomb\n\n```{r example_passes_sb}\n# ggsoccer enables you to rescale coordinates from one data provider to another, too\nto_statsbomb \u003c- rescale_coordinates(from = pitch_opta, to = pitch_statsbomb)\n\npasses_rescaled \u003c- data.frame(x  = to_statsbomb$x(pass_data$x),\n                              y  = to_statsbomb$y(pass_data$y),\n                              x2 = to_statsbomb$x(pass_data$x2),\n                              y2 = to_statsbomb$y(pass_data$y2))\n\nggplot(passes_rescaled) +\n  annotate_pitch(dimensions = pitch_statsbomb) +\n  geom_segment(aes(x = x, y = y, xend = x2, yend = y2),\n               colour = \"coral\",\n               arrow = arrow(length = unit(0.25, \"cm\"),\n                             type = \"closed\")) +\n  theme_pitch() +\n  direction_label(x_label = 60) +\n  ggtitle(\"Simple passmap\", \n          \"Statsbomb co-ordinates\")\n```\n\n#### Custom data\n\nTo plot data for a dataset not provided, ggsoccer requires a pitch specification. \nThis is a list containing the required pitch dimensions like so:\n\n```{r example_custom}\npitch_custom \u003c- list(\n  length = 150,\n  width = 100,\n  penalty_box_length = 25,\n  penalty_box_width = 60,\n  six_yard_box_length = 8,\n  six_yard_box_width = 26,\n  penalty_spot_distance = 16,\n  goal_width = 12,\n  origin_x = -50,\n  origin_y = -75\n)\n\nggplot() +\n  annotate_pitch(dimensions = pitch_custom) +\n  theme_pitch()\n```\n\n### Goals\n\nggsoccer allows you to customise your goals markings by supplying a function to \nthe `goals` argument of `annotate_pitch`:\n\n```{r example_goals_line}\nggplot() +\n  annotate_pitch(fill = \"steelblue4\", colour = \"white\", goals = goals_line) +\n  theme_pitch() +\n  theme(panel.background = element_rect(fill = \"steelblue4\"))\n```\n\n```{r example_goals_strip}\nggplot() +\n  annotate_pitch(goals = goals_strip, fill = \"lightgray\") +\n  theme_pitch()\n```\n\nThis argument takes a function (or one-sided formula). You can use the supplied functions,\nor create your own goal markings function. The `goals` argument also supports using \none-sided formulas as lambda functions\n(see [`rlang::as_function`](https://rlang.r-lib.org/reference/as_function.html)).\n\nCustom goals functions must accept the arguments used by `annotate_pitch`: `colour`, `fill`, \n`dimensions`, `linewidth`, `alpha`, and `linetype`. Additional arguments can also be added.\n\n```{r example_goals_custom}\ngoals_custom \u003c- function(colour, fill, dimensions, ...) {\n  goals_strip(colour, fill, dimensions, lineend = \"square\", linewidth = 3.5)\n}\n\nggplot() +\n  annotate_pitch(\n    goals = goals_custom, \n    fill = \"lightgray\"\n  ) +\n  theme_pitch()\n```\n\nSee `help(goals_box)` for the full list of available functions.\n\nThe idea for having multiple goal markings was taken and adapted from the [fc.rstats](https://github.com/FCrSTATS/fc.rstats) package.\n\n### Further customisation\n\nYou can also alter the style of pitch markings with `linewidth`, `alpha`, and `linetype`:\n\n```{r example_further_customisation}\nggplot() +\n  annotate_pitch(\n    colour = \"white\", \n    linewidth = 1.5, \n    linetype = \"12\", \n    alpha = 0.2, \n    goals = goals_line\n  ) +\n  theme_pitch() +\n  theme(panel.background = element_rect(fill = \"steelblue\"))\n```\n\n## Other options\n\nThere are other packages that offer alternative pitch plotting options. \nDepending on your use case, you may want to check these out too:\n\n* [soccermatics](https://github.com/JoGall/soccermatics)\n* [SBpitch](https://github.com/FCrSTATS/SBpitch)\n* [fc.rstats](https://github.com/FCrSTATS/fc.rstats)\n* [sportyR](https://github.com/sportsdataverse/sportyR)\n\n### Python\n\nThere are a couple of pitch plotting options for matplotlib, too:\n\n* [mplsoccer](https://github.com/andrewRowlinson/mplsoccer)\n* [matplotsoccer](https://github.com/TomDecroos/matplotsoccer)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/torvaney.github.io%2Fggsoccer%2F","html_url":"https://awesome.ecosyste.ms/projects/torvaney.github.io%2Fggsoccer%2F","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/torvaney.github.io%2Fggsoccer%2F/lists"}