Source code for helper_func

import numpy as np
import pandas as pd
import seaborn as sns
import math
import os

from activity import Activity, Schedule, ActivityFactory
from typing import List, Dict, Tuple, Union, Optional



[docs]def generate_discret_sched(block_size:float=0.25, list_act:Optional[List]=None) -> Dict: """Returns a random 24h schedule discretized in blocks of size (duration) n Parameters: -------------- - Block_size: expressed in hours - list_act: If a list of activities is passed, then all the activities of the list are scheduled. Otherwise, schedules are generated randomly from the default list of activities. Returns: ------------- - Random schedule as a dictionary """ total_dur = 24 / block_size t = 0 if list_act is None: list_act = [ "home", "work", "education", "shopping", "errands_services", "business_trip", "leisure", "escort", ] # boundary conditions: leave at least one block at home on each side of the schedule end = 24 - block_size sequence = list( np.arange(block_size, 24 - block_size, block_size) ) # (label,act_id, start_time, feasible start, feasible end, duration) schedule = { 0: "dawn", end: "dusk", } # dict with keys being the block, and value is the activity label index = 1 remaining_dur = total_dur - 2 * block_size # remove boundary blocks n_act = 0 next_duration = 0 time_tracker = 0 while remaining_dur > 0 and list_act: next_act = np.random.choice(list_act) # pick an activity if n_act < 1: # first activity next_start = np.random.choice(sequence) # pick a start time first_activity = next_act else: next_start = time_tracker next_duration = np.random.choice(np.arange(remaining_dur, step=block_size)) # check that the duration is feasible, i.e. the blocks should be unassigned # if not --> fill the remaining space current_dur = 0 # tracks the actual duration assigned to activity for i in np.arange(next_start, next_start + next_duration, block_size): if i not in schedule.keys(): schedule[i] = next_act current_dur += block_size else: break # update trackers if current_dur > 0: remaining_dur -= current_dur list_act = list_act.remove(next_act) time_tracker = next_start + current_dur n_act += 1 # adjust time at home first_activity = list(schedule.keys())[1] # first out of home last_activity = list(schedule.keys())[-2] # last out of home for i in sequence: if i not in list(schedule.keys()): if i < last_activity: schedule[i] = "dawn" else: schedule[i] = "dusk" # sort schedule sorted_schedule = {k: schedule[k] for k in sorted(schedule)} return sorted_schedule
[docs]def discretize_sched(schedule:pd.DataFrame, block_size:float = 0.5)->Dict: '''Discretizes given schedule in blocks of size (duration) n Parameters: ------------- -schedule: schedule as a pandas dataframe -block_size: discretization in hours Returns -------------- Schedule as a dictionary, where keys are the chosen discretization. ''' d_sched = {} time_slots = range(int(24/block_size)) sched = schedule.copy() mask = (sched.duration >= block_size) sched['d_start'] = sched.start_time.apply(lambda x: int(x / block_size)) stimes = {t : a for t,a in zip(sched[mask].d_start.values, sched[mask].act_label.values)} current_act = 'home' for n in time_slots: if n in stimes.keys(): current_act = stimes[n] d_sched[n] = current_act return d_sched
[docs]def parse_schedule(schedule: Dict)-> Schedule: """ Transforms a dictionary schedule into a Schedule object. Parameters: ----------- - schedule: dataframe schedule - tt_mat: travel time matrix Returns: ----------- Schedule object """ new_schedule = {} block_length = list(schedule.keys())[1]-list(schedule.keys())[0] for k, v in schedule.iteritems(): new_schedule[k] = Activity(label = v, start_time = k, duration = block_length) return schedule
[docs]def parse_df_schedule(schedule:pd.DataFrame, tt_mat:Optional[Dict] = None)-> Schedule: """ Transforms a pandas DataFrame schedule into a Schedule object. Parameters: ----------- - schedule: dataframe schedule - tt_mat: travel time matrix Returns: ----------- Schedule object """ list_a = [] af = ActivityFactory() all_act = ["home", "work","education","shopping", "errands_services","business_trip","leisure","escort"] for _, row in schedule.iterrows(): cols = ['start_time', 'end_time', 'duration', 'location'] params = {x: row[x] for x in cols} params['label'] = row.act_label params['mode'] = 'driving' new_act = af.create(**params) list_a.append(new_act) schedule = Schedule(list_act = list_a, travel_time_mat = tt_mat) return schedule
[docs]def round_nearest(x:float, a:int) -> int: """ Rounds x to nearest integer a """ return math.floor(x / a) * a
[docs]def lookup_discret() -> pd.DataFrame: """Precomputed table to convert one time discretisation to another.""" lookup_block = { round(block, 2): { "hour": round_nearest(block, 1), "half": round_nearest(block, 0.5), "quart": round_nearest(block, 0.25), } for block in np.linspace(0, 24, num=288, endpoint=False) } pd_lookup = pd.DataFrame.from_dict(lookup_block, orient="index") pd_lookup = pd_lookup.reset_index().rename(columns={"index": "five"}) return pd_lookup
[docs]def sched_from_dict(dict_sched: Dict) -> pd.DataFrame: """ Creates a pandas DataFrame schedule from a dictionary """ df = pd.DataFrame.from_dict(dict_sched, 'index').reset_index() df.rename({'index': 'start_time', 0: 'label'}, axis = 1, inplace = True) df['shift'] = (df['label'].shift() != df['label']).cumsum() #groupby consecutive values of activities to compute duration df = df.groupby(['label','shift'])['start_time'].agg(['first', 'last']) #get first and last occurence of activity df = df.sort_values(by = 'shift').reset_index() df['duration'] = df['first'].shift(-1).fillna(24) - df['first'] df['end_time'] = df['first'] + df['duration'] df = df[['label', 'first', 'end_time','duration']].rename({'first': 'start_time', 'label': 'act_label'},axis = 1) label = df.act_label.tolist() label[0] = 'dawn' label[-1] = 'dusk' label = list(map(lambda x: str(x[1]) + str(label[:x[0]].count(x[1]) + 1) if label.count(x[1]) > 1 else str(x[1]), enumerate(label))) df['label'] = label labels_act = {1: "home",2: "work",3: "education",4: "shopping", 5: "errands_services",6: "business_trip",8: "leisure", 9: "escort"} act_ids = {v : k for k, v in labels_act.items()} df['act_id'] = df.act_label.map(act_ids) df = df[['act_id', 'act_label', 'label', 'start_time', 'end_time', 'duration']] return df
[docs]def schedule_to_pandas(schedule: Schedule)->pd.DataFrame: '''Transform a Schedule object in a Pandas dataframe. Parameters: ------------ -schedule: Schedule object Returns: ------------ Schedule as a pandas DataFrame. ''' if not isinstance(schedule, Schedule): print("The input is not a valid Schedule object. Please pass a valid Schedule object") print(schedule) return None cols = ['act_id', 'act_label', 'label', 'start_time', 'end_time', 'duration', 'mode', 'location', 'travel_time'] df = pd.DataFrame(columns = cols) labels_act = {1: "home",2: "work",3: "education",4: "shopping", 5: "errands_services",6: "business_trip",8: "leisure", 9: "escort"} act_ids = {v : k for k, v in labels_act.items()} df.act_label = pd.Series([x.label for x in schedule.list_act]) df.start_time = pd.Series([x.start_time for x in schedule.list_act]) df.end_time = pd.Series([x.end_time for x in schedule.list_act]) df.duration = pd.Series([x.duration for x in schedule.list_act]) df.mode = pd.Series([x.mode for x in schedule.list_act]) df.location = pd.Series([x.location for x in schedule.list_act]) df.travel_time = pd.Series([schedule.get_travel_time(schedule.list_act[i].location, schedule.list_act[i+1].location, schedule.list_act[i].mode) for i in range(len(schedule.list_act)-1)]) label = df.act_label.tolist() label[0] = 'dawn' label[-1] = 'dusk' label = list(map(lambda x: str(x[1]) + str(label[:x[0]].count(x[1]) + 1) if label.count(x[1]) > 1 else str(x[1]), enumerate(label))) df.label = label df.act_id = df.act_label.map(act_ids) return df
[docs]def activity_colors(list_act: List =None, palette: str ="colorblind")-> List: """Match each activity from list to a color from the input palette. Useful to keep consistent colors across visualizations Parameters: ---------------------- - list_act: list of activity all_labels - palette; name of matplotlib/searborn color palette. """ if list_act is None: list_act = [ "home", "work", "education", "shopping", "errands_services", "business_trip", "leisure", "escort", ] colors = { a: c for a, c in zip(list_act, sns.color_palette(palette, len(list_act)).as_hex()) } colors["home"] = "gainsboro" if "home" in colors.keys(): colors["dawn"] = colors["home"] colors["dusk"] = colors["home"] return colors