Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/linters_and_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
defaults:
run:
shell: bash
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import datetime as dt
import json
import os
import xml.etree.ElementTree as ET
from pathlib import Path

import chardet
import numpy as np
import pandas as pd
import requests
from bs4 import BeautifulSoup
from lxml import etree
from tqdm import tqdm

from databallpy.data_parsers import Metadata
Expand Down Expand Up @@ -165,7 +165,7 @@ def _get_tracking_data_xml(
frames_df["start_datetime_td"] = pd.to_datetime(frames_df["start_datetime_td"])
frames_df["end_datetime_td"] = pd.to_datetime(frames_df["end_datetime_td"])

context = etree.iterparse(tracab_loc, events=("start", "end"))
context = ET.iterparse(tracab_loc, events=("start", "end"))
event, _ = next(context)

frame_values = []
Expand Down Expand Up @@ -194,7 +194,7 @@ def _get_tracking_data_xml(
"datetime": ["NaT"] * size_lines,
}

context = etree.iterparse(tracab_loc, events=("start", "end"))
context = ET.iterparse(tracab_loc, events=("start", "end"))
event, _ = next(context)

if verbose:
Expand Down
39 changes: 35 additions & 4 deletions databallpy/features/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def _filter_data(

if filter_type == "savitzky_golay":
try:
return savgol_filter(
return _savgol_with_nan_compat(
array, window_length=window_length, polyorder=polyorder, mode="interp"
)
except Exception as e:
Expand Down Expand Up @@ -128,11 +128,11 @@ def filter_tracking_data(
]
for col in xy_columns:
if filter_type == "savitzky_golay":
tracking_data[col] = savgol_filter(
tracking_data[col].values,
tracking_data[col] = _filter_data(
tracking_data[col].to_numpy(),
filter_type="savitzky_golay",
window_length=window_length,
polyorder=polyorder,
mode="interp",
)
elif filter_type == "moving_average":
tracking_data[col] = np.convolve(
Expand All @@ -141,3 +141,34 @@ def filter_tracking_data(

if not inplace:
return tracking_data


def _savgol_with_nan_compat(
array: np.ndarray,
window_length: int,
polyorder: int,
mode: str = "interp",
) -> np.ndarray:
arr = np.asarray(array, dtype=float)

mask = ~np.isfinite(arr)

if not mask.any():
return savgol_filter(arr, window_length, polyorder, mode=mode).round(2)

valid_count = (~mask).sum()
if valid_count < max(window_length, polyorder + 1):
warnings.warn(
"Not enough finite samples to apply Savitzky–Golay filter; "
"returning original data for backward compatibility."
)
return arr

x = np.arange(arr.size)
arr_filled = arr.copy()
arr_filled[mask] = np.interp(x[mask], x[~mask], arr[~mask])

filtered = savgol_filter(arr_filled, window_length, polyorder, mode=mode).round(2)

filtered[mask] = np.nan
return filtered
2 changes: 1 addition & 1 deletion databallpy/schemas/event_data.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Optional

import pandas as pd
import pandera as pa
import pandera.pandas as pa

from databallpy.utils.constants import DATABALLPY_EVENTS

Expand Down
2 changes: 1 addition & 1 deletion databallpy/schemas/players.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Optional

import pandera as pa
import pandera.pandas as pa

from databallpy.utils.constants import DATABALLPY_POSITIONS

Expand Down
10 changes: 5 additions & 5 deletions databallpy/schemas/tracking_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import numpy as np
import pandas as pd
import pandera as pa
import pandera.extensions as extensions
import pandera.pandas as pa
from scipy.spatial import KDTree

from databallpy.features.covered_distance import (
Expand All @@ -15,7 +15,7 @@
)
from databallpy.features.differentiate import _differentiate
from databallpy.features.feature_utils import _check_column_ids
from databallpy.features.filters import savgol_filter
from databallpy.features.filters import _filter_data
from databallpy.features.pitch_control import get_pitch_control_single_frame
from databallpy.features.player_possession import (
get_ball_losses_and_updated_gain_idxs,
Expand Down Expand Up @@ -533,11 +533,11 @@ def filter_tracking_data(
]
for col in xy_columns:
if filter_type == "savitzky_golay":
self[col] = savgol_filter(
self[col].values,
self[col] = _filter_data(
self[col].to_numpy(),
filter_type="savitzky_golay",
window_length=window_length,
polyorder=polyorder,
mode="interp",
)
elif filter_type == "moving_average":
self[col] = np.convolve(
Expand Down
Loading
Loading