From bd6656718dd40a9efde6ae149c88af2fb67b3b27 Mon Sep 17 00:00:00 2001 From: simei94 Date: Mon, 13 May 2024 19:48:33 +0200 Subject: [PATCH] re-structure tables --- .../drt/DrtPostProcessingAverageAnalysis.java | 41 +- .../matsim/dashboard/AverageDrtDashboard.java | 461 ++++++------------ 2 files changed, 178 insertions(+), 324 deletions(-) diff --git a/src/main/java/org/matsim/analysis/postAnalysis/drt/DrtPostProcessingAverageAnalysis.java b/src/main/java/org/matsim/analysis/postAnalysis/drt/DrtPostProcessingAverageAnalysis.java index 1babb3d4..035d0c26 100644 --- a/src/main/java/org/matsim/analysis/postAnalysis/drt/DrtPostProcessingAverageAnalysis.java +++ b/src/main/java/org/matsim/analysis/postAnalysis/drt/DrtPostProcessingAverageAnalysis.java @@ -8,7 +8,6 @@ import org.matsim.application.options.InputOptions; import org.matsim.application.options.OutputOptions; import org.matsim.core.utils.io.IOUtils; -import org.matsim.simwrapper.SimWrapper; import picocli.CommandLine; import tech.tablesaw.api.ColumnType; import tech.tablesaw.api.Row; @@ -26,7 +25,11 @@ @CommandLine.Command(name = "average-drt", description = "Calculates average drt stats based on several sim runs with different random seeds.") @CommandSpec( requires = {"runs", "mode"}, - produces = {"avg_demand_stats.csv", "avg_supply_stats.csv"} + produces = {"rides_per_veh_avg_demand_stats.csv", "avg_wait_time_avg_demand_stats.csv", "requests_avg_demand_stats.csv", "avg_total_travel_time_avg_demand_stats.csv", + "rides_avg_demand_stats.csv", "avg_direct_distance_[km]_avg_demand_stats.csv", "rejections_avg_demand_stats.csv", "95th_percentile_wait_time_avg_demand_stats.csv", + "avg_in-vehicle_time_avg_demand_stats.csv", "avg_ride_distance_[km]_avg_demand_stats.csv", "rejection_rate_avg_demand_stats.csv", + "avg_fare_[MoneyUnit]_avg_demand_stats.csv", "total_service_hours_avg_supply_stats.csv", "pooling_ratio_avg_supply_stats.csv", "detour_ratio_avg_supply_stats.csv", + "total_vehicle_mileage_[km]_avg_supply_stats.csv", "empty_ratio_avg_supply_stats.csv", "number_of_stops_avg_supply_stats.csv", "total_pax_distance_[km]_avg_supply_stats.csv", "vehicles_avg_supply_stats.csv"} ) public class DrtPostProcessingAverageAnalysis implements MATSimAppCommand { @@ -41,6 +44,7 @@ public class DrtPostProcessingAverageAnalysis implements MATSimAppCommand { private final Map> supplyStats = new HashMap<>(); private final Map demandAvgs = new HashMap<>(); private final Map supplyAvgs = new HashMap<>(); + Map> params = new HashMap<>(); private final CsvOptions csv = new CsvOptions(); @@ -109,23 +113,38 @@ public Integer call() throws Exception { fillAvgMap(demandStats, demandAvgs); fillAvgMap(supplyStats, supplyAvgs); - writeFile("avg_demand_stats.csv", demandAvgs); - writeFile("avg_supply_stats.csv", supplyAvgs); + params.put("avg_demand_stats.csv", List.of("rides_per_veh", "avg_wait_time", "requests", "avg_total_travel_time", "rides", "avg_direct_distance_[km]", + "rejections", "95th_percentile_wait_time", "avg_in-vehicle_time", "avg_ride_distance_[km]", "rejection_rate", "avg_fare_[MoneyUnit]")); + params.put("avg_supply_stats.csv", List.of("total_service_hours", "pooling_ratio", "detour_ratio", "total_vehicle_mileage_[km]", "empty_ratio", "number_of_stops", + "total_pax_distance_[km]", "vehicles")); + + for (Map.Entry> e : params.entrySet()) { + for (String param : params.get(e.getKey())) { + if (e.getKey().contains("demand")) { + writeFile(e.getKey(), demandAvgs, param); + } else { + writeFile(e.getKey(), supplyAvgs, param); + } + } + } return 0; } - private void writeFile(String fileName, Map values) throws IOException { - try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(output.getPath(fileName)), CSVFormat.DEFAULT)) { + private void writeFile(String fileName, Map values, String param) throws IOException { + try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(output.getPath(param + "_" + fileName)), CSVFormat.DEFAULT)) { printer.printRecord("info", value); for (Map.Entry e : values.entrySet()) { - printer.printRecord("mean-" + e.getKey(), e.getValue()[0]); - printer.printRecord("median-" + e.getKey(), e.getValue()[1]); - printer.printRecord("sd-" + e.getKey(), e.getValue()[2]); - printer.printRecord("min-" + e.getKey(), e.getValue()[3]); - printer.printRecord("max-" + e.getKey(), e.getValue()[4]); + String transformed = e.getKey().toLowerCase().replace(".", "").replace(" ", "_"); + if (transformed.contains(param)) { + printer.printRecord("mean-" + e.getKey(), e.getValue()[0]); + printer.printRecord("median-" + e.getKey(), e.getValue()[1]); + printer.printRecord("sd-" + e.getKey(), e.getValue()[2]); + printer.printRecord("min-" + e.getKey(), e.getValue()[3]); + printer.printRecord("max-" + e.getKey(), e.getValue()[4]); + } } } } diff --git a/src/main/java/org/matsim/dashboard/AverageDrtDashboard.java b/src/main/java/org/matsim/dashboard/AverageDrtDashboard.java index 71542e58..6edf3461 100644 --- a/src/main/java/org/matsim/dashboard/AverageDrtDashboard.java +++ b/src/main/java/org/matsim/dashboard/AverageDrtDashboard.java @@ -40,329 +40,164 @@ public void configure(Header header, Layout layout) { "/n" + "This dashboard shows average values for " + noRuns + " simulation runs. For the results of the specific runs please choose the according directory next to this dashboard.yaml."; - layout.row("Results") +// DEMAND + layout.row("one") .el(Table.class, (viz, data) -> { - viz.title = "Service results"; - viz.description = "Final configuration and service KPI."; - viz.dataset = postProcess(data, "avg_supply_stats.csv"); -// viz.style = "topsheet"; + viz.title = "Rides per vehicle"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "rides_per_veh_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Avg wait time"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "avg_wait_time_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Requests"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "requests_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }); + + layout.row("two") + .el(Table.class, (viz, data) -> { + viz.title = "Avg total travel time"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "avg_total_travel_time_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Rides"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "rides_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Avg direct distance [km]"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "avg_direct_distance_[km]_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }); + + layout.row("three") + .el(Table.class, (viz, data) -> { + viz.title = "Rejections"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "rejections_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "95th percentile wait time"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "95th_percentile_wait_time_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Avg in-vehicle time"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "avg_in-vehicle_time_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }); + + layout.row("four") + .el(Table.class, (viz, data) -> { + viz.title = "Avg ride distance [km]"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "avg_ride_distance_[km]_avg_demand_stats.csv"); viz.showAllRows = true; viz.width = 1.; }) .el(Table.class, (viz, data) -> { - viz.title = "Demand results"; + viz.title = "Rejection rate"; viz.description = "Final demand statistics and KPI."; - viz.dataset = postProcess(data, "avg_demand_stats.csv"); -// viz.style = "topsheet"; + viz.dataset = postProcess(data, "rejection_rate_avg_demand_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Avg fare [MoneyUnit]"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "avg_fare_[MoneyUnit]_avg_demand_stats.csv"); viz.showAllRows = true; viz.width = 1.; }); -// .el(MapPlot.class, (viz, data) -> { -// switch (drtConfigGroup.operationalScheme) { -// case stopbased -> { -// viz.title = "Map of stops"; -// viz.setShape(postProcess(data, "stops.shp"), "id"); -// -// viz.addDataset("trips", postProcess(data, "trips_per_stop.csv")); -// -// viz.display.radius.dataset = "trips"; -// viz.display.radius.columnName = "departures"; -// viz.display.radius.join = "stop_id"; -// viz.display.radius.scaleFactor = 10d; -// -// } -// case door2door -> { -// //TODO add drtNetwork !? -// } -// case serviceAreaBased -> { -// viz.title = "Service area"; -// viz.setShape(postProcess(data, "serviceArea.shp"), "something"); -// } -// } -// viz.center = data.context().getCenter(); -// viz.zoom = data.context().mapZoomLevel; -// viz.width = 2.; -// }); -// -// //TODO: discuss: should we make XYTime plots out of those ?? -// layout.row("Spatial demand distribution") -// .el(Hexagons.class, (viz, data) -> { -// viz.title = "Spatial demand distribution"; -// viz.description = "Origins and destinations."; -// viz.projection = this.crs; -// viz.file = data.output("*output_drt_legs_" + drtConfigGroup.mode + ".csv"); -// viz.addAggregation("OD", "origins", "fromX", "fromY", "destinations", "toX", "toY"); -// -// viz.center = data.context().getCenter(); -// viz.zoom = data.context().mapZoomLevel; -// viz.height = 7d; -// }) -// .el(Hexagons.class, (viz, data) -> { -// viz.title = "Spatial rejection distribution"; -// viz.description = "Requested (and rejected) origins and destinations."; -// viz.projection = this.crs; -// viz.file = data.output("ITERS/it." + lastIteration + "/*rejections_" + drtConfigGroup.mode + ".csv"); -// viz.addAggregation("rejections", "origins", "fromX", "fromY", "destinations", "toX", "toY"); -// -// viz.center = data.context().getCenter(); -// viz.zoom = data.context().mapZoomLevel; -// }) -// ; -// -//// This plot is not absolutely necesarry given the hex plots -//// if (drtConfigGroup.operationalScheme == DrtConfigGroup.OperationalScheme.stopbased) -//// layout.row("od").el(AggregateOD.class, (viz, data) -> { -//// -//// viz.shpFile = postProcess(data, "stops.shp"); -//// viz.dbfFile = postProcess(data, "stops.shp").replace(".shp", ".dbf"); -//// viz.csvFile = postProcess(data, "od.csv"); -//// -//// }); -// -// //Final Demand stats -// layout.row("Final Demand And Wait Time Statistics") -// .el(Plotly.class, (viz, data) -> { -// viz.title = "Final Demand and Wait Stats over day time"; -// viz.description = "Number of rides (customers) is displayed in bars, wait statistics in lines"; -// -// Plotly.DataSet waitStats = viz.addDataset(data.output("*_waitStats_" + drtConfigGroup.mode + ".csv")); -// Plotly.DataSet rejections = viz.addDataset(data.output("*drt_rejections_perTimeBin_" + drtConfigGroup.mode + ".csv")); -// -// viz.layout = tech.tablesaw.plotly.components.Layout.builder() -// .xAxis(Axis.builder().title("Time Bin").build()) -// .yAxis(Axis.builder().title("Wait Time [s]").build()) -// .yAxis2(Axis.builder().title("Nr of Rides/Rejections") -// .side(Axis.Side.right) -// .overlaying(ScatterTrace.YAxis.Y) -// .build()) -// .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) -// .build(); -// -// viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) -// .mode(ScatterTrace.Mode.LINE) -// .name("Average") -// .build(), -// waitStats.mapping() -// .x("timebin") -// .y("average_wait") -// ); -// -// viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) -// .mode(ScatterTrace.Mode.LINE) -// .name("P5") -// .build(), -// waitStats.mapping() -// .x("timebin") -// .y("p_5") -// ); -// -// viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) -// .mode(ScatterTrace.Mode.LINE) -// .name("P95") -// .build(), -// waitStats.mapping() -// .x("timebin") -// .y("p_95") -// ); -// -// viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) -// .opacity(0.3) -// .yAxis(ScatterTrace.YAxis.Y2.toString()) -// .name("Rides") -// .build(), -// waitStats.mapping() -// .x("timebin") -// .y("legs") -// ); -// -// viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) -// .opacity(0.3) -// .yAxis(ScatterTrace.YAxis.Y2.toString()) -// .name("Rejections") -// .build(), -// rejections.mapping() -// .x("timebin") -// .y("rejections") -// ); -// -// }) -// .el(Area.class, (viz, data) -> { -// //actually, without title the area plot won't work -// viz.title = "Vehicle occupancy"; -// viz.description = "Number of passengers on board at a time"; -// viz.dataset = data.output("ITERS/it." + lastIteration + "/*occupancy_time_profiles_" + drtConfigGroup.mode + ".txt"); -// viz.x = "time"; -// viz.xAxisName = "Time"; -// viz.yAxisName = "Vehicles [1]"; -// }); -// -// -// //from here on, we show the 'evolution' of statistics over iterations -// //based on CL's feedback this is not helpful for low iteration numbers -// //this implementation actually does not account for situations where firstIteration > 0. -// if (lastIteration >= 3) { -// -// //Demand stats over iterations -// layout.row("Demand and Wait Time Statistics per iteration") -// .el(Plotly.class, (viz, data) -> { -// viz.title = "Rides and rejections per iteration"; -// viz.description = "Number of rides (customers) and rejections over the course of the simulation"; -// -// Plotly.DataSet dataset = viz.addDataset(data.output("*_customer_stats_" + drtConfigGroup.mode + ".csv")); -// -// viz.layout = tech.tablesaw.plotly.components.Layout.builder() -// .xAxis(Axis.builder().title("Iteration").build()) -// .yAxis(Axis.builder().title("Wait Time [s]").build()) -// .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) -// .build(); -// -// viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) -// .name("Rejections") -// .build(), -// dataset.mapping() -// .x("iteration") -// .y("rejections") -//// .color(Plotly.ColorScheme.RdBu) -// ); -// -// viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) -// .name("Rides") -// .build(), -// dataset.mapping() -// .x("iteration") -// .y("rides") -//// .color(Plotly.ColorScheme.RdBu) -// ); -// -// }) -// .el(Line.class, (viz, data) -> { -// viz.title = "Waiting time statistics per iteration"; -// viz.description = ""; -// viz.dataset = data.output("*customer_stats_" + drtConfigGroup.mode + ".csv"); -// viz.x = "iteration"; -// viz.columns = List.of("wait_average", "wait_median", "wait_p95"); -// viz.legendName = List.of("Average", "Median", "95th Percentile"); -// viz.xAxisName = "Iteration"; -// viz.yAxisName = "Waiting Time [s]"; -// }) -// ; -// -// layout.row("Demand And Travel Time Statistics per iteration") -// .el(Plotly.class, (viz, data) -> { -// viz.title = "Travel time components per iteration"; -// viz.description = "Comparing mean wait vs. mean in-vehicle travel time per customer"; -// -// Plotly.DataSet dataset = viz.addDataset(data.output("*_customer_stats_" + drtConfigGroup.mode + ".csv")); -// -// viz.layout = tech.tablesaw.plotly.components.Layout.builder() -// .xAxis(Axis.builder().title("Iteration").build()) -// .yAxis(Axis.builder().title("Time [s]") -// .build()) -// .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) -// .build(); -// -// viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) -//// .opacity(0.5) -// .name("In Vehicle") -// .build(), -// dataset.mapping() -// .x("iteration") -// .y("inVehicleTravelTime_mean") -//// .color(Plotly.ColorScheme.RdBu) -// ); -// -// viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) -//// .opacity(0.5) -// .name("Wait") -// .build(), -// dataset.mapping() -// .x("iteration") -// .y("wait_average") -//// .color(Plotly.ColorScheme.RdBu) -// ); -// }) -// .el(Line.class, (viz, data) -> { -// viz.title = "Customer travel distance per iteration"; -// viz.dataset = data.output("*customer_stats_" + drtConfigGroup.mode + ".csv"); -// viz.description = "Customer traveled distance versus customer direct distance"; -// viz.x = "iteration"; -// viz.columns = List.of("directDistance_m_mean", "distance_m_mean"); -// viz.legendName = List.of("Mean direct distance", "Mean driven distance"); -// viz.xAxisName = "Iteration"; -// viz.yAxisName = "distance [m]"; -// }); -// -// -// //Evolution of fleet stats -// layout.row("Fleet Stats per Iteration") -// .el(Plotly.class, (viz, data) -> { -// viz.title = "Fleet stats per iteration"; -// viz.description = "Number of " + drtConfigGroup.mode + " vehicles (customers) is displayed as bars, distance stats as lines"; -// -// Plotly.DataSet dataset = viz.addDataset(data.output("*_vehicle_stats_" + drtConfigGroup.mode + ".csv")); -// -// viz.layout = tech.tablesaw.plotly.components.Layout.builder() -// .xAxis(Axis.builder().title("Iteration").build()) -// .yAxis(Axis.builder().title("Total Distance [m]") -//// .overlaying(ScatterTrace.YAxis.Y2) -// .build()) -// .yAxis2(Axis.builder().title("Nr Of Vehicles") -// .side(Axis.Side.right) -// .overlaying(ScatterTrace.YAxis.Y) -// .build()) -// .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) -// .build(); -// -// viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) -// .mode(ScatterTrace.Mode.LINE) -// .name("Pax Dist.") -// .build(), -// dataset.mapping() -// .x("iteration") -// .y("totalPassengerDistanceTraveled") -// ); -// -// viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) -// .mode(ScatterTrace.Mode.LINE) -// .name("Vehicle mileage") -// .build(), -// dataset.mapping() -// .x("iteration") -// .y("totalDistance") -// ); -// -// viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) -// .mode(ScatterTrace.Mode.LINE) -// .name("Empty mileage") -// .build(), -// dataset.mapping() -// .x("iteration") -// .y("totalEmptyDistance") -// ); -// -// viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) -// .opacity(0.2) -// .yAxis(ScatterTrace.YAxis.Y2.toString()) -// .name("Vehicles") -// .build(), -// dataset.mapping() -// .x("iteration") -// .y("vehicles") -//// .color(Plotly.ColorScheme.RdBu) -// ); -// }) -// .el(Line.class, (viz, data) -> { -// viz.title = "Relative Statistics per iteration"; -// viz.dataset = data.output("*vehicle_stats_" + drtConfigGroup.mode + ".csv"); -// viz.description = "Pooling ratio (Pax distance / Vehicle mileage), Detour ratio, and Empty Ratio"; -// viz.x = "iteration"; -// viz.columns = List.of("d_p/d_t", "l_det", "emptyRatio"); -// viz.legendName = List.of("Pooling ratio", "Detour ratio", "Empty Ratio"); -// viz.xAxisName = "Iteration"; -// viz.yAxisName = "Value"; -// }); -// } +// SUPPLY + supplyTabs(layout); + } + private void supplyTabs(Layout layout) { + layout.row("six") + .el(Table.class, (viz, data) -> { + viz.title = "Total service hours"; + viz.description = "Final configuration and service KPI."; + viz.dataset = postProcess(data, "total_service_hours_avg_supply_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Pooling ratio"; + viz.description = "Final configuration and service KPI."; + viz.dataset = postProcess(data, "pooling_ratio_avg_supply_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Detour ratio"; + viz.description = "Final configuration and service KPI."; + viz.dataset = postProcess(data, "detour_ratio_avg_supply_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }); + + layout.row("seven") + .el(Table.class, (viz, data) -> { + viz.title = "Total vehicle mileage [km]"; + viz.description = "Final configuration and service KPI."; + viz.dataset = postProcess(data, "total_vehicle_mileage_[km]_avg_supply_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Empty ratio"; + viz.description = "Final configuration and service KPI."; + viz.dataset = postProcess(data, "empty_ratio_avg_supply_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Number of stops"; + viz.description = "Final configuration and service KPI."; + viz.dataset = postProcess(data, "number_of_stops_avg_supply_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }); + + layout.row("eight") + .el(Table.class, (viz, data) -> { + viz.title = "Total pax distance [km]"; + viz.description = "Final configuration and service KPI."; + viz.dataset = postProcess(data, "total_pax_distance_[km]_avg_supply_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Vehicles"; + viz.description = "Final configuration and service KPI."; + viz.dataset = postProcess(data, "vehicles_avg_supply_stats.csv"); + viz.showAllRows = true; + viz.width = 1.; + }); } }