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