From 2e30dd5b6aa02ed4679019e4145e786c8254752e Mon Sep 17 00:00:00 2001 From: flych3r Date: Tue, 13 Jul 2021 17:57:02 -0300 Subject: [PATCH 1/5] changed typing style to python 3.10 --- pymove/core/dask.py | 19 +- pymove/core/dataframe.py | 19 +- pymove/core/grid.py | 100 +----- pymove/core/pandas.py | 195 +++++------ pymove/core/pandas_discrete.py | 43 ++- pymove/models/pattern_mining/clustering.py | 17 +- pymove/preprocessing/compression.py | 13 +- pymove/preprocessing/filters.py | 83 ++--- pymove/preprocessing/segmentation.py | 55 +-- pymove/preprocessing/stay_point_detection.py | 21 +- pymove/query/query.py | 27 +- pymove/semantic/semantic.py | 71 ++-- pymove/tests/test_visualization_matplotlib.py | 2 +- pymove/utils/conversions.py | 106 +++--- pymove/utils/data_augmentation.py | 27 +- pymove/utils/datetime.py | 44 +-- pymove/utils/distances.py | 36 +- pymove/utils/geoutils.py | 19 +- pymove/utils/integration.py | 90 ++--- pymove/utils/log.py | 23 +- pymove/utils/math.py | 18 +- pymove/utils/mem.py | 22 +- pymove/utils/trajectories.py | 38 +-- pymove/utils/visual.py | 19 +- pymove/visualization/folium.py | 314 +++++++++--------- pymove/visualization/matplotlib.py | 122 +++++-- 26 files changed, 773 insertions(+), 770 deletions(-) diff --git a/pymove/core/dask.py b/pymove/core/dask.py index 99f654de..05c15a90 100644 --- a/pymove/core/dask.py +++ b/pymove/core/dask.py @@ -1,6 +1,7 @@ """DaskMoveDataFrame class.""" +from __future__ import annotations -from typing import TYPE_CHECKING, Dict, List, Text, Union +from typing import TYPE_CHECKING import dask import numpy as np @@ -27,11 +28,11 @@ class DaskMoveDataFrame(DataFrame, MoveDataFrameAbstractModel): def __init__( self, - data: Union[DataFrame, List, Dict], - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE, - datetime: Text = DATETIME, - traj_id: Text = TRAJ_ID, + data: DataFrame | list | dict, + latitude: str = LATITUDE, + longitude: str = LONGITUDE, + datetime: str = DATETIME, + traj_id: str = TRAJ_ID, n_partitions: int = 1, ): """ @@ -501,8 +502,8 @@ def to_csv(self, *args, **kwargs): raise NotImplementedError('To be implemented') def convert_to( - self, new_type: Text - ) -> Union[MoveDataFrame, 'PandasMoveDataFrame', 'DaskMoveDataFrame']: + self, new_type: str + ) -> MoveDataFrame | 'PandasMoveDataFrame' | 'DaskMoveDataFrame': """ Convert an object from one type to another specified by the user. @@ -530,7 +531,7 @@ def convert_to( else: return self - def get_type(self) -> Text: + def get_type(self) -> str: """ Returns the type of the object. diff --git a/pymove/core/dataframe.py b/pymove/core/dataframe.py index 3e586116..ce92cd8a 100644 --- a/pymove/core/dataframe.py +++ b/pymove/core/dataframe.py @@ -1,6 +1,5 @@ """MoveDataFrame class.""" - -from typing import Dict, List, Text, Union +from __future__ import annotations from dateutil.parser._parser import ParserError from pandas.core.frame import DataFrame @@ -21,12 +20,12 @@ class MoveDataFrame: @staticmethod def __new__( self, - data: Union[DataFrame, Dict, List], - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE, - datetime: Text = DATETIME, - traj_id: Text = TRAJ_ID, - type_: Text = TYPE_PANDAS, + data: DataFrame | dict | list, + latitude: str = LATITUDE, + longitude: str = LONGITUDE, + datetime: str = DATETIME, + traj_id: str = TRAJ_ID, + type_: str = TYPE_PANDAS, n_partitions: int = 1, ): """ @@ -125,8 +124,8 @@ def validate_move_data_frame(data: DataFrame): @staticmethod def format_labels( - current_id: Text, current_lat: Text, current_lon: Text, current_datetime: Text - ) -> Dict: + current_id: str, current_lat: str, current_lon: str, current_datetime: str + ) -> dict: """ Format the labels for the PyMove lib pattern labels output lat, lon and datatime. diff --git a/pymove/core/grid.py b/pymove/core/grid.py index a29c65a7..f42c782a 100644 --- a/pymove/core/grid.py +++ b/pymove/core/grid.py @@ -1,12 +1,11 @@ """Grid class.""" +from __future__ import annotations import math -from typing import Callable, Dict, Optional, Text, Tuple, Union +from typing import Callable import joblib -import matplotlib.pyplot as plt import numpy as np -from matplotlib.pyplot import figure from pandas import DataFrame from shapely.geometry import Polygon @@ -17,7 +16,6 @@ INDEX_GRID_LON, LATITUDE, LONGITUDE, - POLYGON, TRAJ_ID, ) from pymove.utils.conversions import lat_meters @@ -30,9 +28,9 @@ class Grid: def __init__( self, - data: Union[DataFrame, Dict], - cell_size: Optional[float] = None, - meters_by_degree: Optional[float] = None + data: DataFrame | dict, + cell_size: float | None = None, + meters_by_degree: float | None = None ): """ Creates a virtual grid from the trajectories. @@ -58,7 +56,7 @@ def __init__( ValueError If one of data or cell grid is not provided """ - self.last_operation: Dict = dict() + self.last_operation: dict = dict() if meters_by_degree is None: meters_by_degree = lat_meters(-3.71839) if isinstance(data, dict): @@ -69,7 +67,7 @@ def __init__( raise ValueError('Must pass either data or cell size.') self.grid_polygon = None - def get_grid(self) -> Dict: + def get_grid(self) -> dict: """ Returns the grid object in a dict format. @@ -91,7 +89,7 @@ def get_grid(self) -> Dict: 'cell_size_by_degree': self.cell_size_by_degree, } - def _grid_from_dict(self, dict_grid: Dict): + def _grid_from_dict(self, dict_grid: dict): """ Coverts the dict grid to a Grid object. @@ -218,8 +216,8 @@ def create_update_index_grid_feature( def convert_two_index_grid_to_one( self, data: DataFrame, - label_grid_lat: Text = INDEX_GRID_LAT, - label_grid_lon: Text = INDEX_GRID_LON, + label_grid_lat: str = INDEX_GRID_LAT, + label_grid_lon: str = INDEX_GRID_LON, ): """ Converts grid lat-lon ids to unique values. @@ -241,7 +239,7 @@ def convert_two_index_grid_to_one( def convert_one_index_grid_to_two( self, data: DataFrame, - label_grid_index: Text = INDEX_GRID, + label_grid_index: str = INDEX_GRID, ): """ Converts grid lat-lon ids to unique values. @@ -360,7 +358,7 @@ def create_all_polygons_to_all_point_on_grid( self.last_operation = end_operation(operation) return datapolygons - def point_to_index_grid(self, event_lat: float, event_lon: float) -> Tuple[int, int]: + def point_to_index_grid(self, event_lat: float, event_lon: float) -> tuple[int, int]: """ Locate the coordinates x and y in a grid of point (lat, long). @@ -394,7 +392,7 @@ def point_to_index_grid(self, event_lat: float, event_lon: float) -> Tuple[int, return indexes_lat_y, indexes_lon_x - def save_grid_pkl(self, filename: Text): + def save_grid_pkl(self, filename: str): """ Save a grid with new file .pkl. @@ -409,7 +407,7 @@ def save_grid_pkl(self, filename: Text): joblib.dump(self.get_grid(), f) self.last_operation = end_operation(operation) - def read_grid_pkl(self, filename: Text) -> 'Grid': + def read_grid_pkl(self, filename: str) -> 'Grid': """ Read grid dict from a file .pkl. @@ -431,74 +429,6 @@ def read_grid_pkl(self, filename: Text) -> 'Grid': self.last_operation = end_operation(operation) return grid - def show_grid_polygons( - self, - data: DataFrame, - markersize: float = 10, - linewidth: float = 2, - figsize: Tuple[int, int] = (10, 10), - return_fig: bool = True, - save_fig: bool = False, - name: Text = 'grid.png', - ) -> Optional[figure]: - """ - Generate a visualization with grid polygons. - - Parameters - ---------- - data : DataFrame - Input trajectory data - markersize : float, optional - Represents visualization size marker, by default 10 - linewidth : float, optional - Represents visualization size line, by default 2 - figsize : tuple(int, int), optional - Represents the size (float: width, float: height) of a figure, - by default (10, 10) - return_fig : bool, optional - Represents whether or not to save the generated picture, by default True - save_fig : bool, optional - Wether to save the figure, by default False - name : str, optional - Represents name of a file, by default 'grid.png' - - Returns - ------- - figure - The generated picture or None - - Raises - ------ - If the dataframe does not contains the POLYGON feature - IndexError - If there is no user with the id passed - - """ - if POLYGON not in data: - raise KeyError('POLYGON feature not in dataframe') - - data.dropna(subset=[POLYGON], inplace=True) - - operation = begin_operation('show_grid_polygons') - - fig = plt.figure(figsize=figsize) - - for _, row in data.iterrows(): - xs, ys = row[POLYGON].exterior.xy - plt.plot(ys, xs, 'g', linewidth=linewidth, markersize=markersize) - xs_start, ys_start = data.iloc[0][POLYGON].exterior.xy - xs_end, ys_end = data.iloc[-1][POLYGON].exterior.xy - plt.plot(ys_start, xs_start, 'bo', markersize=markersize * 1.5) - plt.plot(ys_end, xs_end, 'bX', markersize=markersize * 1.5) # start point - - if save_fig: - plt.savefig(fname=name) - - self.last_operation = end_operation(operation) - - if return_fig: - return fig - def __repr__(self) -> str: """ String representation of grid. @@ -512,5 +442,5 @@ def __repr__(self) -> str: grid_size_lon_x: grid longitude size cell_size_by_degree: grid cell size """ - text = ['{}: {}'.format(k, v) for k, v in self.get_grid().items()] + text = [f'{k}: {v}' for k, v in self.get_grid().items()] return '\n'.join(text) diff --git a/pymove/core/pandas.py b/pymove/core/pandas.py index 0f0bf91c..e5da791d 100644 --- a/pymove/core/pandas.py +++ b/pymove/core/pandas.py @@ -1,6 +1,7 @@ """PandasMoveDataFrame class.""" +from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Text, Tuple, Union +from typing import TYPE_CHECKING, Any, Callable import numpy as np from pandas import DataFrame, DateOffset, Series, Timedelta @@ -54,11 +55,11 @@ class PandasMoveDataFrame(DataFrame): def __init__( self, - data: Union[DataFrame, List, Dict], - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE, - datetime: Text = DATETIME, - traj_id: Text = TRAJ_ID, + data: DataFrame | list | dict, + latitude: str = LATITUDE, + longitude: str = LONGITUDE, + datetime: str = DATETIME, + traj_id: str = TRAJ_ID, ): """ Checks whether past data has 'lat', 'lon', 'datetime' columns. @@ -113,9 +114,9 @@ def __init__( if MoveDataFrame.has_columns(tdf): MoveDataFrame.validate_move_data_frame(tdf) - super(PandasMoveDataFrame, self).__init__(tdf) + super().__init__(tdf) self._type = TYPE_PANDAS - self.last_operation: Dict = dict() + self.last_operation: dict = dict() else: raise KeyError( @@ -190,13 +191,13 @@ def datetime(self): def rename( self, - mapper: Optional[Union[Dict, Callable]] = None, - index: Optional[Union[Dict, Callable]] = None, - columns: Optional[Union[Dict, Callable]] = None, - axis: Optional[Union[int, Text]] = None, + mapper: dict | Callable | None = None, + index: dict | Callable | None = None, + columns: dict | Callable | None = None, + axis: int | str | None = None, copy: bool = True, inplace: bool = False - ) -> Optional[Union['PandasMoveDataFrame', DataFrame]]: + ) -> 'PandasMoveDataFrame' | DataFrame | None: """ Alter axes labels. @@ -245,7 +246,7 @@ def rename( if inplace: if MoveDataFrame.has_columns(rename_): self._mgr = rename_._mgr - self._item_cache: Dict = dict() + self._item_cache: dict = dict() rename_ = None else: raise AttributeError( @@ -352,7 +353,7 @@ def get_users_number(self) -> int: def to_grid( self, cell_size: float, - meters_by_degree: Optional[float] = None + meters_by_degree: float | None = None ) -> Grid: """ Converts trajectory data to grid format. @@ -394,7 +395,7 @@ def to_data_frame(self) -> DataFrame: return DataFrame(self) def to_dicrete_move_df( - self, local_label: Text = LOCAL_LABEL + self, local_label: str = LOCAL_LABEL ) -> 'PandasMoveDataFrame': """ Generate a discrete dataframe move. @@ -413,7 +414,7 @@ def to_dicrete_move_df( if local_label not in self: raise ValueError( - 'columns {} not in df'.format(local_label) + f'columns {local_label} not in df' ) self.last_operation = end_operation(operation) @@ -464,10 +465,10 @@ def copy(self, deep: bool = True) -> 'PandasMoveDataFrame': def generate_tid_based_on_id_datetime( self, - str_format: Text = '%Y%m%d%H', + str_format: str = '%Y%m%d%H', sort: bool = True, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Create or update trajectory id based on id and datetime. @@ -513,7 +514,7 @@ def generate_tid_based_on_id_datetime( def generate_date_features( self, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Create or update date feature based on datetime. @@ -546,7 +547,7 @@ def generate_date_features( def generate_hour_features( self, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Create or update hour features based on datetime. @@ -579,7 +580,7 @@ def generate_hour_features( def generate_day_of_the_week_features( self, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Create or update day of the week features based on datetime. @@ -613,7 +614,7 @@ def generate_weekend_features( self, create_day_of_week: bool = False, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Adds information to rows determining if it is a weekend day. @@ -661,7 +662,7 @@ def generate_weekend_features( def generate_time_of_day_features( self, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Create or update time of day features based on datetime. @@ -719,9 +720,9 @@ def generate_time_of_day_features( def generate_datetime_in_format_cyclical( self, - label_datetime: Text = DATETIME, + label_datetime: str = DATETIME, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Create or update column with cyclical datetime feature. @@ -763,8 +764,8 @@ def generate_datetime_in_format_cyclical( @staticmethod def _prepare_generate_data( - data_: DataFrame, sort: bool, label_id: Text - ) -> Tuple[Any, int, None]: + data_: DataFrame, sort: bool, label_id: str + ) -> tuple[Any, int, None]: """ Processes the data and create variables for generate methods. @@ -807,11 +808,11 @@ def _prepare_generate_data( def generate_dist_time_speed_features( self, - label_id: Text = TRAJ_ID, + label_id: str = TRAJ_ID, label_dtype: Callable = np.float64, sort: bool = True, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Adds distance, time and speed information to the dataframe. @@ -903,11 +904,11 @@ def generate_dist_time_speed_features( def generate_dist_features( self, - label_id: Text = TRAJ_ID, + label_id: str = TRAJ_ID, label_dtype: Callable = np.float64, sort: bool = True, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Create the three distance in meters to an GPS point P. @@ -991,11 +992,11 @@ def generate_dist_features( def generate_time_features( self, - label_id: Text = TRAJ_ID, + label_id: str = TRAJ_ID, label_dtype: Callable = np.float64, sort: bool = True, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Create the three time in seconds to an GPS point P. @@ -1070,11 +1071,11 @@ def generate_time_features( def generate_speed_features( self, - label_id: Text = TRAJ_ID, + label_id: str = TRAJ_ID, label_dtype: Callable = np.float64, sort: bool = True, inplace: bool = True - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Create the three speed in meter by seconds to an GPS point P. @@ -1145,7 +1146,7 @@ def generate_speed_features( def generate_move_and_stop_by_radius( self, radius: float = 0, - target_label: Text = DIST_TO_PREV, + target_label: str = DIST_TO_PREV, inplace: bool = True ): """ @@ -1208,7 +1209,7 @@ def time_interval(self) -> Timedelta: return time_diff - def get_bbox(self) -> Tuple[float, float, float, float]: + def get_bbox(self) -> tuple[float, float, float, float]: """ Returns the bounding box of the dataframe. @@ -1291,7 +1292,7 @@ def show_trajectories_info(self): if LATITUDE and LONGITUDE in self: print( - 'Bounding Box:%s\n' % (self.get_bbox(),) + f'Bounding Box:{self.get_bbox()}\n' ) # bbox return = Lat_min , Long_min, Lat_max, Long_max if TIME_TO_PREV in self: @@ -1326,9 +1327,9 @@ def show_trajectories_info(self): def astype( self, - dtype: Union[Callable, Dict], + dtype: Callable | dict, copy: bool = True, - errors: Text = 'raise' + errors: str = 'raise' ) -> DataFrame: """ Cast a pandas object to a specified dtype. @@ -1382,13 +1383,13 @@ def astype( def sort_values( self, - by: Union[Text, List[Text]], + by: str | list[str], axis: int = 0, ascending: bool = True, inplace: bool = False, - kind: Text = 'quicksort', - na_position: Text = 'last', - ) -> Optional['PandasMoveDataFrame']: + kind: str = 'quicksort', + na_position: str = 'last', + ) -> 'PandasMoveDataFrame' | None: """ Sorts the values of the _data, along an axis. @@ -1441,12 +1442,12 @@ def sort_values( def reset_index( self, - level: Optional[Union[int, Text, Tuple, List]] = None, + level: int | str | tuple | list | None = None, drop: bool = False, inplace: bool = False, - col_level: Union[int, Text] = 0, - col_fill: Text = '' - ) -> Optional['PandasMoveDataFrame']: + col_level: int | str = 0, + col_fill: str = '' + ) -> 'PandasMoveDataFrame' | None: """ Resets the DataFrame's index, and use the default one. @@ -1489,12 +1490,12 @@ def reset_index( def set_index( self, - keys: Union[Text, List[Text]], + keys: str | list[str], drop: bool = True, append: bool = False, inplace: bool = False, verify_integrity: bool = False, - ) -> Optional[Union['PandasMoveDataFrame', DataFrame]]: + ) -> 'PandasMoveDataFrame' | DataFrame | None: """ Set the DataFrame index (row labels) using one or more existing columns or arrays. @@ -1558,14 +1559,14 @@ def set_index( def drop( self, - labels: Optional[Union[Text, List[Text]]] = None, - axis: Union[int, Text] = 0, - index: Optional[Union[Text, List[Text]]] = None, - columns: Optional[Union[Text, List[Text]]] = None, - level: Optional[Union[int, Text]] = None, + labels: str | list[str] | None = None, + axis: int | str = 0, + index: str | list[str] | None = None, + columns: str | list[str] | None = None, + level: int | str | None = None, inplace: bool = False, - errors: Text = 'raise', - ) -> Optional[Union['PandasMoveDataFrame', DataFrame]]: + errors: str = 'raise', + ) -> 'PandasMoveDataFrame' | DataFrame | None: """ Removes rows or columns. @@ -1651,10 +1652,10 @@ def drop( def drop_duplicates( self, - subset: Optional[Union[int, Text]] = None, - keep: Union[Text, bool] = 'first', + subset: int | str | None = None, + keep: str | bool = 'first', inplace: bool = False - ) -> Optional['PandasMoveDataFrame']: + ) -> 'PandasMoveDataFrame' | None: """ Uses the pandas's function drop_duplicates, to remove duplicated rows from data. @@ -1694,9 +1695,9 @@ def drop_duplicates( def shift( self, periods: int = 1, - freq: Optional[Union[DateOffset, Timedelta, Text]] = None, - axis: Union[int, Text] = 0, - fill_value: Optional[Any] = None + freq: DateOffset | Timedelta | str | None = None, + axis: int | str = 0, + fill_value: Any | None = None ) -> 'PandasMoveDataFrame': """ Shift index by desired number of periods with an optional time freq. @@ -1740,12 +1741,12 @@ def shift( def fillna( self, - value: Optional[Any] = None, - method: Optional[Text] = None, - axis: Optional[Union[int, Text]] = None, + value: Any | None = None, + method: str | None = None, + axis: int | str | None = None, inplace: bool = False, - limit: Optional[int] = None, - downcast: Optional[Dict] = None, + limit: int | None = None, + downcast: dict | None = None, ): """ Fill NA/NaN values using the specified method. @@ -1803,10 +1804,10 @@ def fillna( def dropna( self, - axis: Union[int, Text] = 0, - how: Text = 'any', - thresh: Optional[float] = None, - subset: Optional[List] = None, + axis: int | str = 0, + how: str = 'any', + thresh: float | None = None, + subset: list | None = None, inplace: bool = False ): """ @@ -1870,12 +1871,12 @@ def dropna( def sample( self, - n: Optional[int] = None, - frac: Optional[float] = None, + n: int | None = None, + frac: float | None = None, replace: bool = False, - weights: Optional[Union[Text, List]] = None, - random_state: Optional[int] = None, - axis: Optional[Union[int, Text]] = None + weights: str | list | None = None, + random_state: int | None = None, + axis: int | str | None = None ) -> 'PandasMoveDataFrame': """ Return a random sample of items from an axis of object. @@ -1938,7 +1939,7 @@ def sample( ) return PandasMoveDataFrame(data=_sample) - def isin(self, values: Union[List, Series, DataFrame, Dict]) -> DataFrame: + def isin(self, values: list | Series | DataFrame | dict) -> DataFrame: """ Determines whether each element in the DataFrame is contained in values. @@ -1965,7 +1966,7 @@ def isin(self, values: Union[List, Series, DataFrame, Dict]) -> DataFrame: def append( self, - other: Union['PandasMoveDataFrame', DataFrame], + other: 'PandasMoveDataFrame' | DataFrame, ignore_index: bool = False, verify_integrity: bool = False, sort: bool = False @@ -2010,11 +2011,11 @@ def append( def join( self, - other: Union['PandasMoveDataFrame', DataFrame], - on: Optional[Union[Text, List]] = None, - how: Text = 'left', - lsuffix: Text = '', - rsuffix: Text = '', + other: 'PandasMoveDataFrame' | DataFrame, + on: str | list | None = None, + how: str = 'left', + lsuffix: str = '', + rsuffix: str = '', sort: bool = False ) -> 'PandasMoveDataFrame': """ @@ -2080,18 +2081,18 @@ def join( def merge( self, - right: Union['PandasMoveDataFrame', DataFrame, Series], - how: Text = 'inner', - on: Optional[Union[Text, List]] = None, - left_on: Optional[Union[Text, List]] = None, - right_on: Optional[Union[Text, List]] = None, + right: 'PandasMoveDataFrame' | DataFrame | Series, + how: str = 'inner', + on: str | list | None = None, + left_on: str | list | None = None, + right_on: str | list | None = None, left_index: bool = False, right_index: bool = False, sort: bool = False, - suffixes: Tuple[Text, Text] = ('_x', '_y'), + suffixes: tuple[str, str] = ('_x', '_y'), copy: bool = True, - indicator: Union[bool, Text] = False, - validate: Optional[Text] = None + indicator: bool | str = False, + validate: str | None = None ) -> 'PandasMoveDataFrame': """ Merge DataFrame or named Series objects with a database-style join. @@ -2180,7 +2181,7 @@ def merge( ) return PandasMoveDataFrame(data=_merge) - def write_file(self, file_name: Text, separator: Text = ','): + def write_file(self, file_name: str, separator: str = ','): """ Write trajectory data to a new file. @@ -2197,8 +2198,8 @@ def write_file(self, file_name: Text, separator: Text = ','): ) def convert_to( - self, new_type: Text - ) -> Union[MoveDataFrame, 'PandasMoveDataFrame', 'DaskMoveDataFrame']: + self, new_type: str + ) -> MoveDataFrame | 'PandasMoveDataFrame' | 'DaskMoveDataFrame': """ Convert an object from one type to another specified by the user. @@ -2231,7 +2232,7 @@ def convert_to( self.last_operation = end_operation(operation) return self - def get_type(self) -> Text: + def get_type(self) -> str: """ Returns the type of the object. diff --git a/pymove/core/pandas_discrete.py b/pymove/core/pandas_discrete.py index 54e9c93e..04d65ab5 100644 --- a/pymove/core/pandas_discrete.py +++ b/pymove/core/pandas_discrete.py @@ -1,6 +1,5 @@ """PandasDiscreteMoveDataFrame class.""" - -from typing import Dict, List, Optional, Text, Union +from __future__ import annotations import numpy as np import pandas as pd @@ -37,12 +36,12 @@ class PandasDiscreteMoveDataFrame(PandasMoveDataFrame): def __init__( self, - data: Union[DataFrame, List, Dict], - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE, - datetime: Text = DATETIME, - traj_id: Text = TRAJ_ID, - local_label: Text = LOCAL_LABEL + data: DataFrame | list | dict, + latitude: str = LATITUDE, + longitude: str = LONGITUDE, + datetime: str = DATETIME, + traj_id: str = TRAJ_ID, + local_label: str = LOCAL_LABEL ): """ Creates a dataframe using local_label as a discrete feature for localization. @@ -69,7 +68,7 @@ def __init__( ValueError, ParserError If the data types can't be converted. """ - super(PandasDiscreteMoveDataFrame, self).__init__( + super().__init__( data=data, latitude=latitude, longitude=longitude, @@ -79,7 +78,7 @@ def __init__( if local_label not in self: raise ValueError( - '{} column not in dataframe'.format(local_label) + f'{local_label} column not in dataframe' ) def discretize_based_grid(self, region_size: int = 1000): @@ -100,11 +99,11 @@ def discretize_based_grid(self, region_size: int = 1000): def generate_prev_local_features( self, - label_id: Text = TRAJ_ID, - local_label: Text = LOCAL_LABEL, + label_id: str = TRAJ_ID, + local_label: str = LOCAL_LABEL, sort: bool = True, inplace: bool = True - ) -> Optional['PandasDiscreteMoveDataFrame']: + ) -> 'PandasDiscreteMoveDataFrame' | None: """ Create a feature prev_local with the label of previous local to current point. @@ -145,7 +144,7 @@ def generate_prev_local_features( if (data_[local_label].dtype == 'int'): data_[local_label] = data_[local_label].astype(np.float16) for idx in progress_bar( - ids, desc='Generating previous {}'.format(local_label) + ids, desc=f'Generating previous {local_label}' ): current_local = data_.at[idx, local_label] current_local = np.array(current_local) @@ -168,15 +167,15 @@ def generate_prev_local_features( def generate_tid_based_statistics( self, - label_id: Text = TRAJ_ID, - local_label: Text = LOCAL_LABEL, + label_id: str = TRAJ_ID, + local_label: str = LOCAL_LABEL, mean_coef: float = 1.0, std_coef: float = 1.0, - statistics: Optional[DataFrame] = None, - label_tid_stat: Text = TID_STAT, + statistics: DataFrame | None = None, + label_tid_stat: str = TID_STAT, drop_single_points: bool = False, inplace: bool = True, - ) -> Optional['PandasDiscreteMoveDataFrame']: + ) -> 'PandasDiscreteMoveDataFrame' | None: """ Splits the trajectories into segments based on time statistics for segments. @@ -223,7 +222,7 @@ def generate_tid_based_statistics( self.generate_dist_time_speed_features(TRAJ_ID) if local_label not in data_: - raise KeyError('{} not in data frame.'.format(local_label)) + raise KeyError(f'{local_label} not in data frame.') if PREV_LOCAL not in data_: data_[local_label] = data_[local_label].astype(np.float64) @@ -234,7 +233,7 @@ def generate_tid_based_statistics( if statistics is None: if (data_[PREV_LOCAL].isna().sum() == data_.shape[0]): raise ValueError( - 'all values in the {} column are null.'.format(PREV_LOCAL) + f'all values in the {PREV_LOCAL} column are null.' ) else: statistics = generate_time_statistics(data_, local_label=local_label) @@ -269,7 +268,7 @@ def generate_tid_based_statistics( if label_id == TID_STAT: self.reset_index(drop=True, inplace=True) logger.debug( - '... {} = {}, then reseting and drop index!'.format(TID, TID_STAT)) + f'... {TID} = {TID_STAT}, then reseting and drop index!') else: self.reset_index(inplace=True) logger.debug('... reseting index\n') diff --git a/pymove/models/pattern_mining/clustering.py b/pymove/models/pattern_mining/clustering.py index d9302b3b..a874b79f 100644 --- a/pymove/models/pattern_mining/clustering.py +++ b/pymove/models/pattern_mining/clustering.py @@ -6,8 +6,9 @@ dbscan_clustering """ +from __future__ import annotations -from typing import Callable, Dict, Optional, Text, Union +from typing import Callable import numpy as np from pandas import DataFrame @@ -24,8 +25,8 @@ def elbow_method( k_initial: int = 1, max_clusters: int = 15, k_iteration: int = 1, - random_state: Optional[int] = None -) -> Dict: + random_state: int | None = None +) -> dict: """ Determines the optimal number of clusters. @@ -84,8 +85,8 @@ def gap_statistic( k_initial: int = 1, max_clusters: int = 15, k_iteration: int = 1, - random_state: Optional[int] = None -) -> Dict: + random_state: int | None = None +) -> dict: """ Calculates optimal clusters numbers using Gap Statistic. @@ -151,13 +152,13 @@ def gap_statistic( @timer_decorator def dbscan_clustering( move_data: DataFrame, - cluster_by: Text, + cluster_by: str, meters: int = 10, min_sample: float = 1680 / 2, earth_radius: float = EARTH_RADIUS, - metric: Union[Text, Callable] = 'euclidean', + metric: str | Callable = 'euclidean', inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Performs density based clustering on the move_dataframe according to cluster_by. diff --git a/pymove/preprocessing/compression.py b/pymove/preprocessing/compression.py index f79963ee..4bba2a38 100644 --- a/pymove/preprocessing/compression.py +++ b/pymove/preprocessing/compression.py @@ -4,8 +4,7 @@ compress_segment_stop_to_point """ - -from typing import Text +from __future__ import annotations import numpy as np from pandas import DataFrame @@ -28,11 +27,11 @@ @timer_decorator def compress_segment_stop_to_point( move_data: DataFrame, - label_segment: Text = SEGMENT_STOP, - label_stop: Text = STOP, - point_mean: Text = 'default', + label_segment: str = SEGMENT_STOP, + label_stop: str = STOP, + point_mean: str = 'default', drop_moves: bool = False, - label_id: Text = TRAJ_ID, + label_id: str = TRAJ_ID, dist_radius: float = 30, time_radius: float = 900, inplace: bool = False, @@ -147,7 +146,7 @@ def compress_segment_stop_to_point( lat_mean[ind_end] = move_data.loc[filter_][LATITUDE].mean() lon_mean[ind_end] = move_data.loc[filter_][LONGITUDE].mean() else: - logger.debug('There are segments with only one point: {}'.format(idx)) + logger.debug(f'There are segments with only one point: {idx}') move_data[LAT_MEAN] = lat_mean move_data[LON_MEAN] = lon_mean diff --git a/pymove/preprocessing/filters.py b/pymove/preprocessing/filters.py index 052881d4..871f17ad 100644 --- a/pymove/preprocessing/filters.py +++ b/pymove/preprocessing/filters.py @@ -17,8 +17,9 @@ clean_id_by_time_max """ +from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Text, Tuple, Union +from typing import TYPE_CHECKING, Any, Callable import numpy as np from pandas import DataFrame @@ -43,8 +44,8 @@ def get_bbox_by_radius( - coordinates: Tuple[float, float], radius: float = 1000 -) -> Tuple[float, float, float, float]: + coordinates: tuple[float, float], radius: float = 1000 +) -> tuple[float, float, float, float]: """ Defines minimum and maximum coordinates, given a distance radius from a point. @@ -82,10 +83,10 @@ def get_bbox_by_radius( def by_bbox( move_data: DataFrame, - bbox: Tuple[float, float, float, float], + bbox: tuple[float, float, float, float], filter_out: bool = False, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Filters points of the trajectories according to specified bounding box. @@ -123,11 +124,11 @@ def by_bbox( def by_datetime( move_data: DataFrame, - start_datetime: Optional[Text] = None, - end_datetime: Optional[Text] = None, + start_datetime: str | None = None, + end_datetime: str | None = None, filter_out: bool = False, inplace: bool = False, -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Filters trajectories points according to specified time range. @@ -173,10 +174,10 @@ def by_datetime( def by_label( move_data: DataFrame, value: Any, - label_name: Text, + label_name: str, filter_out: bool = False, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Filters trajectories points according to specified value and column label. @@ -212,11 +213,11 @@ def by_label( def by_id( move_data: DataFrame, - id_: Optional[int] = None, - label_id: Text = TRAJ_ID, + id_: int | None = None, + label_id: str = TRAJ_ID, filter_out: bool = False, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Filters trajectories points according to specified trajectory id. @@ -250,10 +251,10 @@ def by_id( def by_tid( move_data: DataFrame, - tid_: Optional[Text] = None, + tid_: str | None = None, filter_out: bool = False, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Filters trajectories points according to a specified trajectory tid. @@ -286,10 +287,10 @@ def by_tid( def clean_consecutive_duplicates( move_data: DataFrame, - subset: Optional[Union[int, Text]] = None, - keep: Union[Text, bool] = 'first', + subset: int | str | None = None, + keep: str | bool = 'first', inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Removes consecutive duplicate rows of the Dataframe. @@ -378,7 +379,7 @@ def _filter_speed_max_radius(move_data: DataFrame, **kwargs): return move_data[filter_] -def _filter_data(move_data: DataFrame, f: Callable, kwargs: Dict): +def _filter_data(move_data: DataFrame, f: Callable, kwargs: dict): """ Filter the dataframe using condition from given function. @@ -466,13 +467,13 @@ def _clean_gps(move_data: DataFrame, f: Callable, **kwargs): def clean_gps_jumps_by_distance( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TRAJ_ID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TRAJ_ID, jump_coefficient: float = 3.0, threshold: float = 1, label_dtype: Callable = np.float64, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Removes the trajectories points that are outliers from the dataframe. @@ -524,12 +525,12 @@ def clean_gps_jumps_by_distance( def clean_gps_nearby_points_by_distances( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TRAJ_ID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TRAJ_ID, radius_area: float = 10.0, label_dtype: Callable = np.float64, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Removes points from the trajectories with smaller distance from the point before. @@ -579,12 +580,12 @@ def clean_gps_nearby_points_by_distances( def clean_gps_nearby_points_by_speed( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TRAJ_ID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TRAJ_ID, speed_radius: float = 0.0, label_dtype: Callable = np.float64, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Removes points from the trajectories with smaller speed of travel. @@ -634,12 +635,12 @@ def clean_gps_nearby_points_by_speed( def clean_gps_speed_max_radius( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TRAJ_ID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TRAJ_ID, speed_max: float = 50.0, label_dtype: Callable = np.float64, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Removes trajectories points with higher speed. @@ -698,11 +699,11 @@ def clean_gps_speed_max_radius( def clean_trajectories_with_few_points( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_tid: Text = TID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_tid: str = TID, min_points_per_trajectory: int = 2, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Removes from the given dataframe, trajectories with fewer points. @@ -775,13 +776,13 @@ def clean_trajectories_with_few_points( def clean_trajectories_short_and_few_points( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TID, min_trajectory_distance: float = 100, min_points_per_trajectory: int = 2, label_dtype: Callable = np.float64, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Eliminates from the given dataframe trajectories with fewer points and shorter length. @@ -863,12 +864,12 @@ def clean_trajectories_short_and_few_points( def clean_id_by_time_max( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TRAJ_ID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TRAJ_ID, time_max: float = 3600, label_dtype: Callable = np.float64, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Clears GPS points with time by ID greater than a user-defined limit. @@ -909,7 +910,7 @@ def clean_id_by_time_max( move_dataid_drop = ( move_data.groupby([label_id], as_index=False) .agg({TIME_TO_PREV: 'sum'}) - .query('%s < %s' % (TIME_TO_PREV, time_max)) + .query(f'{TIME_TO_PREV} < {time_max}') ) logger.debug( '...Ids total: %s\nIds to drop:%s' diff --git a/pymove/preprocessing/segmentation.py b/pymove/preprocessing/segmentation.py index 59c94f01..4dfcfe37 100644 --- a/pymove/preprocessing/segmentation.py +++ b/pymove/preprocessing/segmentation.py @@ -8,8 +8,9 @@ by_max_speed """ +from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Text, Tuple, Union +from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -34,7 +35,7 @@ @timer_decorator -def bbox_split(bbox: Tuple[int, int, int, int], number_grids: int) -> DataFrame: +def bbox_split(bbox: tuple[int, int, int, int], number_grids: int) -> DataFrame: """ Splits the bounding box in N grids of the same size. @@ -60,7 +61,7 @@ def bbox_split(bbox: Tuple[int, int, int, int], number_grids: int) -> DataFrame: const_lat = abs(abs(lat_max) - abs(lat_min)) / number_grids const_lon = abs(abs(lon_max) - abs(lon_min)) / number_grids - logger.debug('const_lat: {}\nconst_lon: {}'.format(const_lat, const_lon)) + logger.debug(f'const_lat: {const_lat}\nconst_lon: {const_lon}') move_data = pd.DataFrame( columns=['lat_min', 'lon_min', 'lat_max', 'lon_max'] @@ -79,7 +80,7 @@ def bbox_split(bbox: Tuple[int, int, int, int], number_grids: int) -> DataFrame: return move_data -def _drop_single_point(move_data: DataFrame, label_new_tid: Text, label_id: Text): +def _drop_single_point(move_data: DataFrame, label_new_tid: str, label_id: str): """ Removes trajectory with single point. @@ -147,7 +148,7 @@ def _filter_and_dist_time_speed( def _filter_or_dist_time_speed( - move_data: DataFrame, idx: int, feature: Text, max_between_adj_points: float + move_data: DataFrame, idx: int, feature: str, max_between_adj_points: float ) -> ndarray: """ Filters the dataframe considering thresholds for time, dist and speed. @@ -172,7 +173,7 @@ def _filter_or_dist_time_speed( return np.nan_to_num(move_data.at[idx, feature]) > max_between_adj_points -def _prepare_segmentation(move_data: DataFrame, label_id: Text, label_new_tid: Text): +def _prepare_segmentation(move_data: DataFrame, label_id: str, label_new_tid: str): """ Resets the dataframe index, collects unique ids and initiates curr_id and count. @@ -196,7 +197,7 @@ def _prepare_segmentation(move_data: DataFrame, label_id: Text, label_new_tid: T """ if move_data.index.name is None: - logger.debug('...setting {} as index'.format(label_id)) + logger.debug(f'...setting {label_id} as index') move_data.set_index(label_id, inplace=True) curr_tid = 0 if label_new_tid not in move_data: @@ -209,8 +210,8 @@ def _prepare_segmentation(move_data: DataFrame, label_id: Text, label_new_tid: T def _update_curr_tid_count( filter_: ndarray, move_data: DataFrame, idx: int, - label_new_tid: Text, curr_tid: int, count: int -) -> Tuple[int, int]: + label_new_tid: str, curr_tid: int, count: int +) -> tuple[int, int]: """ Updates the tid. @@ -239,7 +240,7 @@ def _update_curr_tid_count( """ curr_tid += 1 if filter_.shape == (): - logger.debug('id: {} has no point to split'.format(idx)) + logger.debug(f'id: {idx} has no point to split') move_data.at[idx, label_new_tid] = curr_tid count += 1 else: @@ -255,7 +256,7 @@ def _update_curr_tid_count( def _filter_by( - move_data: DataFrame, label_id: Text, label_new_tid: Text, + move_data: DataFrame, label_id: str, label_new_tid: str, drop_single_points: bool, **kwargs ) -> DataFrame: """ @@ -333,15 +334,15 @@ def _filter_by( @timer_decorator def by_dist_time_speed( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TRAJ_ID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TRAJ_ID, max_dist_between_adj_points: float = 3000, max_time_between_adj_points: float = 900, max_speed_between_adj_points: float = 50.0, drop_single_points: bool = True, - label_new_tid: Text = TID_PART, + label_new_tid: str = TID_PART, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Splits the trajectories into segments based on distance, time and speed. @@ -413,13 +414,13 @@ def by_dist_time_speed( @timer_decorator def by_max_dist( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TRAJ_ID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TRAJ_ID, max_dist_between_adj_points: float = 3000, drop_single_points: bool = True, - label_new_tid: Text = TID_DIST, + label_new_tid: str = TID_DIST, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Segments the trajectories based on distance. @@ -480,13 +481,13 @@ def by_max_dist( @timer_decorator def by_max_time( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TRAJ_ID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TRAJ_ID, max_time_between_adj_points: float = 900.0, drop_single_points: bool = True, - label_new_tid: Text = TID_TIME, + label_new_tid: str = TID_TIME, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Splits the trajectories into segments based on a maximum. @@ -548,13 +549,13 @@ def by_max_time( @timer_decorator def by_max_speed( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_id: Text = TRAJ_ID, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_id: str = TRAJ_ID, max_speed_between_adj_points: float = 50.0, drop_single_points: bool = True, - label_new_tid: Text = TID_SPEED, + label_new_tid: str = TID_SPEED, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Splits the trajectories into segments based on a maximum speed. diff --git a/pymove/preprocessing/stay_point_detection.py b/pymove/preprocessing/stay_point_detection.py index ba819f5f..a7631699 100644 --- a/pymove/preprocessing/stay_point_detection.py +++ b/pymove/preprocessing/stay_point_detection.py @@ -5,8 +5,9 @@ create_or_update_move_and_stop_by_radius """ +from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Text, Union +from typing import TYPE_CHECKING import numpy as np @@ -29,13 +30,13 @@ @timer_decorator def create_or_update_move_stop_by_dist_time( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', dist_radius: float = 30, time_radius: float = 900, - label_id: Text = TRAJ_ID, - new_label: Text = SEGMENT_STOP, + label_id: str = TRAJ_ID, + new_label: str = SEGMENT_STOP, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Determines the stops and moves points of the dataframe. @@ -97,7 +98,7 @@ def create_or_update_move_stop_by_dist_time( move_dataagg_tid = ( move_data.groupby(by=new_label) .agg({TIME_TO_PREV: 'sum'}) - .query('%s > %s' % (TIME_TO_PREV, time_radius)) + .query(f'{TIME_TO_PREV} > {time_radius}') .index ) idx = move_data[ @@ -112,12 +113,12 @@ def create_or_update_move_stop_by_dist_time( @timer_decorator def create_or_update_move_and_stop_by_radius( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', radius: float = 0, - target_label: Text = DIST_TO_PREV, - new_label: Text = SITUATION, + target_label: str = DIST_TO_PREV, + new_label: str = SITUATION, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Finds the stops and moves points of the dataframe. diff --git a/pymove/query/query.py b/pymove/query/query.py index 3ea17ee2..aa02915b 100644 --- a/pymove/query/query.py +++ b/pymove/query/query.py @@ -5,8 +5,7 @@ knn_query """ - -from typing import Text +from __future__ import annotations import numpy as np import pandas as pd @@ -20,12 +19,12 @@ def range_query( traj: DataFrame, move_df: DataFrame, - _id: Text = TRAJ_ID, + _id: str = TRAJ_ID, min_dist: float = 1000, - distance: Text = MEDP, - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE, - datetime: Text = DATETIME + distance: str = MEDP, + latitude: str = LATITUDE, + longitude: str = LONGITUDE, + datetime: str = DATETIME ) -> DataFrame: """ Returns all trajectories that have a distance equal to or less than the trajectory. @@ -81,7 +80,7 @@ def dist_measure(traj, this, latitude, longitude, datetime): raise ValueError('Unknown distance measure. Use MEDP or MEDT') for traj_id in progress_bar( - move_df[_id].unique(), desc='Querying range by {}'.format(distance) + move_df[_id].unique(), desc=f'Querying range by {distance}' ): this = move_df.loc[move_df[_id] == traj_id] if dist_measure(traj, this, latitude, longitude, datetime) < min_dist: @@ -94,11 +93,11 @@ def knn_query( traj: DataFrame, move_df: DataFrame, k: int = 5, - id_: Text = TRAJ_ID, - distance: Text = MEDP, - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE, - datetime: Text = DATETIME + id_: str = TRAJ_ID, + distance: str = MEDP, + latitude: str = LATITUDE, + longitude: str = LONGITUDE, + datetime: str = DATETIME ) -> DataFrame: """ Returns the k neighboring trajectories closest to the trajectory. @@ -154,7 +153,7 @@ def dist_measure(traj, this, latitude, longitude, datetime): raise ValueError('Unknown distance measure. Use MEDP or MEDT') for traj_id in progress_bar( - move_df[id_].unique(), desc='Querying knn by {}'.format(distance) + move_df[id_].unique(), desc=f'Querying knn by {distance}' ): if (traj_id != traj[id_].values[0]): this = move_df.loc[move_df[id_] == traj_id] diff --git a/pymove/semantic/semantic.py b/pymove/semantic/semantic.py index ab69f96d..5f7e5d65 100644 --- a/pymove/semantic/semantic.py +++ b/pymove/semantic/semantic.py @@ -12,8 +12,9 @@ filter_longer_time_to_stop_segment_by_id """ +from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Text, Tuple, Union +from typing import TYPE_CHECKING import numpy as np from pandas import DataFrame @@ -42,8 +43,8 @@ def _end_create_operation( - move_data: DataFrame, new_label: Text, inplace: bool -) -> Optional[DataFrame]: + move_data: DataFrame, new_label: str, inplace: bool +) -> DataFrame | None: """ Returns the dataframe after create operation. @@ -69,8 +70,8 @@ def _end_create_operation( def _process_simple_filter( - move_data: DataFrame, new_label: Text, feature: Text, value: float, inplace: bool -) -> Optional[DataFrame]: + move_data: DataFrame, new_label: str, feature: str, value: float, inplace: bool +) -> DataFrame | None: """ Processes create operation with simple filter. @@ -108,12 +109,12 @@ def _process_simple_filter( @timer_decorator def outliers( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', jump_coefficient: float = 3.0, threshold: float = 1, - new_label: Text = OUTLIER, + new_label: str = OUTLIER, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Create or update a boolean feature to detect outliers. @@ -173,10 +174,10 @@ def outliers( @timer_decorator def create_or_update_out_of_the_bbox( move_data: DataFrame, - bbox: Tuple[int, int, int, int], - new_label: Text = OUT_BBOX, + bbox: tuple[int, int, int, int], + new_label: str = OUT_BBOX, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Create or update a boolean feature to detect points out of the bbox. @@ -229,11 +230,11 @@ def create_or_update_out_of_the_bbox( @timer_decorator def create_or_update_gps_deactivated_signal( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', max_time_between_adj_points: float = 7200, - new_label: Text = DEACTIVATED, + new_label: str = DEACTIVATED, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Creates a new feature that inform if point invalid. @@ -279,11 +280,11 @@ def create_or_update_gps_deactivated_signal( @timer_decorator def create_or_update_gps_jump( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', max_dist_between_adj_points: float = 3000, - new_label: Text = JUMP, + new_label: str = JUMP, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Creates a new feature that inform if point is a gps jump. @@ -328,15 +329,15 @@ def create_or_update_gps_jump( @timer_decorator def create_or_update_short_trajectory( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', max_dist_between_adj_points: float = 3000, max_time_between_adj_points: float = 7200, max_speed_between_adj_points: float = 50, k_segment_max: int = 50, - label_tid: Text = TID_PART, - new_label: Text = SHORT, + label_tid: str = TID_PART, + new_label: str = SHORT, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Creates a new feature that inform if point belongs to a short trajectory. @@ -397,12 +398,12 @@ def create_or_update_short_trajectory( @timer_decorator def create_or_update_gps_block_signal( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', max_time_stop: float = 7200, - new_label: Text = BLOCK, - label_tid: Text = TID_PART, + new_label: str = BLOCK, + label_tid: str = TID_PART, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Creates a new feature that inform segments with periods without moving. @@ -460,13 +461,13 @@ def create_or_update_gps_block_signal( @timer_decorator def filter_block_signal_by_repeated_amount_of_points( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', amount_max_of_points_stop: float = 30.0, max_time_stop: float = 7200, filter_out: bool = False, - label_tid: Text = TID_PART, + label_tid: str = TID_PART, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Filters from dataframe points with blocked signal by amount of points. @@ -521,12 +522,12 @@ def filter_block_signal_by_repeated_amount_of_points( @timer_decorator def filter_block_signal_by_time( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', max_time_stop: float = 7200, filter_out: bool = False, - label_tid: Text = TID_PART, + label_tid: str = TID_PART, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Filters from dataframe points with blocked signal by time. @@ -582,14 +583,14 @@ def filter_block_signal_by_time( @timer_decorator def filter_longer_time_to_stop_segment_by_id( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', dist_radius: float = 30, time_radius: float = 900, - label_id: Text = TRAJ_ID, - label_segment_stop: Text = SEGMENT_STOP, + label_id: str = TRAJ_ID, + label_segment_stop: str = SEGMENT_STOP, filter_out: bool = False, inplace: bool = False -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Filters from dataframe segment with longest stop time. diff --git a/pymove/tests/test_visualization_matplotlib.py b/pymove/tests/test_visualization_matplotlib.py index 8b4e49f6..ddf0644e 100644 --- a/pymove/tests/test_visualization_matplotlib.py +++ b/pymove/tests/test_visualization_matplotlib.py @@ -67,7 +67,7 @@ def test_show_object_id_by_date(tmpdir): def test_plot_trajectory_by_id(tmpdir): move_df = _default_move_df() - move_df[TID] = ['1', '1', '2', '2', '2'] + move_df[TRAJ_ID] = ['1', '1', '2', '2', '2'] d = tmpdir.mkdir('visualization') diff --git a/pymove/utils/conversions.py b/pymove/utils/conversions.py index 9cb45b84..fd39efbd 100644 --- a/pymove/utils/conversions.py +++ b/pymove/utils/conversions.py @@ -24,10 +24,10 @@ hours_to_seconds """ - +from __future__ import annotations import math -from typing import TYPE_CHECKING, List, Optional, Text, Union +from typing import TYPE_CHECKING import numpy as np from numpy import ndarray @@ -109,7 +109,7 @@ def meters_to_eps( return radius_meters / earth_radius -def list_to_str(input_list: List, delimiter: Text = ',') -> Text: +def list_to_str(input_list: list, delimiter: str = ',') -> str: """ Concatenates a list elements, joining them by the separator `delimiter`. @@ -138,7 +138,7 @@ def list_to_str(input_list: List, delimiter: Text = ',') -> Text: ) -def list_to_csv_str(input_list: List) -> Text: +def list_to_csv_str(input_list: list) -> str: """ Concatenates the elements of the list, joining them by ",". @@ -163,7 +163,7 @@ def list_to_csv_str(input_list: List) -> Text: return list_to_str(input_list) -def list_to_svm_line(original_list: List) -> Text: +def list_to_svm_line(original_list: list) -> str: """ Concatenates list elements in consecutive element pairs. @@ -188,11 +188,11 @@ def list_to_svm_line(original_list: List) -> Text: list_size = len(original_list) svm_line = '%s ' % original_list[0] for i in range(1, list_size): - svm_line += '%s:%s ' % (i, original_list[i]) + svm_line += f'{i}:{original_list[i]} ' return svm_line.rstrip() -def lon_to_x_spherical(lon: Union[float, ndarray]) -> Union[float, ndarray]: +def lon_to_x_spherical(lon: float | ndarray) -> float | ndarray: """ Convert longitude to X EPSG:3857 WGS 84/Pseudo-Mercator. @@ -222,7 +222,7 @@ def lon_to_x_spherical(lon: Union[float, ndarray]) -> Union[float, ndarray]: return 6378137 * np.radians(lon) -def lat_to_y_spherical(lat: Union[float, ndarray]) -> Union[float, ndarray]: +def lat_to_y_spherical(lat: float | ndarray) -> float | ndarray: """ Convert latitude to Y EPSG:3857 WGS 84/Pseudo-Mercator. @@ -252,7 +252,7 @@ def lat_to_y_spherical(lat: Union[float, ndarray]) -> Union[float, ndarray]: return 6378137 * np.log(np.tan(np.pi / 4 + np.radians(lat) / 2.0)) -def x_to_lon_spherical(x: Union[float, ndarray]) -> Union[float, ndarray]: +def x_to_lon_spherical(x: float | ndarray) -> float | ndarray: """ Convert X EPSG:3857 WGS 84 / Pseudo-Mercator to longitude. @@ -281,7 +281,7 @@ def x_to_lon_spherical(x: Union[float, ndarray]) -> Union[float, ndarray]: return np.degrees(x / 6378137.0) -def y_to_lat_spherical(y: Union[float, ndarray]) -> Union[float, ndarray]: +def y_to_lat_spherical(y: float | ndarray) -> float | ndarray: """ Convert Y EPSG:3857 WGS 84 / Pseudo-Mercator to latitude. @@ -312,7 +312,7 @@ def y_to_lat_spherical(y: Union[float, ndarray]) -> Union[float, ndarray]: def geometry_points_to_lat_and_lon( move_data: DataFrame, - geometry_label: Text = GEOMETRY, + geometry_label: str = GEOMETRY, drop_geometry: bool = False, inplace: bool = False ) -> DataFrame: @@ -369,8 +369,8 @@ def geometry_points_to_lat_and_lon( def lat_and_lon_decimal_degrees_to_decimal( move_data: DataFrame, - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE + latitude: str = LATITUDE, + longitude: str = LONGITUDE ) -> DataFrame: """ Converts latitude and longitude format from decimal degrees to decimal format. @@ -419,11 +419,11 @@ def _decimal_degree_to_decimal(row): def ms_to_kmh( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_speed: Text = SPEED_TO_PREV, - new_label: Text = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_speed: str = SPEED_TO_PREV, + new_label: str = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in ms, in label_speed column to kmh. @@ -496,11 +496,11 @@ def ms_to_kmh( def kmh_to_ms( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_speed: Text = SPEED_TO_PREV, - new_label: Optional[Text] = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_speed: str = SPEED_TO_PREV, + new_label: str | None = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in kmh, in label_speed column to ms. @@ -567,11 +567,11 @@ def kmh_to_ms( def meters_to_kilometers( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_distance: Text = DIST_TO_PREV, - new_label: Optional[Text] = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_distance: str = DIST_TO_PREV, + new_label: str | None = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in meters, in label_distance column to kilometers. @@ -637,11 +637,11 @@ def meters_to_kilometers( def kilometers_to_meters( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_distance: Text = DIST_TO_PREV, - new_label: Optional[Text] = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_distance: str = DIST_TO_PREV, + new_label: str | None = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in kilometers, in label_distance column to meters. @@ -708,11 +708,11 @@ def kilometers_to_meters( def seconds_to_minutes( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_time: Text = TIME_TO_PREV, - new_label: Optional[Text] = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_time: str = TIME_TO_PREV, + new_label: str | None = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in seconds, in label_distance column to minutes. @@ -778,11 +778,11 @@ def seconds_to_minutes( def minute_to_seconds( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_time: Text = TIME_TO_PREV, - new_label: Optional[Text] = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_time: str = TIME_TO_PREV, + new_label: str | None = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in minutes, in label_distance column to seconds. @@ -849,11 +849,11 @@ def minute_to_seconds( def minute_to_hours( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_time: Text = TIME_TO_PREV, - new_label: Optional[Text] = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_time: str = TIME_TO_PREV, + new_label: str | None = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in minutes, in label_distance column to hours. @@ -921,11 +921,11 @@ def minute_to_hours( def hours_to_minute( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_time: Text = TIME_TO_PREV, - new_label: Optional[Text] = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_time: str = TIME_TO_PREV, + new_label: str | None = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in hours, in label_distance column to minute. @@ -992,11 +992,11 @@ def hours_to_minute( def seconds_to_hours( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_time: Text = TIME_TO_PREV, - new_label: Optional[Text] = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_time: str = TIME_TO_PREV, + new_label: str | None = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in seconds, in label_distance column to hours. @@ -1063,11 +1063,11 @@ def seconds_to_hours( def hours_to_seconds( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - label_time: Text = TIME_TO_PREV, - new_label: Optional[Text] = None, + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + label_time: str = TIME_TO_PREV, + new_label: str | None = None, inplace: bool = False, -) -> Optional[Union['PandasMoveDataFrame', 'DaskMoveDataFrame']]: +) -> 'PandasMoveDataFrame' | 'DaskMoveDataFrame' | None: """ Convert values, in hours, in label_distance column to seconds. diff --git a/pymove/utils/data_augmentation.py b/pymove/utils/data_augmentation.py index d5f2e1b5..1bbb2771 100644 --- a/pymove/utils/data_augmentation.py +++ b/pymove/utils/data_augmentation.py @@ -11,8 +11,9 @@ instance_crossover_augmentation """ +from __future__ import annotations -from typing import TYPE_CHECKING, Dict, List, Optional, Text, Tuple, Union +from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -29,8 +30,8 @@ def append_row( data: DataFrame, - row: Optional[Series] = None, - columns: Optional[Dict] = None + row: Series | None = None, + columns: dict | None = None ): """ Insert a new line in the dataframe with the information passed by parameter. @@ -56,7 +57,7 @@ def append_row( def generate_trajectories_df( - data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'] + data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame' ) -> DataFrame: """ Generates a dataframe with the sequence of location points of a trajectory. @@ -105,7 +106,7 @@ def generate_trajectories_df( def generate_start_feature( - data: DataFrame, label_trajectory: Text = TRAJECTORY + data: DataFrame, label_trajectory: str = TRAJECTORY ): """ Removes the last point from the trajectory and adds it in a new column 'destiny'. @@ -125,7 +126,7 @@ def generate_start_feature( def generate_destiny_feature( - data: DataFrame, label_trajectory: Text = TRAJECTORY + data: DataFrame, label_trajectory: str = TRAJECTORY ): """ Removes the first point from the trajectory and adds it in a new column 'start'. @@ -145,8 +146,8 @@ def generate_destiny_feature( def split_crossover( - sequence_a: List, sequence_b: List, frac: float = 0.5 -) -> Tuple[List, List]: + sequence_a: list, sequence_b: list, frac: float = 0.5 +) -> tuple[list, list]: """ Divides two arrays in the indicated ratio and exchange their halves. @@ -239,9 +240,9 @@ def _augmentation(data: DataFrame, aug_df: DataFrame, frac: float = 0.5): def augmentation_trajectories_df( - data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - restriction: Text = 'destination only', - label_trajectory: Text = TRAJECTORY, + data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + restriction: str = 'destination only', + label_trajectory: str = TRAJECTORY, insert_at_df: bool = False, frac: float = 0.5, ) -> DataFrame: @@ -336,8 +337,8 @@ def insert_points_in_df(data: DataFrame, aug_df: DataFrame): def instance_crossover_augmentation( data: DataFrame, - restriction: Text = 'destination only', - label_trajectory: Text = TRAJECTORY, + restriction: str = 'destination only', + label_trajectory: str = TRAJECTORY, frac: float = 0.5 ): """ diff --git a/pymove/utils/datetime.py b/pymove/utils/datetime.py index 7bc6a61a..9ea32068 100644 --- a/pymove/utils/datetime.py +++ b/pymove/utils/datetime.py @@ -21,9 +21,9 @@ threshold_time_statistics """ +from __future__ import annotations from datetime import datetime -from typing import Optional, Text, Union import holidays from pandas import DataFrame, Timestamp @@ -44,7 +44,7 @@ ) -def date_to_str(dt: datetime) -> Text: +def date_to_str(dt: datetime) -> str: """ Get date, in string format, from timestamp. @@ -73,7 +73,7 @@ def date_to_str(dt: datetime) -> Text: return dt.strftime('%Y-%m-%d') -def str_to_datetime(dt_str: Text) -> datetime: +def str_to_datetime(dt_str: str) -> datetime: """ Converts a datetime in string format to datetime format. @@ -107,7 +107,7 @@ def str_to_datetime(dt_str: Text) -> datetime: return datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S') -def datetime_to_str(dt: datetime) -> Text: +def datetime_to_str(dt: datetime) -> str: """ Converts a date in datetime format to string format. @@ -221,9 +221,9 @@ def to_day_of_week_int(dt: datetime) -> int: def working_day( - dt: Union[Text, datetime], - country: Text = 'BR', - state: Optional[Text] = None + dt: str | datetime, + country: str = 'BR', + state: str | None = None ) -> bool: """ Indices if a day specified by the user is a working day. @@ -280,7 +280,7 @@ def working_day( return result -def now_str() -> Text: +def now_str() -> str: """ Get datetime of now. @@ -298,7 +298,7 @@ def now_str() -> Text: return datetime_to_str(datetime.now()) -def deltatime_str(deltatime_seconds: float) -> Text: +def deltatime_str(deltatime_seconds: float) -> str: """ Convert time in a format appropriate of time. @@ -327,14 +327,14 @@ def deltatime_str(deltatime_seconds: float) -> Text: hours, rem = divmod(deltatime_seconds, 3600) minutes, seconds = divmod(rem, 60) if hours: - return '{:0>2}h:{:0>2}m:{:05.2f}s'.format(int(hours), int(minutes), seconds) + return f'{int(hours):0>2}h:{int(minutes):0>2}m:{seconds:05.2f}s' elif minutes: - return '{:0>2}m:{:05.2f}s'.format(int(minutes), seconds) + return f'{int(minutes):0>2}m:{seconds:05.2f}s' else: - return '{:05.2f}s'.format(seconds) + return f'{seconds:05.2f}s' -def timestamp_to_millis(timestamp: Text) -> int: +def timestamp_to_millis(timestamp: str) -> int: """ Converts a local datetime to a POSIX timestamp in milliseconds (like in Java). @@ -380,7 +380,7 @@ def millis_to_timestamp(milliseconds: float) -> Timestamp: return Timestamp(milliseconds, unit='ms') -def time_to_str(time: Timestamp) -> Text: +def time_to_str(time: Timestamp) -> str: """ Get time, in string format, from timestamp. @@ -403,7 +403,7 @@ def time_to_str(time: Timestamp) -> Text: return time.strftime('%H:%M:%S') -def str_to_time(dt_str: Text) -> datetime: +def str_to_time(dt_str: str) -> datetime: """ Converts a time in string format "%H:%M:%S" to datetime format. @@ -491,10 +491,10 @@ def create_time_slot_in_minute( data: DataFrame, slot_interval: int = 15, initial_slot: int = 0, - label_datetime: Text = DATETIME, - label_time_slot: Text = TIME_SLOT, + label_datetime: str = DATETIME, + label_time_slot: str = TIME_SLOT, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Partitions the time in slot windows. @@ -537,7 +537,7 @@ def create_time_slot_in_minute( 3 39.984224 116.319402 2008-10-23 06:10:15 1 24 """ if data.dtypes[label_datetime] != 'datetime64[ns]': - raise ValueError('{} colum must be of type datetime'.format(label_datetime)) + raise ValueError(f'{label_datetime} colum must be of type datetime') if not inplace: data = data.copy() minute_day = data[label_datetime].dt.hour * 60 + data[label_datetime].dt.minute @@ -548,7 +548,7 @@ def create_time_slot_in_minute( def generate_time_statistics( data: DataFrame, - local_label: Text = LOCAL_LABEL + local_label: str = LOCAL_LABEL ): """ Calculates time statistics of the pairwise local labels. @@ -631,7 +631,7 @@ def _calc_time_threshold(seg_mean: float, seg_std: float) -> float: 0.0 """ threshold = seg_std + seg_mean - threshold = float('{:.1f}'.format(threshold)) + threshold = float(f'{threshold:.1f}') return threshold @@ -640,7 +640,7 @@ def threshold_time_statistics( mean_coef: float = 1.0, std_coef: float = 1.0, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Calculates and creates the threshold column. diff --git a/pymove/utils/distances.py b/pymove/utils/distances.py index ce27dddc..5c81711f 100644 --- a/pymove/utils/distances.py +++ b/pymove/utils/distances.py @@ -8,7 +8,7 @@ medt """ -from typing import Text, Union +from __future__ import annotations import numpy as np import pandas as pd @@ -21,13 +21,13 @@ def haversine( - lat1: Union[float, ndarray], - lon1: Union[float, ndarray], - lat2: Union[float, ndarray], - lon2: Union[float, ndarray], + lat1: float | ndarray, + lon1: float | ndarray, + lat2: float | ndarray, + lon2: float | ndarray, to_radians: bool = True, earth_radius: float = EARTH_RADIUS -) -> Union[float, ndarray]: +) -> float | ndarray: """ Calculates the great circle distance between two points on the earth. @@ -83,11 +83,11 @@ def haversine( def euclidean_distance_in_meters( - lat1: Union[float, ndarray], - lon1: Union[float, ndarray], - lat2: Union[float, ndarray], - lon2: Union[float, ndarray] -) -> Union[float, ndarray]: + lat1: float | ndarray, + lon1: float | ndarray, + lat2: float | ndarray, + lon2: float | ndarray +) -> float | ndarray: """ Calculate the euclidean distance in meters between two points. @@ -130,8 +130,8 @@ def euclidean_distance_in_meters( def nearest_points( traj1: DataFrame, traj2: DataFrame, - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE, + latitude: str = LATITUDE, + longitude: str = LONGITUDE, ) -> DataFrame: """ Returns the point closest to another trajectory based on the Euclidean distance. @@ -191,8 +191,8 @@ def nearest_points( def medp( traj1: DataFrame, traj2: DataFrame, - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE + latitude: str = LATITUDE, + longitude: str = LONGITUDE ) -> float: """ Returns the Mean Euclidian Distance Predictive between two trajectories. @@ -243,9 +243,9 @@ def medp( def medt( traj1: DataFrame, traj2: DataFrame, - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE, - datetime: Text = DATETIME + latitude: str = LATITUDE, + longitude: str = LONGITUDE, + datetime: str = DATETIME ) -> float: """ Returns the Mean Euclidian Distance Trajectory between two trajectories. diff --git a/pymove/utils/geoutils.py b/pymove/utils/geoutils.py index 6137e6fd..c7c5086b 100644 --- a/pymove/utils/geoutils.py +++ b/pymove/utils/geoutils.py @@ -7,8 +7,7 @@ decode_geohash_to_latlon, """ - -from typing import Text, Tuple +from __future__ import annotations import geohash2 as gh import numpy as np @@ -30,15 +29,13 @@ BINARY = [ np.asarray( - list('{0:05b}'.format(x)), dtype=int + list(f'{x:05b}'), dtype=int ) for x in range(0, len(BASE_32)) ] - - BASE_32_TO_BIN = dict(zip(BASE_32, BINARY)) -def v_color(ob: BaseGeometry) -> Text: +def v_color(ob: BaseGeometry) -> str: """ Returns '#ffcc33' if object crosses otherwise it returns '#6699cc'. @@ -69,7 +66,7 @@ def v_color(ob: BaseGeometry) -> Text: return COLORS[ob.is_simple + 33] -def _encode(lat: float, lon: float, precision: float = 15) -> Text: +def _encode(lat: float, lon: float, precision: float = 15) -> str: """ Encodes latitude/longitude to geohash. @@ -102,7 +99,7 @@ def _encode(lat: float, lon: float, precision: float = 15) -> Text: return gh.encode(lat, lon, precision) -def _decode(geohash: Text) -> Tuple[float, float]: +def _decode(geohash: str) -> tuple[float, float]: """ Decode geohash to latitude/longitude. @@ -169,7 +166,7 @@ def _bin_geohash(lat: float, lon: float, precision: float = 15) -> ndarray: def _reset_and_create_arrays_none( data: DataFrame, reset_index: bool = True -) -> Tuple[ndarray, ndarray, ndarray, ndarray]: +) -> tuple[ndarray, ndarray, ndarray, ndarray]: """ Reset the df index and create arrays of none values. @@ -310,7 +307,7 @@ def create_bin_geohash_df(data: DataFrame, precision: float = 15): def decode_geohash_to_latlon( data: DataFrame, - label_geohash: Text = GEOHASH, + label_geohash: str = GEOHASH, reset_index: bool = True ): """ @@ -350,7 +347,7 @@ def decode_geohash_to_latlon( 4 39.984217 116.319422 wx4eqyvhyyr2yy8 39.984217 116.319422 """ if label_geohash not in data: - raise ValueError('feature {} not in df'.format(label_geohash)) + raise ValueError(f'feature {label_geohash} not in df') lat, lon, _, _ = _reset_and_create_arrays_none(data, reset_index=reset_index) diff --git a/pymove/utils/integration.py b/pymove/utils/integration.py index 0cedb7ff..44052cef 100644 --- a/pymove/utils/integration.py +++ b/pymove/utils/integration.py @@ -15,9 +15,9 @@ merge_home_with_poi """ +from __future__ import annotations from collections import namedtuple -from typing import List, Optional, Text, Tuple import numpy as np from numpy import ndarray @@ -50,10 +50,10 @@ def union_poi_bank( data: DataFrame, - label_poi: Text = TYPE_POI, - banks: Optional[List[Text]] = None, + label_poi: str = TYPE_POI, + banks: list[str] | None = None, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Performs the union between the different bank categories. @@ -114,7 +114,7 @@ def union_poi_bank( if not inplace: data = data.copy() logger.debug('union bank categories to one category') - logger.debug('... There are {} -- {}'.format(data[label_poi].nunique(), label_poi)) + logger.debug(f'... There are {data[label_poi].nunique()} -- {label_poi}') if banks is None: banks = [ 'bancos_filiais', @@ -131,10 +131,10 @@ def union_poi_bank( def union_poi_bus_station( data: DataFrame, - label_poi: Text = TYPE_POI, - bus_stations: Optional[List[Text]] = None, + label_poi: str = TYPE_POI, + bus_stations: list[str] | None = None, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Performs the union between the different bus station categories. @@ -203,10 +203,10 @@ def union_poi_bus_station( def union_poi_bar_restaurant( data: DataFrame, - label_poi: Text = TYPE_POI, - bar_restaurant: Optional[List[Text]] = None, + label_poi: str = TYPE_POI, + bar_restaurant: list[str] | None = None, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Performs the union between bar and restaurant categories. @@ -270,10 +270,10 @@ def union_poi_bar_restaurant( def union_poi_parks( data: DataFrame, - label_poi: Text = TYPE_POI, - parks: Optional[List[Text]] = None, + label_poi: str = TYPE_POI, + parks: list[str] | None = None, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Performs the union between park categories. @@ -337,10 +337,10 @@ def union_poi_parks( def union_poi_police( data: DataFrame, - label_poi: Text = TYPE_POI, - police: Optional[List[Text]] = None, + label_poi: str = TYPE_POI, + police: list[str] | None = None, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Performs the union between police categories. @@ -405,9 +405,9 @@ def union_poi_police( def join_collective_areas( data: DataFrame, areas: DataFrame, - label_geometry: Text = GEOMETRY, + label_geometry: str = GEOMETRY, inplace: bool = False -) -> Optional[DataFrame]: +) -> DataFrame | None: """ Performs the integration between trajectories and collective areas. @@ -487,7 +487,7 @@ def _reset_and_creates_id_and_lat_lon( df_pois: DataFrame, lat_lon_poi: bool = True, reset_index: bool = True -) -> Tuple[ndarray, ndarray, ndarray, ndarray, ndarray]: +) -> tuple[ndarray, ndarray, ndarray, ndarray, ndarray]: """ Resets the indexes of the dataframes. @@ -555,8 +555,8 @@ def _reset_and_creates_id_and_lat_lon( def _reset_set_window__and_creates_event_id_type( - data: DataFrame, df_events: DataFrame, time_window: float, label_date: Text = DATETIME -) -> Tuple[Series, Series, ndarray, ndarray, ndarray]: + data: DataFrame, df_events: DataFrame, time_window: float, label_date: str = DATETIME +) -> tuple[Series, Series, ndarray, ndarray, ndarray]: """ Resets the indexes of the dataframes. @@ -619,8 +619,8 @@ def _reset_set_window__and_creates_event_id_type( def _reset_set_window_and_creates_event_id_type_all( - data: DataFrame, df_events: DataFrame, time_window: float, label_date: Text = DATETIME -) -> Tuple[Series, Series, ndarray, ndarray, ndarray]: + data: DataFrame, df_events: DataFrame, time_window: float, label_date: str = DATETIME +) -> tuple[Series, Series, ndarray, ndarray, ndarray]: """ Resets the indexes of the dataframes. @@ -685,8 +685,8 @@ def _reset_set_window_and_creates_event_id_type_all( def join_with_pois( data: DataFrame, df_pois: DataFrame, - label_id: Text = TRAJ_ID, - label_poi_name: Text = NAME_POI, + label_id: str = TRAJ_ID, + label_poi_name: str = NAME_POI, reset_index: bool = True, inplace: bool = False ): @@ -806,8 +806,8 @@ def join_with_pois( def join_with_pois_by_category( data: DataFrame, df_pois: DataFrame, - label_category: Text = TYPE_POI, - label_id: Text = TRAJ_ID, + label_category: str = TYPE_POI, + label_id: str = TRAJ_ID, inplace: bool = False ): """ @@ -888,7 +888,7 @@ def join_with_pois_by_category( df_category = df_pois[df_pois[label_category] == c] df_category.reset_index(drop=True, inplace=True) - desc = 'computing dist to {} category ({}/{})'.format(c, i, size_categories) + desc = f'computing dist to {c} category ({i}/{size_categories})' for idx, row in progress_bar(data.iterrows(), total=len(data), desc=desc): lat_user = np.full( df_category.shape[0], row[LATITUDE], dtype=np.float64 @@ -923,10 +923,10 @@ def join_with_pois_by_category( def join_with_events( data: DataFrame, df_events: DataFrame, - label_date: Text = DATETIME, + label_date: str = DATETIME, time_window: int = 900, - label_event_id: Text = EVENT_ID, - label_event_type: Text = EVENT_TYPE, + label_event_id: str = EVENT_ID, + label_event_type: str = EVENT_TYPE, inplace: bool = False ): """ @@ -1070,9 +1070,9 @@ def join_with_events( def join_with_event_by_dist_and_time( data: DataFrame, df_events: DataFrame, - label_date: Text = DATETIME, - label_event_id: Text = EVENT_ID, - label_event_type: Text = EVENT_TYPE, + label_date: str = DATETIME, + label_event_id: str = EVENT_ID, + label_event_type: str = EVENT_TYPE, time_window: float = 3600, radius: float = 1000, inplace: bool = False @@ -1219,9 +1219,9 @@ def join_with_event_by_dist_and_time( def join_with_home_by_id( data: DataFrame, df_home: DataFrame, - label_id: Text = TRAJ_ID, - label_address: Text = ADDRESS, - label_city: Text = CITY, + label_id: str = TRAJ_ID, + label_address: str = ADDRESS, + label_city: str = CITY, drop_id_without_home: bool = False, inplace: bool = False ): @@ -1288,7 +1288,7 @@ def join_with_home_by_id( ids_without_home = [] if data.index.name is None: - logger.debug('...setting {} as index'.format(label_id)) + logger.debug(f'...setting {label_id} as index') data.set_index(label_id, inplace=True) for idx in progress_bar( @@ -1297,7 +1297,7 @@ def join_with_home_by_id( filter_home = df_home[label_id] == idx if df_home[filter_home].shape[0] == 0: - logger.debug('...id: {} has not HOME'.format(idx)) + logger.debug(f'...id: {idx} has not HOME') ids_without_home.append(idx) else: home = df_home[filter_home].iloc[0] @@ -1338,11 +1338,11 @@ def join_with_home_by_id( def merge_home_with_poi( data: DataFrame, - label_dist_poi: Text = DIST_POI, - label_name_poi: Text = NAME_POI, - label_id_poi: Text = ID_POI, - label_home: Text = HOME, - label_dist_home: Text = DIST_HOME, + label_dist_poi: str = DIST_POI, + label_name_poi: str = NAME_POI, + label_id_poi: str = ID_POI, + label_home: str = HOME, + label_dist_home: str = DIST_HOME, drop_columns: bool = True, inplace: bool = False ): diff --git a/pymove/utils/log.py b/pymove/utils/log.py index c8fed867..2603594c 100644 --- a/pymove/utils/log.py +++ b/pymove/utils/log.py @@ -6,12 +6,13 @@ timer_decorator """ +from __future__ import annotations import logging import os import time from functools import wraps -from typing import Callable, Iterable, Optional, Text +from typing import Callable, Iterable from IPython import get_ipython from IPython.display import display @@ -42,7 +43,7 @@ def wrapper(*args, **kwargs): t_start = time.time() result = func(*args, **kwargs) t_total = deltatime_str(time.time() - t_start) - message = '%s took %s' % (func.__name__, t_total) + message = f'{func.__name__} took {t_total}' logger.debug('{}\n{}\n{}'.format('*' * len(message), message, '*' * len(message))) return result @@ -51,9 +52,9 @@ def wrapper(*args, **kwargs): def _log_progress( sequence: Iterable, - desc: Optional[Text] = None, - total: Optional[int] = None, - miniters: Optional[int] = None + desc: str | None = None, + total: int | None = None, + miniters: int | None = None ): """ Make and display a progress bar. @@ -102,10 +103,10 @@ def _log_progress( for index, record in enumerate(sequence, 1): if index == 1 or index % miniters == 0: if is_iterator: - label.value = '%s: %s / ?' % (desc, index) + label.value = f'{desc}: {index} / ?' else: progress.value = index - label.value = u'%s: %s / %s' % (desc, index, total) + label.value = f'{desc}: {index} / {total}' yield record except Exception: progress.bar_style = 'danger' @@ -113,7 +114,7 @@ def _log_progress( else: progress.bar_style = 'success' progress.value = index - label.value = '%s: %s' % (desc, str(index or '?')) + label.value = '{}: {}'.format(desc, str(index or '?')) try: @@ -127,9 +128,9 @@ def _log_progress( def progress_bar( sequence: Iterable, - desc: Optional[Text] = None, - total: Optional[int] = None, - miniters: Optional[int] = None + desc: str | None = None, + total: int | None = None, + miniters: int | None = None ): """ Make and display a progress bar. diff --git a/pymove/utils/math.py b/pymove/utils/math.py index 14dd4b94..f03b3e80 100644 --- a/pymove/utils/math.py +++ b/pymove/utils/math.py @@ -11,12 +11,12 @@ interpolation """ +from __future__ import annotations import math -from typing import List, Optional, Tuple, Union -def is_number(value: Union[int, float, str]): +def is_number(value: int | float | str): """ Returns if value is numerical or not. @@ -49,7 +49,7 @@ def is_number(value: Union[int, float, str]): return True -def std(values_array: List[float]) -> float: +def std(values_array: list[float]) -> float: """ Compute standard deviation. @@ -77,12 +77,12 @@ def std(values_array: List[float]) -> float: """ size = len(values_array) mean = sum(values_array) / size - sum_sq = sum([(i - mean) * (i - mean) for i in values_array]) + sum_sq = sum((i - mean) * (i - mean) for i in values_array) return math.sqrt(sum_sq / size) -def avg_std(values_array: List[float]) -> Tuple[float, float]: +def avg_std(values_array: list[float]) -> tuple[float, float]: """ Compute the average of standard deviation. @@ -109,7 +109,7 @@ def avg_std(values_array: List[float]) -> Tuple[float, float]: return avg, std(values_array) -def std_sample(values_array: List[float]) -> float: +def std_sample(values_array: list[float]) -> float: """ Compute the standard deviation of sample. @@ -134,7 +134,7 @@ def std_sample(values_array: List[float]) -> float: return std(values_array) * math.sqrt(size / (size - 1)) -def avg_std_sample(values_array: List[float]) -> Tuple[float, float]: +def avg_std_sample(values_array: list[float]) -> tuple[float, float]: """ Compute the average of standard deviation of sample. @@ -162,7 +162,7 @@ def avg_std_sample(values_array: List[float]) -> Tuple[float, float]: def arrays_avg( - values_array: List[float], weights_array: Optional[List[float]] = None + values_array: list[float], weights_array: list[float] | None = None ) -> float: """ Computes the mean of the elements of the array. @@ -211,7 +211,7 @@ def arrays_avg( return result / n -def array_stats(values_array: List[float]) -> Tuple[float, float, int]: +def array_stats(values_array: list[float]) -> tuple[float, float, int]: """ Computes statistics about the array. diff --git a/pymove/utils/mem.py b/pymove/utils/mem.py index f67b26ae..c5194484 100644 --- a/pymove/utils/mem.py +++ b/pymove/utils/mem.py @@ -9,6 +9,7 @@ top_mem_vars """ +from __future__ import annotations import os import re @@ -16,7 +17,6 @@ from collections import deque from itertools import chain from sys import getsizeof -from typing import Dict, Text import numpy as np import psutil @@ -52,7 +52,7 @@ def reduce_mem_usage_automatic(df: DataFrame): dtype: object """ start_mem = df.memory_usage().sum() / 1024 ** 2 - logger.info('Memory usage of dataframe is {:.2f} MB'.format(start_mem)) + logger.info(f'Memory usage of dataframe is {start_mem:.2f} MB') for col in df.columns: col_type = df[col].dtype @@ -113,14 +113,14 @@ def reduce_mem_usage_automatic(df: DataFrame): df[col] = df[col].astype(np.float64) end_mem = df.memory_usage().sum() / 1024 ** 2 - logger.info('Memory usage after optimization is: {:.2f} MB'.format(end_mem)) + logger.info(f'Memory usage after optimization is: {end_mem:.2f} MB') logger.info( - 'Decreased by {:.1f} %'.format(100 * (start_mem - end_mem) / start_mem) + f'Decreased by {100 * (start_mem - end_mem) / start_mem:.1f} %' ) def total_size( - o: object, handlers: Dict = None, verbose: bool = True + o: object, handlers: dict = None, verbose: bool = True ) -> float: """ Calculates the approximate memory footprint of an given object. @@ -195,14 +195,14 @@ def sizeof(o): if verbose: - logger.info('Size in bytes: {}, Type: {}'.format(s, type(o))) + logger.info(f'Size in bytes: {s}, Type: {type(o)}') return s return sizeof(o) -def begin_operation(name: Text) -> Dict: +def begin_operation(name: str) -> dict: """ Gets the stats for the current operation. @@ -233,7 +233,7 @@ def begin_operation(name: Text) -> Dict: return {'process': process, 'init': init, 'start': start, 'name': name} -def end_operation(operation: Dict) -> Dict: +def end_operation(operation: dict) -> dict: """ Gets the time and memory usage of the operation. @@ -269,7 +269,7 @@ def end_operation(operation: Dict) -> Dict: } -def sizeof_fmt(mem_usage: float, suffix: Text = 'B') -> Text: +def sizeof_fmt(mem_usage: float, suffix: str = 'B') -> str: """ Returns the memory usage calculation of the last function. @@ -295,13 +295,13 @@ def sizeof_fmt(mem_usage: float, suffix: Text = 'B') -> Text: """ for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: if abs(mem_usage) < 1024.0: - return '{:3.1f} {}{}'.format(mem_usage, unit, suffix) + return f'{mem_usage:3.1f} {unit}{suffix}' mem_usage /= 1024.0 return '{:.1f} {}{}'.format(mem_usage, 'Yi', suffix) def top_mem_vars( - variables: Dict, n: int = 10, hide_private=True + variables: dict, n: int = 10, hide_private=True ) -> DataFrame: """ Shows the sizes of the active variables. diff --git a/pymove/utils/trajectories.py b/pymove/utils/trajectories.py index 7b2036a9..bfa989b5 100644 --- a/pymove/utils/trajectories.py +++ b/pymove/utils/trajectories.py @@ -11,10 +11,10 @@ column_to_array """ - +from __future__ import annotations from itertools import chain -from typing import Any, Dict, List, Optional, Text, Tuple, Union +from typing import Any import numpy as np from numpy import ndarray @@ -29,11 +29,11 @@ def read_csv( filepath_or_buffer: FilePathOrBuffer, - latitude: Text = LATITUDE, - longitude: Text = LONGITUDE, - datetime: Text = DATETIME, - traj_id: Text = TRAJ_ID, - type_: Text = TYPE_PANDAS, + latitude: str = LATITUDE, + longitude: str = LONGITUDE, + datetime: str = DATETIME, + traj_id: str = TRAJ_ID, + type_: str = TYPE_PANDAS, n_partitions: int = 1, **kwargs ) -> MoveDataFrame: @@ -94,7 +94,7 @@ def read_csv( ) -def invert_dict(d: Dict) -> Dict: +def invert_dict(d: dict) -> dict: """ Inverts the key:value relation of a dictionary. @@ -119,10 +119,10 @@ def invert_dict(d: Dict) -> Dict: def flatten_dict( - d: Dict, - parent_key: Text = '', - sep: Text = '_' -) -> Dict: + d: dict, + parent_key: str = '', + sep: str = '_' +) -> dict: """ Flattens a nested dictionary. @@ -153,7 +153,7 @@ def flatten_dict( """ if not isinstance(d, dict): return {parent_key: d} - items: List[Tuple[Text, Any]] = [] + items: list[tuple[str, Any]] = [] for k, v in d.items(): new_key = f'{parent_key}{sep}{k}' if parent_key else k if isinstance(v, dict): @@ -163,7 +163,7 @@ def flatten_dict( return dict(items) -def flatten_columns(data: DataFrame, columns: List) -> DataFrame: +def flatten_columns(data: DataFrame, columns: list) -> DataFrame: """ Transforms columns containing dictionaries in individual columns. @@ -223,9 +223,9 @@ def flatten_columns(data: DataFrame, columns: List) -> DataFrame: def shift( - arr: Union[List, Series, ndarray], + arr: list | Series | ndarray, num: int, - fill_value: Optional[Any] = None + fill_value: Any | None = None ) -> ndarray: """ Shifts the elements of the given array by the number of periods specified. @@ -288,7 +288,7 @@ def shift( return result -def fill_list_with_new_values(original_list: List, new_list_values: List): +def fill_list_with_new_values(original_list: list, new_list_values: list): """ Copies elements from one list to another. @@ -314,7 +314,7 @@ def fill_list_with_new_values(original_list: List, new_list_values: List): original_list[:n] = new_list_values -def object_for_array(object_: Text) -> ndarray: +def object_for_array(object_: str) -> ndarray: """ Transforms an object into an array. @@ -346,7 +346,7 @@ def object_for_array(object_: Text) -> ndarray: return conv.astype('object_') -def column_to_array(data: DataFrame, column: Text) -> DataFrame: +def column_to_array(data: DataFrame, column: str) -> DataFrame: """ Transforms all columns values to list. diff --git a/pymove/utils/visual.py b/pymove/utils/visual.py index 80d0a36f..4f10bd5e 100644 --- a/pymove/utils/visual.py +++ b/pymove/utils/visual.py @@ -10,8 +10,9 @@ save_wkt """ +from __future__ import annotations -from typing import Sequence, Text, Tuple, Union +from typing import Sequence from branca.element import MacroElement, Template from folium import Map @@ -23,7 +24,7 @@ from pymove.utils.constants import COLORS, LATITUDE, LONGITUDE, TRAJ_ID -def add_map_legend(m: Map, title: Text, items: Union[Tuple, Sequence[Tuple]]): +def add_map_legend(m: Map, title: str, items: tuple | Sequence[tuple]): """ Adds a legend for a folium map. @@ -202,7 +203,7 @@ def add_map_legend(m: Map, title: Text, items: Union[Tuple, Sequence[Tuple]]): m.get_root().add_child(macro, name='map_legend') -def generate_color() -> Text: +def generate_color() -> str: """ Generates a random color. @@ -221,7 +222,7 @@ def generate_color() -> Text: return COLORS[randint(0, len(COLORS))] -def rgb(rgb_colors: Tuple[float, float, float]) -> Tuple[int, int, int]: +def rgb(rgb_colors: tuple[float, float, float]) -> tuple[int, int, int]: """ Return a tuple of integers, as used in AWT/Java plots. @@ -250,7 +251,7 @@ def rgb(rgb_colors: Tuple[float, float, float]) -> Tuple[int, int, int]: return int(red * 255), int(green * 255), int(blue * 255) -def hex_rgb(rgb_colors: Tuple[float, float, float]) -> Text: +def hex_rgb(rgb_colors: tuple[float, float, float]) -> str: """ Return a hex str, as used in Tk plots. @@ -276,7 +277,7 @@ def hex_rgb(rgb_colors: Tuple[float, float, float]) -> Text: return '#%02X%02X%02X' % rgb(rgb_colors) -def cmap_hex_color(cmap: ListedColormap, i: int) -> Text: +def cmap_hex_color(cmap: ListedColormap, i: int) -> str: """ Convert a Colormap to hex color. @@ -305,7 +306,7 @@ def cmap_hex_color(cmap: ListedColormap, i: int) -> Text: return rgb2hex(cmap(i)) -def get_cmap(cmap: Text) -> Colormap: +def get_cmap(cmap: str) -> Colormap: """ Returns a matplotlib colormap instance. @@ -329,7 +330,7 @@ def get_cmap(cmap: Text) -> Colormap: def save_wkt( - move_data: DataFrame, filename: Text, label_id: Text = TRAJ_ID + move_data: DataFrame, filename: str, label_id: str = TRAJ_ID ): """ Save a visualization in a map in a new file .wkt. @@ -370,7 +371,7 @@ def save_wkt( move_df = move_data[move_data[label_id] == id_] curr_str = '%s;LINESTRING(' % id_ curr_str += ','.join( - '%s %s' % (x[0], x[1]) + f'{x[0]} {x[1]}' for x in move_df[[LONGITUDE, LATITUDE]].values ) curr_str += ')\n' diff --git a/pymove/visualization/folium.py b/pymove/visualization/folium.py index 3e057d9b..07bf43b6 100644 --- a/pymove/visualization/folium.py +++ b/pymove/visualization/folium.py @@ -22,9 +22,10 @@ plot_traj_timestamp_geo_json """ +from __future__ import annotations from datetime import date -from typing import Any, Dict, List, Optional, Sequence, Text, Tuple, Union +from typing import Any, Sequence import folium import numpy as np @@ -38,11 +39,9 @@ DATE, DATETIME, DAY, - EVENT_ID, EVENT_POINT, HOUR, LATITUDE, - LINE_COLOR, LONGITUDE, PERIOD, POI_POINT, @@ -50,7 +49,6 @@ STOP, TILES, TRAJ_ID, - UID, USER_POINT, ) from pymove.utils.datetime import str_to_datetime @@ -60,12 +58,12 @@ def save_map( move_data: DataFrame, - filename: Text, - tiles: Text = TILES[0], - label_id: Text = TRAJ_ID, - cmap: Text = 'Set1', + filename: str, + tiles: str = TILES[0], + label_id: str = TRAJ_ID, + cmap: str = 'Set1', return_map: bool = False -) -> Optional[Map]: +) -> Map | None: """ Save a visualization in a map in a new file. @@ -130,9 +128,9 @@ def save_map( def create_base_map( move_data: DataFrame, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, - tile: Text = TILES[0], + lat_origin: float | None = None, + lon_origin: float | None = None, + tile: str = TILES[0], default_zoom_start: float = 12, ) -> Map: """ @@ -182,15 +180,15 @@ def create_base_map( def heatmap( move_data: DataFrame, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, radius: float = 8, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - filename: Text = 'heatmap.html', + filename: str = 'heatmap.html', ) -> Map: """ Generate visualization of Heat Map using folium plugin. @@ -267,17 +265,17 @@ def heatmap( def heatmap_with_time( move_data: DataFrame, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, radius: float = 8, min_opacity: float = 0.5, max_opacity: float = 0.8, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - filename: Text = 'heatmap_time.html', + filename: str = 'heatmap_time.html', ) -> Map: """ Generate visualization of Heat Map using folium plugin. @@ -371,14 +369,14 @@ def heatmap_with_time( def cluster( move_data: DataFrame, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - filename: Text = 'cluster.html', + filename: str = 'cluster.html', ) -> Map: """ Generate visualization of Heat Map using folium plugin. @@ -461,14 +459,14 @@ def cluster( def faster_cluster( move_data: DataFrame, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - filename: Text = 'faster_cluster.html', + filename: str = 'faster_cluster.html', ) -> Map: """ Generate visualization of Heat Map using folium plugin. @@ -546,14 +544,14 @@ def faster_cluster( def plot_markers( move_data: DataFrame, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - filename: Text = 'markers.html', + filename: str = 'markers.html', ) -> Map: """ Generate visualization of Heat Map using folium plugin. @@ -646,11 +644,11 @@ def plot_markers( def _filter_and_generate_colors( move_data: DataFrame, - id_: Optional[int] = None, - n_rows: Optional[int] = None, - color: Optional[Union[Text, List[Text]]] = None, - color_by_id: Optional[Dict] = None -) -> Tuple[DataFrame, List[Tuple[Any, Any]]]: + id_: int | None = None, + n_rows: int | None = None, + color: str | list[str] | None = None, + color_by_id: dict | None = None +) -> tuple[DataFrame, list[tuple[Any, Any]]]: """ Filters the dataframe and generate colors for folium map. @@ -744,7 +742,7 @@ def _filter_and_generate_colors( def _filter_generated_feature( - move_data: DataFrame, feature: Text, values: Any + move_data: DataFrame, feature: str, values: Any ) -> DataFrame: """ Filters the values from the dataframe. @@ -795,8 +793,8 @@ def _filter_generated_feature( def _add_begin_end_markers_to_map( move_data: DataFrame, base_map: Map, - color: Optional[Text] = None, - _id: Optional[int] = None + color: str | None = None, + _id: int | None = None ): """ Adds markers to the beggining and end of a trajectory. @@ -857,11 +855,11 @@ def _add_begin_end_markers_to_map( def _add_trajectories_to_map( move_data: DataFrame, - items: Sequence[Tuple], + items: Sequence[tuple], base_map: Map, legend: bool = True, save_as_html: bool = True, - filename: Text = 'map.html', + filename: str = 'map.html', ): """ Adds a trajectory to a folium map with begin and end markers. @@ -914,17 +912,17 @@ def _add_trajectories_to_map( def plot_trajectories( move_data: DataFrame, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, legend: bool = True, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - color: Optional[Union[Text, List[Text]]] = None, - color_by_id: Optional[Dict] = None, - filename: Text = 'plot_trajectories.html', + color: str | list[str] | None = None, + color_by_id: dict | None = None, + filename: str = 'plot_trajectories.html', ) -> Map: """ Generate visualization of all trajectories with folium. @@ -1003,16 +1001,16 @@ def plot_trajectories( def plot_trajectory_by_id( move_data: DataFrame, id_: int, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, legend: bool = True, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - color: Optional[Union[Text, List[Text]]] = None, - filename: Text = 'plot_trajectories.html', + color: str | list[str] | None = None, + filename: str = 'plot_trajectories.html', ) -> Map: """ Generate visualization of all trajectories with folium. @@ -1091,19 +1089,19 @@ def plot_trajectory_by_id( def plot_trajectory_by_period( move_data: PandasMoveDataFrame, - period: Text, - id_: Optional[int] = None, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + period: str, + id_: int | None = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, legend: bool = True, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - color: Optional[Union[Text, List[Text]]] = None, - color_by_id: Optional[Dict] = None, - filename: Text = 'plot_trajectories_by_period.html', + color: str | list[str] | None = None, + color_by_id: dict | None = None, + filename: str = 'plot_trajectories_by_period.html', ) -> Map: """ Generate visualization of all trajectories with folium. @@ -1196,19 +1194,19 @@ def plot_trajectory_by_period( def plot_trajectory_by_day_week( move_data: PandasMoveDataFrame, - day_week: Text, - id_: Optional[int] = None, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + day_week: str, + id_: int | None = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, legend: bool = True, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - color: Optional[Union[Text, List[Text]]] = None, - color_by_id: Optional[Dict] = None, - filename: Text = 'plot_trajectories_by_day_week.html', + color: str | list[str] | None = None, + color_by_id: dict | None = None, + filename: str = 'plot_trajectories_by_day_week.html', ) -> Map: """ Generate visualization of all trajectories with folium. @@ -1301,20 +1299,20 @@ def plot_trajectory_by_day_week( def plot_trajectory_by_date( move_data: PandasMoveDataFrame, - start_date: Union[Text, date], - end_date: Union[Text, date], - id_: Optional[int] = None, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + start_date: str | date, + end_date: str | date, + id_: int | None = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, legend: bool = True, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - color: Optional[Union[Text, List[Text]]] = None, - color_by_id: Optional[Dict] = None, - filename: Text = 'plot_trajectories_by_date.html', + color: str | list[str] | None = None, + color_by_id: dict | None = None, + filename: str = 'plot_trajectories_by_date.html', ) -> Map: """ Generate visualization of all trajectories with folium. @@ -1419,20 +1417,20 @@ def plot_trajectory_by_date( def plot_trajectory_by_hour( move_data: PandasMoveDataFrame, - start_hour: Text, - end_hour: Text, - id_: Optional[int] = None, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + start_hour: str, + end_hour: str, + id_: int | None = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, legend: bool = True, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - color: Optional[Union[Text, List[Text]]] = None, - color_by_id: Optional[Dict] = None, - filename: Text = 'plot_trajectories_by_hour.html', + color: str | list[str] | None = None, + color_by_id: dict | None = None, + filename: str = 'plot_trajectories_by_hour.html', ) -> Map: """ Generate visualization of all trajectories with folium. @@ -1529,17 +1527,17 @@ def plot_stops( move_data: PandasMoveDataFrame, radius: float = 0, weight: float = 3, - id_: Optional[int] = None, - n_rows: Optional[int] = None, - lat_origin: Optional[float] = None, - lon_origin: Optional[float] = None, + id_: int | None = None, + n_rows: int | None = None, + lat_origin: float | None = None, + lon_origin: float | None = None, zoom_start: float = 12, legend: bool = True, - base_map: Optional[Map] = None, - tile: Text = TILES[0], + base_map: Map | None = None, + tile: str = TILES[0], save_as_html: bool = False, - color: Optional[Union[Text, List[Text]]] = None, - filename: Text = 'plot_stops.html', + color: str | list[str] | None = None, + filename: str = 'plot_stops.html', ) -> Map: """ Generate visualization of all trajectories with folium. @@ -1649,12 +1647,12 @@ def plot_stops( def plot_bbox( - bbox_tuple: Tuple[float, float, float, float], - base_map: Optional[Map] = None, - tiles: Text = TILES[0], - color: Text = 'red', + bbox_tuple: tuple[float, float, float, float], + base_map: Map | None = None, + tiles: str = TILES[0], + color: str = 'red', save_as_html: bool = False, - filename: Text = 'bbox.html' + filename: str = 'bbox.html' ) -> Map: """ Plots a bbox using Folium. @@ -1707,7 +1705,7 @@ def plot_bbox( return base_map -def _format_tags(line: Union[List, Dict], slice_: List) -> Text: +def _format_tags(line: list | dict, slice_: list) -> str: """ Create or format tags. @@ -1742,17 +1740,17 @@ def _format_tags(line: Union[List, Dict], slice_: List) -> Text: >>> ) lat: 39.984094
lon: 116.319236
datetime: 2008-10-23 05:53:05
id: 1 """ - map_formated_tags = map(lambda tag: '{}: {}'.format(tag, line[tag]), slice_) + map_formated_tags = map(lambda tag: f'{tag}: {line[tag]}', slice_) return '
'.join(map_formated_tags) def _circle_maker( iter_tuple: DataFrame, - user_lat: Text, - user_lon: Text, - slice_tags: List, - user_point: Text, + user_lat: str, + user_lon: str, + slice_tags: list, + user_point: str, radius: float, map_: Map ): @@ -1814,15 +1812,15 @@ def _circle_maker( def plot_points( move_data: DataFrame, - user_lat: Text = LATITUDE, - user_lon: Text = LONGITUDE, - user_point: Text = USER_POINT, + user_lat: str = LATITUDE, + user_lon: str = LONGITUDE, + user_point: str = USER_POINT, radius: float = 2, - base_map: Optional[Map] = None, - slice_tags: Optional[List] = None, - tiles: Text = TILES[0], + base_map: Map | None = None, + slice_tags: list | None = None, + tiles: str = TILES[0], save_as_html: bool = False, - filename: Text = 'points.html' + filename: str = 'points.html' ) -> Map: """ Generates a folium map with the trajectories plots and a point. @@ -1901,15 +1899,15 @@ def plot_points( def plot_poi( move_data: DataFrame, - poi_lat: Text = LATITUDE, - poi_lon: Text = LONGITUDE, - poi_point: Text = POI_POINT, + poi_lat: str = LATITUDE, + poi_lon: str = LONGITUDE, + poi_point: str = POI_POINT, radius: float = 2, - base_map: Optional[Map] = None, - slice_tags: Optional[List] = None, - tiles: Text = TILES[0], + base_map: Map | None = None, + slice_tags: list | None = None, + tiles: str = TILES[0], save_as_html: bool = False, - filename: Text = 'pois.html' + filename: str = 'pois.html' ) -> Map: """ Receives a MoveDataFrame and returns a folium map with poi points. @@ -1970,15 +1968,15 @@ def plot_poi( def plot_event( move_data: DataFrame, - event_lat: Text = LATITUDE, - event_lon: Text = LONGITUDE, - event_point: Text = EVENT_POINT, + event_lat: str = LATITUDE, + event_lon: str = LONGITUDE, + event_point: str = EVENT_POINT, radius: float = 2, - base_map: Optional[Map] = None, - slice_tags: Optional[List] = None, - tiles: Text = TILES[0], + base_map: Map | None = None, + slice_tags: list | None = None, + tiles: str = TILES[0], save_as_html: bool = False, - filename: Text = 'events.html' + filename: str = 'events.html' ) -> Map: """ Receives a MoveDataFrame and returns a folium map with events. @@ -2036,10 +2034,10 @@ def plot_event( def _create_geojson_features_line( move_data: DataFrame, - label_lat: Text = LATITUDE, - label_lon: Text = LONGITUDE, - label_datetime: Text = DATETIME -) -> List: + label_lat: str = LATITUDE, + label_lon: str = LONGITUDE, + label_datetime: str = DATETIME +) -> list: """ Create geojson features. @@ -2155,12 +2153,12 @@ def _create_geojson_features_line( def plot_traj_timestamp_geo_json( move_data: DataFrame, - label_lat: Text = LATITUDE, - label_lon: Text = LONGITUDE, - label_datetime: Text = DATETIME, - tiles: Text = TILES[0], + label_lat: str = LATITUDE, + label_lon: str = LONGITUDE, + label_datetime: str = DATETIME, + tiles: str = TILES[0], save_as_html: bool = False, - filename: Text = 'events.html' + filename: str = 'events.html' ) -> Map: """ Plot trajectories wit geo_json. diff --git a/pymove/visualization/matplotlib.py b/pymove/visualization/matplotlib.py index ec45a4dd..e72de5ba 100644 --- a/pymove/visualization/matplotlib.py +++ b/pymove/visualization/matplotlib.py @@ -4,14 +4,16 @@ show_object_id_by_date, plot_trajectories, plot_trajectory_by_id, +plot_grid_polygons, plot_all_features plot_coords, plot_bounds, plot_line """ +from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Text, Tuple, Union +from typing import TYPE_CHECKING, Any, Callable import matplotlib.pyplot as plt from matplotlib.pyplot import axes, figure @@ -19,6 +21,7 @@ from shapely.geometry import LineString, MultiLineString from shapely.geometry.base import BaseGeometry +from pymove.core.grid import Grid from pymove.utils.constants import ( DATE, DAY, @@ -26,7 +29,7 @@ LATITUDE, LONGITUDE, PERIOD, - TID, + POLYGON, TRAJ_ID, ) @@ -36,13 +39,13 @@ def show_object_id_by_date( - move_data: Union['PandasMoveDataFrame', 'DaskMoveDataFrame'], - kind: Optional[List] = None, - figsize: Tuple[float, float] = (21, 9), + move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', + kind: list | None = None, + figsize: tuple[float, float] = (21, 9), return_fig: bool = False, save_fig: bool = False, - name: Text = 'shot_points_by_date.png', -) -> Optional[figure]: + name: str = 'shot_points_by_date.png', +) -> figure | None: """ Generates four visualizations based on datetime feature. @@ -128,13 +131,13 @@ def show_object_id_by_date( def plot_trajectories( move_data: DataFrame, - markers: Text = 'o', + markers: str = 'o', markersize: float = 12, - figsize: Tuple[float, float] = (10, 10), + figsize: tuple[float, float] = (10, 10), return_fig: bool = False, save_fig: bool = False, - name: Text = 'trajectories.png', -) -> Optional[figure]: + name: str = 'trajectories.png', +) -> figure | None: """ Generate a visualization that show trajectories. @@ -193,17 +196,17 @@ def plot_trajectories( def plot_trajectory_by_id( move_data: DataFrame, - id_: Union[int, Text], - label: Text = TID, - feature: Optional[Text] = None, - value: Optional[Any] = None, + id_: int | str, + label: str = TRAJ_ID, + feature: str | None = None, + value: Any | None = None, linewidth: float = 3, markersize: float = 20, - figsize: Tuple[float, float] = (10, 10), + figsize: tuple[float, float] = (10, 10), return_fig: bool = False, save_fig: bool = False, - name: Optional[Text] = None, -) -> Optional[figure]: + name: str | None = None, +) -> figure | None: """ Generate a visualization that shows a trajectory with the specified tid. @@ -302,14 +305,83 @@ def plot_trajectory_by_id( return fig +def plot_grid_polygons( + data: DataFrame, + grid: Grid | None = None, + markersize: float = 10, + linewidth: float = 2, + figsize: tuple[int, int] = (10, 10), + return_fig: bool = False, + save_fig: bool = False, + name: str = 'grid.png', +) -> figure | None: + """ + Generate a visualization with grid polygons. + + Parameters + ---------- + data : DataFrame + Input trajectory data + markersize : float, optional + Represents visualization size marker, by default 10 + linewidth : float, optional + Represents visualization size line, by default 2 + figsize : tuple(int, int), optional + Represents the size (float: width, float: height) of a figure, + by default (10, 10) + return_fig : bool, optional + Represents whether or not to save the generated picture, by default False + save_fig : bool, optional + Wether to save the figure, by default False + name : str, optional + Represents name of a file, by default 'grid.png' + + Returns + ------- + figure + The generated picture or None + + Raises + ------ + If the dataframe does not contains the POLYGON feature + IndexError + If there is no user with the id passed + + """ + if POLYGON not in data: + if grid is None: + raise KeyError('POLYGON feature not in dataframe') + data = grid.create_all_polygons_to_all_point_on_grid(data) + + data = data.copy() + + data.dropna(subset=[POLYGON], inplace=True) + + fig = plt.figure(figsize=figsize) + + for _, row in data.iterrows(): + xs, ys = row[POLYGON].exterior.xy + plt.plot(ys, xs, 'g', linewidth=linewidth, markersize=markersize) + xs_start, ys_start = data.iloc[0][POLYGON].exterior.xy + xs_end, ys_end = data.iloc[-1][POLYGON].exterior.xy + plt.plot(ys_start, xs_start, 'bo', markersize=markersize * 1.5) + plt.plot(ys_end, xs_end, 'bX', markersize=markersize * 1.5) + + if save_fig: + plt.savefig(fname=name) + + if return_fig: + return fig + + def plot_all_features( move_data: DataFrame, dtype: Callable = float, - figsize: Tuple[float, float] = (21, 15), + figsize: tuple[float, float] = (21, 15), return_fig: bool = False, save_fig: bool = False, - name: Text = 'features.png', -) -> Optional[figure]: + name: str = 'features.png', +) -> figure | None: """ Generate a visualization for each columns that type is equal dtype. @@ -369,7 +441,7 @@ def plot_all_features( return fig -def plot_coords(ax: axes, ob: BaseGeometry, color: Text = 'r'): +def plot_coords(ax: axes, ob: BaseGeometry, color: str = 'r'): """ Plot the coordinates of each point of the object in a 2D chart. @@ -394,7 +466,7 @@ def plot_coords(ax: axes, ob: BaseGeometry, color: Text = 'r'): ax.plot(x, y, 'o', color=color, zorder=1) -def plot_bounds(ax: axes, ob: Union[LineString, MultiLineString], color='b'): +def plot_bounds(ax: axes, ob: LineString | MultiLineString, color='b'): """ Plot the limits of geometric object. @@ -422,10 +494,10 @@ def plot_bounds(ax: axes, ob: Union[LineString, MultiLineString], color='b'): def plot_line( ax: axes, ob: LineString, - color: Text = 'r', + color: str = 'r', alpha: float = 0.7, linewidth: float = 3, - solid_capstyle: Text = 'round', + solid_capstyle: str = 'round', zorder: float = 2 ): """ From eab12c22b1412393a9e25a79f1580597a9e76a12 Mon Sep 17 00:00:00 2001 From: flych3r Date: Tue, 13 Jul 2021 17:57:26 -0300 Subject: [PATCH 2/5] added multi python version testing --- .github/workflows/lint_and_test.yml | 53 ++++++++++++----------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index e87807ea..4b1eaa4a 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -2,37 +2,26 @@ name: Lint and Test on: [push] jobs: - lint: - name: Code Linting + lint-test: + name: Lint and Test runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.7, 3.8, 3.9,.3.10] steps: - - uses: actions/checkout@main - - name: Set up Python 3.7 - uses: actions/setup-python@main - with: - python-version: 3.7 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - make dev - - name: Lint - working-directory: ${{ github.workspace }} - run: | - make lint - test: - name: Code Testing - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@main - - name: Set up Python 3.7 - uses: actions/setup-python@main - with: - python-version: 3.7 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - make dev - - name: Test - working-directory: ${{ github.workspace }} - run: | - make test + - uses: actions/checkout@main + - uses: actions/setup-python@main + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + make dev + - name: Check code style + working-directory: ${{ github.workspace }} + run: | + make lint + - name: Runs unit tests + working-directory: ${{ github.workspace }} + run: | + make test From ac9298a877ee298ca5efe3a76f403fba10e9e95b Mon Sep 17 00:00:00 2001 From: flych3r Date: Tue, 13 Jul 2021 17:58:26 -0300 Subject: [PATCH 3/5] fix typo in version --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 4b1eaa4a..ec478ce3 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9,.3.10] + python-version: [3.7, 3.8, 3.9, 3.10] steps: - uses: actions/checkout@main - uses: actions/setup-python@main From 7943f2f124fe26790e140a32ba323cee5d285bca Mon Sep 17 00:00:00 2001 From: flych3r Date: Tue, 13 Jul 2021 18:00:45 -0300 Subject: [PATCH 4/5] changed versions to string --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index ec478ce3..d0cb62ed 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9, 3.10] + python-version: ['3.7', '3.8', '3.9', '3.10'] steps: - uses: actions/checkout@main - uses: actions/setup-python@main From 2350cdfd90aba71cfe659bba75ce218395ca9109 Mon Sep 17 00:00:00 2001 From: flych3r Date: Tue, 13 Jul 2021 18:02:08 -0300 Subject: [PATCH 5/5] removed python 3.10 --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index d0cb62ed..ce999458 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.7', '3.8', '3.9', '3.10'] + python-version: ['3.7', '3.8', '3.9'] steps: - uses: actions/checkout@main - uses: actions/setup-python@main