Skip to content

Commit

Permalink
MNT: compatibility with pandas>=2.1.0 and upcoming deprecations
Browse files Browse the repository at this point in the history
  • Loading branch information
theOehrly committed Jan 2, 2024
1 parent 5c17e2b commit 5868a13
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 170 deletions.
2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
'../fastf1/signalr_aio'
]

nitpicky = True

nitpick_ignore_regex = [
(r'py:data', r'typing\..*'),
(r'py:.*', r'datetime\..*'),
Expand Down
6 changes: 0 additions & 6 deletions docs/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,38 @@
.. autoclass:: fastf1.core.Session
:members:
:autosummary:
:show-inheritance:

Laps
++++

.. autoclass:: fastf1.core.Laps
:members:
:autosummary:
:show-inheritance:

Lap
+++

.. autoclass:: fastf1.core.Lap
:members:
:autosummary:
:show-inheritance:

Telemetry
+++++++++

.. autoclass:: fastf1.core.Telemetry
:members:
:autosummary:
:show-inheritance:

Results
+++++++

.. autoclass:: fastf1.core.SessionResults
:members:
:autosummary:
:show-inheritance:

.. autoclass:: fastf1.core.DriverResult
:members:
:autosummary:
:show-inheritance:


CircuitInfo
Expand Down
107 changes: 23 additions & 84 deletions fastf1/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@
from functools import cached_property
import warnings
import typing
from typing import Optional, List, Literal, Iterable, Union, Tuple, Any
from typing import Optional, List, Literal, Iterable, Union, Tuple, Any, Type

import numpy as np
import pandas as pd

import fastf1
from fastf1 import _api as api
from fastf1 import ergast
from fastf1.internals.pandas_base import BaseDataFrame, BaseSeries
from fastf1.livetiming.data import LiveTimingData
from fastf1.mvapi import get_circuit_info, CircuitInfo
from fastf1.logger import get_logger, soft_exceptions
Expand Down Expand Up @@ -212,10 +213,7 @@ def __init__(self,

@property
def _constructor(self):
def _new(*args, **kwargs):
return Telemetry(*args, **kwargs).__finalize__(self)

return _new
return Telemetry

@property
def base_class_view(self):
Expand Down Expand Up @@ -1707,7 +1705,8 @@ def _fix_missing_laps_retired_on_track(self):
})

# add generated laps at the end and fix sorting at the end
self._laps = pd.concat([self._laps, new_last])
self._laps = (pd.concat([self._laps, new_last])
.__finalize__(self._laps))
any_new = True

if any_new:
Expand Down Expand Up @@ -2412,7 +2411,7 @@ def _calculate_t0_date(self, *tel_data_sets: dict):
self._t0_date = date_offset.round('ms')


class Laps(pd.DataFrame):
class Laps(BaseDataFrame):
"""Object for accessing lap (timing) data of multiple laps.
Args:
Expand Down Expand Up @@ -2566,8 +2565,7 @@ class Laps(pd.DataFrame):
}

_metadata = ['session']
_internal_names = pd.DataFrame._internal_names \
+ ['base_class_view', 'telemetry']
_internal_names = BaseDataFrame._internal_names + ['telemetry']
_internal_names_set = set(_internal_names)

QUICKLAP_THRESHOLD = 1.07
Expand Down Expand Up @@ -2606,30 +2604,8 @@ def __init__(self,
self.session = session

@property
def _constructor(self):
def _new(*args, **kwargs):
return Laps(*args, **kwargs).__finalize__(self)

return _new

@property
def _constructor_sliced(self):
def _new(*args, **kwargs):
name = kwargs.get('name')
if name and (name in self.columns):
# vertical slice
return pd.Series(*args, **kwargs).__finalize__(self)

# horizontal slice
return Lap(*args, **kwargs).__finalize__(self)

return _new

@property
def base_class_view(self):
"""For a nicer debugging experience; can now view as
dataframe in various IDEs"""
return pd.DataFrame(self)
def _constructor_sliced_horizontal(self) -> Type["Lap"]:
return Lap

@cached_property
def telemetry(self) -> Telemetry:
Expand Down Expand Up @@ -3265,28 +3241,26 @@ def iterlaps(self, require: Optional[Iterable] = None) \
yield index, lap


class Lap(pd.Series):
class Lap(BaseSeries):
"""
Object for accessing lap (timing) data of a single lap.
This class wraps :class:`pandas.Series`. It provides extra functionality
for accessing a lap's associated
telemetry data.
Args:
*args: passed through to :class:`pandas.Series` super class
**kwargs: passed through to :class:`pandas.Series`
super class
"""
_metadata = ['session']
_internal_names = pd.Series._internal_names + ['telemetry']
_internal_names = BaseSeries._internal_names + ['telemetry']
_internal_names_set = set(_internal_names)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

@property
def _constructor(self):
def _new(*args, **kwargs):
return Lap(*args, **kwargs).__finalize__(self)

return _new

@cached_property
def telemetry(self) -> Telemetry:
"""Telemetry data for this lap
Expand Down Expand Up @@ -3447,7 +3421,7 @@ def get_weather_data(self) -> pd.Series:
return pd.Series(index=self.session.weather_data.columns)


