Source code for pymove.utils.datetime

"""
Datetime operations.

date_to_str,
str_to_datetime,
datetime_to_str,
datetime_to_min,
min_to_datetime,
to_day_of_week_int,
working_day,
now_str,
deltatime_str,
timestamp_to_millis,
millis_to_timestamp,
time_to_str,
str_to_time,
elapsed_time_dt,
diff_time,
create_time_slot_in_minute,
generate_time_statistics,
threshold_time_statistics

"""
from __future__ import annotations

from datetime import datetime

import holidays
from pandas import DataFrame, Timestamp

from pymove.utils.constants import (
    COUNT,
    DATETIME,
    LOCAL_LABEL,
    MAX,
    MEAN,
    MIN,
    PREV_LOCAL,
    STD,
    SUM,
    THRESHOLD,
    TIME_SLOT,
    TIME_TO_PREV,
)


[docs]def date_to_str(dt: datetime) -> str: """ Get date, in string format, from timestamp. Parameters ---------- dt : datetime Represents a date Returns ------- str Represents the date in string format Example ------- >>> from datetime import datatime >>> from pymove.utils.datetime import date_to_str >>> time_now = datetime.now() >>> print(time_now) '2021-04-29 11:01:29.909340' >>> print(type(time_now)) '<class 'datetime.datetime'>' >>> print(date_to_str(time_now), type(time_now)) '2021-04-29 <class 'str'>' """ return dt.strftime('%Y-%m-%d')
[docs]def str_to_datetime(dt_str: str) -> datetime: """ Converts a datetime in string format to datetime format. Parameters ---------- dt_str : str Represents a datetime in string format, "%Y-%m-%d" or "%Y-%m-%d %H:%M:%S" Returns ------- datetime Represents a datetime in datetime format Example ------- >>> from pymove.utils.datetime import str_to_datetime >>> time_1 = '2020-06-29' >>> time_2 = '2020-06-29 12:45:59' >>> print(type(time_1), type(time_2)) '<class 'str'> <class 'str'>' >>> print( str_to_datetime(time_1), type(str_to_datetime(time_1))) '2020-06-29 00:00:00 <class 'datetime.datetime'>' >>> print(str_to_datetime(time_2), type(str_to_datetime(time_2))) '2020-06-29 12:45:59 <class 'datetime.datetime'>' """ if len(dt_str) == 10: return datetime.strptime(dt_str, '%Y-%m-%d') else: return datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S')
[docs]def datetime_to_str(dt: datetime) -> str: """ Converts a date in datetime format to string format. Parameters ---------- dt : datetime Represents a datetime in datetime format. Returns ------- str Represents a datetime in string format "%Y-%m-%d %H:%M:%S". Example: ------- >>> from pymove.utils.datetime import datetime_to_str >>> from datetime import datetime >>> time_now = datetime.now() >>> print(time_now) '2021-04-29 14:15:29.708113' >>> print(type(time_now)) '<class 'datetime.datetime'>' >>> print(datetime_to_str(time_now), type(datetime_to_str(time_now))) '2021-04-29 14:15:29 <class 'str' >' """ return dt.strftime('%Y-%m-%d %H:%M:%S')
[docs]def datetime_to_min(dt: datetime) -> int: """ Converts a datetime to an int representation in minutes. To do the reverse use: min_to_datetime. Parameters ---------- dt : datetime Represents a datetime in datetime format Returns ------- int Represents minutes from Example ------- >>> from pymove.utils.datetime import datetime_to_min >>> from datetime import datetime >>> time_now = datetime.now() >>> print(type(datetime_to_min(time_now))) '<class 'int'>' >>> datetime_to_min(time_now) '26996497' """ # get an integer time slot from a datetime return int( (dt - dt.utcfromtimestamp(0)).total_seconds() / 60 )
[docs]def min_to_datetime(minutes: int) -> datetime: """ Converts an int representation in minutes to a datetime. To do the reverse use: datetime_to_min. Parameters ---------- minutes : int Represents minutes Returns ------- datetime Represents minutes in datetime format Example ------- >>> from pymove.utils.datetime import min_to_datetime >>> print(min_to_datetime(26996497), type(min_to_datetime(26996497))) '2021-04-30 13:37:00 <class 'datetime.datetime'>' """ return datetime.utcfromtimestamp(minutes * 60)
[docs]def to_day_of_week_int(dt: datetime) -> int: """ Get day of week of a date. Monday == 0...Sunday == 6. Parameters ---------- dt : datetime Represents a datetime in datetime format. Returns ------- int Represents day of week. Example ------- >>> from pymove.utils.datetime import str_to_datetime >>> monday = str_to_datetime('2021-05-3 12:00:01') >>> friday = str_to_datetime('2021-05-7 12:00:01') >>> print(to_day_of_week_int(monday), type(to_day_of_week_int(monday))) '0 <class 'int'>' >>> print(to_day_of_week_int(friday), type(to_day_of_week_int(friday))) '4 <class 'int'>' """ return dt.weekday()
[docs]def working_day( dt: str | datetime, country: str = 'BR', state: str | None = None ) -> bool: """ Indices if a day specified by the user is a working day. Parameters ---------- dt : str or datetime Specifies the day the user wants to know if it is a business day. country : str Indicates country to check for vacation days, by default 'BR' state: str Indicates state to check for vacation days, by default None Returns ------- boolean if true, means that the day informed by the user is a working day. if false, means that the day is not a working day. Examples -------- >>> from pymove.utils.datetime import str_to_datetime >>> independence_day = str_to_datetime('2021-09-7 12:00:01') # Holiday in Brazil >>> next_day = str_to_datetime('2021-09-8 12:00:01') # Not a Holiday in Brazil >>> print(working_day(independence_day, 'BR')) False >>> print(type(working_day(independence_day, 'BR'))) <class 'bool'> >>> print(working_day(next_day, 'BR')) True >>> print(type(working_day(next_day, 'BR'))) '<class 'bool'>' References ---------- Countries and States names available in https://pypi.org/project/holidays/ """ result = True if isinstance(dt, str): dt = str_to_datetime(dt) if isinstance(dt, datetime): dt = datetime(dt.year, dt.month, dt.day) if dt in holidays.CountryHoliday(country=country, prov=None, state=state): result = False else: dow = to_day_of_week_int(dt) # 5 == Saturday, 6 == Sunday if dow == 5 or dow == 6: result = False return result
[docs]def now_str() -> str: """ Get datetime of now. Returns ------- str Represents a date Examples -------- >>> from pymove.utils.datetime import now_str >>> now_str() '2019-09-02 13:54:16' """ return datetime_to_str(datetime.now())
[docs]def deltatime_str(deltatime_seconds: float) -> str: """ Convert time in a format appropriate of time. Parameters ---------- deltatime_seconds : float Represents the elapsed time in seconds Returns ------- time_str : str Represents time in a format hh:mm:ss Examples -------- >>> from pymove.utils.datetime import deltatime_str >>> deltatime_str(1082.7180936336517) '18m:02.718s' Notes ----- Output example if more than 24 hours: 25:33:57 https://stackoverflow.com/questions/3620943/measuring-elapsed-time-with-the-time-module """ hours, rem = divmod(deltatime_seconds, 3600) minutes, seconds = divmod(rem, 60) if hours: return f'{int(hours):0>2}h:{int(minutes):0>2}m:{seconds:05.2f}s' elif minutes: return f'{int(minutes):0>2}m:{seconds:05.2f}s' else: return f'{seconds:05.2f}s'
[docs]def timestamp_to_millis(timestamp: str) -> int: """ Converts a local datetime to a POSIX timestamp in milliseconds (like in Java). Parameters ---------- timestamp : str Represents a date Returns ------- int Represents millisecond results Examples -------- >>> from pymove.utils.datetime import timestamp_to_millis >>> timestamp_to_millis('2015-12-12 08:00:00.123000') 1449907200123 (UTC) """ return Timestamp(timestamp).value // 1000000
[docs]def millis_to_timestamp(milliseconds: float) -> Timestamp: """ Converts milliseconds to timestamp. Parameters ---------- milliseconds : int Represents millisecond. Returns ------- Timestamp Represents the date corresponding. Examples -------- >>> from pymove.utils.datetime import millis_to_timestamp >>> millis_to_timestamp(1449907200123) '2015-12-12 08:00:00.123000' """ return Timestamp(milliseconds, unit='ms')
[docs]def time_to_str(time: Timestamp) -> str: """ Get time, in string format, from timestamp. Parameters ---------- time : Timestamp Represents a time Returns ------- str Represents the time in string format Examples -------- >>> from pymove.utils.datetime import time_to_str >>> time_to_str("2015-12-12 08:00:00.123000") '08:00:00' """ return time.strftime('%H:%M:%S')
[docs]def str_to_time(dt_str: str) -> datetime: """ Converts a time in string format "%H:%M:%S" to datetime format. Parameters ---------- dt_str : str Represents a time in string format Returns ------- datetime Represents a time in datetime format Examples -------- >>> from pymove.utils.datetime import str_to_time >>> str_to_time("08:00:00") datetime(1900, 1, 1, 8, 0) """ return datetime.strptime(dt_str, '%H:%M:%S')
[docs]def elapsed_time_dt(start_time: datetime) -> int: """ Computes the elapsed time from a specific start time. Parameters ---------- start_time : datetime Specifies the start time of the time range to be computed Returns ------- int Represents the time elapsed from the start time to the current time (when the function was called). Examples -------- >>> from datetime import datetime >>> from pymove.utils.datetime import str_to_datetime >>> start_time_1 = datetime(2020, 6, 29, 0, 0) >>> start_time_2 = str_to_datetime('2020-06-29 12:45:59') >>> print(elapsed_time_dt(start_time_1)) 26411808666 >>> print(elapsed_time_dt(start_time_2)) 26365849667 """ return diff_time(start_time, datetime.now())
[docs]def diff_time(start_time: datetime, end_time: datetime) -> int: """ Computes the elapsed time from the start time to the end time specified by the user. Parameters ---------- start_time : datetime Specifies the start time of the time range to be computed end_time : datetime Specifies the start time of the time range to be computed Returns ------- int Represents the time elapsed from the start time to the current time (when the function was called). Examples -------- >>> from datetime import datetime >>> from pymove.utils.datetime import str_to_datetime >>> time_now = datetime.now() >>> start_time_1 = datetime(2020, 6, 29, 0, 0) >>> start_time_2 = str_to_datetime('2020-06-29 12:45:59') >>> print(diff_time(start_time_1, time_now)) 26411808665 >>> print(diff_time(start_time_2, time_now)) 26365849665 """ return int((end_time - start_time).total_seconds() * 1000)
[docs]def create_time_slot_in_minute( data: DataFrame, slot_interval: int = 15, initial_slot: int = 0, label_datetime: str = DATETIME, label_time_slot: str = TIME_SLOT, inplace: bool = False ) -> DataFrame | None: """ Partitions the time in slot windows. Parameters ---------- data : DataFrame dataframe with datetime column slot_interval : int, optional size of the slot window in minutes, by default 5 initial_slot : int, optional initial window time, by default 0 label_datetime : str, optional name of the datetime column, by default DATETIME label_time_slot : str, optional name of the time slot column, by default TIME_SLOT inplace : boolean, optional wether the operation will be done in the original dataframe, by default False Returns ------- DataFrame data with converted time slots or None Examples -------- >>> from pymove.utils.datetime import create_time_slot_in_minute >>> from pymove import datetime >>> data lat lon datetime id 0 39.984094 116.319236 2008-10-23 05:44:05 1 1 39.984198 116.319322 2008-10-23 05:56:06 1 2 39.984224 116.319402 2008-10-23 05:56:11 1 3 39.984224 116.319402 2008-10-23 06:10:15 1 >>> datetime.create_time_slot_in_minute(data, inplace=False) lat lon datetime id time_slot 0 39.984094 116.319236 2008-10-23 05:44:05 1 22 1 39.984198 116.319322 2008-10-23 05:56:06 1 23 2 39.984224 116.319402 2008-10-23 05:56:11 1 23 3 39.984224 116.319402 2008-10-23 06:10:15 1 24 """ if data.dtypes[label_datetime] != 'datetime64[ns]': 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 data[label_time_slot] = minute_day // slot_interval + initial_slot if not inplace: return data
[docs]def generate_time_statistics( data: DataFrame, local_label: str = LOCAL_LABEL ): """ Calculates time statistics of the pairwise local labels. (average, standard deviation, minimum, maximum, sum and count) of the pairwise local labels of a symbolic trajectory. Parameters ---------- data : DataFrame The input trajectories date. local_label : str, optional The name of the feature with local id, by default LOCAL_LABEL Return ------ DataFrame Statistics infomations of the pairwise local labels Example ------- >>> from pymove.utils.datetime import generate_time_statistics >>> df local_label prev_local time_to_prev id 0 house NaN NaN 1 1 market house 720.0 1 2 market market 5.0 1 3 market market 1.0 1 4 school market 844.0 1 >>> generate_time_statistics(df) local_label prev_local mean std \ min max sum count 0 house market 844.0 0.000000 \ 844.0 844.0 844.0 1 1 market house 720.0 0.000000 \ 720.0 720.0 720.0 1 2 market market 3.0 2.828427 \ 1.0 5.0 6.0 2 """ df_statistics = data.groupby( [local_label, PREV_LOCAL] ).agg({TIME_TO_PREV: [ MEAN, STD, MIN, MAX, SUM, COUNT ]}) df_statistics.columns = df_statistics.columns.droplevel(0) df_statistics.fillna(0, inplace=True) df_statistics.reset_index(inplace=True) return df_statistics
def _calc_time_threshold(seg_mean: float, seg_std: float) -> float: """ Auxiliary function for calculating the threshold. Based on the mean and standard deviation of the time transitions between adjacent places on discrete MoveDataFrame. Parameters ---------- seg_mean : float The time mean between two local labels (segment). seg_std : float The time mean between two local labels (segment). Return ------ float The threshold based on the mean and standard deviation of transition time for the segment. Examples -------- >>> from pymove.utils.datetime import _calc_time_threshold >>> print(_calc_time_threshold(12.3, 2.1)) 14.4 >>> print(_calc_time_threshold(1, 1.5)) 2.5 >>> print(_calc_time_threshold(-2, 2)) 0.0 """ threshold = seg_std + seg_mean threshold = float(f'{threshold:.1f}') return threshold
[docs]def threshold_time_statistics( df_statistics: DataFrame, mean_coef: float = 1.0, std_coef: float = 1.0, inplace: bool = False ) -> DataFrame | None: """ Calculates and creates the threshold column. The values are based in the time statistics dataframe for each segment. Parameters ---------- df_statistics : DataFrame Time Statistics of the pairwise local labels. mean_coef : float Multiplication coefficient of the mean time for the segment, by default 1.0 std_coef : float Multiplication coefficient of sdt time for the segment, by default 1.0 inplace : boolean, optional wether the operation will be done in the original dataframe, by default False Return ------ DataFrame DataFrame of time statistics with the aditional feature: threshold, which indicates the time limit of the trajectory segment, or None Example ------- >>> from pymove.utils.datetime import generate_time_statistics >>> df local_label prev_local time_to_prev id 0 house NaN NaN 1 1 market house 720.0 1 2 market market 5.0 1 3 market market 1.0 1 4 school market 844.0 1 >>> statistics = generate_time_statistics(df) >>> statistics local_label prev_local mean std min max sum count 0 house market 844.0 0.000000 844.0 844.0 844.0 1 1 market house 720.0 0.000000 720.0 720.0 720.0 1 2 market market 3.0 2.828427 1.0 5.0 6.0 2 >>> threshold_time_statistics(statistics) local_label prev_local mean std min \ max sum count threshold 0 house market 844.0 0.000000 844.0 \ 844.0 844.0 1 844.0 1 market house 720.0 0.000000 720.0 \ 720.0 720.0 1 720.0 2 market market 3.0 2.828427 1.0 \ 5.0 6.0 2 5.8 """ if not inplace: df_statistics = df_statistics.copy() df_statistics[THRESHOLD] = df_statistics.apply( lambda x: _calc_time_threshold(x[MEAN] * mean_coef, x[STD] * std_coef), axis=1 ) if not inplace: return df_statistics