problems.py 3.39 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import numpy as np

FIELDS = ["person_id", "trip_index", "preceding_purpose", "following_purpose", "mode", "travel_time", "activity_duration", "arrival_time"]
FIXED_PURPOSES = ["home"]


def find_bare_assignment_problems(df):
    problem = None

    for row in df[FIELDS].itertuples(index=False):
        person_id, trip_index, preceding_purpose, following_purpose, mode, travel_time, act_dur, arr_time = row

        if not problem is None and person_id != problem["person_id"]:
            # We switch person, but we're still tracking a problem. This is a tail!
            yield problem
            problem = None

        if problem is None:
            # Start a new problem
            problem = dict(
                person_id=person_id, trip_index=trip_index, purposes=[preceding_purpose],
                modes=[], travel_times=[], activity_duration = [], activity_start_time = []
            )

        problem["purposes"].append(following_purpose)
        problem["modes"].append(mode)
        problem["travel_times"].append(travel_time)
        problem["activity_duration"].append(act_dur)
        problem["activity_start_time"].append(arr_time)

        if problem["purposes"][-1] in FIXED_PURPOSES:
            # The current chain (or initial tail) ends with a fixed activity.
            yield problem
            problem = None


LOCATION_FIELDS = ["person_id", "home"]


def find_assignment_problems(df, df_locations):
    """
        Enriches assignment problems with:
          - Locations of the fixed activities
          - Size of the problem
          - Reduces purposes to the variable ones
    """
    location_iterator = df_locations[LOCATION_FIELDS].itertuples(index=False)
    current_location = None

    for problem in find_bare_assignment_problems(df):
        origin_purpose = problem["purposes"][0]
        destination_purpose = problem["purposes"][-1]

        # Reduce purposes
        if origin_purpose in FIXED_PURPOSES and destination_purpose in FIXED_PURPOSES:
            problem["purposes"] = problem["purposes"][1:-1]

        elif origin_purpose in FIXED_PURPOSES:
            problem["purposes"] = problem["purposes"][1:]

        elif destination_purpose in FIXED_PURPOSES:
            problem["purposes"] = problem["purposes"][:-1]

        else:
            raise RuntimeError("The presented 'problem' is neither a chain nor a tail")

        # Define size
        problem["size"] = len(problem["purposes"])

        if problem["size"] == 0:
            continue  # We can skip if there are no variable activities

        # Advance location iterator until we arrive at the current problem's person
        while current_location is None or current_location[0] != problem["person_id"]:
            current_location = next(location_iterator)

        # Define origin and destination locations if they have fixed purposes
        problem["origin"] = None
        problem["destination"] = None

        if origin_purpose in FIXED_PURPOSES:
            problem["origin"] = current_location[LOCATION_FIELDS.index(origin_purpose)]  # Shapely POINT
            problem["origin"] = np.array([[problem["origin"].x, problem["origin"].y]])

        if destination_purpose in FIXED_PURPOSES:
            problem["destination"] = current_location[LOCATION_FIELDS.index(destination_purpose)]  # Shapely POINT
            problem["destination"] = np.array([[problem["destination"].x, problem["destination"].y]])

        yield problem