{"id":19840926,"url":"https://github.com/torvaney/mezzala","last_synced_at":"2025-10-04T07:33:10.276Z","repository":{"id":57441270,"uuid":"367326176","full_name":"Torvaney/mezzala","owner":"Torvaney","description":"Models for estimating football (soccer) team-strength","archived":false,"fork":false,"pushed_at":"2021-10-19T22:08:57.000Z","size":287,"stargazers_count":36,"open_issues_count":3,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-24T05:55:21.338Z","etag":null,"topics":["dixon-coles","poisson-regression","soccer","soccer-analytics","team-strength"],"latest_commit_sha":null,"homepage":"https://torvaney.github.io/mezzala/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Torvaney.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-05-14T10:20:22.000Z","updated_at":"2025-03-17T15:51:17.000Z","dependencies_parsed_at":"2022-09-06T02:01:32.486Z","dependency_job_id":null,"html_url":"https://github.com/Torvaney/mezzala","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Torvaney/mezzala","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Torvaney%2Fmezzala","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Torvaney%2Fmezzala/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Torvaney%2Fmezzala/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Torvaney%2Fmezzala/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Torvaney","download_url":"https://codeload.github.com/Torvaney/mezzala/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Torvaney%2Fmezzala/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264171761,"owners_count":23567751,"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":["dixon-coles","poisson-regression","soccer","soccer-analytics","team-strength"],"created_at":"2024-11-12T12:28:46.058Z","updated_at":"2025-10-04T07:33:05.257Z","avatar_url":"https://github.com/Torvaney.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mezzala\n\u003e Models for estimating football (soccer) team-strength\n\n\n## Install\n\n`pip install mezzala`\n\n## How to use\n\n```python\nimport mezzala\n```\n\nFitting a Dixon-Coles team strength model:\n\nFirst, we need to get some data\n\n```python\nimport itertools\nimport json\nimport urllib.request\n\n\n# Use 2016/17 Premier League data from the openfootball repo\nurl = 'https://raw.githubusercontent.com/openfootball/football.json/master/2016-17/en.1.json'\n\n\nresponse = urllib.request.urlopen(url)\ndata_raw = json.loads(response.read())\n\n# Reshape the data to just get the matches\ndata = list(itertools.chain(*[d['matches'] for d in data_raw['rounds']]))\n\ndata[0:3]\n```\n\n\n\n\n    [{'date': '2016-08-13',\n      'team1': 'Hull City AFC',\n      'team2': 'Leicester City FC',\n      'score': {'ft': [2, 1]}},\n     {'date': '2016-08-13',\n      'team1': 'Everton FC',\n      'team2': 'Tottenham Hotspur FC',\n      'score': {'ft': [1, 1]}},\n     {'date': '2016-08-13',\n      'team1': 'Crystal Palace FC',\n      'team2': 'West Bromwich Albion FC',\n      'score': {'ft': [0, 1]}}]\n\n\n\n### Fitting a model\n\nTo fit a model with mezzala, you need to create an \"adapter\". Adapters are used to connect a model to a data source.\n\nBecause our data is a list of dicts, we are going to use a `KeyAdapter`.\n\n```python\nadapter = mezzala.KeyAdapter(       # `KeyAdapter` = datum['...']\n    home_team='team1',\n    away_team='team2',\n    home_goals=['score', 'ft', 0],  # Get nested fields with lists of fields\n    away_goals=['score', 'ft', 1],  # i.e. datum['score']['ft'][1]\n)\n\n# You'll never need to call the methods on an \n# adapter directly, but just to show that it \n# works as expected:\nadapter.home_team(data[0])\n```\n\n\n\n\n    'Hull City AFC'\n\n\n\nOnce we have an adapter for our specific data source, we can fit the model:\n\n```python\nmodel = mezzala.DixonColes(adapter=adapter)\nmodel.fit(data)\n```\n\n\n\n\n    DixonColes(adapter=KeyAdapter(home_goals=['score', 'ft', 0], away_goals=['score', 'ft', 1], home_team='team1', away_team='team2'), blocks=[TeamStrength(), BaseRate(), HomeAdvantage()]), weight=UniformWeight()\n\n\n\n### Making predictions\n\nBy default, you only need to supply the home and away team to get predictions. This should be supplied in the same format as the training data.\n\n`DixonColes` has two methods for making predictions:\n\n* `predict_one` - for predicting a single match\n* `predict` - for predicting multiple matches\n\n```python\nmatch_to_predict = {\n    'team1': 'Manchester City FC',\n    'team2': 'Swansea City FC',\n}\n\nscorelines = model.predict_one(match_to_predict)\n\nscorelines[0:5]\n```\n\n\n\n\n    [ScorelinePrediction(home_goals=0, away_goals=0, probability=0.023625049697587167),\n     ScorelinePrediction(home_goals=0, away_goals=1, probability=0.012682094432376022),\n     ScorelinePrediction(home_goals=0, away_goals=2, probability=0.00623268833779594),\n     ScorelinePrediction(home_goals=0, away_goals=3, probability=0.0016251514235046444),\n     ScorelinePrediction(home_goals=0, away_goals=4, probability=0.00031781436109636405)]\n\n\n\nEach of these methods return predictions in the form of `ScorelinePredictions`. \n\n* `predict_one` returns a list of `ScorelinePredictions`\n* `predict` returns a list of `ScorelinePredictions` for each predicted match (i.e. a list of lists)\n\nHowever, it can sometimes be more useful to have predictions in the form of match _outcomes_. Mezzala exposes the `scorelines_to_outcomes` function for this purpose:\n\n```python\nmezzala.scorelines_to_outcomes(scorelines)\n```\n\n\n\n\n    {Outcomes('Home win'): OutcomePrediction(outcome=Outcomes('Home win'), probability=0.8255103334702835),\n     Outcomes('Draw'): OutcomePrediction(outcome=Outcomes('Draw'), probability=0.11615659853961693),\n     Outcomes('Away win'): OutcomePrediction(outcome=Outcomes('Away win'), probability=0.058333067990098304)}\n\n\n\n### Extending the model\n\nIt's possible to fit more sophisticated models with mezzala, using **weights** and **model blocks**\n\n#### Weights\n\nYou can weight individual data points by supplying a function (or callable) to the `weight` argument to `DixonColes`:\n\n```python\nmezzala.DixonColes(\n    adapter=adapter,\n    # By default, all data points are weighted equally,\n    # which is equivalent to:\n    weight=lambda x: 1\n)\n```\n\n\n\n\n    DixonColes(adapter=KeyAdapter(home_goals=['score', 'ft', 0], away_goals=['score', 'ft', 1], home_team='team1', away_team='team2'), blocks=[TeamStrength(), BaseRate(), HomeAdvantage()]), weight=\u003cfunction \u003clambda\u003e at 0x123067488\u003e\n\n\n\nMezzala also provides an `ExponentialWeight` for the purpose of time-discounting:\n\n```python\nmezzala.DixonColes(\n    adapter=adapter,\n    weight=mezzala.ExponentialWeight(\n        epsilon=-0.0065,               # Decay rate\n        key=lambda x: x['days_ago']\n    )\n)\n```\n\n\n\n\n    DixonColes(adapter=KeyAdapter(home_goals=['score', 'ft', 0], away_goals=['score', 'ft', 1], home_team='team1', away_team='team2'), blocks=[TeamStrength(), BaseRate(), HomeAdvantage()]), weight=ExponentialWeight(epsilon=-0.0065, key=\u003cfunction \u003clambda\u003e at 0x122f938c8\u003e)\n\n\n\n#### Model blocks\n\nModel \"blocks\" define the calculation and estimation of home and away goalscoring rates.\n\n```python\nmezzala.DixonColes(\n    adapter=adapter,\n    # By default, only team strength and home advantage,\n    # is estimated:\n    blocks=[\n        mezzala.blocks.HomeAdvantage(),\n        mezzala.blocks.TeamStrength(),\n        mezzala.blocks.BaseRate(),      # Adds \"average goalscoring rate\" as a distinct parameter\n    ]\n)\n```\n\n\n\n\n    DixonColes(adapter=KeyAdapter(home_goals=['score', 'ft', 0], away_goals=['score', 'ft', 1], home_team='team1', away_team='team2'), blocks=[TeamStrength(), HomeAdvantage(), BaseRate()]), weight=UniformWeight()\n\n\n\nTo add custom parameters (e.g. per-league home advantage), you need to add additional model blocks.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftorvaney%2Fmezzala","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftorvaney%2Fmezzala","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftorvaney%2Fmezzala/lists"}