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 |
+
|