From 3640d58b84ff4fb00489a9eb55f8e4b0f670e8b6 Mon Sep 17 00:00:00 2001 From: Carsopre Date: Wed, 17 Apr 2024 09:03:31 +0000 Subject: [PATCH 01/25] chore: autoformat isort & black --- ra2ce/.idea/ra2ce.iml | 10 ++++++++++ tests/test_examples.py | 5 ++--- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 ra2ce/.idea/ra2ce.iml diff --git a/ra2ce/.idea/ra2ce.iml b/ra2ce/.idea/ra2ce.iml new file mode 100644 index 000000000..2895e3a37 --- /dev/null +++ b/ra2ce/.idea/ra2ce.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/tests/test_examples.py b/tests/test_examples.py index c6f98b6bd..40d257ae3 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,11 +1,10 @@ - -from tests import test_examples from pathlib import Path +import pytest from pytest_notebook.execution import execute_notebook from pytest_notebook.notebook import load_notebook -import pytest +from tests import test_examples _supported_examples = lambda x: "DIY" not in x.stem _jupyter_examples = [ From 4f97663f9ee88112a4aa8f3e9d6c8909abbad3fa Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Fri, 19 Apr 2024 15:06:40 +0200 Subject: [PATCH 02/25] feat: filter criticality_analysis based on the flood threshold. --- ra2ce/analysis/indirect/losses.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index 2a395fe25..c7d034070 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -221,19 +221,19 @@ def _get_disrupted_criticality_analysis_results( # filter out all links not affected by the hazard if self.analysis.aggregate_wl == AggregateWlEnum.NONE: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_ma"] != 0 + criticality_analysis["EV1_ma"] >= self.analysis.threshold ] elif self.analysis.aggregate_wl == AggregateWlEnum.MAX: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_max"] != 0 + criticality_analysis["EV1_max"] >= self.analysis.threshold ] elif self.analysis.aggregate_wl == AggregateWlEnum.MEAN: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_mean"] != 0 + criticality_analysis["EV1_mean"] >= self.analysis.threshold ] elif self.analysis.aggregate_wl == AggregateWlEnum.MIN: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_min"] != 0 + criticality_analysis["EV1_min"] >= self.analysis.threshold ] else: self.criticality_analysis = criticality_analysis From c383bc073070ef1d0a038b3ed0a4c50151ded2b0 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Fri, 19 Apr 2024 15:14:36 +0200 Subject: [PATCH 03/25] chore: reverted from filtering criticality_analysis based on the flood threshold. --- ra2ce/analysis/indirect/losses.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index c7d034070..2a395fe25 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -221,19 +221,19 @@ def _get_disrupted_criticality_analysis_results( # filter out all links not affected by the hazard if self.analysis.aggregate_wl == AggregateWlEnum.NONE: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_ma"] >= self.analysis.threshold + criticality_analysis["EV1_ma"] != 0 ] elif self.analysis.aggregate_wl == AggregateWlEnum.MAX: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_max"] >= self.analysis.threshold + criticality_analysis["EV1_max"] != 0 ] elif self.analysis.aggregate_wl == AggregateWlEnum.MEAN: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_mean"] >= self.analysis.threshold + criticality_analysis["EV1_mean"] != 0 ] elif self.analysis.aggregate_wl == AggregateWlEnum.MIN: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_min"] >= self.analysis.threshold + criticality_analysis["EV1_min"] != 0 ] else: self.criticality_analysis = criticality_analysis From 136505e217b7cec240bef1107ca93c45f2141d25 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Sat, 20 Apr 2024 00:05:28 +0200 Subject: [PATCH 04/25] feat/chore: filtering criticality_analysis based on the flood threshold + non_disrupted links are created for both multi and single link losses + analysis.ini updated --- examples/data/multi_link_losses/analysis.ini | 2 +- examples/data/single_link_losses/analysis.ini | 1 + ra2ce/analysis/indirect/losses.py | 50 +++++++++---------- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/examples/data/multi_link_losses/analysis.ini b/examples/data/multi_link_losses/analysis.ini index 41fdbb821..f5a6d59d8 100644 --- a/examples/data/multi_link_losses/analysis.ini +++ b/examples/data/multi_link_losses/analysis.ini @@ -4,7 +4,7 @@ name = beira [analysis1] name = beira_multi_link_losses analysis = multi_link_losses -threshold = 0.5 +threshold = 0 weighing = time duration_event = 600 production_loss_per_capita_per_day = 1000 diff --git a/examples/data/single_link_losses/analysis.ini b/examples/data/single_link_losses/analysis.ini index 69f10cbfb..cc80784d1 100644 --- a/examples/data/single_link_losses/analysis.ini +++ b/examples/data/single_link_losses/analysis.ini @@ -5,6 +5,7 @@ name = beira name = beira_single_losses analysis = single_link_losses weighing = time +threshold = 0 duration_event = 600 production_loss_per_capita_per_day = 1000 part_of_day = day diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index 2a395fe25..29bf2ce90 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -217,26 +217,23 @@ def _get_disrupted_criticality_analysis_results( else: criticality_analysis = criticality_analysis.drop_duplicates(["u", "v"]) - if self.analysis.analysis == AnalysisIndirectEnum.SINGLE_LINK_LOSSES: - # filter out all links not affected by the hazard - if self.analysis.aggregate_wl == AggregateWlEnum.NONE: - self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_ma"] != 0 - ] - elif self.analysis.aggregate_wl == AggregateWlEnum.MAX: - self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_max"] != 0 - ] - elif self.analysis.aggregate_wl == AggregateWlEnum.MEAN: - self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_mean"] != 0 - ] - elif self.analysis.aggregate_wl == AggregateWlEnum.MIN: - self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_min"] != 0 - ] - else: - self.criticality_analysis = criticality_analysis + # filter out all links not affected by the hazard + if self.analysis.aggregate_wl == AggregateWlEnum.NONE: + self.criticality_analysis = criticality_analysis[ + criticality_analysis["EV1_ma"] > float(self.analysis.threshold) + ] + elif self.analysis.aggregate_wl == AggregateWlEnum.MAX: + self.criticality_analysis = criticality_analysis[ + criticality_analysis["EV1_max"] > float(self.analysis.threshold) + ] + elif self.analysis.aggregate_wl == AggregateWlEnum.MEAN: + self.criticality_analysis = criticality_analysis[ + criticality_analysis["EV1_mean"] > float(self.analysis.threshold) + ] + elif self.analysis.aggregate_wl == AggregateWlEnum.MIN: + self.criticality_analysis = criticality_analysis[ + criticality_analysis["EV1_min"] > float(self.analysis.threshold) + ] self.criticality_analysis_non_disrupted = criticality_analysis[ ~criticality_analysis.index.isin(self.criticality_analysis.index) @@ -306,7 +303,7 @@ def _get_range(height: float) -> str: return f"{x}-{y}" raise ValueError(f"No matching range found for height {height}") - def _create_result(vlh: gpd.GeoDataFrame) -> gpd.GeoDataFrame: + def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.GeoDataFrame: """ Args: vlh: calculated vehicle_loss_hours GeoDataFrame. For single_link_losses it only includes the @@ -317,8 +314,6 @@ def _create_result(vlh: gpd.GeoDataFrame) -> gpd.GeoDataFrame: Multi_link_losses this is not necessary because of the underlying multi_link_redundancy analysis. """ - if self.analysis.analysis == AnalysisIndirectEnum.MULTI_LINK_LOSSES: - return vlh result = pd.concat( [ vlh, @@ -328,7 +323,7 @@ def _create_result(vlh: gpd.GeoDataFrame) -> gpd.GeoDataFrame: f"{self.link_type_column}", "geometry", f"{self.performance_metric}", - "detour", + connectivity_attribute, ] + list(events.columns) ], @@ -383,7 +378,8 @@ def _create_result(vlh: gpd.GeoDataFrame) -> gpd.GeoDataFrame: # Check if the index name exists in the columns if vehicle_loss_hours_df.index.name in vehicle_loss_hours_df.columns: vehicle_loss_hours_df.reset_index(drop=True, inplace=True) - vehicle_loss_hours_df.reset_index(inplace=True) + else: + vehicle_loss_hours_df.reset_index(inplace=True) # find the link_type and the hazard intensity connectivity_attribute = None @@ -445,7 +441,7 @@ def _create_result(vlh: gpd.GeoDataFrame) -> gpd.GeoDataFrame: performance_row[-1]["v"], performance_key, ) - if math.isnan(row_performance_change): + if math.isnan(row_performance_change) or row_performance_change == 0: self._calculate_production_loss_per_capita( vehicle_loss_hours, vlh_row, event ) @@ -458,7 +454,7 @@ def _create_result(vlh: gpd.GeoDataFrame) -> gpd.GeoDataFrame: event, ) - vehicle_loss_hours_result = _create_result(vehicle_loss_hours) + vehicle_loss_hours_result = _create_result(vehicle_loss_hours, connectivity_attribute) return vehicle_loss_hours_result def _calculate_production_loss_per_capita( From e83982af01ae6f1d9ab1f92c25b839dd9fa0a3e6 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Sat, 20 Apr 2024 00:31:17 +0200 Subject: [PATCH 05/25] chore: added a condition to only include the links in the losses that are also included in the multi_link_redundancy analysis. For instance bridges are excluded in multi_redundancy but used to be included in the multi_link_losses. This is corrected. --- ra2ce/analysis/indirect/losses.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index 29bf2ce90..b0cc8e7c2 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -414,6 +414,7 @@ def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.Ge for event in events.columns.tolist(): for _, vlh_row in vehicle_loss_hours.iterrows(): row_hazard_range = _get_range(vlh_row[event]) + row_connectivity = vlh_row[connectivity_attribute] row_performance_changes = performance_change.loc[ [vlh_row[self.link_id]] ] @@ -441,11 +442,11 @@ def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.Ge performance_row[-1]["v"], performance_key, ) - if math.isnan(row_performance_change) or row_performance_change == 0: + if (math.isnan(row_performance_change) and row_connectivity == 0) or row_performance_change == 0: self._calculate_production_loss_per_capita( vehicle_loss_hours, vlh_row, event ) - elif (u, v, k) == row_u_v_k: + elif (math.isnan(row_performance_change) and row_connectivity == 0) and ((u, v, k) == row_u_v_k): self._populate_vehicle_loss_hour( vehicle_loss_hours, row_hazard_range, From 113349413390e892e312cd358095a3de84510d70 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Sat, 20 Apr 2024 00:37:58 +0200 Subject: [PATCH 06/25] chore: added a condition to only include the links in the losses that are also included in the multi_link_redundancy analysis. For instance bridges are excluded in multi_redundancy but used to be included in the multi_link_losses. This is corrected. --- ra2ce/analysis/indirect/losses.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index b0cc8e7c2..562b5a927 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -446,7 +446,8 @@ def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.Ge self._calculate_production_loss_per_capita( vehicle_loss_hours, vlh_row, event ) - elif (math.isnan(row_performance_change) and row_connectivity == 0) and ((u, v, k) == row_u_v_k): + elif (not(math.isnan(row_performance_change) and math.isnan(row_connectivity)) and + ((u, v, k) == row_u_v_k)): self._populate_vehicle_loss_hour( vehicle_loss_hours, row_hazard_range, From a569a2f314e9a165d74f9856fcc7f57ff6552766 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Sat, 20 Apr 2024 00:51:43 +0200 Subject: [PATCH 07/25] chore: precision updated --- ra2ce/analysis/indirect/single_link_redundancy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ra2ce/analysis/indirect/single_link_redundancy.py b/ra2ce/analysis/indirect/single_link_redundancy.py index 6051dbe56..5262a5968 100644 --- a/ra2ce/analysis/indirect/single_link_redundancy.py +++ b/ra2ce/analysis/indirect/single_link_redundancy.py @@ -84,7 +84,7 @@ def execute(self) -> GeoDataFrame: - _weighing_analyser.weighing_data[ self.analysis.weighing.config_value ], - 2, + 7, ) ) From f5fd0bb69ea24a20c4f02ef40c36de2dc050ef56 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Sat, 20 Apr 2024 21:42:55 +0200 Subject: [PATCH 08/25] test: threshold added. --- tests/analysis/indirect/test_losses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/analysis/indirect/test_losses.py b/tests/analysis/indirect/test_losses.py index 76b046ba4..7cea4bf06 100644 --- a/tests/analysis/indirect/test_losses.py +++ b/tests/analysis/indirect/test_losses.py @@ -144,6 +144,7 @@ def create_linestring(row): _analysis = AnalysisSectionIndirect( part_of_day=part_of_day, + threshold=0, resilience_curve_file=_losses_csv_data.joinpath("resilience_curve.csv"), traffic_intensities_file=_losses_csv_data.joinpath( "traffic_intensities.csv" From 2255e53f049980ddfd7f86b85f180a8d4b854811 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Sat, 20 Apr 2024 21:45:25 +0200 Subject: [PATCH 09/25] chore: threshold default to 0. --- ra2ce/analysis/analysis_config_data/analysis_config_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ra2ce/analysis/analysis_config_data/analysis_config_data.py b/ra2ce/analysis/analysis_config_data/analysis_config_data.py index 3386df46a..2c24c2ca9 100644 --- a/ra2ce/analysis/analysis_config_data/analysis_config_data.py +++ b/ra2ce/analysis/analysis_config_data/analysis_config_data.py @@ -108,7 +108,7 @@ class AnalysisSectionIndirect(AnalysisSectionBase): # the redundancy analysis) and the intensities # accessibility analyses aggregate_wl: AggregateWlEnum = field(default_factory=lambda: AggregateWlEnum.NONE) - threshold: float = math.nan + threshold: float = 0.0 threshold_destinations: float = math.nan uniform_duration: float = math.nan gdp_percapita: float = math.nan From 222f9206e818bf66138d6fa6e57f790639942f56 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Mon, 22 Apr 2024 15:48:15 +0200 Subject: [PATCH 10/25] chore: Remove ra2ce/.idea directory from version control --- ra2ce/.idea/ra2ce.iml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 ra2ce/.idea/ra2ce.iml diff --git a/ra2ce/.idea/ra2ce.iml b/ra2ce/.idea/ra2ce.iml deleted file mode 100644 index 2895e3a37..000000000 --- a/ra2ce/.idea/ra2ce.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file From 3d249f3831b55f2dd1f175ef0c2bba28446c19bc Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Mon, 22 Apr 2024 15:49:57 +0200 Subject: [PATCH 11/25] chore: float casting of threshold is removed. --- ra2ce/.idea/ra2ce.iml | 10 ++++++++++ ra2ce/analysis/indirect/losses.py | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 ra2ce/.idea/ra2ce.iml diff --git a/ra2ce/.idea/ra2ce.iml b/ra2ce/.idea/ra2ce.iml new file mode 100644 index 000000000..2895e3a37 --- /dev/null +++ b/ra2ce/.idea/ra2ce.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index 562b5a927..a1bcfeec0 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -220,19 +220,19 @@ def _get_disrupted_criticality_analysis_results( # filter out all links not affected by the hazard if self.analysis.aggregate_wl == AggregateWlEnum.NONE: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_ma"] > float(self.analysis.threshold) + criticality_analysis["EV1_ma"] > self.analysis.threshold ] elif self.analysis.aggregate_wl == AggregateWlEnum.MAX: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_max"] > float(self.analysis.threshold) + criticality_analysis["EV1_max"] > self.analysis.threshold ] elif self.analysis.aggregate_wl == AggregateWlEnum.MEAN: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_mean"] > float(self.analysis.threshold) + criticality_analysis["EV1_mean"] > self.analysis.threshold ] elif self.analysis.aggregate_wl == AggregateWlEnum.MIN: self.criticality_analysis = criticality_analysis[ - criticality_analysis["EV1_min"] > float(self.analysis.threshold) + criticality_analysis["EV1_min"] > self.analysis.threshold ] self.criticality_analysis_non_disrupted = criticality_analysis[ From 1e1cb3a30285d2661468ecacbec9a03017cf79cf Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Mon, 22 Apr 2024 15:50:18 +0200 Subject: [PATCH 12/25] chore: Remove ra2ce/.idea directory from version control --- ra2ce/.idea/ra2ce.iml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 ra2ce/.idea/ra2ce.iml diff --git a/ra2ce/.idea/ra2ce.iml b/ra2ce/.idea/ra2ce.iml deleted file mode 100644 index 2895e3a37..000000000 --- a/ra2ce/.idea/ra2ce.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file From a520b438c9b50c28fa119b84190de9116ef8898e Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Tue, 23 Apr 2024 09:49:02 +0200 Subject: [PATCH 13/25] feat: divisor added to accept %-based or less-than-one functionality losses in the resilience_curve file --- ra2ce/analysis/indirect/losses.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index a1bcfeec0..7b8fedf1f 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -522,13 +522,19 @@ def _populate_vehicle_loss_hour( self.resilience_curve["link_type_hazard_intensity"] == link_type_hazard_range ] + + if all(ratio < 1 for ratio in row_relevant_curve["functionality_loss_ratio"]): + divisor = 1 + else: + divisor = 100 + disruption = ( ( row_relevant_curve["duration_steps"].apply(pd.Series) * (row_relevant_curve["functionality_loss_ratio"]).apply( pd.Series ) - / 100 + / divisor ).sum(axis=1) ).squeeze() if disruption > max_disruption: @@ -546,6 +552,12 @@ def _populate_vehicle_loss_hour( raise Exception( f"""{link_type_hazard_range} was not found in the introduced resilience_curve""" ) + + if all(ratio < 1 for ratio in row_relevant_curve["functionality_loss_ratio"]): + divisor = 1 + else: + divisor = 100 + duration_steps: list = relevant_curve["duration_steps"].item() functionality_loss_ratios: list = relevant_curve[ "functionality_loss_ratio" @@ -562,11 +574,11 @@ def _populate_vehicle_loss_hour( ) vlh_trip_type_event_series = sum( - intensity_trip_type + (intensity_trip_type * duration * loss_ratio * performance_change - * vot_trip_type + * vot_trip_type) / divisor for duration, loss_ratio in zip( duration_steps, functionality_loss_ratios ) From 372c77daba68de4bf51465b531b91d28602687f2 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Tue, 23 Apr 2024 11:07:33 +0200 Subject: [PATCH 14/25] chore: divisor identification is updated --- ra2ce/analysis/indirect/losses.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index 7b8fedf1f..fc698568a 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -522,11 +522,6 @@ def _populate_vehicle_loss_hour( self.resilience_curve["link_type_hazard_intensity"] == link_type_hazard_range ] - - if all(ratio < 1 for ratio in row_relevant_curve["functionality_loss_ratio"]): - divisor = 1 - else: - divisor = 100 disruption = ( ( @@ -534,7 +529,6 @@ def _populate_vehicle_loss_hour( * (row_relevant_curve["functionality_loss_ratio"]).apply( pd.Series ) - / divisor ).sum(axis=1) ).squeeze() if disruption > max_disruption: @@ -553,7 +547,7 @@ def _populate_vehicle_loss_hour( f"""{link_type_hazard_range} was not found in the introduced resilience_curve""" ) - if all(ratio < 1 for ratio in row_relevant_curve["functionality_loss_ratio"]): + if all(ratio <= 1 for ratio_tuple in relevant_curve["functionality_loss_ratio"] for ratio in ratio_tuple): divisor = 1 else: divisor = 100 From c75afee884b0029b720ee053fa9c95d3f8a1987a Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Wed, 24 Apr 2024 17:19:07 +0200 Subject: [PATCH 15/25] chore: result assembling and columns are updated --- ra2ce/analysis/indirect/losses.py | 46 ++++++++++++------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index fc698568a..358248108 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -314,36 +314,33 @@ def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.Ge Multi_link_losses this is not necessary because of the underlying multi_link_redundancy analysis. """ + columns_without_index = [col for col in self.criticality_analysis_non_disrupted.columns if col not in ["level_0"]] + # Get the vlh_columns from vehicle_loss_hours that vlh calculations are filled in. + vlh_columns = list( + set(vlh.columns) - set(self.criticality_analysis_non_disrupted[columns_without_index].columns) + ) + vlh[vlh_columns] = vlh[vlh_columns].fillna(0) + result = pd.concat( [ vlh, - self.criticality_analysis_non_disrupted[ - [ - f"{self.link_id}", - f"{self.link_type_column}", - "geometry", - f"{self.performance_metric}", - connectivity_attribute, - ] - + list(events.columns) - ], + self.criticality_analysis_non_disrupted[columns_without_index], ] ) result = result.reset_index() - # Get the columns from vehicle_loss_hours that are not in common - additional_columns = list( - set(vlh.columns) - set(self.criticality_analysis_non_disrupted.columns) - ) - - # Fill 0 for the additional columns of self.criticality_analysis_non_disrupted + # Fill 0 for the vlh_columns of vlh and self.criticality_analysis_non_disrupted result.loc[ - result.index.difference(vlh.index), additional_columns + result.index.difference(vlh.index), vlh_columns ] = result.loc[ - result.index.difference(vlh.index), additional_columns + result.index.difference(vlh.index), vlh_columns ].fillna( 0 ) + for col in ['index', 'level_0']: + if col in result.columns: + result = result.drop(col, axis=1) + return result _check_validity_criticality_analysis() @@ -391,18 +388,9 @@ def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.Ge if "detour" in self.criticality_analysis.columns else "connected" ) - + vlh_additional_columns = self.criticality_analysis.columns.difference(vehicle_loss_hours_df.columns).tolist() vehicle_loss_hours_df = pd.merge( - vehicle_loss_hours_df, - self.criticality_analysis[ - [ - f"{self.link_type_column}", - "geometry", - f"{self.performance_metric}", - connectivity_attribute, - ] - + list(events.columns) - ], + vehicle_loss_hours_df, self.criticality_analysis[vlh_additional_columns], left_on=self.link_id, right_index=True, ) From 79707903a7fe8e89537bf133dd921fe7e6c9465f Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Wed, 24 Apr 2024 17:54:05 +0200 Subject: [PATCH 16/25] chore: avoid adding multiple time columns --- .../indirect/multi_link_redundancy.py | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/ra2ce/analysis/indirect/multi_link_redundancy.py b/ra2ce/analysis/indirect/multi_link_redundancy.py index b76ebfdb5..c477235c9 100644 --- a/ra2ce/analysis/indirect/multi_link_redundancy.py +++ b/ra2ce/analysis/indirect/multi_link_redundancy.py @@ -46,27 +46,29 @@ def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame """ if ( WeighingEnum.TIME.config_value not in gdf_graph.columns - or WeighingEnum.TIME.config_value not in gdf_calculated.columns + and WeighingEnum.TIME.config_value not in gdf_calculated.columns ): return gdf_graph - gdf_graph[WeighingEnum.TIME.config_value] = gdf_calculated[ - WeighingEnum.TIME.config_value - ] - for i, row in gdf_graph.iterrows(): - row_avgspeed = row.get("avgspeed", None) - row_length = row.get("length", None) - if ( - pd.isna(row[WeighingEnum.TIME.config_value]) - and row_avgspeed - and row_length - ): - gdf_graph.at[i, WeighingEnum.TIME.config_value] = ( - row_length * 1e-3 / row_avgspeed - ) - else: - gdf_graph.at[i, WeighingEnum.TIME.config_value] = row.get( - WeighingEnum.TIME.config_value, None - ) + + elif WeighingEnum.TIME.config_value in gdf_calculated.columns: + gdf_graph[WeighingEnum.TIME.config_value] = gdf_calculated[ + WeighingEnum.TIME.config_value + ] + for i, row in gdf_graph.iterrows(): + row_avgspeed = row.get("avgspeed", None) + row_length = row.get("length", None) + if ( + pd.isna(row[WeighingEnum.TIME.config_value]) + and row_avgspeed + and row_length + ): + gdf_graph.at[i, WeighingEnum.TIME.config_value] = ( + row_length * 1e-3 / row_avgspeed + ) + else: + gdf_graph.at[i, WeighingEnum.TIME.config_value] = row.get( + WeighingEnum.TIME.config_value, None + ) return gdf_graph def execute(self) -> GeoDataFrame: @@ -185,14 +187,19 @@ def _is_not_none(value): df_calculated[f"alt_{self.analysis.weighing.config_value}"], errors="coerce", ) + + gdf = self._update_time(df_calculated, gdf) + if WeighingEnum.TIME.config_value in df_calculated.columns: + df_calculated_time_excluded = df_calculated.drop(columns=[WeighingEnum.TIME.config_value]) + # Merge the dataframes if "rfid" in gdf: - gdf = gdf.merge(df_calculated, how="left", on=["u", "v", "rfid"]) + gdf = gdf.merge(df_calculated_time_excluded, how="left", on=["u", "v", "rfid"]) else: - gdf = gdf.merge(df_calculated, how="left", on=["u", "v"]) + gdf = gdf.merge(df_calculated_time_excluded, how="left", on=["u", "v"]) - gdf = self._update_time(df_calculated, gdf) + gdf["hazard"] = hazard_name From 854ef17ca49b2b986613b03ddfbba080f96cebd5 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Wed, 24 Apr 2024 18:05:48 +0200 Subject: [PATCH 17/25] chore: avoid adding multiple time columns, implementation revised --- ra2ce/analysis/indirect/multi_link_redundancy.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/ra2ce/analysis/indirect/multi_link_redundancy.py b/ra2ce/analysis/indirect/multi_link_redundancy.py index c477235c9..b1630f6ee 100644 --- a/ra2ce/analysis/indirect/multi_link_redundancy.py +++ b/ra2ce/analysis/indirect/multi_link_redundancy.py @@ -40,7 +40,7 @@ def __init__( self.output_path = analysis_input.output_path self.hazard_names = analysis_input.hazard_names - def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame): + def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame) -> tuple: """ updates the time column with the calculated dataframe and updates the rest of the gdf_graph if time is None. """ @@ -48,7 +48,8 @@ def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame WeighingEnum.TIME.config_value not in gdf_graph.columns and WeighingEnum.TIME.config_value not in gdf_calculated.columns ): - return gdf_graph + gdf_calculated = gdf_calculated.drop(columns=[WeighingEnum.TIME.config_value]) + return gdf_graph, gdf_calculated elif WeighingEnum.TIME.config_value in gdf_calculated.columns: gdf_graph[WeighingEnum.TIME.config_value] = gdf_calculated[ @@ -69,7 +70,7 @@ def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame gdf_graph.at[i, WeighingEnum.TIME.config_value] = row.get( WeighingEnum.TIME.config_value, None ) - return gdf_graph + return gdf_graph, gdf_calculated def execute(self) -> GeoDataFrame: """Calculates the multi-link redundancy of a NetworkX graph. @@ -188,16 +189,13 @@ def _is_not_none(value): errors="coerce", ) - gdf = self._update_time(df_calculated, gdf) - - if WeighingEnum.TIME.config_value in df_calculated.columns: - df_calculated_time_excluded = df_calculated.drop(columns=[WeighingEnum.TIME.config_value]) + gdf, df_calculated = self._update_time(df_calculated, gdf) # Merge the dataframes if "rfid" in gdf: - gdf = gdf.merge(df_calculated_time_excluded, how="left", on=["u", "v", "rfid"]) + gdf = gdf.merge(df_calculated, how="left", on=["u", "v", "rfid"]) else: - gdf = gdf.merge(df_calculated_time_excluded, how="left", on=["u", "v"]) + gdf = gdf.merge(df_calculated, how="left", on=["u", "v"]) From 66386fe551e3ddf755c90ffcf7837a427495af3a Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Wed, 24 Apr 2024 18:21:40 +0200 Subject: [PATCH 18/25] chore: avoid adding multiple time columns, implementation corrected --- ra2ce/analysis/indirect/multi_link_redundancy.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ra2ce/analysis/indirect/multi_link_redundancy.py b/ra2ce/analysis/indirect/multi_link_redundancy.py index b1630f6ee..a607641dd 100644 --- a/ra2ce/analysis/indirect/multi_link_redundancy.py +++ b/ra2ce/analysis/indirect/multi_link_redundancy.py @@ -44,11 +44,17 @@ def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame """ updates the time column with the calculated dataframe and updates the rest of the gdf_graph if time is None. """ + if ( + WeighingEnum.TIME.config_value in gdf_graph.columns + and WeighingEnum.TIME.config_value in gdf_calculated.columns + ): + gdf_calculated = gdf_calculated.drop(columns=[WeighingEnum.TIME.config_value]) + return gdf_graph, gdf_calculated + if ( WeighingEnum.TIME.config_value not in gdf_graph.columns and WeighingEnum.TIME.config_value not in gdf_calculated.columns ): - gdf_calculated = gdf_calculated.drop(columns=[WeighingEnum.TIME.config_value]) return gdf_graph, gdf_calculated elif WeighingEnum.TIME.config_value in gdf_calculated.columns: From e6c086fc800ce3978bc1a5e766bb98e0aa027f59 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Tue, 30 Apr 2024 14:39:04 +0200 Subject: [PATCH 19/25] chore: pass np.nan where the current weighing value is None --- ra2ce/analysis/indirect/multi_link_redundancy.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ra2ce/analysis/indirect/multi_link_redundancy.py b/ra2ce/analysis/indirect/multi_link_redundancy.py index a607641dd..bed996b45 100644 --- a/ra2ce/analysis/indirect/multi_link_redundancy.py +++ b/ra2ce/analysis/indirect/multi_link_redundancy.py @@ -166,13 +166,12 @@ def _is_not_none(value): alt_value = _weighing_analyser.calculate_distance() alt_nodes, connected = np.NaN, 0 - diff = round( - alt_value - - _weighing_analyser.weighing_data[ + current_value = _weighing_analyser.weighing_data[ self.analysis.weighing.config_value - ], - 3, - ) + ] + if not current_value: # if None + current_value = np.nan + diff = round(alt_value - current_value, 3) data = { "u": [u], From 447e64b7a8cf79f12819eaa3f8760398e6b70adb Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Tue, 30 Apr 2024 15:12:32 +0200 Subject: [PATCH 20/25] chore: convert str to integer for link_id --- ra2ce/analysis/indirect/losses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index 358248108..e5801f52b 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -269,7 +269,7 @@ def _get_intensities_simplified_graph(self) -> pd.DataFrame: row_data = max_intensities.squeeze() else: - row_data = self.intensities.loc[index] + row_data = self.intensities.loc[int(index)] _intensities_simplified_graph_list.append(row_data) _intensities_simplified_graph = pd.DataFrame( From b597671430f9734529b96e19d735582a51c16a53 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Tue, 14 May 2024 09:49:39 +0200 Subject: [PATCH 21/25] feat: gitignore updated --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 26b99a6c8..42f4ab093 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,4 @@ dmypy.json examples/data/*/static/output_graph/ examples/data/*/output/ examples/cache/ +/ra2ce/.idea/ra2ce.iml From 0517e83eed35843b96661ec456313b37d3b428a2 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Tue, 14 May 2024 10:07:07 +0200 Subject: [PATCH 22/25] chore: updates proposed during the PR review --- ra2ce/analysis/indirect/losses.py | 112 +++++++++--------- .../indirect/multi_link_redundancy.py | 7 +- 2 files changed, 60 insertions(+), 59 deletions(-) diff --git a/ra2ce/analysis/indirect/losses.py b/ra2ce/analysis/indirect/losses.py index e5801f52b..b5e7aa760 100644 --- a/ra2ce/analysis/indirect/losses.py +++ b/ra2ce/analysis/indirect/losses.py @@ -49,10 +49,10 @@ def _load_df_from_csv( - csv_path: Path, - columns_to_interpret: list[str], - index: Optional[str | None], - sep: str = ",", + csv_path: Path, + columns_to_interpret: list[str], + index: Optional[str | None], + sep: str = ",", ) -> pd.DataFrame: if csv_path is None or not csv_path.exists(): logging.warning("No `csv` file found at {}.".format(csv_path)) @@ -80,9 +80,9 @@ class Losses(AnalysisIndirectProtocol): hazard_names: HazardNames def __init__( - self, - analysis_input: AnalysisInputWrapper, - analysis_config: AnalysisConfigWrapper, + self, + analysis_input: AnalysisInputWrapper, + analysis_config: AnalysisConfigWrapper, ) -> None: self.analysis_input = analysis_input self.analysis_config = analysis_config @@ -100,7 +100,7 @@ def __init__( self.duration_event: float = self.analysis.duration_event self.hours_per_day: float = self.analysis.hours_per_day self.production_loss_per_capita_per_hour = ( - self.analysis.production_loss_per_capita_per_day / self.hours_per_day + self.analysis.production_loss_per_capita_per_day / self.hours_per_day ) self._check_validity_analysis_files() self.intensities = _load_df_from_csv( @@ -126,9 +126,9 @@ def __init__( def _check_validity_analysis_files(self): if ( - self.analysis.traffic_intensities_file is None - or self.analysis.resilience_curve_file is None - or self.analysis.values_of_time_file is None + self.analysis.traffic_intensities_file is None + or self.analysis.resilience_curve_file is None + or self.analysis.values_of_time_file is None ): raise ValueError( f"traffic_intensities_file, resilience_curve_file, and values_of_time_file should be given" @@ -141,7 +141,7 @@ def _check_validity_df(self): """ _required_values_of_time_keys = ["trip_types", "value_of_time", "occupants"] if not all( - key in self.values_of_time.columns for key in _required_values_of_time_keys + key in self.values_of_time.columns for key in _required_values_of_time_keys ): raise ValueError( f"Missing required columns in values_of_time: {_required_values_of_time_keys}" @@ -153,16 +153,16 @@ def _check_validity_df(self): "functionality_loss_ratio", ] if len(self.resilience_curve) > 0 and not all( - key in self.resilience_curve.columns - for key in _required_resilience_curve_keys + key in self.resilience_curve.columns + for key in _required_resilience_curve_keys ): raise ValueError( f"Missing required columns in resilience_curve: {_required_resilience_curve_keys}" ) if ( - self.link_id not in self.intensities.columns - and self.link_id not in self.intensities.index.name + self.link_id not in self.intensities.columns + and self.link_id not in self.intensities.index.name ): raise Exception( f"""traffic_intensities_file and input graph do not have the same link_id. @@ -187,7 +187,7 @@ def _get_vot_intensity_per_trip_purpose(self) -> dict[str, pd.DataFrame]: occupancy_var_name = f"occupants_{trip_purpose}" partofday_trip_purpose_name = f"{self.part_of_day}_{trip_purpose}" partofday_trip_purpose_intensity_name = ( - "intensity_" + partofday_trip_purpose_name + "intensity_" + partofday_trip_purpose_name ) # read and set the vot's _vot_dict[vot_var_name] = self.values_of_time.loc[ @@ -200,13 +200,13 @@ def _get_vot_intensity_per_trip_purpose(self) -> dict[str, pd.DataFrame]: ].item() # read and set the intensities _vot_dict[partofday_trip_purpose_intensity_name] = ( - self.intensities_simplified_graph[partofday_trip_purpose_name] - / self.hours_per_day + self.intensities_simplified_graph[partofday_trip_purpose_name] + / self.hours_per_day ) return dict(_vot_dict) def _get_disrupted_criticality_analysis_results( - self, criticality_analysis: gpd.GeoDataFrame + self, criticality_analysis: gpd.GeoDataFrame ): criticality_analysis.reset_index(inplace=True) @@ -221,19 +221,19 @@ def _get_disrupted_criticality_analysis_results( if self.analysis.aggregate_wl == AggregateWlEnum.NONE: self.criticality_analysis = criticality_analysis[ criticality_analysis["EV1_ma"] > self.analysis.threshold - ] + ] elif self.analysis.aggregate_wl == AggregateWlEnum.MAX: self.criticality_analysis = criticality_analysis[ criticality_analysis["EV1_max"] > self.analysis.threshold - ] + ] elif self.analysis.aggregate_wl == AggregateWlEnum.MEAN: self.criticality_analysis = criticality_analysis[ criticality_analysis["EV1_mean"] > self.analysis.threshold - ] + ] elif self.analysis.aggregate_wl == AggregateWlEnum.MIN: self.criticality_analysis = criticality_analysis[ criticality_analysis["EV1_min"] > self.analysis.threshold - ] + ] self.criticality_analysis_non_disrupted = criticality_analysis[ ~criticality_analysis.index.isin(self.criticality_analysis.index) @@ -314,7 +314,8 @@ def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.Ge Multi_link_losses this is not necessary because of the underlying multi_link_redundancy analysis. """ - columns_without_index = [col for col in self.criticality_analysis_non_disrupted.columns if col not in ["level_0"]] + columns_without_index = [col for col in self.criticality_analysis_non_disrupted.columns if + col not in ["level_0"]] # Get the vlh_columns from vehicle_loss_hours that vlh calculations are filled in. vlh_columns = list( set(vlh.columns) - set(self.criticality_analysis_non_disrupted[columns_without_index].columns) @@ -340,7 +341,7 @@ def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.Ge for col in ['index', 'level_0']: if col in result.columns: result = result.drop(col, axis=1) - + return result _check_validity_criticality_analysis() @@ -381,7 +382,7 @@ def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.Ge # find the link_type and the hazard intensity connectivity_attribute = None if any( - col in self.criticality_analysis.columns for col in ["detour", "connected"] + col in self.criticality_analysis.columns for col in ["detour", "connected"] ): connectivity_attribute = ( "detour" @@ -448,10 +449,10 @@ def _create_result(vlh: gpd.GeoDataFrame, connectivity_attribute: str) -> gpd.Ge return vehicle_loss_hours_result def _calculate_production_loss_per_capita( - self, - vehicle_loss_hours: gpd.GeoDataFrame, - vlh_row: pd.Series, - hazard_col_name: str, + self, + vehicle_loss_hours: gpd.GeoDataFrame, + vlh_row: pd.Series, + hazard_col_name: str, ): """ In cases where there is no alternative route in the event of disruption of the road, we propose to use a @@ -478,10 +479,10 @@ def _calculate_production_loss_per_capita( self.vot_intensity_per_trip_collection[f"occupants_{trip_type}"] ) vlh_trip_type_event_series = ( - self.duration_event - * intensity_trip_type - * occupancy_trip_type - * self.production_loss_per_capita_per_hour + self.duration_event + * intensity_trip_type + * occupancy_trip_type + * self.production_loss_per_capita_per_hour ) vlh_trip_type_event = vlh_trip_type_event_series.squeeze() vehicle_loss_hours.loc[ @@ -493,12 +494,12 @@ def _calculate_production_loss_per_capita( ] = vlh_total def _populate_vehicle_loss_hour( - self, - vehicle_loss_hours: gpd.GeoDataFrame, - row_hazard_range: str, - vlh_row: pd.Series, - performance_change: float, - hazard_col_name: str, + self, + vehicle_loss_hours: gpd.GeoDataFrame, + row_hazard_range: str, + vlh_row: pd.Series, + performance_change: float, + hazard_col_name: str, ): vlh_total = 0 @@ -509,14 +510,14 @@ def _populate_vehicle_loss_hour( row_relevant_curve = self.resilience_curve[ self.resilience_curve["link_type_hazard_intensity"] == link_type_hazard_range - ] + ] disruption = ( ( - row_relevant_curve["duration_steps"].apply(pd.Series) - * (row_relevant_curve["functionality_loss_ratio"]).apply( - pd.Series - ) + row_relevant_curve["duration_steps"].apply(pd.Series) + * (row_relevant_curve["functionality_loss_ratio"]).apply( + pd.Series + ) ).sum(axis=1) ).squeeze() if disruption > max_disruption: @@ -529,17 +530,16 @@ def _populate_vehicle_loss_hour( relevant_curve = self.resilience_curve[ self.resilience_curve["link_type_hazard_intensity"] == link_type_hazard_range - ] + ] if relevant_curve.size == 0: raise Exception( f"""{link_type_hazard_range} was not found in the introduced resilience_curve""" ) - + + divisor = 100 if all(ratio <= 1 for ratio_tuple in relevant_curve["functionality_loss_ratio"] for ratio in ratio_tuple): - divisor = 1 - else: - divisor = 100 - + divisor = 1 + duration_steps: list = relevant_curve["duration_steps"].item() functionality_loss_ratios: list = relevant_curve[ "functionality_loss_ratio" @@ -557,10 +557,10 @@ def _populate_vehicle_loss_hour( vlh_trip_type_event_series = sum( (intensity_trip_type - * duration - * loss_ratio - * performance_change - * vot_trip_type) / divisor + * duration + * loss_ratio + * performance_change + * vot_trip_type) / divisor for duration, loss_ratio in zip( duration_steps, functionality_loss_ratios ) diff --git a/ra2ce/analysis/indirect/multi_link_redundancy.py b/ra2ce/analysis/indirect/multi_link_redundancy.py index bed996b45..672810f5c 100644 --- a/ra2ce/analysis/indirect/multi_link_redundancy.py +++ b/ra2ce/analysis/indirect/multi_link_redundancy.py @@ -40,7 +40,8 @@ def __init__( self.output_path = analysis_input.output_path self.hazard_names = analysis_input.hazard_names - def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame) -> tuple: + def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame) \ + -> tuple[gpd.GeoDataFrame, gpd.GeoDataFrame]: """ updates the time column with the calculated dataframe and updates the rest of the gdf_graph if time is None. """ @@ -50,13 +51,13 @@ def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame ): gdf_calculated = gdf_calculated.drop(columns=[WeighingEnum.TIME.config_value]) return gdf_graph, gdf_calculated - + if ( WeighingEnum.TIME.config_value not in gdf_graph.columns and WeighingEnum.TIME.config_value not in gdf_calculated.columns ): return gdf_graph, gdf_calculated - + elif WeighingEnum.TIME.config_value in gdf_calculated.columns: gdf_graph[WeighingEnum.TIME.config_value] = gdf_calculated[ WeighingEnum.TIME.config_value From cda2e914bd48da0a53c83a71f3415a05821c4d06 Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Tue, 14 May 2024 10:09:37 +0200 Subject: [PATCH 23/25] chore: updated the returning variable typing --- ra2ce/analysis/indirect/multi_link_redundancy.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ra2ce/analysis/indirect/multi_link_redundancy.py b/ra2ce/analysis/indirect/multi_link_redundancy.py index 672810f5c..d359f3648 100644 --- a/ra2ce/analysis/indirect/multi_link_redundancy.py +++ b/ra2ce/analysis/indirect/multi_link_redundancy.py @@ -6,7 +6,6 @@ import numpy as np import osmnx import pandas as pd -from geopandas import GeoDataFrame from ra2ce.analysis.analysis_config_data.analysis_config_data import ( AnalysisSectionIndirect, @@ -41,7 +40,7 @@ def __init__( self.hazard_names = analysis_input.hazard_names def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame) \ - -> tuple[gpd.GeoDataFrame, gpd.GeoDataFrame]: + -> tuple[gpd.GeoDataFrame, pd.DataFrame]: """ updates the time column with the calculated dataframe and updates the rest of the gdf_graph if time is None. """ @@ -79,7 +78,7 @@ def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame ) return gdf_graph, gdf_calculated - def execute(self) -> GeoDataFrame: + def execute(self) -> gpd.GeoDataFrame: """Calculates the multi-link redundancy of a NetworkX graph. The function removes all links of a variable that have a minimum value From 986819c34cb006bd06f5e75bc49064ab0ddfb5fd Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Tue, 14 May 2024 10:13:06 +0200 Subject: [PATCH 24/25] chore: updated the variable naming and the method output order --- .../indirect/multi_link_redundancy.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ra2ce/analysis/indirect/multi_link_redundancy.py b/ra2ce/analysis/indirect/multi_link_redundancy.py index d359f3648..871505024 100644 --- a/ra2ce/analysis/indirect/multi_link_redundancy.py +++ b/ra2ce/analysis/indirect/multi_link_redundancy.py @@ -39,26 +39,26 @@ def __init__( self.output_path = analysis_input.output_path self.hazard_names = analysis_input.hazard_names - def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame) \ - -> tuple[gpd.GeoDataFrame, pd.DataFrame]: + def _update_time(self, df_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame) \ + -> tuple[pd.DataFrame, gpd.GeoDataFrame]: """ updates the time column with the calculated dataframe and updates the rest of the gdf_graph if time is None. """ if ( WeighingEnum.TIME.config_value in gdf_graph.columns - and WeighingEnum.TIME.config_value in gdf_calculated.columns + and WeighingEnum.TIME.config_value in df_calculated.columns ): - gdf_calculated = gdf_calculated.drop(columns=[WeighingEnum.TIME.config_value]) - return gdf_graph, gdf_calculated + df_calculated = df_calculated.drop(columns=[WeighingEnum.TIME.config_value]) + return df_calculated, gdf_graph if ( WeighingEnum.TIME.config_value not in gdf_graph.columns - and WeighingEnum.TIME.config_value not in gdf_calculated.columns + and WeighingEnum.TIME.config_value not in df_calculated.columns ): - return gdf_graph, gdf_calculated + return df_calculated, gdf_graph - elif WeighingEnum.TIME.config_value in gdf_calculated.columns: - gdf_graph[WeighingEnum.TIME.config_value] = gdf_calculated[ + elif WeighingEnum.TIME.config_value in df_calculated.columns: + gdf_graph[WeighingEnum.TIME.config_value] = df_calculated[ WeighingEnum.TIME.config_value ] for i, row in gdf_graph.iterrows(): @@ -76,7 +76,7 @@ def _update_time(self, gdf_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame gdf_graph.at[i, WeighingEnum.TIME.config_value] = row.get( WeighingEnum.TIME.config_value, None ) - return gdf_graph, gdf_calculated + return df_calculated, gdf_graph def execute(self) -> gpd.GeoDataFrame: """Calculates the multi-link redundancy of a NetworkX graph. @@ -194,7 +194,7 @@ def _is_not_none(value): errors="coerce", ) - gdf, df_calculated = self._update_time(df_calculated, gdf) + df_calculated, gdf = self._update_time(df_calculated, gdf) # Merge the dataframes if "rfid" in gdf: From 5791c8ea4ba29e6a16c92f4c65d3653943f0909a Mon Sep 17 00:00:00 2001 From: sahand_asgarpour Date: Tue, 14 May 2024 10:13:46 +0200 Subject: [PATCH 25/25] chore: formatting --- .../indirect/multi_link_redundancy.py | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/ra2ce/analysis/indirect/multi_link_redundancy.py b/ra2ce/analysis/indirect/multi_link_redundancy.py index 871505024..b8a7f29d4 100644 --- a/ra2ce/analysis/indirect/multi_link_redundancy.py +++ b/ra2ce/analysis/indirect/multi_link_redundancy.py @@ -29,8 +29,8 @@ class MultiLinkRedundancy(AnalysisIndirectProtocol): hazard_names: HazardNames def __init__( - self, - analysis_input: AnalysisInputWrapper, + self, + analysis_input: AnalysisInputWrapper, ) -> None: self.analysis = analysis_input.analysis self.graph_file_hazard = analysis_input.graph_file_hazard @@ -45,15 +45,15 @@ def _update_time(self, df_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame) updates the time column with the calculated dataframe and updates the rest of the gdf_graph if time is None. """ if ( - WeighingEnum.TIME.config_value in gdf_graph.columns - and WeighingEnum.TIME.config_value in df_calculated.columns + WeighingEnum.TIME.config_value in gdf_graph.columns + and WeighingEnum.TIME.config_value in df_calculated.columns ): df_calculated = df_calculated.drop(columns=[WeighingEnum.TIME.config_value]) return df_calculated, gdf_graph if ( - WeighingEnum.TIME.config_value not in gdf_graph.columns - and WeighingEnum.TIME.config_value not in df_calculated.columns + WeighingEnum.TIME.config_value not in gdf_graph.columns + and WeighingEnum.TIME.config_value not in df_calculated.columns ): return df_calculated, gdf_graph @@ -65,12 +65,12 @@ def _update_time(self, df_calculated: pd.DataFrame, gdf_graph: gpd.GeoDataFrame) row_avgspeed = row.get("avgspeed", None) row_length = row.get("length", None) if ( - pd.isna(row[WeighingEnum.TIME.config_value]) - and row_avgspeed - and row_length + pd.isna(row[WeighingEnum.TIME.config_value]) + and row_avgspeed + and row_length ): gdf_graph.at[i, WeighingEnum.TIME.config_value] = ( - row_length * 1e-3 / row_avgspeed + row_length * 1e-3 / row_avgspeed ) else: gdf_graph.at[i, WeighingEnum.TIME.config_value] = row.get( @@ -91,10 +91,10 @@ def execute(self) -> gpd.GeoDataFrame: def _is_not_none(value): return ( - value is not None - and value is not pd.NA - and not pd.isna(value) - and not np.isnan(value) + value is not None + and value is not pd.NA + and not pd.isna(value) + and not np.isnan(value) ) results = [] @@ -112,8 +112,8 @@ def _is_not_none(value): edges_remove = [] for e in _graph.edges.data(keys=True): if (hazard_name in e[-1]) and ( - ("bridge" not in e[-1]) - or ("bridge" in e[-1] and e[-1]["bridge"] != "yes") + ("bridge" not in e[-1]) + or ("bridge" in e[-1] and e[-1]["bridge"] != "yes") ): edges_remove.append(e) edges_remove = [e for e in edges_remove if (e[-1][hazard_name] is not None)] @@ -121,14 +121,14 @@ def _is_not_none(value): e for e in edges_remove if (hazard_name in e[-1]) - and ( - _is_not_none(e[-1][hazard_name]) - and (e[-1][hazard_name] > float(self.analysis.threshold)) - and ( - ("bridge" not in e[-1]) - or ("bridge" in e[-1] and e[-1]["bridge"] != "yes") - ) - ) + and ( + _is_not_none(e[-1][hazard_name]) + and (e[-1][hazard_name] > float(self.analysis.threshold)) + and ( + ("bridge" not in e[-1]) + or ("bridge" in e[-1] and e[-1]["bridge"] != "yes") + ) + ) ] _graph.remove_edges_from(edges_remove) @@ -167,8 +167,8 @@ def _is_not_none(value): alt_nodes, connected = np.NaN, 0 current_value = _weighing_analyser.weighing_data[ - self.analysis.weighing.config_value - ] + self.analysis.weighing.config_value + ] if not current_value: # if None current_value = np.nan diff = round(alt_value - current_value, 3) @@ -193,17 +193,15 @@ def _is_not_none(value): df_calculated[f"alt_{self.analysis.weighing.config_value}"], errors="coerce", ) - + df_calculated, gdf = self._update_time(df_calculated, gdf) - + # Merge the dataframes if "rfid" in gdf: gdf = gdf.merge(df_calculated, how="left", on=["u", "v", "rfid"]) else: gdf = gdf.merge(df_calculated, how="left", on=["u", "v"]) - - gdf["hazard"] = hazard_name results.append(gdf)