Skip to content

Commit

Permalink
fix(opta): apply _fix_unintentional_ball_touches only to passes
Browse files Browse the repository at this point in the history
The _fix_unintentional_ball_touches method was added to deal with "unintentional
ball touch" events (type_id=61 + outcome=0). These are typically deflections. It
sets the result of an action to "success" if the team manages to keep
possession.

But this should only be applied to passes (i.e., actions for wich the result
depends on keeping possession). For example, the result of a shot should depend
on whether a goal was scored.

Fixes #725
  • Loading branch information
probberechts committed Jun 22, 2024
1 parent 27e7b50 commit 430c9c5
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 7 deletions.
3 changes: 2 additions & 1 deletion socceraction/spadl/opta.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,13 @@ def _fix_unintentional_ball_touches(
Opta event dataframe without any unintentional ball touches.
"""
df_actions_next = df_actions.shift(-2)
selector_pass = df_actions["type_id"] == spadlconfig.actiontypes.index("pass")
selector_deflected = (opta_type.shift(-1) == "ball touch") & (opta_outcome.shift(-1))
selector_same_team = df_actions["team_id"] == df_actions_next["team_id"]
df_actions.loc[selector_deflected, ["end_x", "end_y"]] = df_actions_next.loc[
selector_deflected, ["start_x", "start_y"]
].values
df_actions.loc[selector_deflected & selector_same_team, "result_id"] = (
df_actions.loc[selector_pass & selector_deflected & selector_same_team, "result_id"] = (
spadlconfig.results.index("success")
)
return df_actions
28 changes: 22 additions & 6 deletions tests/spadl/test_opta.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ def setup_method(self) -> None:
)

self.events = loader.events(1009316)
self.actions = opta.convert_to_actions(self.events, 174)

def test_convert_to_actions(self) -> None:
df_actions = opta.convert_to_actions(self.events, 174)
assert len(df_actions) > 0
SPADLSchema.validate(df_actions)
assert (df_actions.game_id == 1009316).all()
assert ((df_actions.team_id == 174) | (df_actions.team_id == 957)).all()
assert len(self.actions) > 0
SPADLSchema.validate(self.actions)
assert (self.actions.game_id == 1009316).all()
assert ((self.actions.team_id == 174) | (self.actions.team_id == 957)).all()

def test_convert_goalkick(self) -> None:
event = pd.DataFrame(
Expand All @@ -52,7 +52,13 @@ def test_convert_goalkick(self) -> None:
"end_y": 18.7,
"assist": False,
"keypass": False,
"qualifiers": {56: "Right", 141: "18.7", 124: True, 140: "73.0", 1: True},
"qualifiers": {
56: "Right",
141: "18.7",
124: True,
140: "73.0",
1: True,
},
"type_name": "pass",
}
]
Expand Down Expand Up @@ -89,6 +95,16 @@ def test_convert_own_goal(self) -> None:
assert action["type_id"] == spadlcfg.actiontypes.index("bad_touch")
assert action["result_id"] == spadlcfg.results.index("owngoal")

def test_fix_deflected_passes(self) -> None:
# for a deflected pass, the end coordinates and result should be fixed
deflected_pass = self.actions.loc[self.actions.original_event_id == 2016736289].iloc[0]
assert deflected_pass["result_id"] == spadlcfg.results.index("success")
assert deflected_pass["end_x"] == (100 - 70.6) / 100 * spadlcfg.field_length
assert deflected_pass["end_y"] == (100 - 72.6) / 100 * spadlcfg.field_width
# other actions that are followed by a ball touch event should not be changed
tackle = self.actions.loc[self.actions.original_event_id == 1820711400].iloc[0]
assert tackle["result_id"] == spadlcfg.results.index("fail")


def test_extract_lineups_f7xml() -> None:
data_dir = os.path.join(os.path.dirname(__file__), os.pardir, "datasets", "opta")
Expand Down

0 comments on commit 430c9c5

Please sign in to comment.