Spaces:
Sleeping
Sleeping
ini commit
Browse files- .DS_Store +0 -0
- Trip Generation.xlsx +0 -0
- __pycache__/tripGenerationFunc.cpython-312.pyc +0 -0
- config.json +64 -0
- main copy.py +333 -0
- main.py +385 -0
- requirements.txt +6 -0
- tripGenerationFunc.py +910 -0
- utils.py +111 -0
.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
Trip Generation.xlsx
ADDED
Binary file (300 kB). View file
|
|
__pycache__/tripGenerationFunc.cpython-312.pyc
ADDED
Binary file (33.6 kB). View file
|
|
config.json
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"STREAM_ID": "ebcfc50abe",
|
3 |
+
"BRANCH_NAME_LAND_USES": "graph_geometry/activity_nodes_with_land_use",
|
4 |
+
"BRANCH_NAME_DISTANCE_MATRIX": "graph_geometry/distance_matrix",
|
5 |
+
"BRANCH_NAME_METRIC_DIST_MATRIX": "graph_geometry/metric_matrix",
|
6 |
+
"TARGET_BRANCH_TM": "graph_geometry/trip_matrix",
|
7 |
+
"distanceMatrixName": "activity_node+distance_matrix_ped_mm_art_noEntr",
|
8 |
+
"SCENARIO_NAME": "PED-MM-DRT_noEntryTime",
|
9 |
+
"TARGET_TRIP_RATE": 3.2,
|
10 |
+
"SCALING_FACTOR": 1,
|
11 |
+
"ALPHA_LOW": 0.0023,
|
12 |
+
"ALPHA_MED": 0.0038,
|
13 |
+
"ALPHA_HIGH": 0.0076,
|
14 |
+
"ALPHA_UNIFORM": 0.0038,
|
15 |
+
"ALPHA": 0.00066,
|
16 |
+
"XLS_FILE_PATH":"Trip Generation.xlsx",
|
17 |
+
|
18 |
+
"F_VALUES_MANUAL": {
|
19 |
+
"activity_node+distance_matrix_ped_mm_art_noEntr": 0,
|
20 |
+
"activity_node+distance_matrix_ped_mm_noEntr": 0.55,
|
21 |
+
"activity_node+distance_matrix_ped_noEntr": -0.08,
|
22 |
+
"activity_node+distance_matrix_ped_art_noEntr": -0.5
|
23 |
+
},
|
24 |
+
"distance_matrices_of_interest": [
|
25 |
+
"activity_node+distance_matrix_ped_mm_art_noEntr",
|
26 |
+
"activity_node+distance_matrix_ped_mm_noEntr",
|
27 |
+
"activity_node+distance_matrix_ped_art_noEntr",
|
28 |
+
"activity_node+distance_matrix_ped_noEntr"
|
29 |
+
],
|
30 |
+
"metric_matrices_of_interest": [
|
31 |
+
"activity_node+metric_matrix_ped_mm_art",
|
32 |
+
"activity_node+metric_matrix_ped_mm",
|
33 |
+
"activity_node+metric_matrix_ped_art",
|
34 |
+
"activity_node+metric_matrix_ped"
|
35 |
+
],
|
36 |
+
"redistributeTrips": [
|
37 |
+
{
|
38 |
+
"from": "activity_node+distance_matrix_ped_mm_art_noEntr",
|
39 |
+
"to": [
|
40 |
+
"activity_node+distance_matrix_ped_mm_noEntr",
|
41 |
+
"activity_node+distance_matrix_ped_art_noEntr",
|
42 |
+
"activity_node+distance_matrix_ped_noEntr"
|
43 |
+
]
|
44 |
+
},
|
45 |
+
{
|
46 |
+
"from": "activity_node+distance_matrix_ped_art_noEntr",
|
47 |
+
"to": [
|
48 |
+
"activity_node+distance_matrix_ped_noEntr"
|
49 |
+
]
|
50 |
+
},
|
51 |
+
{
|
52 |
+
"from": "activity_node+distance_matrix_ped_mm_noEntr",
|
53 |
+
"to": [
|
54 |
+
"activity_node+distance_matrix_ped_noEntr"
|
55 |
+
]
|
56 |
+
}
|
57 |
+
],
|
58 |
+
"DISTANCE_BRACKETS": [
|
59 |
+
800,
|
60 |
+
2400,
|
61 |
+
4800
|
62 |
+
]
|
63 |
+
}
|
64 |
+
|
main copy.py
ADDED
@@ -0,0 +1,333 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import json
|
3 |
+
import pandas as pd
|
4 |
+
import copy
|
5 |
+
from functools import wraps
|
6 |
+
from specklepy.api.client import SpeckleClient
|
7 |
+
from tripGenerationFunc import *
|
8 |
+
import speckle_utils
|
9 |
+
import data_utils
|
10 |
+
|
11 |
+
|
12 |
+
# get config file:# Parse JSON
|
13 |
+
current_directory = os.path.dirname(os.path.abspath(__file__))
|
14 |
+
# Path to the config.json file
|
15 |
+
config_file_path = os.path.join(current_directory, "config.json")
|
16 |
+
|
17 |
+
|
18 |
+
#def runAll():
|
19 |
+
|
20 |
+
speckle_token = os.environ.get("SPECKLE_TOKEN")
|
21 |
+
# Check if the config.json file exists
|
22 |
+
if os.path.exists(config_file_path):
|
23 |
+
# Load the JSON data from config.json
|
24 |
+
with open(config_file_path, 'r') as f:
|
25 |
+
config = json.load(f)
|
26 |
+
|
27 |
+
# Convert to Python variables with the same names as the keys in the JSON
|
28 |
+
locals().update(config)
|
29 |
+
print("varaibles from json")
|
30 |
+
# Now you can access the variables directly
|
31 |
+
print(STREAM_ID)
|
32 |
+
print(BRANCH_NAME_LAND_USES)
|
33 |
+
print(TARGET_TRIP_RATE)
|
34 |
+
print(ALPHA_LOW)
|
35 |
+
print(F_VALUES_MANUAL)
|
36 |
+
print(distance_matrices_of_interest)
|
37 |
+
print(redistributeTrips)
|
38 |
+
print(DISTANCE_BRACKETS)
|
39 |
+
print(XLS_FILE_PATH)
|
40 |
+
print("==================")
|
41 |
+
else:
|
42 |
+
print("Error: config.json file not found in the current directory.")
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
xls_file_path = os.path.join(current_directory, XLS_FILE_PATH)
|
47 |
+
print("full path", xls_file_path)
|
48 |
+
# fetch speckle data
|
49 |
+
CLIENT = SpeckleClient(host="https://speckle.xyz/")
|
50 |
+
CLIENT.authenticate_with_token(token="52566d1047b881764e16ad238356abeb2fc35d8b42")
|
51 |
+
|
52 |
+
# get land use stream
|
53 |
+
stream_land_use = speckle_utils.getSpeckleStream(STREAM_ID,
|
54 |
+
BRANCH_NAME_LAND_USES,
|
55 |
+
CLIENT,
|
56 |
+
commit_id = "")
|
57 |
+
# navigate to list with speckle objects of interest
|
58 |
+
stream_data = stream_land_use["@Data"]["@{0}"]
|
59 |
+
|
60 |
+
# transform stream_data to dataframe (create a backup copy of this dataframe)
|
61 |
+
df_speckle_lu = speckle_utils.get_dataframe(stream_data, return_original_df=False)
|
62 |
+
df_main = df_speckle_lu.copy()
|
63 |
+
|
64 |
+
# set index column
|
65 |
+
df_main = df_main.set_index("ids", drop=False)
|
66 |
+
|
67 |
+
|
68 |
+
# get distance matrix stream
|
69 |
+
stream_distance_matrice = speckle_utils.getSpeckleStream(STREAM_ID,
|
70 |
+
BRANCH_NAME_DISTANCE_MATRIX,
|
71 |
+
CLIENT,
|
72 |
+
commit_id = "")
|
73 |
+
|
74 |
+
# navigate to list with speckle objects of interest
|
75 |
+
distance_matrices = {}
|
76 |
+
for distM in stream_distance_matrice["@Data"]['@{0}']:
|
77 |
+
for kk in distM.__dict__.keys():
|
78 |
+
try:
|
79 |
+
if kk.split("+")[1].startswith("distance_matrix"):
|
80 |
+
distance_matrix_dict = json.loads(distM[kk])
|
81 |
+
origin_ids = distance_matrix_dict["origin_uuid"]
|
82 |
+
destination_ids = distance_matrix_dict["destination_uuid"]
|
83 |
+
distance_matrix = distance_matrix_dict["matrix"]
|
84 |
+
# Convert the distance matrix to a DataFrame
|
85 |
+
df_distances = pd.DataFrame(distance_matrix, index=origin_ids, columns=destination_ids)
|
86 |
+
|
87 |
+
# i want to add the index & colum names to dist_m_csv
|
88 |
+
#distance_matrices[kk] = dist_m_csv[kk]
|
89 |
+
distance_matrices[kk] = df_distances
|
90 |
+
|
91 |
+
except:
|
92 |
+
pass
|
93 |
+
|
94 |
+
|
95 |
+
# get metric matrix stream
|
96 |
+
stream_metric_matrice = speckle_utils.getSpeckleStream(STREAM_ID,
|
97 |
+
BRANCH_NAME_METRIC_DIST_MATRIX,
|
98 |
+
CLIENT,
|
99 |
+
commit_id = "")
|
100 |
+
|
101 |
+
|
102 |
+
# navigate to list with speckle objects of interest
|
103 |
+
metric_matrices = {}
|
104 |
+
for distM in stream_metric_matrice["@Data"]['@{0}']:
|
105 |
+
print(distM.__dict__.keys())
|
106 |
+
for kk in distM.__dict__.keys():
|
107 |
+
try:
|
108 |
+
if kk.split("+")[1].startswith("metric_matrix"):
|
109 |
+
metric_matrix_dict = json.loads(distM[kk])
|
110 |
+
origin_ids = metric_matrix_dict["origin_uuid"]
|
111 |
+
destination_ids = metric_matrix_dict["destination_uuid"]
|
112 |
+
metric_matrix = metric_matrix_dict["matrix"]
|
113 |
+
# Convert the distance matrix to a DataFrame
|
114 |
+
df_metric_dist = pd.DataFrame(metric_matrix, index=origin_ids, columns=destination_ids)
|
115 |
+
metric_matrices[kk] = df_metric_dist*10 #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
116 |
+
|
117 |
+
print("metric_matrix_dict", metric_matrix_dict.keys())
|
118 |
+
except:
|
119 |
+
pass
|
120 |
+
|
121 |
+
metric_matrices = extract_distance_matrices(stream_metric_matrice, metric_matrices_of_interest)
|
122 |
+
|
123 |
+
|
124 |
+
sourceCommits = {
|
125 |
+
"landuseCommitID": stream_land_use.id,
|
126 |
+
"distanceMatrixCommitID": stream_distance_matrice.id,
|
127 |
+
"metricMatrixCommitID": stream_metric_matrice.id
|
128 |
+
}
|
129 |
+
|
130 |
+
|
131 |
+
# READ XLS FILE ======================================
|
132 |
+
# Read Excel file into Pandas DataFrame
|
133 |
+
#Production
|
134 |
+
# Load Excel file separately
|
135 |
+
#xls_file_path = os.path.join(current_directory, XLS_FILE_PATH)
|
136 |
+
if os.path.exists(xls_file_path):
|
137 |
+
# Production
|
138 |
+
df_production = pd.read_excel(xls_file_path, sheet_name='Production')
|
139 |
+
df_production_transposed = df_production.T
|
140 |
+
df_production = preprocess_dataFrame(df_production, headerRow_idx=2, numRowsStart_idx=3)
|
141 |
+
df_production_transposed = preprocess_dataFrame(df_production_transposed, headerRow_idx=0, numRowsStart_idx=4,
|
142 |
+
numColsStart_idx=4, rowNames_idx=2)
|
143 |
+
|
144 |
+
# Attraction
|
145 |
+
df_attraction = pd.read_excel(xls_file_path, sheet_name='Attraction')
|
146 |
+
df_attraction = preprocess_dataFrame(df_attraction, headerRow_idx=0, numRowsStart_idx=2)
|
147 |
+
|
148 |
+
# Distribution_Matrix
|
149 |
+
df_distributionMatrix = pd.read_excel(xls_file_path, sheet_name='Distribution_Matrix')
|
150 |
+
df_distributionMatrix = preprocess_dataFrame(df_distributionMatrix, headerRow_idx=0, numRowsStart_idx=2,
|
151 |
+
numRowsEnd_idx=None, numColsStart_idx=2, numColsEnd_idx=None,
|
152 |
+
rowNames_idx=0)
|
153 |
+
|
154 |
+
# Alphas
|
155 |
+
df_alphas = pd.read_excel(xls_file_path, sheet_name='Alphas')
|
156 |
+
df_alphas.columns = df_alphas.iloc[1]
|
157 |
+
df_alphas = df_alphas.iloc[0, 2:]
|
158 |
+
|
159 |
+
# Land use
|
160 |
+
df_lu = pd.read_excel(xls_file_path, sheet_name='Example_Land_Use')
|
161 |
+
df_lu = preprocess_dataFrame(df_lu, headerRow_idx=0, numRowsStart_idx=1)
|
162 |
+
df_lu["nameCombined"] = df_lu.iloc[:, 1].astype(str) + "+" + df_lu.iloc[:, 0].astype(str)
|
163 |
+
|
164 |
+
# Distance Matrix
|
165 |
+
df_distMatrix = pd.read_excel(xls_file_path, sheet_name='Example_Distance_Matrix')
|
166 |
+
df_distMatrix = preprocess_dataFrame(df_distMatrix, headerRow_idx=0, numRowsStart_idx=1, numRowsEnd_idx=None,
|
167 |
+
numColsStart_idx=1, numColsEnd_idx=None, rowNames_idx=0)
|
168 |
+
else:
|
169 |
+
print("Error: Excel file specified in config.json not found.")
|
170 |
+
|
171 |
+
|
172 |
+
|
173 |
+
# Land use strucutre =======
|
174 |
+
# THIS IS THE DISTANCE MATRIX THATS USED DOWN THE ROAD
|
175 |
+
df_distances_aligned, df_lu_stream_aligned = align_dataframes(distance_matrices[distanceMatrixName], df_main, 'ids')
|
176 |
+
|
177 |
+
#Create a df with lanuses
|
178 |
+
lu_cols = [col for col in df_lu_stream_aligned.columns if col.startswith("lu+")]
|
179 |
+
df_lu_stream = df_lu_stream_aligned[lu_cols]
|
180 |
+
|
181 |
+
# Remove "lu+" from the beginning of column names
|
182 |
+
df_lu_stream.columns = df_lu_stream.columns.str.lstrip('lu+')
|
183 |
+
df_lu_stream = df_lu_stream.T
|
184 |
+
|
185 |
+
df_lu_stream_t = df_lu_stream.T
|
186 |
+
|
187 |
+
df_lu_stream_with_nameLu_column = df_lu_stream.reset_index(drop=False).rename(columns={'index': 'nameLu'})
|
188 |
+
|
189 |
+
#---
|
190 |
+
df_lu_names_xlsx = pd.concat([df_lu.iloc[:, 0:2], df_lu.iloc[:, -1]], axis=1)
|
191 |
+
df_lu_names_xlsx.index = df_lu_names_xlsx.iloc[:, 1]
|
192 |
+
column_names = ['nameTripType', 'nameLu', 'nameCombined']
|
193 |
+
df_lu_names_xlsx.columns = column_names
|
194 |
+
print(f"df_lu_names_xlsx shape: {df_lu_names_xlsx.shape}")
|
195 |
+
df_lu_names_xlsx.head()
|
196 |
+
|
197 |
+
#--
|
198 |
+
|
199 |
+
# Merge DataFrames using an outer join
|
200 |
+
merged_df = pd.merge(df_lu_stream_with_nameLu_column, df_lu_names_xlsx, on='nameLu', how='outer')
|
201 |
+
|
202 |
+
# Get the unique names and their counts from df_lu_names_xlsx
|
203 |
+
name_counts = df_lu_names_xlsx['nameLu'].value_counts()
|
204 |
+
#print(name_counts)
|
205 |
+
|
206 |
+
# Identify names in df_lu_stream_with_nameLu_column that are not in df_lu_names_xlsx
|
207 |
+
missing_names = df_lu_stream_with_nameLu_column.loc[~df_lu_stream_with_nameLu_column['nameLu'].isin(df_lu_names_xlsx['nameLu'])]
|
208 |
+
|
209 |
+
# Append missing rows to df_lu_stream_with_nameLu_column
|
210 |
+
df_lu_stream_duplicated = pd.concat([merged_df, missing_names], ignore_index=True)
|
211 |
+
|
212 |
+
|
213 |
+
#--
|
214 |
+
# Find names in df_lu_names_xlsx that are not in df_lu_stream_with_nameLu_column
|
215 |
+
missing_names = df_lu_names_xlsx.loc[~df_lu_names_xlsx['nameLu'].isin(df_lu_stream_with_nameLu_column['nameLu'])]
|
216 |
+
|
217 |
+
#--
|
218 |
+
# print existing names (?)
|
219 |
+
df_lu_names_sorted = df_lu_names_xlsx.sort_values(by='nameLu')
|
220 |
+
df_lu_stream_duplicated_sorted = df_lu_stream_duplicated.sort_values(by='nameLu')
|
221 |
+
#--
|
222 |
+
# Merge DataFrames to get the order of names
|
223 |
+
merged_order = pd.merge(df_lu_names_xlsx[['nameCombined']], df_lu_stream_duplicated[['nameCombined']], on='nameCombined', how='inner')
|
224 |
+
|
225 |
+
# Sort df_lu_stream_duplicated based on the order of names in df_lu_names_xlsx
|
226 |
+
df_lu_stream_sorted = df_lu_stream_duplicated.sort_values(by='nameCombined', key=lambda x: pd.Categorical(x, categories=merged_order['nameCombined'], ordered=True))
|
227 |
+
|
228 |
+
# Reorganize columns
|
229 |
+
column_order = ['nameTripType', 'nameCombined'] + [col for col in df_lu_stream_sorted.columns if col not in ['nameTripType', 'nameCombined']]
|
230 |
+
|
231 |
+
# Create a new DataFrame with the desired column order
|
232 |
+
df_lu_stream_reordered = df_lu_stream_sorted[column_order]
|
233 |
+
|
234 |
+
df_lu_stream_reordered_t = df_lu_stream_reordered.T
|
235 |
+
|
236 |
+
#--
|
237 |
+
df_lu_stream_with_index = df_lu_stream_reordered_t.reset_index(drop=False).rename(columns={'index': 'ids'})
|
238 |
+
df_lu_stream_with_index.index = df_lu_stream_reordered_t.index
|
239 |
+
|
240 |
+
df_lu_num_t_index = df_lu_stream_with_index.iloc[3:]
|
241 |
+
|
242 |
+
df_distances_aligned_index = df_distances_aligned.reset_index(drop=False).rename(columns={'index': 'ids'})
|
243 |
+
df_distances_aligned_index.index = df_distances_aligned.index
|
244 |
+
|
245 |
+
df_lu_namesCombined = df_lu_stream_with_index.loc["nameCombined"].iloc[1:]
|
246 |
+
|
247 |
+
# Sort df_lu_stream_with_index based on the 'ids' column in df_distances_aligned_index
|
248 |
+
df_lu_stream_sorted = df_lu_stream_with_index.sort_values(by=['ids'], key=lambda x: pd.Categorical(x, categories=df_distances_aligned_index['ids'], ordered=True))
|
249 |
+
|
250 |
+
|
251 |
+
df_lu_num = df_lu_stream_sorted.T.iloc[1:, :-3]
|
252 |
+
df_lu_num.index = df_lu_namesCombined
|
253 |
+
|
254 |
+
df_distMatrix_speckle = df_distances_aligned
|
255 |
+
|
256 |
+
df_attraction_num = df_attraction.reset_index().iloc[:-1, 6:]
|
257 |
+
|
258 |
+
# =============================================================================
|
259 |
+
# TRIP GENERATION
|
260 |
+
|
261 |
+
# ATTRACTION & PRODUCTION ======================================================
|
262 |
+
"""
|
263 |
+
INPUTS
|
264 |
+
df_attraction_num
|
265 |
+
df_lu_num
|
266 |
+
df_production
|
267 |
+
df_lu
|
268 |
+
df_production_transposed
|
269 |
+
"""
|
270 |
+
|
271 |
+
df_attraction_proNode_sum_total = attraction_proNode_full_iter(df_attraction_num, df_lu_num, True)
|
272 |
+
|
273 |
+
#Get the sqmProPerson
|
274 |
+
df_sqmProPerson = df_production.iloc[0, 4:].reset_index()[3]
|
275 |
+
|
276 |
+
#Get the trip rate
|
277 |
+
df_tripRate = copy.deepcopy(df_production) # create a copy ensures df_tripRate doenst point to df_production
|
278 |
+
df_tripRate.index = df_tripRate.iloc[:, 0] #Set the row names
|
279 |
+
df_tripRate = df_tripRate.iloc[1:, 2]
|
280 |
+
|
281 |
+
#Numerical df from production ==============================================
|
282 |
+
df_production_num = df_production.iloc[1:, 4:]
|
283 |
+
df_production_transposed1 = df_production_num.T
|
284 |
+
|
285 |
+
df_total_trips_allNodes = production_proNode_total(df_lu,
|
286 |
+
df_sqmProPerson,
|
287 |
+
df_tripRate,
|
288 |
+
df_production_num,
|
289 |
+
df_production_transposed,
|
290 |
+
df_lu_num, printSteps=False)
|
291 |
+
# Convert data types to float
|
292 |
+
df_total_trips_allNodes = df_total_trips_allNodes.astype(float)
|
293 |
+
df_tripRate = df_tripRate.astype(float)
|
294 |
+
|
295 |
+
df_total_trips_allNodes_sumPerson = df_total_trips_allNodes.div(df_tripRate, axis=0).sum()
|
296 |
+
df_total_trips_allNodes_sumPerson_proCat = df_total_trips_allNodes.div(df_tripRate, axis=0)
|
297 |
+
df_total_trips_allNodes_sumPerson_proCat_t = df_total_trips_allNodes_sumPerson_proCat.T
|
298 |
+
df_total_trips_allNodes_sumPerson_proCat_t_sum = df_total_trips_allNodes_sumPerson_proCat_t.sum()
|
299 |
+
|
300 |
+
# get total population
|
301 |
+
total_population = df_total_trips_allNodes_sumPerson_proCat_t_sum["Tot_Res"] + df_total_trips_allNodes_sumPerson_proCat_t_sum["Tot_tou"]
|
302 |
+
|
303 |
+
|
304 |
+
|
305 |
+
# =============================================================================
|
306 |
+
distance_matrices = extract_distance_matrices(stream_distance_matrice, distance_matrices_of_interest)
|
307 |
+
metric_matrices_ = extract_distance_matrices(stream_metric_matrice, metric_matrices_of_interest)
|
308 |
+
metric_matrices = { k:v*10 for k, v in metric_matrices_.items()} # scale (speckle issue)
|
309 |
+
|
310 |
+
logs = computeTrips(
|
311 |
+
df_distributionMatrix,
|
312 |
+
df_total_trips_allNodes,
|
313 |
+
df_distMatrix_speckle,
|
314 |
+
df_alphas,
|
315 |
+
df_attraction_proNode_sum_total,
|
316 |
+
df_distances_aligned,
|
317 |
+
TARGET_TRIP_RATE,
|
318 |
+
SCALING_FACTOR,
|
319 |
+
total_population,
|
320 |
+
df_total_trips_allNodes_sumPerson_proCat_t_sum["Tot_Res"],
|
321 |
+
df_total_trips_allNodes_sumPerson_proCat_t_sum["Tot_tou"],
|
322 |
+
distance_matrices,
|
323 |
+
metric_matrices,
|
324 |
+
redistributeTrips,
|
325 |
+
DISTANCE_BRACKETS,
|
326 |
+
ALPHA_LOW, ALPHA_MED, ALPHA_HIGH, ALPHA, ALPHA_UNIFORM, F_VALUES_MANUAL,
|
327 |
+
CLIENT,
|
328 |
+
STREAM_ID,
|
329 |
+
TARGET_BRANCH_TM,
|
330 |
+
sourceCommits
|
331 |
+
)
|
332 |
+
|
333 |
+
print(logs)
|
main.py
ADDED
@@ -0,0 +1,385 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import json
|
3 |
+
import pandas as pd
|
4 |
+
import copy
|
5 |
+
from functools import wraps
|
6 |
+
from specklepy.api.client import SpeckleClient
|
7 |
+
from tripGenerationFunc import *
|
8 |
+
import speckle_utils
|
9 |
+
import data_utils
|
10 |
+
import gradio as gr
|
11 |
+
import requests
|
12 |
+
from huggingface_hub import webhook_endpoint, WebhookPayload
|
13 |
+
from fastapi import Request
|
14 |
+
import datetime
|
15 |
+
|
16 |
+
current_directory = os.path.dirname(os.path.abspath(__file__))
|
17 |
+
# Path to the config.json file
|
18 |
+
config_file_path = os.path.join(current_directory, "config.json")
|
19 |
+
|
20 |
+
# Check if the config.json file exists
|
21 |
+
if os.path.exists(config_file_path):
|
22 |
+
# Load the JSON data from config.json
|
23 |
+
with open(config_file_path, 'r') as f:
|
24 |
+
config = json.load(f)
|
25 |
+
|
26 |
+
# Convert to Python variables with the same names as the keys in the JSON
|
27 |
+
locals().update(config)
|
28 |
+
print("varaibles from json")
|
29 |
+
# Now you can access the variables directly
|
30 |
+
print(STREAM_ID)
|
31 |
+
print(BRANCH_NAME_LAND_USES)
|
32 |
+
print(TARGET_TRIP_RATE)
|
33 |
+
print(ALPHA_LOW)
|
34 |
+
print(F_VALUES_MANUAL)
|
35 |
+
print(distance_matrices_of_interest)
|
36 |
+
print(redistributeTrips)
|
37 |
+
print(DISTANCE_BRACKETS)
|
38 |
+
print(XLS_FILE_PATH)
|
39 |
+
print("==================")
|
40 |
+
else:
|
41 |
+
print("Error: config.json file not found in the current directory.")
|
42 |
+
|
43 |
+
|
44 |
+
# checks payload of webhook and runs the main code if webhook was triggered by specified stream + one of the branches
|
45 |
+
listendStreams = [STREAM_ID]
|
46 |
+
listendBranchNames = [BRANCH_NAME_LAND_USES,BRANCH_NAME_DISTANCE_MATRIX,BRANCH_NAME_METRIC_DIST_MATRIX]
|
47 |
+
|
48 |
+
@webhook_endpoint
|
49 |
+
async def update_streams(request: Request):
|
50 |
+
# Initialize flag
|
51 |
+
should_continue = False
|
52 |
+
|
53 |
+
# Read the request body as JSON
|
54 |
+
payload = await request.json()
|
55 |
+
|
56 |
+
# Check if the payload structure matches the expected format
|
57 |
+
if "event" in payload and "data" in payload["event"]:
|
58 |
+
event_data = payload["event"]["data"]
|
59 |
+
|
60 |
+
# Check if the event type is "commit_create"
|
61 |
+
if "type" in event_data and event_data["type"] == "commit_create":
|
62 |
+
# Check if the stream name matches the specified list
|
63 |
+
if "stream" in event_data and event_data["stream"] in listendStreams:
|
64 |
+
# Check if the branch name matches the specified list
|
65 |
+
if "commit" in event_data and "branchName" in event_data["commit"]:
|
66 |
+
if event_data["commit"]["branchName"] in listendBranchNames:
|
67 |
+
should_continue = True
|
68 |
+
else:
|
69 |
+
print("Branch name not found in payload.")
|
70 |
+
else:
|
71 |
+
print("Stream name not found or not in the specified list.")
|
72 |
+
else:
|
73 |
+
print("Event type is not 'commit_create'.")
|
74 |
+
else:
|
75 |
+
print("Payload structure does not match the expected format.")
|
76 |
+
|
77 |
+
# If the flag is True, continue running the main part of the code
|
78 |
+
if should_continue:
|
79 |
+
# Your main code logic goes here
|
80 |
+
runAll()
|
81 |
+
else:
|
82 |
+
print("Flag is False. Skipping further execution.")
|
83 |
+
|
84 |
+
return "Webhook processing complete."
|
85 |
+
|
86 |
+
|
87 |
+
|
88 |
+
def runAll():
|
89 |
+
# get config file:# Parse JSON
|
90 |
+
|
91 |
+
speckle_token = os.environ.get("SPECKLE_TOKEN")
|
92 |
+
|
93 |
+
|
94 |
+
|
95 |
+
|
96 |
+
|
97 |
+
xls_file_path = os.path.join(current_directory, XLS_FILE_PATH)
|
98 |
+
print("full path", xls_file_path)
|
99 |
+
# fetch speckle data
|
100 |
+
CLIENT = SpeckleClient(host="https://speckle.xyz/")
|
101 |
+
CLIENT.authenticate_with_token(token="52566d1047b881764e16ad238356abeb2fc35d8b42")
|
102 |
+
|
103 |
+
# get land use stream
|
104 |
+
stream_land_use = speckle_utils.getSpeckleStream(STREAM_ID,
|
105 |
+
BRANCH_NAME_LAND_USES,
|
106 |
+
CLIENT,
|
107 |
+
commit_id = "")
|
108 |
+
# navigate to list with speckle objects of interest
|
109 |
+
stream_data = stream_land_use["@Data"]["@{0}"]
|
110 |
+
|
111 |
+
# transform stream_data to dataframe (create a backup copy of this dataframe)
|
112 |
+
df_speckle_lu = speckle_utils.get_dataframe(stream_data, return_original_df=False)
|
113 |
+
df_main = df_speckle_lu.copy()
|
114 |
+
|
115 |
+
# set index column
|
116 |
+
df_main = df_main.set_index("ids", drop=False)
|
117 |
+
|
118 |
+
|
119 |
+
# get distance matrix stream
|
120 |
+
stream_distance_matrice = speckle_utils.getSpeckleStream(STREAM_ID,
|
121 |
+
BRANCH_NAME_DISTANCE_MATRIX,
|
122 |
+
CLIENT,
|
123 |
+
commit_id = "")
|
124 |
+
|
125 |
+
# navigate to list with speckle objects of interest
|
126 |
+
distance_matrices = {}
|
127 |
+
for distM in stream_distance_matrice["@Data"]['@{0}']:
|
128 |
+
for kk in distM.__dict__.keys():
|
129 |
+
try:
|
130 |
+
if kk.split("+")[1].startswith("distance_matrix"):
|
131 |
+
distance_matrix_dict = json.loads(distM[kk])
|
132 |
+
origin_ids = distance_matrix_dict["origin_uuid"]
|
133 |
+
destination_ids = distance_matrix_dict["destination_uuid"]
|
134 |
+
distance_matrix = distance_matrix_dict["matrix"]
|
135 |
+
# Convert the distance matrix to a DataFrame
|
136 |
+
df_distances = pd.DataFrame(distance_matrix, index=origin_ids, columns=destination_ids)
|
137 |
+
|
138 |
+
# i want to add the index & colum names to dist_m_csv
|
139 |
+
#distance_matrices[kk] = dist_m_csv[kk]
|
140 |
+
distance_matrices[kk] = df_distances
|
141 |
+
|
142 |
+
except:
|
143 |
+
pass
|
144 |
+
|
145 |
+
|
146 |
+
# get metric matrix stream
|
147 |
+
stream_metric_matrice = speckle_utils.getSpeckleStream(STREAM_ID,
|
148 |
+
BRANCH_NAME_METRIC_DIST_MATRIX,
|
149 |
+
CLIENT,
|
150 |
+
commit_id = "")
|
151 |
+
|
152 |
+
|
153 |
+
# navigate to list with speckle objects of interest
|
154 |
+
metric_matrices = {}
|
155 |
+
for distM in stream_metric_matrice["@Data"]['@{0}']:
|
156 |
+
print(distM.__dict__.keys())
|
157 |
+
for kk in distM.__dict__.keys():
|
158 |
+
try:
|
159 |
+
if kk.split("+")[1].startswith("metric_matrix"):
|
160 |
+
metric_matrix_dict = json.loads(distM[kk])
|
161 |
+
origin_ids = metric_matrix_dict["origin_uuid"]
|
162 |
+
destination_ids = metric_matrix_dict["destination_uuid"]
|
163 |
+
metric_matrix = metric_matrix_dict["matrix"]
|
164 |
+
# Convert the distance matrix to a DataFrame
|
165 |
+
df_metric_dist = pd.DataFrame(metric_matrix, index=origin_ids, columns=destination_ids)
|
166 |
+
metric_matrices[kk] = df_metric_dist*10 #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
167 |
+
|
168 |
+
print("metric_matrix_dict", metric_matrix_dict.keys())
|
169 |
+
except:
|
170 |
+
pass
|
171 |
+
|
172 |
+
metric_matrices = extract_distance_matrices(stream_metric_matrice, metric_matrices_of_interest)
|
173 |
+
|
174 |
+
|
175 |
+
sourceCommits = {
|
176 |
+
"landuseCommitID": stream_land_use.id,
|
177 |
+
"distanceMatrixCommitID": stream_distance_matrice.id,
|
178 |
+
"metricMatrixCommitID": stream_metric_matrice.id
|
179 |
+
}
|
180 |
+
|
181 |
+
|
182 |
+
# READ XLS FILE ======================================
|
183 |
+
# Read Excel file into Pandas DataFrame
|
184 |
+
#Production
|
185 |
+
# Load Excel file separately
|
186 |
+
#xls_file_path = os.path.join(current_directory, XLS_FILE_PATH)
|
187 |
+
if os.path.exists(xls_file_path):
|
188 |
+
# Production
|
189 |
+
df_production = pd.read_excel(xls_file_path, sheet_name='Production')
|
190 |
+
df_production_transposed = df_production.T
|
191 |
+
df_production = preprocess_dataFrame(df_production, headerRow_idx=2, numRowsStart_idx=3)
|
192 |
+
df_production_transposed = preprocess_dataFrame(df_production_transposed, headerRow_idx=0, numRowsStart_idx=4,
|
193 |
+
numColsStart_idx=4, rowNames_idx=2)
|
194 |
+
|
195 |
+
# Attraction
|
196 |
+
df_attraction = pd.read_excel(xls_file_path, sheet_name='Attraction')
|
197 |
+
df_attraction = preprocess_dataFrame(df_attraction, headerRow_idx=0, numRowsStart_idx=2)
|
198 |
+
|
199 |
+
# Distribution_Matrix
|
200 |
+
df_distributionMatrix = pd.read_excel(xls_file_path, sheet_name='Distribution_Matrix')
|
201 |
+
df_distributionMatrix = preprocess_dataFrame(df_distributionMatrix, headerRow_idx=0, numRowsStart_idx=2,
|
202 |
+
numRowsEnd_idx=None, numColsStart_idx=2, numColsEnd_idx=None,
|
203 |
+
rowNames_idx=0)
|
204 |
+
|
205 |
+
# Alphas
|
206 |
+
df_alphas = pd.read_excel(xls_file_path, sheet_name='Alphas')
|
207 |
+
df_alphas.columns = df_alphas.iloc[1]
|
208 |
+
df_alphas = df_alphas.iloc[0, 2:]
|
209 |
+
|
210 |
+
# Land use
|
211 |
+
df_lu = pd.read_excel(xls_file_path, sheet_name='Example_Land_Use')
|
212 |
+
df_lu = preprocess_dataFrame(df_lu, headerRow_idx=0, numRowsStart_idx=1)
|
213 |
+
df_lu["nameCombined"] = df_lu.iloc[:, 1].astype(str) + "+" + df_lu.iloc[:, 0].astype(str)
|
214 |
+
|
215 |
+
# Distance Matrix
|
216 |
+
df_distMatrix = pd.read_excel(xls_file_path, sheet_name='Example_Distance_Matrix')
|
217 |
+
df_distMatrix = preprocess_dataFrame(df_distMatrix, headerRow_idx=0, numRowsStart_idx=1, numRowsEnd_idx=None,
|
218 |
+
numColsStart_idx=1, numColsEnd_idx=None, rowNames_idx=0)
|
219 |
+
else:
|
220 |
+
print("Error: Excel file specified in config.json not found.")
|
221 |
+
|
222 |
+
|
223 |
+
|
224 |
+
# Land use strucutre =======
|
225 |
+
# THIS IS THE DISTANCE MATRIX THATS USED DOWN THE ROAD
|
226 |
+
df_distances_aligned, df_lu_stream_aligned = align_dataframes(distance_matrices[distanceMatrixName], df_main, 'ids')
|
227 |
+
|
228 |
+
#Create a df with lanuses
|
229 |
+
lu_cols = [col for col in df_lu_stream_aligned.columns if col.startswith("lu+")]
|
230 |
+
df_lu_stream = df_lu_stream_aligned[lu_cols]
|
231 |
+
|
232 |
+
# Remove "lu+" from the beginning of column names
|
233 |
+
df_lu_stream.columns = df_lu_stream.columns.str.lstrip('lu+')
|
234 |
+
df_lu_stream = df_lu_stream.T
|
235 |
+
|
236 |
+
df_lu_stream_t = df_lu_stream.T
|
237 |
+
|
238 |
+
df_lu_stream_with_nameLu_column = df_lu_stream.reset_index(drop=False).rename(columns={'index': 'nameLu'})
|
239 |
+
|
240 |
+
#---
|
241 |
+
df_lu_names_xlsx = pd.concat([df_lu.iloc[:, 0:2], df_lu.iloc[:, -1]], axis=1)
|
242 |
+
df_lu_names_xlsx.index = df_lu_names_xlsx.iloc[:, 1]
|
243 |
+
column_names = ['nameTripType', 'nameLu', 'nameCombined']
|
244 |
+
df_lu_names_xlsx.columns = column_names
|
245 |
+
print(f"df_lu_names_xlsx shape: {df_lu_names_xlsx.shape}")
|
246 |
+
df_lu_names_xlsx.head()
|
247 |
+
|
248 |
+
#--
|
249 |
+
|
250 |
+
# Merge DataFrames using an outer join
|
251 |
+
merged_df = pd.merge(df_lu_stream_with_nameLu_column, df_lu_names_xlsx, on='nameLu', how='outer')
|
252 |
+
|
253 |
+
# Get the unique names and their counts from df_lu_names_xlsx
|
254 |
+
name_counts = df_lu_names_xlsx['nameLu'].value_counts()
|
255 |
+
#print(name_counts)
|
256 |
+
|
257 |
+
# Identify names in df_lu_stream_with_nameLu_column that are not in df_lu_names_xlsx
|
258 |
+
missing_names = df_lu_stream_with_nameLu_column.loc[~df_lu_stream_with_nameLu_column['nameLu'].isin(df_lu_names_xlsx['nameLu'])]
|
259 |
+
|
260 |
+
# Append missing rows to df_lu_stream_with_nameLu_column
|
261 |
+
df_lu_stream_duplicated = pd.concat([merged_df, missing_names], ignore_index=True)
|
262 |
+
|
263 |
+
|
264 |
+
#--
|
265 |
+
# Find names in df_lu_names_xlsx that are not in df_lu_stream_with_nameLu_column
|
266 |
+
missing_names = df_lu_names_xlsx.loc[~df_lu_names_xlsx['nameLu'].isin(df_lu_stream_with_nameLu_column['nameLu'])]
|
267 |
+
|
268 |
+
#--
|
269 |
+
# print existing names (?)
|
270 |
+
df_lu_names_sorted = df_lu_names_xlsx.sort_values(by='nameLu')
|
271 |
+
df_lu_stream_duplicated_sorted = df_lu_stream_duplicated.sort_values(by='nameLu')
|
272 |
+
#--
|
273 |
+
# Merge DataFrames to get the order of names
|
274 |
+
merged_order = pd.merge(df_lu_names_xlsx[['nameCombined']], df_lu_stream_duplicated[['nameCombined']], on='nameCombined', how='inner')
|
275 |
+
|
276 |
+
# Sort df_lu_stream_duplicated based on the order of names in df_lu_names_xlsx
|
277 |
+
df_lu_stream_sorted = df_lu_stream_duplicated.sort_values(by='nameCombined', key=lambda x: pd.Categorical(x, categories=merged_order['nameCombined'], ordered=True))
|
278 |
+
|
279 |
+
# Reorganize columns
|
280 |
+
column_order = ['nameTripType', 'nameCombined'] + [col for col in df_lu_stream_sorted.columns if col not in ['nameTripType', 'nameCombined']]
|
281 |
+
|
282 |
+
# Create a new DataFrame with the desired column order
|
283 |
+
df_lu_stream_reordered = df_lu_stream_sorted[column_order]
|
284 |
+
|
285 |
+
df_lu_stream_reordered_t = df_lu_stream_reordered.T
|
286 |
+
|
287 |
+
#--
|
288 |
+
df_lu_stream_with_index = df_lu_stream_reordered_t.reset_index(drop=False).rename(columns={'index': 'ids'})
|
289 |
+
df_lu_stream_with_index.index = df_lu_stream_reordered_t.index
|
290 |
+
|
291 |
+
df_lu_num_t_index = df_lu_stream_with_index.iloc[3:]
|
292 |
+
|
293 |
+
df_distances_aligned_index = df_distances_aligned.reset_index(drop=False).rename(columns={'index': 'ids'})
|
294 |
+
df_distances_aligned_index.index = df_distances_aligned.index
|
295 |
+
|
296 |
+
df_lu_namesCombined = df_lu_stream_with_index.loc["nameCombined"].iloc[1:]
|
297 |
+
|
298 |
+
# Sort df_lu_stream_with_index based on the 'ids' column in df_distances_aligned_index
|
299 |
+
df_lu_stream_sorted = df_lu_stream_with_index.sort_values(by=['ids'], key=lambda x: pd.Categorical(x, categories=df_distances_aligned_index['ids'], ordered=True))
|
300 |
+
|
301 |
+
|
302 |
+
df_lu_num = df_lu_stream_sorted.T.iloc[1:, :-3]
|
303 |
+
df_lu_num.index = df_lu_namesCombined
|
304 |
+
|
305 |
+
df_distMatrix_speckle = df_distances_aligned
|
306 |
+
|
307 |
+
df_attraction_num = df_attraction.reset_index().iloc[:-1, 6:]
|
308 |
+
|
309 |
+
# =============================================================================
|
310 |
+
# TRIP GENERATION
|
311 |
+
|
312 |
+
# ATTRACTION & PRODUCTION ======================================================
|
313 |
+
"""
|
314 |
+
INPUTS
|
315 |
+
df_attraction_num
|
316 |
+
df_lu_num
|
317 |
+
df_production
|
318 |
+
df_lu
|
319 |
+
df_production_transposed
|
320 |
+
"""
|
321 |
+
|
322 |
+
df_attraction_proNode_sum_total = attraction_proNode_full_iter(df_attraction_num, df_lu_num, True)
|
323 |
+
|
324 |
+
#Get the sqmProPerson
|
325 |
+
df_sqmProPerson = df_production.iloc[0, 4:].reset_index()[3]
|
326 |
+
|
327 |
+
#Get the trip rate
|
328 |
+
df_tripRate = copy.deepcopy(df_production) # create a copy ensures df_tripRate doenst point to df_production
|
329 |
+
df_tripRate.index = df_tripRate.iloc[:, 0] #Set the row names
|
330 |
+
df_tripRate = df_tripRate.iloc[1:, 2]
|
331 |
+
|
332 |
+
#Numerical df from production ==============================================
|
333 |
+
df_production_num = df_production.iloc[1:, 4:]
|
334 |
+
df_production_transposed1 = df_production_num.T
|
335 |
+
|
336 |
+
df_total_trips_allNodes = production_proNode_total(df_lu,
|
337 |
+
df_sqmProPerson,
|
338 |
+
df_tripRate,
|
339 |
+
df_production_num,
|
340 |
+
df_production_transposed,
|
341 |
+
df_lu_num, printSteps=False)
|
342 |
+
# Convert data types to float
|
343 |
+
df_total_trips_allNodes = df_total_trips_allNodes.astype(float)
|
344 |
+
df_tripRate = df_tripRate.astype(float)
|
345 |
+
|
346 |
+
df_total_trips_allNodes_sumPerson = df_total_trips_allNodes.div(df_tripRate, axis=0).sum()
|
347 |
+
df_total_trips_allNodes_sumPerson_proCat = df_total_trips_allNodes.div(df_tripRate, axis=0)
|
348 |
+
df_total_trips_allNodes_sumPerson_proCat_t = df_total_trips_allNodes_sumPerson_proCat.T
|
349 |
+
df_total_trips_allNodes_sumPerson_proCat_t_sum = df_total_trips_allNodes_sumPerson_proCat_t.sum()
|
350 |
+
|
351 |
+
# get total population
|
352 |
+
total_population = df_total_trips_allNodes_sumPerson_proCat_t_sum["Tot_Res"] + df_total_trips_allNodes_sumPerson_proCat_t_sum["Tot_tou"]
|
353 |
+
|
354 |
+
|
355 |
+
|
356 |
+
# =============================================================================
|
357 |
+
distance_matrices = extract_distance_matrices(stream_distance_matrice, distance_matrices_of_interest)
|
358 |
+
metric_matrices_ = extract_distance_matrices(stream_metric_matrice, metric_matrices_of_interest)
|
359 |
+
metric_matrices = { k:v*10 for k, v in metric_matrices_.items()} # scale (speckle issue)
|
360 |
+
|
361 |
+
logs = computeTrips(
|
362 |
+
df_distributionMatrix,
|
363 |
+
df_total_trips_allNodes,
|
364 |
+
df_distMatrix_speckle,
|
365 |
+
df_alphas,
|
366 |
+
df_attraction_proNode_sum_total,
|
367 |
+
df_distances_aligned,
|
368 |
+
TARGET_TRIP_RATE,
|
369 |
+
SCALING_FACTOR,
|
370 |
+
total_population,
|
371 |
+
df_total_trips_allNodes_sumPerson_proCat_t_sum["Tot_Res"],
|
372 |
+
df_total_trips_allNodes_sumPerson_proCat_t_sum["Tot_tou"],
|
373 |
+
distance_matrices,
|
374 |
+
metric_matrices,
|
375 |
+
redistributeTrips,
|
376 |
+
DISTANCE_BRACKETS,
|
377 |
+
ALPHA_LOW, ALPHA_MED, ALPHA_HIGH, ALPHA, ALPHA_UNIFORM, F_VALUES_MANUAL,
|
378 |
+
CLIENT,
|
379 |
+
STREAM_ID,
|
380 |
+
TARGET_BRANCH_TM,
|
381 |
+
sourceCommits
|
382 |
+
)
|
383 |
+
|
384 |
+
print(logs)
|
385 |
+
|
requirements.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
pandas==1.3.3
|
2 |
+
numpy==1.21.2
|
3 |
+
numba==0.54.1
|
4 |
+
gradio
|
5 |
+
specklepy
|
6 |
+
requests
|
tripGenerationFunc.py
ADDED
@@ -0,0 +1,910 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import pandas as pd
|
3 |
+
from numba import jit
|
4 |
+
import math
|
5 |
+
import json
|
6 |
+
import os
|
7 |
+
import sys
|
8 |
+
from specklepy.api.client import SpeckleClient
|
9 |
+
from specklepy.api.credentials import get_default_account, get_local_accounts
|
10 |
+
from specklepy.transports.server import ServerTransport
|
11 |
+
from specklepy.api import operations
|
12 |
+
from specklepy.objects.geometry import Polyline, Point
|
13 |
+
from specklepy.objects import Base
|
14 |
+
from specklepy.api import operations, models
|
15 |
+
from specklepy.transports.server import ServerTransport
|
16 |
+
import time
|
17 |
+
from functools import wraps
|
18 |
+
|
19 |
+
|
20 |
+
import speckle_utils
|
21 |
+
import data_utils
|
22 |
+
|
23 |
+
|
24 |
+
|
25 |
+
|
26 |
+
# !!! lots of hard coded values in computeTrips !!!
|
27 |
+
|
28 |
+
# UTILS
|
29 |
+
def reconstruct_dataframe(alpha_low, alpha_med, alpha_high, original_df):
|
30 |
+
# Define the mapping from original values to new alpha parameters
|
31 |
+
value_to_alpha = {
|
32 |
+
0.00191: alpha_low,
|
33 |
+
0.00767: alpha_high,
|
34 |
+
0.0038: alpha_med
|
35 |
+
}
|
36 |
+
|
37 |
+
# Check if each value is present at least once in the DataFrame
|
38 |
+
for original_value in value_to_alpha.keys():
|
39 |
+
if not (original_df == original_value).any().any():
|
40 |
+
raise ValueError(f"Value {original_value} not found in the input DataFrame.")
|
41 |
+
|
42 |
+
# Create a new DataFrame based on the original one
|
43 |
+
new_df = original_df.copy()
|
44 |
+
|
45 |
+
# Apply the mapping to each element in the DataFrame
|
46 |
+
for original_value, new_value in value_to_alpha.items():
|
47 |
+
new_df = new_df.replace(original_value, new_value)
|
48 |
+
|
49 |
+
return new_df
|
50 |
+
|
51 |
+
def preprocess_dataFrame(df, headerRow_idx=0, numRowsStart_idx = None, numRowsEnd_idx=None, numColsStart_idx=None, numColsEnd_idx=None, rowNames_idx=None):
|
52 |
+
df.columns = df.iloc[headerRow_idx] #Set the header
|
53 |
+
if rowNames_idx is not None:
|
54 |
+
df.index = df.iloc[:, rowNames_idx] #Set the row names
|
55 |
+
df = df.iloc[numRowsStart_idx : numRowsEnd_idx, numColsStart_idx:numColsEnd_idx] #Slice the dataset to numerical data
|
56 |
+
return df
|
57 |
+
|
58 |
+
|
59 |
+
|
60 |
+
|
61 |
+
def timeit(f):
|
62 |
+
def timed(*args, **kw):
|
63 |
+
ts = time.time()
|
64 |
+
result = f(*args, **kw)
|
65 |
+
te = time.time()
|
66 |
+
print ('func:%r args:[%r, %r] took: %2.4f sec' % \
|
67 |
+
(f.__name__, te-ts))
|
68 |
+
#(f.__name__, args, kw, te-ts))
|
69 |
+
return result
|
70 |
+
return timed
|
71 |
+
|
72 |
+
|
73 |
+
|
74 |
+
|
75 |
+
|
76 |
+
def timing_decorator(func):
|
77 |
+
@wraps(func)
|
78 |
+
def wrapper(*args, **kwargs):
|
79 |
+
start_time = time.time()
|
80 |
+
result = func(*args, **kwargs)
|
81 |
+
end_time = time.time()
|
82 |
+
|
83 |
+
duration = end_time - start_time
|
84 |
+
timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(end_time))
|
85 |
+
|
86 |
+
print(f"{func.__name__} took {duration:.4f} seconds. Finished at {timestamp}")
|
87 |
+
return result
|
88 |
+
|
89 |
+
return wrapper
|
90 |
+
|
91 |
+
|
92 |
+
# Function to compare two dataframes after converting and rounding
|
93 |
+
def compare_dataframes(df1, df2, decimals=8):
|
94 |
+
# Function to convert DataFrame columns to float and then round
|
95 |
+
def convert_and_round_dataframe(df, decimals):
|
96 |
+
# Convert all columns to float
|
97 |
+
df_float = df.astype(float)
|
98 |
+
# Round to the specified number of decimals
|
99 |
+
return df_float.round(decimals)
|
100 |
+
|
101 |
+
rounded_df1 = convert_and_round_dataframe(df1, decimals)
|
102 |
+
rounded_df2 = convert_and_round_dataframe(df2, decimals)
|
103 |
+
|
104 |
+
are_equal = rounded_df1.equals(rounded_df2)
|
105 |
+
|
106 |
+
print("Both methods are equal:", are_equal)
|
107 |
+
|
108 |
+
print("Numba shape:", df2.shape)
|
109 |
+
print("Original shape:", df1.shape)
|
110 |
+
|
111 |
+
print("======== ORIGINAL OUTPUT (first item in output list, head() for the first 5 columns)")
|
112 |
+
print(df1.iloc[0:5].head(2))
|
113 |
+
|
114 |
+
print("======== New method OUTPUT (first item in output list, head() for the first 5 columns)")
|
115 |
+
print(df2.iloc[0:5].head(2))
|
116 |
+
|
117 |
+
|
118 |
+
def align_dataframes(df1, df2, key):
|
119 |
+
"""
|
120 |
+
Align two dataframes based on a common key, ensuring that both dataframes
|
121 |
+
have only the rows with matching keys.
|
122 |
+
|
123 |
+
Parameters:
|
124 |
+
- df1: First dataframe.
|
125 |
+
- df2: Second dataframe.
|
126 |
+
- key: Column name to align dataframes on.
|
127 |
+
|
128 |
+
Returns:
|
129 |
+
- df1_aligned, df2_aligned: Tuple of aligned dataframes.
|
130 |
+
"""
|
131 |
+
common_ids = df1.index.intersection(df2[key])
|
132 |
+
df1_aligned = df1.loc[common_ids]
|
133 |
+
df2_aligned = df2[df2[key].isin(common_ids)].set_index(key, drop=False)
|
134 |
+
return df1_aligned, df2_aligned
|
135 |
+
|
136 |
+
|
137 |
+
|
138 |
+
#==================================================================================================
|
139 |
+
|
140 |
+
def attraction_proNode(df_attraction_num, df_lu, df_lu_anName=None, printSteps=False):
|
141 |
+
#lu_proNode
|
142 |
+
df_lu_proNode = df_lu.reset_index()[df_lu_anName]
|
143 |
+
if printSteps:
|
144 |
+
print(df_lu_proNode.shape)
|
145 |
+
df_lu_proNode.head(50)
|
146 |
+
|
147 |
+
#attraction_proNode
|
148 |
+
if printSteps:
|
149 |
+
print("df_attraction_num:", df_attraction_num.shape)
|
150 |
+
print("df_lu_proNode:", df_lu_proNode.shape)
|
151 |
+
df_attraction_proNode = df_attraction_num.mul(df_lu_proNode, axis=0)
|
152 |
+
if printSteps:
|
153 |
+
print("df_attraction_proNode:", df_attraction_proNode.shape)
|
154 |
+
df_attraction_proNode.head(100)
|
155 |
+
|
156 |
+
# Sum the values of each column
|
157 |
+
df_attraction_proNode_sum = pd.DataFrame(df_attraction_proNode.sum(), columns=['Sum'])
|
158 |
+
if printSteps:
|
159 |
+
print("df_attraction_proNode_sum:", df_attraction_proNode_sum.shape)
|
160 |
+
df_attraction_proNode_sum.head(100)
|
161 |
+
|
162 |
+
return df_attraction_proNode_sum
|
163 |
+
|
164 |
+
#Non vectorized iterative function
|
165 |
+
def attraction_proNode_full_iter(df_attraction_num, df_lu_num, printSteps=False):
|
166 |
+
|
167 |
+
# Initialize an empty DataFrame
|
168 |
+
df_attraction_proNode_sum_total = pd.DataFrame()
|
169 |
+
|
170 |
+
for column_name, column_data in df_lu_num.items():
|
171 |
+
df_attraction_proNode_sum = attraction_proNode(df_attraction_num, df_lu_num, df_lu_anName=column_name)
|
172 |
+
|
173 |
+
# Concatenate DataFrames along columns
|
174 |
+
df_attraction_proNode_sum_total = pd.concat([df_attraction_proNode_sum_total, df_attraction_proNode_sum], axis=1)
|
175 |
+
|
176 |
+
# Rename columns in df_distBasedAttr_step2 with the same column names as in df_distributionMatrix_step1
|
177 |
+
df_attraction_proNode_sum_total.columns = df_lu_num.columns
|
178 |
+
|
179 |
+
return df_attraction_proNode_sum_total
|
180 |
+
|
181 |
+
|
182 |
+
|
183 |
+
# PRODUCTION ================================================
|
184 |
+
|
185 |
+
def production_proNode(df_lu, df_sqmProPerson, df_tripRate, df_production_num, df_production_transposed, printSteps=False, df_lu_anName=None):
|
186 |
+
|
187 |
+
#lu_proNode - reset index
|
188 |
+
df_lu_proNode = df_lu.reset_index()[df_lu_anName]
|
189 |
+
if printSteps:
|
190 |
+
print(df_lu_proNode.shape)
|
191 |
+
df_lu_proNode.head(50)
|
192 |
+
|
193 |
+
#Get the person count - Divide corresponding values of one DataFrame by another
|
194 |
+
df_personCount = df_lu_proNode.div(df_sqmProPerson)
|
195 |
+
if printSteps:
|
196 |
+
print(df_personCount.shape)
|
197 |
+
print(df_personCount)
|
198 |
+
|
199 |
+
# Ensure the index is unique in df_personCount
|
200 |
+
df_personCount = df_personCount.reset_index(drop=True)
|
201 |
+
df_production_transposed = df_production_transposed.reset_index(drop=True)
|
202 |
+
if printSteps:
|
203 |
+
df_production_transposed.head()
|
204 |
+
|
205 |
+
if printSteps:
|
206 |
+
df_personCount.head()
|
207 |
+
df_tripRate.head()
|
208 |
+
|
209 |
+
|
210 |
+
#Calculate trip production pro node
|
211 |
+
|
212 |
+
df_production_proNode = df_production_transposed
|
213 |
+
df_production_proNode = df_production_proNode.mul(df_personCount, axis=0)
|
214 |
+
|
215 |
+
|
216 |
+
df_production_proNode = df_production_proNode.T
|
217 |
+
df_production_proNode = df_production_proNode.mul(df_tripRate, axis=0)
|
218 |
+
|
219 |
+
|
220 |
+
#Total trips
|
221 |
+
df_production_proNode_rowSum = df_production_proNode.sum(axis=1)
|
222 |
+
df_total_trips = df_production_proNode_rowSum
|
223 |
+
#if printSteps:
|
224 |
+
#df_total_trips.head(50)
|
225 |
+
|
226 |
+
return df_total_trips
|
227 |
+
|
228 |
+
#Non vectorized iterative function
|
229 |
+
def production_proNode_total(df_lu, df_sqmProPerson, df_tripRate, df_production_num, df_production_transposed, df_lu_num, printSteps=False):
|
230 |
+
|
231 |
+
# Initialize an empty DataFrame
|
232 |
+
df_total_trips_allNodes = pd.DataFrame()
|
233 |
+
|
234 |
+
for column_name, column_data in df_lu_num.items():
|
235 |
+
df_total_trips_proNode = production_proNode(df_lu_num, df_sqmProPerson, df_tripRate, df_production_num, df_production_transposed, printSteps=False, df_lu_anName=column_name)
|
236 |
+
|
237 |
+
# Concatenate DataFrames along columns
|
238 |
+
df_total_trips_allNodes = pd.concat([df_total_trips_allNodes, df_total_trips_proNode], axis=1)
|
239 |
+
|
240 |
+
# Rename columns in df_distBasedAttr_step2 with the same column names as in df_distributionMatrix_step1
|
241 |
+
df_total_trips_allNodes.columns = df_lu_num.columns
|
242 |
+
|
243 |
+
return df_total_trips_allNodes
|
244 |
+
|
245 |
+
|
246 |
+
#df_total_trips_allNodes = production_proNode_total(df_lu, df_sqmProPerson, df_tripRate, df_production_num, df_production_transposed, df_lu_num, printSteps=False)
|
247 |
+
#df_total_trips_allNodes.head(50)
|
248 |
+
|
249 |
+
#==================================================================================================
|
250 |
+
|
251 |
+
#STEP 1
|
252 |
+
def step_1(df_distributionMatrix, df_total_trips_allNodes):
|
253 |
+
l = []
|
254 |
+
#counter=0
|
255 |
+
for column_name, column_data in df_total_trips_allNodes.items():
|
256 |
+
df_distributionMatrix_step1_proNode = df_distributionMatrix.mul(column_data, axis = 0)
|
257 |
+
l.append(df_distributionMatrix_step1_proNode)
|
258 |
+
|
259 |
+
return l
|
260 |
+
|
261 |
+
#STEP 2
|
262 |
+
def step_2_vectorized(df_distMatrix, df_alphas):
|
263 |
+
# Convert df_distMatrix to a 2D array: Shape (1464, 1464)
|
264 |
+
distMatrix_array = df_distMatrix.values
|
265 |
+
|
266 |
+
# Convert df_alphas to a 1D array: Shape (26,)
|
267 |
+
alphas_array = df_alphas.values
|
268 |
+
|
269 |
+
# Initialize an empty array to store results: Shape (1464, 1464, 26)
|
270 |
+
result_3d = np.zeros((distMatrix_array.shape[0], distMatrix_array.shape[1], len(alphas_array)))
|
271 |
+
|
272 |
+
# Loop over alphas and perform element-wise multiplication followed by exponential function
|
273 |
+
for i in range(len(alphas_array)):
|
274 |
+
result_3d[:, :, i] = np.exp(-distMatrix_array * alphas_array[i])
|
275 |
+
|
276 |
+
# Construct the final list of DataFrames
|
277 |
+
final_list = [pd.DataFrame(result_3d[i, :, :], columns=df_alphas.index, index=df_distMatrix.index) for i in range(result_3d.shape[0])]
|
278 |
+
|
279 |
+
return final_list
|
280 |
+
|
281 |
+
# Step 3
|
282 |
+
@jit(nopython=True)
|
283 |
+
def multiply_and_sum(arr, attraction_arr):
|
284 |
+
# Element-wise multiplication
|
285 |
+
multiplied_arr = arr * attraction_arr
|
286 |
+
# Sum the values of each column
|
287 |
+
summed_arr = multiplied_arr.sum(axis=0)
|
288 |
+
return multiplied_arr, summed_arr
|
289 |
+
|
290 |
+
def step_3_numba(df_attraction_proNode_sum_total, df_step_2):
|
291 |
+
# Convert df_attraction_proNode_sum_total to a NumPy array and transpose it
|
292 |
+
attraction_array = df_attraction_proNode_sum_total.values.T.astype(np.float64) # Ensure float64 dtype
|
293 |
+
|
294 |
+
multiplied_results = []
|
295 |
+
summed_results = []
|
296 |
+
|
297 |
+
for df in df_step_2:
|
298 |
+
# Convert DataFrame to NumPy array with float64 dtype
|
299 |
+
df_array = df.values.astype(np.float64)
|
300 |
+
|
301 |
+
# Perform element-wise multiplication and summing
|
302 |
+
multiplied_arr, summed_arr = multiply_and_sum(df_array, attraction_array)
|
303 |
+
|
304 |
+
# Convert results back to DataFrames
|
305 |
+
df_multiplied = pd.DataFrame(multiplied_arr, columns=df.columns, index=df.index)
|
306 |
+
|
307 |
+
# Reshape summed_arr to have shape (26,1) and then convert to DataFrame
|
308 |
+
df_summed = pd.DataFrame(summed_arr.reshape(-1, 1), index=df.columns, columns=['Sum'])
|
309 |
+
|
310 |
+
multiplied_results.append(df_multiplied)
|
311 |
+
summed_results.append(df_summed)
|
312 |
+
|
313 |
+
return multiplied_results, summed_results
|
314 |
+
|
315 |
+
|
316 |
+
# step 4
|
317 |
+
@jit(nopython=True)
|
318 |
+
def divide_and_sum(arr, divisor_arr):
|
319 |
+
# Ensure divisor_arr is broadcastable to arr's shape
|
320 |
+
divisor_arr_expanded = divisor_arr.reshape((divisor_arr.shape[0], 1, divisor_arr.shape[1]))
|
321 |
+
|
322 |
+
# Initialize arrays to store results
|
323 |
+
divided_result = np.zeros_like(arr)
|
324 |
+
summed_result = np.zeros((arr.shape[0], arr.shape[2]))
|
325 |
+
|
326 |
+
for i in range(arr.shape[0]):
|
327 |
+
for j in range(arr.shape[1]):
|
328 |
+
for k in range(arr.shape[2]):
|
329 |
+
if divisor_arr_expanded[i, 0, k] != 0:
|
330 |
+
divided_result[i, j, k] = arr[i, j, k] / divisor_arr_expanded[i, 0, k]
|
331 |
+
summed_result[i, k] += divided_result[i, j, k]
|
332 |
+
|
333 |
+
return divided_result, summed_result
|
334 |
+
|
335 |
+
def step_4_numba(distAndAreaBasedAttr_step3, distAndAreaBasedAttr_step3_sum):
|
336 |
+
# Convert lists of DataFrames to 3D arrays with dtype float64
|
337 |
+
array_step3 = np.array([df.values for df in distAndAreaBasedAttr_step3]).astype(np.float64)
|
338 |
+
array_step3_sum = np.array([df.values for df in distAndAreaBasedAttr_step3_sum]).astype(np.float64)
|
339 |
+
|
340 |
+
# Perform division and summation using Numba
|
341 |
+
divided_result, summed_result = divide_and_sum(array_step3, array_step3_sum)
|
342 |
+
|
343 |
+
# Convert results back to lists of DataFrames
|
344 |
+
df_distAndAreaBasedAttr_step4 = [pd.DataFrame(divided_result[i], columns=distAndAreaBasedAttr_step3[0].columns, index=distAndAreaBasedAttr_step3[0].index) for i in range(divided_result.shape[0])]
|
345 |
+
|
346 |
+
# Correct the creation of the summed DataFrame to avoid setting the 'Sum' index
|
347 |
+
df_distAndAreaBasedAttr_step4_sum = [pd.DataFrame(summed_result[i]).T.set_axis(['Sum'], axis='index').set_axis(distAndAreaBasedAttr_step3[0].columns, axis='columns') for i in range(summed_result.shape[0])]
|
348 |
+
|
349 |
+
return df_distAndAreaBasedAttr_step4, df_distAndAreaBasedAttr_step4_sum
|
350 |
+
|
351 |
+
# step 5
|
352 |
+
@jit(nopython=True)
|
353 |
+
def tripsPerArctivity_numba(matrix, attrs):
|
354 |
+
rows, cols = attrs.shape[0], matrix.shape[0] # 1464, 26
|
355 |
+
result = np.zeros((cols, rows), dtype=np.float64) # Prepare result matrix (26, 1464)
|
356 |
+
|
357 |
+
for i in range(rows): # Iterate over each area
|
358 |
+
for j in range(cols): # Iterate over each land use category
|
359 |
+
sum_val = 0.0
|
360 |
+
for k in range(cols): # Iterate over each element in the distribution matrix row
|
361 |
+
sum_val += matrix[j, k] * attrs[i, k]
|
362 |
+
result[j, i] = sum_val
|
363 |
+
|
364 |
+
return result
|
365 |
+
|
366 |
+
def step_5_numba(distributionMatrix_step1, distAndAreaBasedAttr_step4):
|
367 |
+
sums = []
|
368 |
+
count = 0
|
369 |
+
total_count = len(distributionMatrix_step1)
|
370 |
+
|
371 |
+
for df_distributionMatrix_step1, df_distAndAreaBasedAttr_step4 in zip(distributionMatrix_step1, distAndAreaBasedAttr_step4):
|
372 |
+
# Convert DataFrames to NumPy arrays with dtype float64
|
373 |
+
matrix = df_distributionMatrix_step1.values.astype(np.float64)
|
374 |
+
attrs = df_distAndAreaBasedAttr_step4.values.astype(np.float64)
|
375 |
+
|
376 |
+
result = tripsPerArctivity_numba(matrix, attrs)
|
377 |
+
df_result = pd.DataFrame(result, index=df_distributionMatrix_step1.columns, columns=df_distAndAreaBasedAttr_step4.index)
|
378 |
+
|
379 |
+
sums.append(df_result)
|
380 |
+
|
381 |
+
count += 1
|
382 |
+
#print(f"Iteration {count} out of {total_count} is finished.")
|
383 |
+
#print("---------")
|
384 |
+
|
385 |
+
return sums
|
386 |
+
|
387 |
+
|
388 |
+
# step 6&7
|
389 |
+
def step_6_7_vectorized(df_trips_proNode_proActivity_total):
|
390 |
+
# Convert each DataFrame to a NumPy array and stack them to form a 3D array
|
391 |
+
array_3d = np.array([df.values for df in df_trips_proNode_proActivity_total])
|
392 |
+
|
393 |
+
# Sum across the middle axis (columns of each DataFrame)
|
394 |
+
summed_array = array_3d.sum(axis=1)
|
395 |
+
|
396 |
+
# Convert the summed array back to a DataFrame
|
397 |
+
final_matrix = pd.DataFrame(summed_array, index=df_trips_proNode_proActivity_total[0].columns, columns=df_trips_proNode_proActivity_total[0].columns)
|
398 |
+
|
399 |
+
return final_matrix
|
400 |
+
|
401 |
+
|
402 |
+
# step 8
|
403 |
+
|
404 |
+
def adjTripRate_adjFactor(tripMatrix,df_total_trips_allNodes_sumPerson, targetRate=1, factor=1 ):
|
405 |
+
df_tripMatrix_total_sum = tripMatrix.sum().sum()
|
406 |
+
df_total_trips_allNodes_sumPerson_total = df_total_trips_allNodes_sumPerson.sum()
|
407 |
+
|
408 |
+
# scale to target trip rate
|
409 |
+
tripRateBeforeAdjustment = df_tripMatrix_total_sum/df_total_trips_allNodes_sumPerson_total
|
410 |
+
print("tripRateBeforeAdjustment",tripRateBeforeAdjustment)
|
411 |
+
adjustmentRate = targetRate/tripRateBeforeAdjustment
|
412 |
+
print("adjustmentRate",adjustmentRate)
|
413 |
+
|
414 |
+
# scale by ... scale factor (outdated, was hardcoded )
|
415 |
+
df_tripMatrix_adjusted = tripMatrix * adjustmentRate
|
416 |
+
#df_tripMatrix_adjusted_scaled = df_tripMatrix_adjusted.div(factor)
|
417 |
+
return df_tripMatrix_adjusted, df_tripMatrix_adjusted # df_tripMatrix_adjusted_scaled
|
418 |
+
|
419 |
+
# Uniform Matrix
|
420 |
+
def decay(d, alpha):
|
421 |
+
return math.exp(d * alpha * -1)
|
422 |
+
|
423 |
+
def distanceDecay(df, alpha):
|
424 |
+
return df.applymap(lambda x: decay(x, alpha))
|
425 |
+
|
426 |
+
def matrix_reduce_add(df):
|
427 |
+
return df[df != sys.float_info.max].sum().sum()
|
428 |
+
|
429 |
+
def replace_maxValue(df):
|
430 |
+
return df.replace(sys.float_info.max, 0)
|
431 |
+
|
432 |
+
|
433 |
+
#Trip gen matrix is used to scale the distance matrix
|
434 |
+
def getUniformMatrix(distanceMatrix, tripGenMatrix, alpha):
|
435 |
+
|
436 |
+
distanceMatrix_withDecay = distanceDecay(distanceMatrix, alpha)
|
437 |
+
distanceMatrix_sum = matrix_reduce_add(distanceMatrix_withDecay)
|
438 |
+
tripGenMatrix_sum = matrix_reduce_add(tripGenMatrix)
|
439 |
+
ratio = distanceMatrix_sum / tripGenMatrix_sum
|
440 |
+
|
441 |
+
uniformMatrix = distanceMatrix_withDecay.div(ratio)
|
442 |
+
|
443 |
+
return replace_maxValue(uniformMatrix)
|
444 |
+
|
445 |
+
|
446 |
+
#==================================================================================================
|
447 |
+
#Modal Split functions
|
448 |
+
|
449 |
+
def computeModalShare(trip_matrix, dist_matrices, alpha, f_values=None):
|
450 |
+
"""
|
451 |
+
Process matrices or DataFrames with exponentiation and normalization.
|
452 |
+
|
453 |
+
Args:
|
454 |
+
trip_matrix (np.ndarray or pd.DataFrame): The trip matrix.
|
455 |
+
dist_matrices (dict of np.ndarray or pd.DataFrame): Dictionary of distance matrices.
|
456 |
+
alpha (float): The alpha coefficient.
|
457 |
+
f_values (dict of float, optional): Dictionary of f coefficients for each matrix. If None, defaults to 0 for each matrix.
|
458 |
+
|
459 |
+
Returns:
|
460 |
+
dict: Normalized matrices.
|
461 |
+
"""
|
462 |
+
|
463 |
+
# Default f_values to 0 for each key in dist_matrices if not provided
|
464 |
+
if not f_values:
|
465 |
+
f_values = {key: 0 for key in dist_matrices.keys()}
|
466 |
+
|
467 |
+
exp_matrices = {}
|
468 |
+
for key, matrix in dist_matrices.items():
|
469 |
+
f = f_values.get(key, 0)
|
470 |
+
|
471 |
+
# Convert DataFrame to numpy array if needed
|
472 |
+
if isinstance(matrix, pd.DataFrame):
|
473 |
+
matrix = matrix.values
|
474 |
+
|
475 |
+
exp_matrix = np.exp(-1 * (matrix * alpha + f))
|
476 |
+
exp_matrices[key] = exp_matrix
|
477 |
+
|
478 |
+
# Calculate the sum of all exponentials
|
479 |
+
sum_exp = sum(exp_matrices.values())
|
480 |
+
|
481 |
+
# Normalize each matrix & multiply by trip matrix and update the matrices
|
482 |
+
normalized_matrices = {key: (exp_matrix / sum_exp) * trip_matrix for key, exp_matrix in exp_matrices.items()}
|
483 |
+
|
484 |
+
return normalized_matrices
|
485 |
+
|
486 |
+
|
487 |
+
def redistributeModalShares(dist_matrices, trip_matrices, redistribution_rules, threshold=0.5):
|
488 |
+
"""
|
489 |
+
Redistribute trips among mobility networks based on given redistribution rules and when travel times are within a specified threshold.
|
490 |
+
|
491 |
+
Args:
|
492 |
+
dist_matrices (dict): Dictionary of distance matrices (travel times) for different mobility networks, keyed by identifier.
|
493 |
+
trip_matrices (dict): Dictionary of matrices representing the number of trips for each mobility network, keyed by identifier.
|
494 |
+
redistribution_rules (list): List of redistribution rules with "from" and "to" network identifiers.
|
495 |
+
threshold (float): The threshold for considering travel times as similar.
|
496 |
+
|
497 |
+
Returns:
|
498 |
+
dict: Updated dictionary of trip matrices with transferred trips.
|
499 |
+
"""
|
500 |
+
|
501 |
+
# Verify that all specified matrices exist in the input dictionaries
|
502 |
+
for rule in redistribution_rules:
|
503 |
+
if rule["from"] not in dist_matrices or rule["from"] not in trip_matrices:
|
504 |
+
raise ValueError(f"Matrix ID {rule['from']} not found in the inputs.")
|
505 |
+
for to_id in rule["to"]:
|
506 |
+
if to_id not in dist_matrices or to_id not in trip_matrices:
|
507 |
+
raise ValueError(f"Matrix ID {to_id} not found in the inputs.")
|
508 |
+
|
509 |
+
# Copy the trip_matrices to avoid modifying the input directly
|
510 |
+
updated_trip_matrices = {k: v.copy() for k, v in trip_matrices.items()}
|
511 |
+
|
512 |
+
# Redistribute trips based on the rules and the threshold
|
513 |
+
for rule in redistribution_rules:
|
514 |
+
from_matrix_id = rule["from"]
|
515 |
+
from_matrix_trips = updated_trip_matrices[from_matrix_id]
|
516 |
+
from_matrix_dist = dist_matrices[from_matrix_id]
|
517 |
+
|
518 |
+
for to_matrix_id in rule["to"]:
|
519 |
+
to_matrix_dist = dist_matrices[to_matrix_id]
|
520 |
+
|
521 |
+
# Create a boolean array where the absolute difference in travel times is less than or equal to the threshold
|
522 |
+
similar_travel_time = np.abs(from_matrix_dist - to_matrix_dist) <= threshold
|
523 |
+
|
524 |
+
# Find the indices where there are trips to transfer under the new condition
|
525 |
+
indices_to_transfer = similar_travel_time & (from_matrix_trips > 0)
|
526 |
+
|
527 |
+
# Transfer trips where the condition is True
|
528 |
+
updated_trip_matrices[to_matrix_id][indices_to_transfer] += from_matrix_trips[indices_to_transfer]
|
529 |
+
|
530 |
+
# Zero out the transferred trips in the from_matrix
|
531 |
+
from_matrix_trips[indices_to_transfer] = 0
|
532 |
+
|
533 |
+
# Return the updated trip matrices dictionary
|
534 |
+
return updated_trip_matrices
|
535 |
+
|
536 |
+
|
537 |
+
|
538 |
+
def computeDistanceBrackets(trip_matrices, metric_dist_matrices, dist_brackets=[800, 2400, 4800]):
|
539 |
+
# Transform the keys of metric_dist_matrices to match with trip_matrices
|
540 |
+
transformed_metric_keys = {key.replace("metric_matrix", "distance_matrix")+"_noEntr": matrix
|
541 |
+
for key, matrix in metric_dist_matrices.items()}
|
542 |
+
|
543 |
+
# Initialize dictionary to store aggregated trips per distance bracket
|
544 |
+
bracket_totals = {bracket: 0 for bracket in dist_brackets}
|
545 |
+
|
546 |
+
# Iterate over each pair of trip matrix and distance matrix
|
547 |
+
for key, trip_matrix in trip_matrices.items():
|
548 |
+
# Find the corresponding distance matrix
|
549 |
+
dist_matrix = transformed_metric_keys.get(key)
|
550 |
+
if dist_matrix is None:
|
551 |
+
print("no matrxi found")
|
552 |
+
continue # Skip if no corresponding distance matrix found
|
553 |
+
|
554 |
+
# Calculate trips for each distance bracket
|
555 |
+
for i, bracket in enumerate(dist_brackets):
|
556 |
+
if i == 0:
|
557 |
+
# For the first bracket, count trips with distance <= bracket
|
558 |
+
bracket_totals[bracket] += (trip_matrix[dist_matrix <= bracket]).sum().sum()
|
559 |
+
else:
|
560 |
+
# For subsequent brackets, count trips within the bracket range
|
561 |
+
prev_bracket = dist_brackets[i - 1]
|
562 |
+
bracket_totals[bracket] += (trip_matrix[(dist_matrix > prev_bracket) & (dist_matrix <= bracket)]).sum().sum()
|
563 |
+
brackets_sum = sum(bracket_totals.values())
|
564 |
+
brackets_rel = {str(bracket): round(total / brackets_sum, 3) for bracket, total in bracket_totals.items()}
|
565 |
+
return brackets_rel
|
566 |
+
|
567 |
+
|
568 |
+
def computeTripStats(trip_matrices, distance_matrices, metric_dist_matrices, pop):
|
569 |
+
# Transform the keys of metric_dist_matrices to match with trip_matrices
|
570 |
+
transformed_metric_keys = {key.replace("metric_matrix", "distance_matrix")+"_noEntr": matrix
|
571 |
+
for key, matrix in metric_dist_matrices.items()}
|
572 |
+
|
573 |
+
trips = 0
|
574 |
+
totalTravelDistance = 0
|
575 |
+
totalTravelTime = 0
|
576 |
+
# Iterate over each pair of trip matrix and distance matrix
|
577 |
+
for key, trip_matrix in trip_matrices.items():
|
578 |
+
# Find the corresponding distance matrix
|
579 |
+
metric_dist_matrix = transformed_metric_keys.get(key)
|
580 |
+
dist_matrix = distance_matrices.get(key)
|
581 |
+
if metric_dist_matrix is None:
|
582 |
+
print("no matrxi found")
|
583 |
+
continue # Skip if no corresponding distance matrix found
|
584 |
+
|
585 |
+
# compute
|
586 |
+
totalTravelTime += (dist_matrix*trip_matrix).sum().sum()
|
587 |
+
trips += trip_matrix.sum().sum()
|
588 |
+
totalTravelDistance += (metric_dist_matrix*trip_matrix).sum().sum()
|
589 |
+
|
590 |
+
MeanTripDistance = totalTravelDistance/trips
|
591 |
+
MeanTravelDistancePerPerson = totalTravelDistance/pop
|
592 |
+
|
593 |
+
MeanTravelTime = totalTravelTime/trips
|
594 |
+
MeanTravelTimePerPerson = totalTravelTime/pop
|
595 |
+
|
596 |
+
return totalTravelDistance, totalTravelTime, MeanTripDistance, MeanTravelDistancePerPerson, MeanTravelTime, MeanTravelTimePerPerson
|
597 |
+
|
598 |
+
def calculate_relative_mode_share(trip_matrices):
|
599 |
+
"""
|
600 |
+
Calculate the relative mode share for a dictionary of trip matrices.
|
601 |
+
|
602 |
+
Args:
|
603 |
+
trip_matrices (dict of np.ndarray or pd.DataFrame): Dictionary of trip matrices.
|
604 |
+
|
605 |
+
Returns:
|
606 |
+
dict: Relative mode distribution for each key in trip_matrices.
|
607 |
+
"""
|
608 |
+
|
609 |
+
# Compute the total trips for each mode
|
610 |
+
total_trips_per_mode = {key: matrix.sum().sum() for key, matrix in trip_matrices.items()}
|
611 |
+
|
612 |
+
# Compute the total trips across all modes
|
613 |
+
total_trips_all_modes = sum(total_trips_per_mode.values())
|
614 |
+
|
615 |
+
# Calculate the relative mode distribution
|
616 |
+
rel_mode_distribution = {key: trips_per_mode / total_trips_all_modes for key, trips_per_mode in total_trips_per_mode.items()}
|
617 |
+
|
618 |
+
return rel_mode_distribution
|
619 |
+
|
620 |
+
|
621 |
+
def extract_distance_matrices(stream, distance_matrices_of_interest):
|
622 |
+
"""
|
623 |
+
Extract distance matrices from the stream and convert them to pandas DataFrames.
|
624 |
+
Args:
|
625 |
+
stream (dict): Stream data containing distance matrices.
|
626 |
+
distance_matrices_of_interest (list of str): List of keys for the distance matrices of interest.
|
627 |
+
Returns:
|
628 |
+
dict: A dictionary of pandas DataFrames, where each key is a distance matrix kind.
|
629 |
+
"""
|
630 |
+
distance_matrices = {}
|
631 |
+
for distMK in distance_matrices_of_interest:
|
632 |
+
for distM in stream["@Data"]['@{0}']:
|
633 |
+
#print( distM.__dict__.keys())
|
634 |
+
try:
|
635 |
+
distMdict = distM.__dict__[distMK]
|
636 |
+
|
637 |
+
distance_matrix_dict = json.loads(distMdict)
|
638 |
+
origin_ids = distance_matrix_dict["origin_uuid"]
|
639 |
+
destination_ids = distance_matrix_dict["destination_uuid"]
|
640 |
+
distance_matrix = distance_matrix_dict["matrix"]
|
641 |
+
|
642 |
+
# Convert the distance matrix to a DataFrame
|
643 |
+
df_distances = pd.DataFrame(distance_matrix, index=origin_ids, columns=destination_ids)
|
644 |
+
distance_matrices[distMK] = df_distances
|
645 |
+
except Exception as e:
|
646 |
+
pass
|
647 |
+
return distance_matrices
|
648 |
+
#==================================================================================================
|
649 |
+
|
650 |
+
|
651 |
+
|
652 |
+
def computeTrips(
|
653 |
+
df_distributionMatrix,
|
654 |
+
df_total_trips_allNodes,
|
655 |
+
df_distMatrix_speckle,
|
656 |
+
df_alphas,
|
657 |
+
df_attraction_proNode_sum_total,
|
658 |
+
df_distances_aligned,
|
659 |
+
TARGET_TRIP_RATE,
|
660 |
+
SCALING_FACTOR,
|
661 |
+
total_population,
|
662 |
+
tot_res,
|
663 |
+
tot_vis,
|
664 |
+
|
665 |
+
|
666 |
+
distance_matrices,
|
667 |
+
metric_matrices,
|
668 |
+
redistributeTrips,
|
669 |
+
DISTANCE_BRACKETS,
|
670 |
+
|
671 |
+
alpha_low, alpha_med, alpha_high,
|
672 |
+
alpha_mode,
|
673 |
+
alpha_uniform,
|
674 |
+
NEW_F_VALUES,
|
675 |
+
|
676 |
+
CLIENT,
|
677 |
+
TARGET_STREAM,
|
678 |
+
TARGET_BRANCH,
|
679 |
+
sourceInfo="",
|
680 |
+
):
|
681 |
+
|
682 |
+
NEW_ALPHAS = reconstruct_dataframe(alpha_low, alpha_med, alpha_high, df_alphas)
|
683 |
+
NEW_MODE_ALPHA = alpha_mode
|
684 |
+
|
685 |
+
|
686 |
+
# ====
|
687 |
+
#step 1
|
688 |
+
distributionMatrix_step1M = step_1(df_distributionMatrix,
|
689 |
+
df_total_trips_allNodes)
|
690 |
+
|
691 |
+
#step 2
|
692 |
+
df_step_2M = step_2_vectorized(df_distMatrix_speckle,
|
693 |
+
NEW_ALPHAS)
|
694 |
+
|
695 |
+
|
696 |
+
|
697 |
+
|
698 |
+
#step 3
|
699 |
+
distAndAreaBasedAttr_step3M, distAndAreaBasedAttr_step3_sumM = step_3_numba(df_attraction_proNode_sum_total,
|
700 |
+
df_step_2M)
|
701 |
+
|
702 |
+
|
703 |
+
#step 4
|
704 |
+
distAndAreaBasedAttr_step4M, distAndAreaBasedAttr_step4_sumM = step_4_numba(distAndAreaBasedAttr_step3M,
|
705 |
+
distAndAreaBasedAttr_step3_sumM)
|
706 |
+
|
707 |
+
|
708 |
+
|
709 |
+
#step 5
|
710 |
+
df_trips_proNode_proActivity_totalM = step_5_numba(distributionMatrix_step1M,
|
711 |
+
distAndAreaBasedAttr_step4M)
|
712 |
+
|
713 |
+
#step 6 & 7
|
714 |
+
df_tripMatrixM = step_6_7_vectorized(df_trips_proNode_proActivity_totalM)
|
715 |
+
|
716 |
+
|
717 |
+
#step 8
|
718 |
+
df_tripMatrix_adjustedM, df_tripMatrix_adjusted_scaledM = adjTripRate_adjFactor(df_tripMatrixM,
|
719 |
+
total_population,
|
720 |
+
TARGET_TRIP_RATE,
|
721 |
+
SCALING_FACTOR )
|
722 |
+
#------
|
723 |
+
#MAIN 1 compute trip matrice per mode
|
724 |
+
trip_matricesM = computeModalShare(df_tripMatrix_adjusted_scaledM,
|
725 |
+
distance_matrices,
|
726 |
+
NEW_MODE_ALPHA,
|
727 |
+
f_values=NEW_F_VALUES)
|
728 |
+
|
729 |
+
#MAIN 2 compute modal shares (redistribute trips in case of identical travel time)
|
730 |
+
trip_matrices_redisM = redistributeModalShares(distance_matrices,
|
731 |
+
trip_matricesM,
|
732 |
+
redistributeTrips)
|
733 |
+
|
734 |
+
#POST 1 compute mode shares
|
735 |
+
rel_mode_distributionM = calculate_relative_mode_share(trip_matrices_redisM)
|
736 |
+
|
737 |
+
|
738 |
+
#POST 2 distance brackets
|
739 |
+
dist_sharesM = computeDistanceBrackets(trip_matrices_redisM,
|
740 |
+
metric_matrices,
|
741 |
+
DISTANCE_BRACKETS)
|
742 |
+
|
743 |
+
#POST 3 compute more stats
|
744 |
+
(totalTravelDistance, totalTravelTime,
|
745 |
+
MeanTripDistance,MeanTravelDistancePerPerson,
|
746 |
+
MeanTripTime, MeanTravelTimePerPerson) = computeTripStats(trip_matrices_redisM,
|
747 |
+
distance_matrices,
|
748 |
+
metric_matrices,
|
749 |
+
total_population)
|
750 |
+
|
751 |
+
|
752 |
+
uniform_tripmatrix = getUniformMatrix(df_distances_aligned, df_tripMatrix_adjustedM, alpha_uniform)
|
753 |
+
|
754 |
+
#add to dataframe
|
755 |
+
# Define your parameter and target values
|
756 |
+
newdata = {
|
757 |
+
# Model Parameter==
|
758 |
+
|
759 |
+
# Alpha - Routing
|
760 |
+
"alpha_low": alpha_low,
|
761 |
+
"alpha_med": alpha_med,
|
762 |
+
"alpha_high": alpha_high,
|
763 |
+
"alpha_uniform":alpha_uniform,
|
764 |
+
|
765 |
+
"fvalues":NEW_F_VALUES,
|
766 |
+
|
767 |
+
|
768 |
+
"alpha_mode":NEW_MODE_ALPHA,
|
769 |
+
|
770 |
+
# Model Indicators ==
|
771 |
+
|
772 |
+
# Modal Shares
|
773 |
+
"share_ped_mm_art": rel_mode_distributionM['activity_node+distance_matrix_ped_mm_art_noEntr'],
|
774 |
+
"share_ped_mm": rel_mode_distributionM['activity_node+distance_matrix_ped_mm_noEntr'],
|
775 |
+
"share_ped": rel_mode_distributionM['activity_node+distance_matrix_ped_noEntr'],
|
776 |
+
"share_ped_art": rel_mode_distributionM['activity_node+distance_matrix_ped_art_noEntr'],
|
777 |
+
|
778 |
+
# Tripshares by Distance Brackets
|
779 |
+
"800": dist_sharesM["800"],
|
780 |
+
"2400": dist_sharesM["2400"],
|
781 |
+
"4800": dist_sharesM["4800"],
|
782 |
+
|
783 |
+
# Travel Time & Distances
|
784 |
+
"totalTravelDistance":totalTravelDistance,
|
785 |
+
"totalTravelTime":totalTravelTime,
|
786 |
+
"MeanTravelTimePerPerson":MeanTravelTimePerPerson,
|
787 |
+
|
788 |
+
# Trip Distances
|
789 |
+
"MeanTripDistance":MeanTripDistance,
|
790 |
+
"MeanTripTime":MeanTripTime,
|
791 |
+
"MeanTravelDistancePerPerson":MeanTravelDistancePerPerson,
|
792 |
+
|
793 |
+
}
|
794 |
+
|
795 |
+
|
796 |
+
|
797 |
+
trip_matrice_adjName = {k.replace("distance", "trip"):v for k, v in trip_matricesM.items()}
|
798 |
+
trip_matrice_adjName["tripMatrix_landuse"] = df_tripMatrix_adjusted_scaledM
|
799 |
+
trip_matrice_adjName["tripMatrix_uniform"] = uniform_tripmatrix
|
800 |
+
|
801 |
+
extraData = {"population":total_population,
|
802 |
+
"residents":tot_res,
|
803 |
+
"visitors":tot_vis,
|
804 |
+
"parameter":newdata,
|
805 |
+
}
|
806 |
+
|
807 |
+
commitMsg = "automatic update"
|
808 |
+
try:
|
809 |
+
commitMsg += " using these commits: #+ "
|
810 |
+
for k,v in sourceInfo.items():
|
811 |
+
commitMsg += f" {k}: {v}"
|
812 |
+
except:
|
813 |
+
pass
|
814 |
+
print(commitMsg)
|
815 |
+
|
816 |
+
commit_id = send_matrices_and_create_commit(
|
817 |
+
trip_matrice_adjName,
|
818 |
+
CLIENT,
|
819 |
+
TARGET_STREAM,
|
820 |
+
TARGET_BRANCH,
|
821 |
+
commitMsg,
|
822 |
+
rows_per_chunk=300,
|
823 |
+
containerMetadata=extraData
|
824 |
+
)
|
825 |
+
print ("===============================")
|
826 |
+
return newdata
|
827 |
+
|
828 |
+
|
829 |
+
#==================================================================================================
|
830 |
+
# speckle send
|
831 |
+
|
832 |
+
def send_row_bundle(rows, indices, transport):
|
833 |
+
bundle_object = Base()
|
834 |
+
bundle_object.rows = rows
|
835 |
+
bundle_object.indices = indices
|
836 |
+
bundle_id = operations.send(base=bundle_object, transports=[transport])
|
837 |
+
return bundle_id
|
838 |
+
|
839 |
+
def send_matrix(matrix_df, transport, rows_per_chunk):
|
840 |
+
matrix_object = Base(metaData="Some metadata")
|
841 |
+
batch_index = 0 # Maintain a separate counter for batch indexing
|
842 |
+
|
843 |
+
# Bundle rows together
|
844 |
+
rows = []
|
845 |
+
indices = []
|
846 |
+
for index, row in matrix_df.iterrows():
|
847 |
+
rows.append([round(r,4) for r in row.tolist()])
|
848 |
+
indices.append(str(index))
|
849 |
+
if len(rows) == rows_per_chunk:
|
850 |
+
bundle_id = send_row_bundle(rows, indices, transport)
|
851 |
+
# Set the reference to the bundle in the matrix object using setattr
|
852 |
+
setattr(matrix_object, f"@batch_{batch_index}", {"referencedId": bundle_id})
|
853 |
+
rows, indices = [], [] # Reset for the next bundle
|
854 |
+
batch_index += 1 # Increment the batch index
|
855 |
+
print( str(rows_per_chunk) +" rows has been sent")
|
856 |
+
|
857 |
+
# Don't forget to send the last bundle if it's not empty
|
858 |
+
if rows:
|
859 |
+
bundle_id = send_row_bundle(rows, indices, transport)
|
860 |
+
setattr(matrix_object, f"@batch_{batch_index}", {"referencedId": bundle_id})
|
861 |
+
|
862 |
+
# Send the matrix object to Speckle
|
863 |
+
matrix_object_id = operations.send(base=matrix_object, transports=[transport])
|
864 |
+
return matrix_object_id
|
865 |
+
|
866 |
+
|
867 |
+
|
868 |
+
|
869 |
+
|
870 |
+
# Main function to send all matrices and create a commit
|
871 |
+
def send_matrices_and_create_commit(matrices, client, stream_id, branch_name, commit_message, rows_per_chunk, containerMetadata):
|
872 |
+
transport = ServerTransport(client=client, stream_id=stream_id)
|
873 |
+
matrix_ids = {}
|
874 |
+
|
875 |
+
# Send each matrix row by row and store its object ID
|
876 |
+
for k, df in matrices.items():
|
877 |
+
matrix_ids[k] = send_matrix(df, transport, rows_per_chunk)
|
878 |
+
print("object: " + k + " has been sent")
|
879 |
+
|
880 |
+
# Create a container object that will hold references to all the matrix objects
|
881 |
+
container_object = Base()
|
882 |
+
|
883 |
+
for k, v in containerMetadata.items():
|
884 |
+
container_object[k] = v
|
885 |
+
|
886 |
+
# Assuming you have a way to reference matrix objects by their IDs in Speckle
|
887 |
+
for k, obj_id in matrix_ids.items():
|
888 |
+
print("obj_id", obj_id)
|
889 |
+
container_object[k] = obj_id
|
890 |
+
|
891 |
+
|
892 |
+
# Dynamically add references to the container object
|
893 |
+
for matrix_name, matrix_id in matrix_ids.items():
|
894 |
+
# This assigns a reference to the matrix object by its ID
|
895 |
+
# You might need to adjust this based on how your Speckle server expects to receive references
|
896 |
+
setattr(container_object, matrix_name, {"referencedId": matrix_id})
|
897 |
+
|
898 |
+
|
899 |
+
|
900 |
+
# Send the container object
|
901 |
+
container_id = operations.send(base=container_object, transports=[transport])
|
902 |
+
|
903 |
+
|
904 |
+
# Now use the container_id when creating the commit
|
905 |
+
commit_id = client.commit.create(
|
906 |
+
stream_id=stream_id,
|
907 |
+
object_id=container_id, # Use the container's ID here
|
908 |
+
branch_name=branch_name,
|
909 |
+
message=commit_message,
|
910 |
+
)
|
utils.py
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import time
|
3 |
+
from functools import wraps
|
4 |
+
|
5 |
+
def reconstruct_dataframe(alpha_low, alpha_med, alpha_high, original_df):
|
6 |
+
# Define the mapping from original values to new alpha parameters
|
7 |
+
value_to_alpha = {
|
8 |
+
0.00191: alpha_low,
|
9 |
+
0.00767: alpha_high,
|
10 |
+
0.0038: alpha_med
|
11 |
+
}
|
12 |
+
|
13 |
+
# Check if each value is present at least once in the DataFrame
|
14 |
+
for original_value in value_to_alpha.keys():
|
15 |
+
if not (original_df == original_value).any().any():
|
16 |
+
raise ValueError(f"Value {original_value} not found in the input DataFrame.")
|
17 |
+
|
18 |
+
# Create a new DataFrame based on the original one
|
19 |
+
new_df = original_df.copy()
|
20 |
+
|
21 |
+
# Apply the mapping to each element in the DataFrame
|
22 |
+
for original_value, new_value in value_to_alpha.items():
|
23 |
+
new_df = new_df.replace(original_value, new_value)
|
24 |
+
|
25 |
+
return new_df
|
26 |
+
|
27 |
+
def preprocess_dataFrame(df, headerRow_idx=0, numRowsStart_idx = None, numRowsEnd_idx=None, numColsStart_idx=None, numColsEnd_idx=None, rowNames_idx=None):
|
28 |
+
df.columns = df.iloc[headerRow_idx] #Set the header
|
29 |
+
if rowNames_idx is not None:
|
30 |
+
df.index = df.iloc[:, rowNames_idx] #Set the row names
|
31 |
+
df = df.iloc[numRowsStart_idx : numRowsEnd_idx, numColsStart_idx:numColsEnd_idx] #Slice the dataset to numerical data
|
32 |
+
return df
|
33 |
+
|
34 |
+
|
35 |
+
|
36 |
+
|
37 |
+
def timeit(f):
|
38 |
+
def timed(*args, **kw):
|
39 |
+
ts = time.time()
|
40 |
+
result = f(*args, **kw)
|
41 |
+
te = time.time()
|
42 |
+
print ('func:%r args:[%r, %r] took: %2.4f sec' % \
|
43 |
+
(f.__name__, te-ts))
|
44 |
+
#(f.__name__, args, kw, te-ts))
|
45 |
+
return result
|
46 |
+
return timed
|
47 |
+
|
48 |
+
|
49 |
+
|
50 |
+
|
51 |
+
|
52 |
+
def timing_decorator(func):
|
53 |
+
@wraps(func)
|
54 |
+
def wrapper(*args, **kwargs):
|
55 |
+
start_time = time.time()
|
56 |
+
result = func(*args, **kwargs)
|
57 |
+
end_time = time.time()
|
58 |
+
|
59 |
+
duration = end_time - start_time
|
60 |
+
timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(end_time))
|
61 |
+
|
62 |
+
print(f"{func.__name__} took {duration:.4f} seconds. Finished at {timestamp}")
|
63 |
+
return result
|
64 |
+
|
65 |
+
return wrapper
|
66 |
+
|
67 |
+
|
68 |
+
# Function to compare two dataframes after converting and rounding
|
69 |
+
def compare_dataframes(df1, df2, decimals=8):
|
70 |
+
# Function to convert DataFrame columns to float and then round
|
71 |
+
def convert_and_round_dataframe(df, decimals):
|
72 |
+
# Convert all columns to float
|
73 |
+
df_float = df.astype(float)
|
74 |
+
# Round to the specified number of decimals
|
75 |
+
return df_float.round(decimals)
|
76 |
+
|
77 |
+
rounded_df1 = convert_and_round_dataframe(df1, decimals)
|
78 |
+
rounded_df2 = convert_and_round_dataframe(df2, decimals)
|
79 |
+
|
80 |
+
are_equal = rounded_df1.equals(rounded_df2)
|
81 |
+
|
82 |
+
print("Both methods are equal:", are_equal)
|
83 |
+
|
84 |
+
print("Numba shape:", df2.shape)
|
85 |
+
print("Original shape:", df1.shape)
|
86 |
+
|
87 |
+
print("======== ORIGINAL OUTPUT (first item in output list, head() for the first 5 columns)")
|
88 |
+
print(df1.iloc[0:5].head(2))
|
89 |
+
|
90 |
+
print("======== New method OUTPUT (first item in output list, head() for the first 5 columns)")
|
91 |
+
print(df2.iloc[0:5].head(2))
|
92 |
+
|
93 |
+
|
94 |
+
def align_dataframes(df1, df2, key):
|
95 |
+
"""
|
96 |
+
Align two dataframes based on a common key, ensuring that both dataframes
|
97 |
+
have only the rows with matching keys.
|
98 |
+
|
99 |
+
Parameters:
|
100 |
+
- df1: First dataframe.
|
101 |
+
- df2: Second dataframe.
|
102 |
+
- key: Column name to align dataframes on.
|
103 |
+
|
104 |
+
Returns:
|
105 |
+
- df1_aligned, df2_aligned: Tuple of aligned dataframes.
|
106 |
+
"""
|
107 |
+
common_ids = df1.index.intersection(df2[key])
|
108 |
+
df1_aligned = df1.loc[common_ids]
|
109 |
+
df2_aligned = df2[df2[key].isin(common_ids)].set_index(key, drop=False)
|
110 |
+
return df1_aligned, df2_aligned
|
111 |
+
|