class SessionResults(pd.DataFrame):
class SessionResults(BaseDataFrame):
"""This class provides driver and result information for all drivers that
participated in a session.
Expand Down Expand Up @@ -3583,9 +3557,6 @@ class SessionResults(pd.DataFrame):
'Points': 'float64'
}

_internal_names = pd.DataFrame._internal_names + ['base_class_view']
_internal_names_set = set(_internal_names)

def __init__(self, *args, force_default_cols: bool = False, **kwargs):
if force_default_cols:
kwargs['columns'] = list(self._COL_TYPES.keys())
Expand All @@ -3604,37 +3575,12 @@ def __init__(self, *args, force_default_cols: bool = False, **kwargs):

self[col] = self[col].astype(_type)

def __repr__(self):
return self.base_class_view.__repr__()

@property
def _constructor(self):
def _new(*args, **kwargs):
return SessionResults(*args, **kwargs).__finalize__(self)

return _new

@property
def _constructor_sliced(self):
def _new(*args, **kwargs):
name = kwargs.get('name')
if name and (name in self.columns):
# vertical slice
return pd.Series(*args, **kwargs).__finalize__(self)

# horizontal slice
return DriverResult(*args, **kwargs).__finalize__(self)

return _new

@property
def base_class_view(self):
"""For a nicer debugging experience; can view DataFrame through
this property in various IDEs"""
return pd.DataFrame(self)
def _constructor_sliced_horizontal(self) -> Type["DriverResult"]:
return DriverResult


class DriverResult(pd.Series):
class DriverResult(BaseSeries):
"""This class provides driver and result information for a single driver.
This class subclasses a :class:`pandas.Series` and the usual methods
Expand All @@ -3647,25 +3593,18 @@ class DriverResult(pd.Series):
:func:`Session.get_driver` or by slicing the session result.
Args:
*args: passed on to :class:`pandas.Series` superclass
**kwargs: passed on to :class:`pandas.Series` superclass
*args: passed through to :class:`pandas.Series` superclass
**kwargs: passed through to :class:`pandas.Series` superclass
.. versionadded:: 2.2
"""

_internal_names = pd.DataFrame._internal_names + ['dnf']
_internal_names = BaseSeries._internal_names + ['dnf']
_internal_names_set = set(_internal_names)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

@property
def _constructor(self):
def _new(*args, **kwargs):
return DriverResult(*args, **kwargs).__finalize__(self)

return _new

@property
def dnf(self) -> bool:
"""True if driver did not finish"""
Expand Down
56 changes: 15 additions & 41 deletions fastf1/ergast/interface.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import copy
import json
from typing import List, Literal, Optional, Union
from typing import List, Literal, Optional, Type, Union

from fastf1 import __version_short__
from fastf1.req import Cache
import fastf1.ergast.structure as API


import pandas as pd
from fastf1.internals.pandas_base import BaseDataFrame, BaseSeries


BASE_URL = 'https://ergast.com/api/f1'
Expand Down Expand Up @@ -101,7 +99,7 @@ def get_prev_result_page(self) -> Union['ErgastSimpleResponse',
)


class ErgastResultFrame(pd.DataFrame):
class ErgastResultFrame(BaseDataFrame):
"""
Wraps a Pandas ``DataFrame``. Additionally, this class can be
initialized from Ergast response data with automatic flattening and type
Expand All @@ -117,7 +115,7 @@ class ErgastResultFrame(pd.DataFrame):
auto_cast: Determines if values are automatically cast to the most
appropriate data type from their original string representation
"""
_internal_names = pd.DataFrame._internal_names + ['base_class_view']
_internal_names = BaseDataFrame._internal_names + ['base_class_view']
_internal_names_set = set(_internal_names)

def __init__(self, data=None, *,
Expand Down Expand Up @@ -164,48 +162,17 @@ def _flatten_element(cls, nested: dict, category: dict, cast: bool):
return nested, flat

@property
def _constructor(self):
def _new(*args, **kwargs):
return ErgastResultFrame(*args, **kwargs).__finalize__(self)

return _new

@property
def _constructor_sliced(self):
def _new(*args, **kwargs):
name = kwargs.get('name')
if name and (name in self.columns):
# vertical slice
return pd.Series(*args, **kwargs).__finalize__(self)

# horizontal slice
return ErgastResultSeries(*args, **kwargs).__finalize__(self)

return _new

@property
def base_class_view(self):
"""For a nicer debugging experience; can view DataFrame through
this property in various IDEs"""
return pd.DataFrame(self)
def _constructor_sliced_horizontal(self):
return ErgastResultSeries


class ErgastResultSeries(pd.Series):
class ErgastResultSeries(BaseSeries):
"""
Wraps a Pandas ``Series``.
Currently, no extra functionality is implemented.
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

@property
def _constructor(self):
def _new(*args, **kwargs):
return ErgastResultSeries(*args, **kwargs).__finalize__(self)

return _new
pass


class ErgastRawResponse(ErgastResponseMixin, list):
Expand Down Expand Up @@ -275,6 +242,13 @@ class ErgastSimpleResponse(ErgastResponseMixin, ErgastResultFrame):
+ ErgastResponseMixin._internal_names
_internal_names_set = set(_internal_names)

@property
def _constructor(self) -> Type["ErgastResultFrame"]:
# drop from ErgastSimpleResponse to ErgastResultFrame, removing the
# ErgastResponseMixin because a slice of the data is no longer a full
# response and pagination, ... is therefore not supported anymore
return ErgastResultFrame


class ErgastMultiResponse(ErgastResponseMixin):
"""
Expand Down
Loading

0 comments on commit 5868a13

Please sign in to comment.