diff --git a/CHANGELOG.md b/CHANGELOG.md index 906b0568f..4e63bdc8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. +### 6.3 (2024-07) +- Include additional trip analysis and updated dashboard + - Mode share is now analyzed by age, income, employment, economic_status +- Updated population, which now include reference modes for certain persons + - The reference modes represent modes that a person has actually used in reality + - Allows to evaluate the quality of the model in terms of mode choice +- ReplanningAnnealer module is now activated by default + ### 6.2 (2024-06) - Updated network and gtfs schedule - Network is now based on late 2022 osm data diff --git a/Makefile b/Makefile index 6655fe6bc..09c9b6940 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ JAR := matsim-berlin-*.jar -V := v6.2 +V := v6.3 CRS := EPSG:25832 p := input/$V @@ -155,9 +155,17 @@ $p/berlin-static-$V-25pct.plans.xml.gz: $p/berlin-only-$V-25pct.plans.xml.gz $p/ $(sc) prepare lookup-regiostar --input $@ --output $@ --xls $(germany)/RegioStaR-Referenzdateien.xlsx -$p/berlin-activities-$V-25pct.plans.xml.gz: $p/berlin-static-$V-25pct.plans.xml.gz +$p/berlin-activities-$V-25pct.plans.xml.gz: $p/berlin-static-$V-25pct.plans.xml.gz $p/berlin-$V-facilities.xml.gz $p/berlin-$V-network.xml.gz $(sc) prepare activity-sampling --seed 1 --input $< --output $@ --persons src/main/python/table-persons.csv --activities src/main/python/table-activities.csv + $(sc) prepare assign-reference-population --population $@ --output $@\ + --persons src/main/python/table-persons.csv\ + --activities src/main/python/table-activities.csv\ + --shp $(germany)/../matsim-berlin/data/SrV/zones/zones.shp\ + --shp-crs $(CRS)\ + --facilities $(word 2,$^)\ + --network $(word 3,$^)\ + $p/berlin-initial-$V-25pct.plans.xml.gz: $p/berlin-activities-$V-25pct.plans.xml.gz $p/berlin-$V-facilities.xml.gz $p/berlin-$V-network.xml.gz $(sc) prepare init-location-choice\ --input $<\ @@ -196,7 +204,7 @@ $p/commercialFacilities.xml.gz: --landuseShapeFileName $(berlin)/input/shp/berlinBrandenburg_landuse_4326.shp\ --shapeFileLanduseTypeColumn "fclass"\ --shapeCRS "EPSG:4326"\ - --pathToInvestigationAreaData $p/commercialTraffic/investigationAreaData.csv + --pathToInvestigationAreaData input/commercialTrafficAreaData.csv $p/berlin-small-scale-commercialTraffic-$V-25pct.plans.xml.gz: $p/berlin-$V-network.xml.gz $p/commercialFacilities.xml.gz $(sc) prepare generate-small-scale-commercial-traffic\ @@ -249,6 +257,15 @@ eval-opt: $p/berlin-initial-$V-25pct.experienced_plans.xml.gz $(sc) run --mode "routeChoice" --iterations 20 --all-car --output "output/eval-$(ERROR_METRIC)" --25pct --population "berlin-$V-25pct.plans_$(ERROR_METRIC).xml.gz"\ --config $p/berlin-$V.config.xml +$p/berlin-$V-25pct.plans_cadyts.xml.gz: + $(sc) prepare extract-plans-idx\ + --input output/cadyts/cadyts.output_plans.xml.gz\ + --output $p/berlin-$V-25pct.plans_selection_cadyts.csv + + $(sc) prepare select-plans-idx\ + --input $p/berlin-cadyts-input-$V-25pct.plans.xml.gz\ + --csv $p/berlin-$V-25pct.plans_selection_cadyts.csv\ + --output $@ # These depend on the output of optimization runs $p/berlin-$V-25pct.plans-initial.xml.gz: $p/berlin-$V-facilities.xml.gz $p/berlin-$V-network.xml.gz $p/berlin-longHaulFreight-$V-25pct.plans.xml.gz @@ -300,5 +317,8 @@ prepare-calibration: $p/berlin-cadyts-input-$V-25pct.plans.xml.gz $p/berlin-$V-n prepare-initial: $p/berlin-$V-25pct.plans-initial.xml.gz $p/berlin-$V-network-with-pt.xml.gz echo "Done" +prepare-drt: $p/berlin-$V.drt-by-rndLocations-10000vehicles-4seats.xml.gz + echo "Done" + prepare: $p/berlin-$V-10pct.plans.xml.gz echo "Done" \ No newline at end of file diff --git a/checkstyle.xml b/checkstyle.xml index 8c49465e7..d7bed60d2 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -251,5 +251,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/input/commercialTraffic/config_demand.xml b/input/commercialTraffic/config_demand.xml deleted file mode 100644 index fe72019b1..000000000 --- a/input/commercialTraffic/config_demand.xml +++ /dev/null @@ -1,579 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/input/commercialTraffic/investigationAreaData.csv b/input/commercialTrafficAreaData.csv similarity index 100% rename from input/commercialTraffic/investigationAreaData.csv rename to input/commercialTrafficAreaData.csv diff --git a/input/v6.2/berlin-v6.2.config.xml b/input/v6.2/berlin-v6.2.config.xml index ede0d75ac..1ba9337c6 100644 --- a/input/v6.2/berlin-v6.2.config.xml +++ b/input/v6.2/berlin-v6.2.config.xml @@ -130,8 +130,8 @@ - - + + diff --git a/input/v6.3/area/area.dbf b/input/v6.3/area/area.dbf new file mode 100644 index 000000000..2caa1f7d9 Binary files /dev/null and b/input/v6.3/area/area.dbf differ diff --git a/input/v6.3/area/area.poly b/input/v6.3/area/area.poly new file mode 100644 index 000000000..ceba25a93 --- /dev/null +++ b/input/v6.3/area/area.poly @@ -0,0 +1,13 @@ +area +1 + 13.219000451630821 52.804645826658245 + 13.563415935578835 52.7636439833311 + 13.762958239770938 52.64337190957148 + 13.816260636096226 52.400094305830414 + 13.644052894122218 52.18278453619655 + 13.0713938156531 52.178684351863836 + 12.744745797146848 52.345425181394226 + 12.983923216555192 52.79644545799282 + 13.219000451630821 52.804645826658245 +END +END diff --git a/input/v6.3/area/area.prj b/input/v6.3/area/area.prj new file mode 100644 index 000000000..f45cbadf0 --- /dev/null +++ b/input/v6.3/area/area.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] \ No newline at end of file diff --git a/input/v6.3/area/area.shp b/input/v6.3/area/area.shp new file mode 100644 index 000000000..a5b387f11 Binary files /dev/null and b/input/v6.3/area/area.shp differ diff --git a/input/v6.3/area/area.shx b/input/v6.3/area/area.shx new file mode 100644 index 000000000..a4a038216 Binary files /dev/null and b/input/v6.3/area/area.shx differ diff --git a/input/commercialTraffic/commercial_VehicleTypes_CV.xml b/input/v6.3/berlin-v6.3-vehicleTypes.xml similarity index 78% rename from input/commercialTraffic/commercial_VehicleTypes_CV.xml rename to input/v6.3/berlin-v6.3-vehicleTypes.xml index 303da3522..84ef8b727 100644 --- a/input/commercialTraffic/commercial_VehicleTypes_CV.xml +++ b/input/v6.3/berlin-v6.3-vehicleTypes.xml @@ -2,6 +2,56 @@ + + + + + 1 + serial + 1 + + + + + + + + + 1 + serial + 1 + + + + + + + + + 1 + serial + 1 + + + + + + + + + + 1 + serial + 1 + + + + + + + + + Golf 1.4 Trendline @@ -135,7 +185,7 @@ Heavy Vehicle 40t - + diff --git a/input/v6.3/berlin-v6.3.config.xml b/input/v6.3/berlin-v6.3.config.xml new file mode 100644 index 000000000..7c3f3744d --- /dev/null +++ b/input/v6.3/berlin-v6.3.config.xml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/input/v6.3/berlin-v6.3.drt-config.xml b/input/v6.3/berlin-v6.3.drt-config.xml new file mode 100644 index 000000000..3365acd48 --- /dev/null +++ b/input/v6.3/berlin-v6.3.drt-config.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/input/v6.2/params/mc_params1.yaml b/input/v6.3/params/mc_params1.yaml similarity index 100% rename from input/v6.2/params/mc_params1.yaml rename to input/v6.3/params/mc_params1.yaml diff --git a/input/v6.2/params/mc_params2.yaml b/input/v6.3/params/mc_params2.yaml similarity index 100% rename from input/v6.2/params/mc_params2.yaml rename to input/v6.3/params/mc_params2.yaml diff --git a/input/v6.2/params/mc_params3.yaml b/input/v6.3/params/mc_params3.yaml similarity index 100% rename from input/v6.2/params/mc_params3.yaml rename to input/v6.3/params/mc_params3.yaml diff --git a/input/v6.2/params/mc_params4.yaml b/input/v6.3/params/mc_params4.yaml similarity index 100% rename from input/v6.2/params/mc_params4.yaml rename to input/v6.3/params/mc_params4.yaml diff --git a/input/v6.2/params/mc_params5.yaml b/input/v6.3/params/mc_params5.yaml similarity index 100% rename from input/v6.2/params/mc_params5.yaml rename to input/v6.3/params/mc_params5.yaml diff --git a/input/v6.3/pt-area/pt-area.cpg b/input/v6.3/pt-area/pt-area.cpg new file mode 100644 index 000000000..3ad133c04 --- /dev/null +++ b/input/v6.3/pt-area/pt-area.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/input/v6.3/pt-area/pt-area.dbf b/input/v6.3/pt-area/pt-area.dbf new file mode 100644 index 000000000..0234ef438 Binary files /dev/null and b/input/v6.3/pt-area/pt-area.dbf differ diff --git a/input/v6.3/pt-area/pt-area.prj b/input/v6.3/pt-area/pt-area.prj new file mode 100644 index 000000000..bd846aeb2 --- /dev/null +++ b/input/v6.3/pt-area/pt-area.prj @@ -0,0 +1 @@ +PROJCS["ETRS_1989_UTM_Zone_32N",GEOGCS["GCS_ETRS_1989",DATUM["D_ETRS_1989",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",9.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/input/v6.3/pt-area/pt-area.qmd b/input/v6.3/pt-area/pt-area.qmd new file mode 100644 index 000000000..e5cd6b250 --- /dev/null +++ b/input/v6.3/pt-area/pt-area.qmd @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + +proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs + 0 + 0 + + + + + false + + + + diff --git a/input/v6.3/pt-area/pt-area.shp b/input/v6.3/pt-area/pt-area.shp new file mode 100644 index 000000000..dc4679f12 Binary files /dev/null and b/input/v6.3/pt-area/pt-area.shp differ diff --git a/input/v6.3/pt-area/pt-area.shx b/input/v6.3/pt-area/pt-area.shx new file mode 100644 index 000000000..afe362e65 Binary files /dev/null and b/input/v6.3/pt-area/pt-area.shx differ diff --git a/pom.xml b/pom.xml index 8911c7ebd..e789e2812 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ matsim-all - 2025.0-PR3292 + 2025.0-PR3350 @@ -15,7 +15,7 @@ 4.0.0 com.github.matsim-scenarios matsim-berlin - 6.2 + 6.3-SNAPSHOT MATSim Open Berlin scenario MATSim Open Berlin scenario @@ -245,7 +245,7 @@ org.apache.poi poi-ooxml - 5.2.5 + 5.3.0 @@ -261,7 +261,7 @@ me.tongfei progressbar - 0.10.0 + 0.10.1 @@ -418,12 +418,17 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.2.0 + 3.4.0 com.puppycrawl.tools checkstyle - 10.6.0 + 10.17.0 + + + com.github.sevntu-checkstyle + sevntu-checks + 1.44.1 diff --git a/src/main/java/org/matsim/dashboard/BerlinDashboardProvider.java b/src/main/java/org/matsim/dashboard/BerlinDashboardProvider.java index b3c230aae..3567536eb 100644 --- a/src/main/java/org/matsim/dashboard/BerlinDashboardProvider.java +++ b/src/main/java/org/matsim/dashboard/BerlinDashboardProvider.java @@ -21,8 +21,10 @@ public class BerlinDashboardProvider implements DashboardProvider { @Override public List getDashboards(Config config, SimWrapper simWrapper) { - TripDashboard trips = new TripDashboard("mode_share_ref.csv", "mode_share_per_dist_ref.csv", "mode_users_ref.csv"); - trips.setAnalysisArgs("--match-id", "^berlin.+", "--shp-filter", "none"); + TripDashboard trips = new TripDashboard("mode_share_ref.csv", "mode_share_per_dist_ref.csv", "mode_users_ref.csv") + .setAnalysisArgs("--match-id", "^berlin.+", "--shp-filter", "none") + .withChoiceEvaluation(true) + .withGroupedRefData("mode_share_per_group_dist_ref.csv", "age", "income", "employment", "economic_status"); return List.of( trips, diff --git a/src/main/java/org/matsim/prepare/FilterRelevantAgents.java b/src/main/java/org/matsim/prepare/FilterRelevantAgents.java index e263bf0fe..455bf3f14 100644 --- a/src/main/java/org/matsim/prepare/FilterRelevantAgents.java +++ b/src/main/java/org/matsim/prepare/FilterRelevantAgents.java @@ -175,7 +175,7 @@ private Coord getCoordinate(Activity act) { return coord; } - private LeastCostPathCalculator createRouter(Network network) { + private static LeastCostPathCalculator createRouter(Network network) { FreeSpeedTravelTime travelTime = new FreeSpeedTravelTime(); LeastCostPathCalculatorFactory factory = new SpeedyALTFactory(); diff --git a/src/main/java/org/matsim/prepare/RunOpenBerlinCalibration.java b/src/main/java/org/matsim/prepare/RunOpenBerlinCalibration.java index fd4162201..67a46d3e7 100644 --- a/src/main/java/org/matsim/prepare/RunOpenBerlinCalibration.java +++ b/src/main/java/org/matsim/prepare/RunOpenBerlinCalibration.java @@ -44,6 +44,8 @@ import org.matsim.core.scoring.SumScoringFunction; import org.matsim.core.scoring.functions.*; import org.matsim.core.utils.geometry.CoordUtils; +import org.matsim.prepare.opt.ExtractPlanIndexFromType; +import org.matsim.prepare.population.AssignReferencePopulation; import org.matsim.prepare.choices.ComputePlanChoices; import org.matsim.prepare.choices.ComputeTripChoices; import org.matsim.prepare.counts.CreateCountsFromGeoPortalBerlin; @@ -86,7 +88,7 @@ CreateCountsFromGeoPortalBerlin.class, CreateCountsFromVMZOld.class, CreateCountsFromVMZ.class, ReprojectNetwork.class, RunActivitySampling.class, MergePlans.class, SplitActivityTypesDuration.class, CleanPopulation.class, CleanAttributes.class, GenerateSmallScaleCommercialTrafficDemand.class, CreateDataDistributionOfStructureData.class, - RunCountOptimization.class, SelectPlansFromIndex.class, + RunCountOptimization.class, SelectPlansFromIndex.class, ExtractPlanIndexFromType.class, AssignReferencePopulation.class, ExtractRelevantFreightTrips.class, CheckCarAvailability.class, FixSubtourModes.class, ComputeTripChoices.class, ComputePlanChoices.class, ApplyNetworkParams.class, SetCarAvailabilityByAge.class, CreateDrtVehicles.class }) @@ -161,11 +163,10 @@ protected Config prepareConfig(Config config) { SimWrapperConfigGroup sw = ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class); + config.replanningAnnealer().setActivateAnnealingModule(false); + if (sample.isSet()) { double sampleSize = sample.getSample(); - - // All car leads to much more congestion even if scaled up compared to a normal run - // therefore scaling is increased a bit more double countScale = allCar ? CAR_FACTOR : 1; config.qsim().setFlowCapFactor(sampleSize * countScale); @@ -185,9 +186,13 @@ protected Config prepareConfig(Config config) { log.info("Running with flow and storage capacity: {} / {}", config.qsim().getFlowCapFactor(), config.qsim().getStorageCapFactor()); - if (allCar) + if (allCar) { config.transit().setUseTransit(false); + // Disable dashboards, for all car runs, these take too many resources + sw.defaultDashboards = SimWrapperConfigGroup.Mode.disabled; + } + // Required for all calibration strategies for (String subpopulation : List.of("person", "commercialPersonTraffic", "commercialPersonTraffic_service", "goodsTraffic")) { config.replanning().addStrategySettings( @@ -244,6 +249,17 @@ protected Config prepareConfig(Config config) { ); } + // Agents should generally use the faster routes, this is without any mode choice + config.scoring().getModes().values().forEach(m -> { + // Only time goes into the score + m.setMarginalUtilityOfTraveling(-config.scoring().getPerforming_utils_hr()); + m.setConstant(0); + m.setMarginalUtilityOfDistance(0); + m.setDailyMonetaryConstant(0); + m.setDailyUtilityConstant(0); + m.setMonetaryDistanceRate(0); + }); + config.controller().setRunId("cadyts"); config.controller().setOutputDirectory("./output/cadyts-" + scaleFactor); @@ -343,7 +359,7 @@ protected void prepareScenario(Scenario scenario) { } } - private Coord getCoord(Scenario scenario, Activity act) { + private static Coord getCoord(Scenario scenario, Activity act) { if (act.getCoord() != null) return act.getCoord(); @@ -383,14 +399,12 @@ public ScoringFunction createNewScoringFunction(Person person) { Config config = controler.getConfig(); - // Only use cadyts, not the usual scoring -// final ScoringParameters params = parameters.getScoringParameters(person); -// sumScoringFunction.addScoringFunction(new CharyparNagelLegScoring(params, controler.getScenario().getNetwork())); -// sumScoringFunction.addScoringFunction(new CharyparNagelActivityScoring(params)); -// sumScoringFunction.addScoringFunction(new CharyparNagelAgentStuckScoring(params)); + // Not using the usual scoring, just cadyts + travel time + // final ScoringParameters params = parameters.getScoringParameters(person); + // sumScoringFunction.addScoringFunction(new CharyparNagelLegScoring(params, controler.getScenario().getNetwork())); final CadytsScoring scoringFunction = new CadytsScoring<>(person.getSelectedPlan(), config, cadytsContext); - scoringFunction.setWeightOfCadytsCorrection(config.scoring().getBrainExpBeta()); + scoringFunction.setWeightOfCadytsCorrection(30 * config.scoring().getBrainExpBeta()); sumScoringFunction.addScoringFunction(scoringFunction); return sumScoringFunction; diff --git a/src/main/java/org/matsim/prepare/choices/ComputePlanChoices.java b/src/main/java/org/matsim/prepare/choices/ComputePlanChoices.java index 21cf2208a..3e2bc5769 100644 --- a/src/main/java/org/matsim/prepare/choices/ComputePlanChoices.java +++ b/src/main/java/org/matsim/prepare/choices/ComputePlanChoices.java @@ -6,20 +6,19 @@ import org.apache.commons.csv.CSVPrinter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; import org.matsim.api.core.v01.population.Population; import org.matsim.application.MATSimAppCommand; +import org.matsim.application.analysis.population.TripAnalysis; import org.matsim.application.options.ScenarioOptions; -import org.matsim.application.options.ShpOptions; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.PlansConfigGroup; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; -import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.PersonUtils; import org.matsim.core.population.algorithms.ParallelPersonAlgorithmUtils; import org.matsim.core.population.algorithms.PersonAlgorithm; import org.matsim.core.router.*; @@ -29,6 +28,8 @@ import org.matsim.modechoice.estimators.DefaultLegScoreEstimator; import org.matsim.modechoice.estimators.FixedCostsEstimator; import org.matsim.modechoice.search.TopKChoicesGenerator; +import org.matsim.prepare.population.Attributes; +import org.matsim.simwrapper.SimWrapperConfigGroup; import picocli.CommandLine; import javax.annotation.Nullable; @@ -36,6 +37,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.regex.Pattern; @CommandLine.Command( @@ -50,30 +52,26 @@ public class ComputePlanChoices implements MATSimAppCommand, PersonAlgorithm { * Rows for the result table. */ private final Queue> rows = new ConcurrentLinkedQueue<>(); + private final MainModeIdentifier mmi = new DefaultAnalysisMainModeIdentifier(); @CommandLine.Mixin private ScenarioOptions scenario; - @CommandLine.Mixin - private ShpOptions shp; - @CommandLine.Option(names = "--trips", description = "Input trips from survey data, in matsim-python-tools format.", required = true) - private Path input; - @CommandLine.Option(names = "--facilities", description = "Shp file with facilities", required = true) - private Path facilities; @CommandLine.Option(names = "--top-k", description = "Use top k estimates", defaultValue = "9") private int topK; @CommandLine.Option(names = "--modes", description = "Modes to include in estimation", split = ",") private Set modes; + @CommandLine.Option(names = "--id-filter", description = "Filter for person ids") + private Pattern idFilter; @CommandLine.Option(names = "--time-util-only", description = "Reset scoring for estimation and only use time utility", defaultValue = "false") private boolean timeUtil; @CommandLine.Option(names = "--calc-scores", description = "Perform pseudo scoring for each plan", defaultValue = "false") private boolean calcScores; - @CommandLine.Option(names = "--plan-candidates", description = "Method to generate plan candidates", defaultValue = "bestK") + @CommandLine.Option(names = "--plan-candidates", description = "Method to generate plan candidates", defaultValue = "diverse") private PlanCandidates planCandidates = PlanCandidates.bestK; - - @CommandLine.Option(names = "--output", description = "Path to output csv.", required = true) + @CommandLine.Option(names = "--output", description = "Path to output csv.", defaultValue = "plan-choices.csv") private Path output; private ThreadLocal thread; private ProgressBar pb; - private MainModeIdentifier mmi = new DefaultAnalysisMainModeIdentifier(); + private double globalAvgIncome; public static void main(String[] args) { new ComputePlanChoices().execute(args); @@ -82,13 +80,8 @@ public static void main(String[] args) { @Override public Integer call() throws Exception { - if (!shp.isDefined()) { - log.error("No shapefile defined. Please specify a shapefile for the zones using the --shp option."); - return 2; - } - - if (!Files.exists(input)) { - log.error("Input file does not exist: " + input); + if (!output.getFileName().toString().contains(".csv")) { + log.error("Output file must be a csv file"); return 2; } @@ -97,8 +90,11 @@ public Integer call() throws Exception { config.controller().setLastIteration(0); config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.overwriteExistingFiles); + SimWrapperConfigGroup sw = ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class); + sw.defaultDashboards = SimWrapperConfigGroup.Mode.disabled; + if (timeUtil) { - // All utilities expect travel time become zero + // All utilities except travel time become zero config.scoring().setMarginalUtlOfWaitingPt_utils_hr(0); config.scoring().setUtilityOfLineSwitch(0); @@ -118,6 +114,10 @@ public Integer call() throws Exception { topK = 2; } + if (idFilter != null) { + log.info("Using person id filter: {}", idFilter); + } + Controler controler = this.scenario.createControler(); controler.addOverridingModule(InformedModeChoiceModule.newBuilder() @@ -136,14 +136,7 @@ public Integer call() throws Exception { Injector injector = controler.getInjector(); - PlanBuilder.addVehiclesToScenario(injector.getInstance(Scenario.class)); - - - Population population = PopulationUtils.createPopulation(config); - - PlanBuilder builder = new PlanBuilder(shp, new ShpOptions(facilities, null, null), population.getFactory()); - - builder.createPlans(input).forEach(population::addPerson); + Population population = controler.getScenario().getPopulation(); thread = ThreadLocal.withInitial(() -> new Ctx( @@ -160,17 +153,32 @@ public Integer call() throws Exception { ) ); + globalAvgIncome = population.getPersons().values().stream() + .map(PersonUtils::getIncome) + .filter(Objects::nonNull) + .mapToDouble(d -> d) + .filter(dd -> dd > 0) + .average() + .orElse(Double.NaN); + pb = new ProgressBar("Computing plan choices", population.getPersons().size()); ParallelPersonAlgorithmUtils.run(population, Runtime.getRuntime().availableProcessors(), this); pb.close(); - try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(output), CSVFormat.DEFAULT)) { + String out = output.toString().replace(".csv", "-%s_%d.csv".formatted(planCandidates, topK)); + + log.info("Writing {} choices to {}", rows.size(), out); + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(Path.of(out)), CSVFormat.DEFAULT.builder().setCommentMarker('#').build())) { // header List header = new ArrayList<>(); header.add("person"); + header.add("weight"); + header.add("income"); + header.add("util_money"); header.add("choice"); for (int i = 1; i <= topK; i++) { @@ -186,6 +194,8 @@ public Integer call() throws Exception { header.add(String.format("plan_%d_valid", i)); } + csv.printComment("Average global income: " + globalAvgIncome); + csv.printRecord(header); for (List row : rows) { @@ -200,14 +210,48 @@ public Integer call() throws Exception { @Override public void run(Person person) { + if (person.getAttributes().getAttribute(Attributes.REF_MODES) == null) { + pb.step(); + return; + } + + if (idFilter != null && !idFilter.matcher(person.getId().toString()).matches()) { + pb.step(); + return; + } + Plan plan = person.getSelectedPlan(); PlanModel model = PlanModel.newInstance(plan); + String refModes = (String) person.getAttributes().getAttribute(TripAnalysis.ATTR_REF_MODES); + String[] split = refModes.strip().split("-"); + String[] currentModes = model.getCurrentModesMutable(); + + if (refModes.isBlank()) { + pb.step(); + return; + } + + if (split.length != currentModes.length) { + if (log.isWarnEnabled()) + log.warn("Number of trips ref/current do not match: {} / {}", Arrays.toString(split), Arrays.toString(currentModes)); + + pb.step(); + return; + } + + // Put reference modes into the current modes + System.arraycopy(split, 0, currentModes, 0, split.length); + Ctx ctx = thread.get(); List row = new ArrayList<>(); - row.add(person.getId()); + row.add(person.getAttributes().getAttribute(TripAnalysis.ATTR_REF_ID)); + row.add(person.getAttributes().getAttribute(TripAnalysis.ATTR_REF_WEIGHT)); + row.add(PersonUtils.getIncome(person)); + row.add(globalAvgIncome / PersonUtils.getIncome(person)); + // choice, always the first one row.add(1); diff --git a/src/main/java/org/matsim/prepare/choices/ComputeTripChoices.java b/src/main/java/org/matsim/prepare/choices/ComputeTripChoices.java index b973e6d37..f7c97be90 100644 --- a/src/main/java/org/matsim/prepare/choices/ComputeTripChoices.java +++ b/src/main/java/org/matsim/prepare/choices/ComputeTripChoices.java @@ -15,7 +15,6 @@ import org.matsim.api.core.v01.population.PlanElement; import org.matsim.application.MATSimAppCommand; import org.matsim.application.options.ScenarioOptions; -import org.matsim.application.options.ShpOptions; import org.matsim.core.config.Config; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; @@ -25,12 +24,14 @@ import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.facilities.FacilitiesUtils; import org.matsim.facilities.Facility; +import org.matsim.prepare.population.Attributes; import org.matsim.utils.objectattributes.attributable.AttributesImpl; import picocli.CommandLine; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.SplittableRandom; import java.util.concurrent.CompletableFuture; @@ -49,13 +50,6 @@ public class ComputeTripChoices implements MATSimAppCommand { @CommandLine.Mixin private ScenarioOptions scenario; - @CommandLine.Mixin - private ShpOptions shp; - @CommandLine.Option(names = "--trips", description = "Input trips from survey data, in matsim-python-tools format.", required = true) - private Path input; - - @CommandLine.Option(names = "--facilities", description = "Shp file with facilities", required = true) - private Path facilities; @CommandLine.Option(names = "--modes", description = "Modes to include in choice set", split = ",", required = true) private List modes; @@ -70,17 +64,6 @@ public static void main(String[] args) { @Override public Integer call() throws Exception { - if (!shp.isDefined()) { - log.error("No shapefile defined. Please specify a shapefile for the zones using the --shp option."); - return 2; - } - - if (!Files.exists(input)) { - log.error("Input file does not exist: " + input); - return 2; - } - - Config config = this.scenario.getConfig(); config.controller().setOutputDirectory("choice-output"); config.controller().setLastIteration(0); @@ -95,17 +78,13 @@ public Integer call() throws Exception { Scenario scenario = injector.getInstance(Scenario.class); - PlanBuilder builder = new PlanBuilder(shp, new ShpOptions(facilities, null, null), - scenario.getPopulation().getFactory()); - - PlanBuilder.addVehiclesToScenario(scenario); - ThreadLocal ctx = ThreadLocal.withInitial(() -> injector.getInstance(TripRouter.class)); ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); List>> futures = new ArrayList<>(); - List persons = builder.createPlans(input); + Collection persons = scenario.getPopulation().getPersons().values(); + // Progress bar will be inaccurate ProgressBar pb = new ProgressBar("Computing choices", persons.size() * 3L); @@ -113,6 +92,10 @@ public Integer call() throws Exception { for (Person person : persons) { + if (person.getAttributes().getAttribute(Attributes.REF_MODES) == null) { + continue; + } + Plan plan = person.getSelectedPlan(); for (TripStructureUtils.Trip trip : TripStructureUtils.getTrips(plan)) { diff --git a/src/main/java/org/matsim/prepare/choices/PlanBuilder.java b/src/main/java/org/matsim/prepare/choices/PlanBuilder.java deleted file mode 100644 index a7b184293..000000000 --- a/src/main/java/org/matsim/prepare/choices/PlanBuilder.java +++ /dev/null @@ -1,325 +0,0 @@ -package org.matsim.prepare.choices; - -import it.unimi.dsi.fastutil.Pair; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; -import me.tongfei.progressbar.ProgressBar; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.locationtech.jts.geom.Geometry; -import org.matsim.api.core.v01.Coord; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.api.core.v01.population.PopulationFactory; -import org.matsim.application.options.ShpOptions; -import org.matsim.application.prepare.population.SplitActivityTypesDuration; -import org.matsim.core.population.PersonUtils; -import org.matsim.core.population.PopulationUtils; -import org.matsim.core.utils.geometry.CoordUtils; -import org.matsim.prepare.population.InitLocationChoice; -import org.matsim.vehicles.Vehicle; -import org.matsim.vehicles.VehicleType; -import org.matsim.vehicles.VehicleUtils; -import org.geotools.api.feature.simple.SimpleFeature; -import tech.tablesaw.api.Row; -import tech.tablesaw.api.Table; - -import java.nio.file.Path; -import java.util.*; - -/** - * Utility class to build plans from data. - */ -public class PlanBuilder { - - private static final Logger log = LogManager.getLogger(PlanBuilder.class); - - /** - * Stores of warning for a zone was generated. - */ - private final Set warnings = new HashSet<>(); - - /** - * Maps zone ids to contained coordinates. - */ - private final Long2ObjectMap> zones = new Long2ObjectOpenHashMap<>(); - - /** - * Maps location key to zone id. - */ - private final Object2LongMap features = new Object2LongOpenHashMap<>(); - - private final SplitActivityTypesDuration splitDuration = new SplitActivityTypesDuration(); - - private final SplittableRandom rnd = new SplittableRandom(); - - private final PopulationFactory f; - - /** - * Drop plans with more than this number of trips. - */ - private int maxTripNumber = 0; - - public PlanBuilder(ShpOptions zones, ShpOptions facilities, PopulationFactory f) { - // Collect all zones - for (SimpleFeature ft : zones.readFeatures()) { - features.put(new Location((String) ft.getAttribute("raum_id"), (String) ft.getAttribute("zone")), - (long) ft.getAttribute("id")); - } - - ShpOptions.Index index = zones.createIndex("id"); - - for (SimpleFeature ft : ProgressBar.wrap(facilities.readFeatures(), "Reading facilities")) { - Geometry geom = (Geometry) ft.getDefaultGeometry(); - Coord coord = new Coord(geom.getCentroid().getX(), geom.getCentroid().getY()); - Long result = index.query(coord); - if (result != null) { - this.zones.computeIfAbsent(result, k -> new HashSet<>()).add(coord); - } - } - - this.f = f; - } - - /** - * Set the maximum number of trips in a plan. - */ - public PlanBuilder setMaxTripNumber(int maxTripNumber) { - this.maxTripNumber = maxTripNumber; - return this; - } - - /** - * Add necesarry vehicles to the scenario. - */ - public static void addVehiclesToScenario(Scenario scenario) { - Id car = Id.createVehicleId("car"); - Vehicle vehicle = scenario.getVehicles().getFactory().createVehicle( - car, scenario.getVehicles().getVehicleTypes().get(Id.create("car", VehicleType.class)) - ); - scenario.getVehicles().addVehicle(vehicle); - - Id ride = Id.createVehicleId("ride"); - vehicle = scenario.getVehicles().getFactory().createVehicle( - ride, scenario.getVehicles().getVehicleTypes().get(Id.create("ride", VehicleType.class)) - ); - scenario.getVehicles().addVehicle(vehicle); - - } - - /** - * Create persons with plans from a table. - */ - public List createPlans(Path input) { - - Table table = Table.read().csv(input.toFile()); - - String currentPerson = null; - int currentSeq = -1; - - List result = new ArrayList<>(); - - List trips = new ArrayList<>(); - - try (ProgressBar pb = new ProgressBar("Reading trips", table.rowCount())) { - - for (int i = 0; i < table.rowCount(); i++) { - - Row row = table.row(i); - - String pId = row.getString("p_id"); - int seq = row.getInt("seq"); - - if (!pId.equals(currentPerson) || seq != currentSeq) { - if (!trips.isEmpty()) { - - // Filter person with too many trips - if (maxTripNumber <= 0 || trips.size() <= maxTripNumber) { - Person person = createPerson(pId, seq, trips); - if (person != null) - result.add(person); - } - - trips.clear(); - } - - currentPerson = pId; - currentSeq = seq; - } - - trips.add(row); - pb.step(); - } - } - - return result; - } - - /** - * Create person from row data. - */ - private Person createPerson(String id, int seq, List trips) { - - Person person = f.createPerson(Id.createPersonId(id + "_" + seq)); - - PopulationUtils.putSubpopulation(person, "person"); - PersonUtils.setCarAvail(person, trips.get(0).getInt("p_age") >= 18 ? "always" : "never"); - - VehicleUtils.insertVehicleIdsIntoPersonAttributes(person, Map.of("car", Id.createVehicleId("car"), - "ride", Id.createVehicleId("ride"))); - - Plan plan = f.createPlan(); - - Pair trip = sampleOD(trips.get(0)); - - if (trip == null) - return null; - - // source-destination purpose - String sd = trips.get(0).getString("sd_group"); - - Activity act = f.createActivityFromCoord(sd.startsWith("home") ? "home": "other", trip.first()); - int departure = trips.get(0).getInt("departure") * 60; - act.setEndTime(departure); - act.getAttributes().putAttribute("n", trips.get(0).getInt("n")); - - plan.addActivity(act); - plan.addLeg(f.createLeg(trips.get(0).getString("main_mode"))); - - act = f.createActivityFromCoord(trips.get(0).getString("purpose"), trip.second()); - act.setStartTime(departure + trips.get(0).getInt("duration") * 60); - plan.addActivity(act); - - for (int i = 1; i < trips.size(); i++) { - - Row row = trips.get(i); - - Coord dest = sampleDest(row, act.getCoord()); - if (dest == null) - return null; - - departure = row.getInt("departure") * 60; - act.setEndTime(departure); - act.getAttributes().putAttribute("n", row.getInt("n")); - - plan.addLeg(f.createLeg(row.getString("main_mode"))); - - act = f.createActivityFromCoord(row.getString("purpose"), dest); - act.setStartTime(departure + row.getInt("duration") * 60); - plan.addActivity(act); - } - - - person.getAttributes().putAttribute("seq", seq); - person.addPlan(plan); - person.setSelectedPlan(plan); - - splitDuration.run(person); - - return person; - } - - /** - * Sample pair of from and to facility. Tries to find relation with similar distance to the input. - */ - private Pair sampleOD(Row row) { - - Set from = matchLocation(row.getString("from_location"), row.getString("from_zone")); - Set to = matchLocation(row.getString("to_location"), row.getString("to_zone")); - if (from == null || from.isEmpty() || to == null || to.isEmpty()) - return null; - - double targetDist = InitLocationChoice.beelineDist(row.getDouble("gis_length") * 1000); - - double dist = Double.POSITIVE_INFINITY; - Coord f = null; - Coord t = null; - int i = 0; - do { - - Coord newF = from.stream().skip(rnd.nextInt(from.size())).findFirst().orElseThrow(); - Coord newT = to.stream().skip(rnd.nextInt(to.size())).findFirst().orElseThrow(); - - double newDist = CoordUtils.calcEuclideanDistance(newF, newT); - if (Math.abs(newDist - targetDist) < Math.abs(dist - targetDist)) { - dist = newDist; - f = newF; - t = newT; - } - - i++; - } while (i < 8); - - - return Pair.of(f, t); - } - - /** - * Sample destination from a row and existing location. - */ - private Coord sampleDest(Row row, Coord coord) { - - Set to = matchLocation(row.getString("to_location"), row.getString("to_zone")); - if (to == null || to.isEmpty()) - return null; - - double targetDist = InitLocationChoice.beelineDist(row.getDouble("gis_length") * 1000); - double dist = Double.POSITIVE_INFINITY; - Coord f = null; - - int i = 0; - do { - - Coord newT = to.stream().skip(rnd.nextInt(to.size())).findFirst().orElseThrow(); - - double newDist = CoordUtils.calcEuclideanDistance(coord, newT); - - if (Math.abs(newDist - targetDist) < Math.abs(dist - targetDist)) { - dist = newDist; - f = newT; - } - - i++; - } while (i < 8); - - - return f; - } - - - /** - * Select a random facility for a person. - */ - private Set matchLocation(String location, String zone) { - - Location loc = new Location(location, zone); - long id = features.getOrDefault(loc, -1); - - if (id == -1) { - if (warnings.add(loc)) - log.error("No zone found for location {} and zone {}", location, zone); - - return null; - } - - Set facilities = zones.get(id); - - if (facilities == null) { - if (warnings.add(loc)) - log.error("No facilities found in zone {}", loc); - - return null; - } - - return facilities; - } - - - private record Location(String name, String zone) { - } -} diff --git a/src/main/java/org/matsim/prepare/choices/ScoringFunctionsForPopulation.java b/src/main/java/org/matsim/prepare/choices/ScoringFunctionsForPopulation.java index 763bcc2ec..4bbf4f044 100644 --- a/src/main/java/org/matsim/prepare/choices/ScoringFunctionsForPopulation.java +++ b/src/main/java/org/matsim/prepare/choices/ScoringFunctionsForPopulation.java @@ -243,7 +243,7 @@ void finishScoringFunctions() { Throwable throwable = this.exception.get(); if (throwable != null) { if (throwable instanceof RuntimeException) { - throw ((RuntimeException) throwable); + throw (RuntimeException) throwable; } else { throw new RuntimeException(throwable); } diff --git a/src/main/java/org/matsim/prepare/counts/CreateCountsFromGeoPortalBerlin.java b/src/main/java/org/matsim/prepare/counts/CreateCountsFromGeoPortalBerlin.java index 8a38b0a83..b35380b12 100644 --- a/src/main/java/org/matsim/prepare/counts/CreateCountsFromGeoPortalBerlin.java +++ b/src/main/java/org/matsim/prepare/counts/CreateCountsFromGeoPortalBerlin.java @@ -81,7 +81,7 @@ private static MathTransform getCoordinateTransformation(String inputCRS, String try { return CRS.findMathTransform(CRS.decode(inputCRS, true), CRS.decode(targetCRS, true)); } catch (FactoryException e) { - throw new RuntimeException("Please check the coordinate systems!"); + throw new RuntimeException("Please check the coordinate systems!", e); } } @@ -250,7 +250,7 @@ private List> createRoadTypeFilter() { for (String type : CreateCountsFromGeoPortalBerlin.ROAD_TYPES) { Predicate p = link -> { - var attr = NetworkUtils.getHighwayType(link); + String attr = NetworkUtils.getHighwayType(link); return attr.toLowerCase().contains(type); }; diff --git a/src/main/java/org/matsim/prepare/counts/CreateCountsFromMonthlyVizData.java b/src/main/java/org/matsim/prepare/counts/CreateCountsFromMonthlyVizData.java index ad4a78b2e..c519baa6d 100644 --- a/src/main/java/org/matsim/prepare/counts/CreateCountsFromMonthlyVizData.java +++ b/src/main/java/org/matsim/prepare/counts/CreateCountsFromMonthlyVizData.java @@ -191,7 +191,7 @@ private void matchWithNetwork(Path networkPath, Path geometries, Map !link.link().getId().toString().startsWith("pt_"))); + index.addLinkFilter((link, station) -> !link.link().getId().toString().startsWith("pt_")); if (roadNames) { index.addLinkFilter((link, station) -> { @@ -249,7 +249,7 @@ private void extractStations(Path path, Map stations, CountsOpt try (XSSFWorkbook wb = new XSSFWorkbook(path.toString())) { sheet = wb.getSheetAt(0); } catch (IOException e) { - throw new RuntimeException(e.getMessage()); + throw new RuntimeException(e); } for (Row row : sheet) { @@ -291,7 +291,7 @@ private List readCountData(List paths) { records.addAll(month); } catch (IOException e) { logger.warn("Error processing file {}: ", path.toString()); - throw new RuntimeException(e.getMessage()); + throw new RuntimeException(e); } } @@ -385,11 +385,11 @@ private void aggregateAndAssignCountData(List records, Map mapping = new Object2IntOpenHashMap<>(); + + @Override + public Integer call() throws Exception { + + StreamingPopulationReader reader = new StreamingPopulationReader(ScenarioUtils.createScenario(ConfigUtils.createConfig())); + + reader.addAlgorithm(this); + + reader.readFile(input.toString()); + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(output), CSVFormat.DEFAULT)) { + csv.printRecord("id", "idx"); + for (Object2IntMap.Entry e : mapping.object2IntEntrySet()) { + csv.printRecord(e.getKey(), e.getIntValue()); + } + } + + return 0; + } + + @Override + public void run(Person person) { + String type = person.getSelectedPlan().getType(); + mapping.put(person.getId().toString(), Integer.parseInt(type)); + } +} diff --git a/src/main/java/org/matsim/prepare/opt/SelectPlansFromIndex.java b/src/main/java/org/matsim/prepare/opt/SelectPlansFromIndex.java index a5c175724..92b9335f1 100644 --- a/src/main/java/org/matsim/prepare/opt/SelectPlansFromIndex.java +++ b/src/main/java/org/matsim/prepare/opt/SelectPlansFromIndex.java @@ -37,6 +37,26 @@ public static void main(String[] args) { new SelectPlansFromIndex().execute(args); } + /** + * Select the plan with the given index and remove all other plans. + * If the index is larger than the number of plans, the index is modulo the number of plans. + */ + public static void selectPlanWithIndex(Person person, int planIndex) { + List plans = person.getPlans(); + Set removePlans = new HashSet<>(); + + // make sure that one plan is always selected, even if there are fewer plans than index + int idx = planIndex % plans.size(); + + for (int i = 0; i < plans.size(); i++) { + if (i == idx) { + person.setSelectedPlan(plans.get(i)); + } else + removePlans.add(plans.get(i)); + } + removePlans.forEach(person::removePlan); + } + @Override public Integer call() throws Exception { @@ -69,24 +89,4 @@ public Integer call() throws Exception { return 0; } - /** - * Select the plan with the given index and remove all other plans. - * If the index is larger than the number of plans, the index is modulo the number of plans. - */ - public static void selectPlanWithIndex(Person person, int planIndex) { - List plans = person.getPlans(); - Set removePlans = new HashSet<>(); - - // make sure that one plan is always selected, even if there are less plans than index - int idx = planIndex % plans.size(); - - for (int i = 0; i < plans.size(); i++) { - if (i == idx) { - person.setSelectedPlan(plans.get(i)); - } else - removePlans.add(plans.get(i)); - } - removePlans.forEach(person::removePlan); - } - } diff --git a/src/main/java/org/matsim/prepare/population/AssignReferencePopulation.java b/src/main/java/org/matsim/prepare/population/AssignReferencePopulation.java new file mode 100644 index 000000000..cc0b6fff0 --- /dev/null +++ b/src/main/java/org/matsim/prepare/population/AssignReferencePopulation.java @@ -0,0 +1,158 @@ +package org.matsim.prepare.population; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import me.tongfei.progressbar.ProgressBar; +import org.apache.commons.csv.CSVRecord; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.Population; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.analysis.population.TripAnalysis; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.population.PopulationUtils; +import org.matsim.core.router.TripStructureUtils; +import picocli.CommandLine; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.SplittableRandom; +import java.util.stream.Collectors; + +@CommandLine.Command( + name = "assign-reference-population", + description = "Assigns persons from reference data to a population." +) +public class AssignReferencePopulation implements MATSimAppCommand { + + private static final Logger log = LogManager.getLogger(AssignReferencePopulation.class); + + + @CommandLine.Option(names = "--population", description = "Input population path.", required = true) + private String populationPath; + + @CommandLine.Option(names = "--persons", description = "Input persons from survey data, in matsim-python-tools format.", required = true) + private Path personsPath; + + @CommandLine.Option(names = "--activities", description = "Path to activity table from survey data.", required = true) + private Path activityPath; + + @CommandLine.Option(names = "--facilities", description = "Path to facilities file.", required = true) + private Path facilityPath; + + @CommandLine.Option(names = "--network", description = "Path to network file", required = true) + private Path networkPath; + + @CommandLine.Option(names = "--output", description = "Output population path.", required = true) + private Path output; + + @CommandLine.Mixin + private ShpOptions shp; + + private FacilityIndex facilities; + + private PersonMatcher persons; + + public static void main(String[] args) { + new AssignReferencePopulation().execute(args); + } + + @Override + public Integer call() throws Exception { + + if (!shp.isDefined()) { + log.error("No shapefile defined. Please specify a shapefile for the zones using the --shp option."); + return 2; + } + + if (!Files.exists(activityPath)) { + log.error("Input activity file does not exist: {}", activityPath); + return 2; + } + + Population population = PopulationUtils.readPopulation(populationPath); + + SplittableRandom rnd = new SplittableRandom(0); + persons = new PersonMatcher("idx", personsPath); + facilities = new FacilityIndex(facilityPath.toString()); + + PlanBuilder planBuilder = new PlanBuilder(shp, facilities, activityPath); + + Long2ObjectMap> homeIndex = planBuilder.createHomeIndex(population); + + // Remove persons without legs, these can not be assigned + for (List list : homeIndex.values()) { + list.removeIf(p -> TripStructureUtils.getLegs(p.getSelectedPlan()).isEmpty()); + } + + RunActivitySampling sampling = new RunActivitySampling(persons, planBuilder.getActivities(), population.getFactory(), 1); + + int i = 0; + outer: + for (Map.Entry e : ProgressBar.wrap(persons, "Assigning reference population")) { + + CSVRecord p = e.getValue(); + if (!p.get("seq").equals("0")) + continue; + + if (p.get("mobile_on_day").equals("false")) + continue; + + long zone = planBuilder.findHomeZone(e.getKey()); + + // No home zone known + if (zone < 0) + continue; + + List refPersons = homeIndex.get(zone); + + if (refPersons == null) + continue; + + + // try matching several persons in case it fails + for (int j = 0; j < 10; j++) { + + Person person = persons.matchEntry(e.getValue(), refPersons, rnd); + + // No persons matched + if (person == null) + continue outer; + + // Create the base daily plan (without locations) + Coord homeCoord = Attributes.getHomeCoord(person); + Plan plan = sampling.createPlan(homeCoord, e.getKey()); + + boolean success = planBuilder.assignLocationsFromZones(e.getKey(), plan, homeCoord); + + if (success) { + sampling.copyAttributes(p, person); + person.getAttributes().putAttribute(TripAnalysis.ATTR_REF_WEIGHT, p.get("p_weight")); + person.removePlan(person.getSelectedPlan()); + person.addPlan(plan); + person.setSelectedPlan(plan); + + String refModes = TripStructureUtils.getLegs(plan).stream().map(Leg::getMode).collect(Collectors.joining("-")); + person.getAttributes().putAttribute(TripAnalysis.ATTR_REF_MODES, refModes); + person.getAttributes().putAttribute(TripAnalysis.ATTR_REF_ID, e.getKey()); + + // remove person that have been used as reference + refPersons.remove(person); + i++; + break; + } + } + } + + log.info("Assigned {}/{} reference persons", i, population.getPersons().size()); + + PopulationUtils.writePopulation(population, output.toString()); + + return 0; + } +} diff --git a/src/main/java/org/matsim/prepare/population/Attributes.java b/src/main/java/org/matsim/prepare/population/Attributes.java index cd17fc903..bbeaf3a9d 100644 --- a/src/main/java/org/matsim/prepare/population/Attributes.java +++ b/src/main/java/org/matsim/prepare/population/Attributes.java @@ -4,6 +4,7 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; +import org.matsim.application.analysis.population.TripAnalysis; import java.util.Objects; @@ -36,6 +37,7 @@ public final class Attributes { public static final String RESTRICTED_MOBILITY = "restricted_mobility"; public static final String ECONOMIC_STATUS = "economic_status"; public static final String HOUSEHOLD_SIZE = "household_size"; + public static final String REF_MODES = TripAnalysis.ATTR_REF_MODES; public static final String ATTRACTION_WORK = "attraction_work"; public static final String ATTRACTION_OTHER = "attraction_other"; diff --git a/src/main/java/org/matsim/prepare/population/InitLocationChoice.java b/src/main/java/org/matsim/prepare/population/InitLocationChoice.java index 2f4b2bb5b..84eb2b118 100644 --- a/src/main/java/org/matsim/prepare/population/InitLocationChoice.java +++ b/src/main/java/org/matsim/prepare/population/InitLocationChoice.java @@ -5,6 +5,7 @@ import me.tongfei.progressbar.ProgressBar; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.geotools.api.feature.simple.SimpleFeature; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.index.strtree.STRtree; import org.matsim.api.core.v01.Coord; @@ -28,7 +29,6 @@ import org.matsim.facilities.ActivityFacility; import org.matsim.prepare.RunOpenBerlinCalibration; import org.matsim.prepare.facilities.AttributedActivityFacility; -import org.geotools.api.feature.simple.SimpleFeature; import picocli.CommandLine; import java.math.BigInteger; @@ -103,17 +103,9 @@ public static void main(String[] args) { new InitLocationChoice().execute(args); } - private static Coord rndCoord(SplittableRandom rnd, double dist, Coord origin) { - var angle = rnd.nextDouble() * Math.PI * 2; - - var x = Math.cos(angle) * dist; - var y = Math.sin(angle) * dist; - - return new Coord(RunOpenBerlinCalibration.roundNumber(origin.getX() + x), RunOpenBerlinCalibration.roundNumber(origin.getY() + y)); - } - /** * Approximate beeline dist from known traveled distance. Distance will be reduced by a fixed detour factor. + * * @param travelDist distance in km * @return beeline distance in meters */ @@ -122,6 +114,15 @@ public static double beelineDist(double travelDist) { return travelDist * 1000 / detourFactor; } + private static Coord rndCoord(SplittableRandom rnd, double dist, Coord origin) { + double angle = rnd.nextDouble() * Math.PI * 2; + + double x = Math.cos(angle) * dist; + double y = Math.sin(angle) * dist; + + return new Coord(RunOpenBerlinCalibration.roundNumber(origin.getX() + x), RunOpenBerlinCalibration.roundNumber(origin.getY() + y)); + } + @Override public Integer call() throws Exception { @@ -195,6 +196,12 @@ public void run(Person person) { Coord homeCoord = Attributes.getHomeCoord(person); + // Reference persons are not assigned locations + if (person.getAttributes().getAttribute(Attributes.REF_MODES) != null) { + pb.step(); + return; + } + // Activities that only occur on one place per person Map fixedLocations = new HashMap<>(); @@ -210,11 +217,12 @@ public void run(Person person) { for (Activity act : acts) { - String type = act.getType(); - total.incrementAndGet(); if (Attributes.isLinkUnassigned(act.getLinkId())) { + + String type = act.getType(); + act.setLinkId(null); ActivityFacility location = null; diff --git a/src/main/java/org/matsim/prepare/population/PersonMatcher.java b/src/main/java/org/matsim/prepare/population/PersonMatcher.java new file mode 100644 index 000000000..e010e427c --- /dev/null +++ b/src/main/java/org/matsim/prepare/population/PersonMatcher.java @@ -0,0 +1,175 @@ +package org.matsim.prepare.population; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.matsim.api.core.v01.population.Person; +import org.matsim.application.options.CsvOptions; +import org.matsim.core.population.PersonUtils; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** + * This class is used to read and match persons from the reference data in csv format. + */ +public class PersonMatcher implements Iterable> { + + private static final Logger log = LogManager.getLogger(PersonMatcher.class); + + private final String idxColumn; + + private final CsvOptions csv = new CsvOptions(CSVFormat.Predefined.Default); + private final Map> groups = new HashMap<>(); + private final Map persons = new HashMap<>(); + + public PersonMatcher(String idxColumn, Path personsPath) { + this.idxColumn = idxColumn; + + try (CSVParser parser = csv.createParser(personsPath)) { + buildSubgroups(parser); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Match reference person to a person in the population. + * @return person id + */ + public String matchPerson(Person person, SplittableRandom rnd) { + + Key key = createKey(person); + + List subgroup = groups.get(key); + if (subgroup == null) { + log.error("No subgroup found for key {}", key); + throw new IllegalStateException("Invalid entry"); + } + + if (subgroup.size() < 30) { + log.warn("Group {} has low sample size: {}", key, subgroup.size()); + } + + return subgroup.get(rnd.nextInt(subgroup.size())); + } + + /** + * Matches a person csv entry to one person in a list. + * @return null if no match was found + */ + public Person matchEntry(CSVRecord p, List refPersons, SplittableRandom rnd) { + + int regionType = Integer.parseInt(p.get("region_type")); + String gender = p.get("gender"); + String employment = p.get("employment"); + int age = Integer.parseInt(p.get("age")); + + Set keys = createKey(gender, age, regionType, employment).collect(Collectors.toSet()); + + List matched = refPersons.stream() + .filter(person -> keys.contains(createKey(person))) + .toList(); + + if (matched.isEmpty()) + return null; + + return matched.get(rnd.nextInt(matched.size())); + } + + /** + * Return reference person with given index. + */ + public CSVRecord getPerson(String personId) { + return persons.get(personId); + } + + /** + * Create subpopulations for sampling. + */ + private void buildSubgroups(CSVParser csv) { + + int i = 0; + + for (CSVRecord r : csv) { + + String idx = r.get(idxColumn); + int regionType = Integer.parseInt(r.get("region_type")); + String gender = r.get("gender"); + String employment = r.get("employment"); + int age = Integer.parseInt(r.get("age")); + + Stream keys = createKey(gender, age, regionType, employment); + keys.forEach(key -> groups.computeIfAbsent(key, (k) -> new ArrayList<>()).add(idx)); + persons.put(idx, r); + i++; + } + + log.info("Read {} persons from csv.", i); + } + + private Stream createKey(String gender, int age, int regionType, String employment) { + if (age < 6) { + return IntStream.rangeClosed(0, 5).mapToObj(i -> new Key(null, i, regionType, null)); + } + if (age <= 10) { + return IntStream.rangeClosed(6, 10).mapToObj(i -> new Key(null, i, regionType, null)); + } + if (age < 18) { + return IntStream.rangeClosed(11, 18).mapToObj(i -> new Key(gender, i, regionType, null)); + } + + Boolean isEmployed = age > 65 ? null : !employment.equals("unemployed"); + int min = Math.max(18, age - 6); + int max = Math.min(65, age + 6); + + // larger groups for older people + if (age > 65) { + min = Math.max(66, age - 10); + max = Math.min(99, age + 10); + } + + return IntStream.rangeClosed(min, max).mapToObj(i -> new Key(gender, i, regionType, isEmployed)); + } + + private Key createKey(Person person) { + + Integer age = PersonUtils.getAge(person); + String gender = PersonUtils.getSex(person); + if (age <= 10) + gender = null; + + Boolean employed = PersonUtils.isEmployed(person); + if (age < 18 || age > 65) + employed = null; + + int regionType = (int) person.getAttributes().getAttribute(Attributes.RegioStaR7); + + // Region types have been reduced to 1 and 3 + if (regionType != 1) + regionType = 3; + + return new Key(gender, age, regionType, employed); + } + + @NotNull + @Override + public Iterator> iterator() { + return persons.entrySet().iterator(); + } + + /** + * Key used to match persons. + */ + public record Key(String gender, int age, int regionType, Boolean employed) { + } + +} diff --git a/src/main/java/org/matsim/prepare/population/PlanBuilder.java b/src/main/java/org/matsim/prepare/population/PlanBuilder.java new file mode 100644 index 000000000..43d070007 --- /dev/null +++ b/src/main/java/org/matsim/prepare/population/PlanBuilder.java @@ -0,0 +1,272 @@ +package org.matsim.prepare.population; + +import it.unimi.dsi.fastutil.doubles.DoubleArrayList; +import it.unimi.dsi.fastutil.doubles.DoubleList; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.geotools.api.feature.simple.SimpleFeature; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.Population; +import org.matsim.application.options.CsvOptions; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.utils.geometry.CoordUtils; +import org.matsim.facilities.ActivityFacility; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.*; + +/** + * Utility class to build plans from activity data. + */ +public class PlanBuilder { + + private static final Logger log = LogManager.getLogger(PlanBuilder.class); + private static final CsvOptions csv = new CsvOptions(CSVFormat.Predefined.Default); + + /** + * Maps zone ids to contained facilities. + */ + private final Long2ObjectMap> zones = new Long2ObjectOpenHashMap<>(); + + private final ShpOptions.Index zoneIndex; + + /** + * Maps location key to zone id. + */ + private final Object2LongMap features = new Object2LongOpenHashMap<>(); + + private final Map> activities = new HashMap<>(); + + private final SplittableRandom rnd = new SplittableRandom(); + + + public PlanBuilder(ShpOptions zones, FacilityIndex facilities, Path activityPath) throws IOException { + // Collect all zones + for (SimpleFeature ft : zones.readFeatures()) { + features.put(new Location((String) ft.getAttribute("raum_id"), (String) ft.getAttribute("zone")), + (long) ft.getAttribute("id")); + } + + zoneIndex = zones.createIndex("id"); + + for (ActivityFacility ft : facilities.all.getFacilities().values()) { + Long result = zoneIndex.query(ft.getCoord()); + if (result != null) { + this.zones.computeIfAbsent(result, k -> new HashSet<>()).add(ft); + } + } + + try (CSVParser parser = csv.createParser(activityPath)) { + readActivities(parser, "p_id"); + } + } + + private void readActivities(CSVParser csv, String idColumn) { + + String currentId = null; + List current = null; + + int i = 0; + for (CSVRecord r : csv) { + + String pId = r.get(idColumn); + + if (!Objects.equals(pId, currentId)) { + if (current != null) + activities.put(currentId, current); + + currentId = pId; + current = new ArrayList<>(); + } + + current.add(r); + i++; + } + + if (current != null && !current.isEmpty()) { + activities.put(currentId, current); + } + + log.info("Read {} activities for {} persons", i, activities.size()); + } + + /** + * Return all read activities. + */ + public Map> getActivities() { + return activities; + } + + /** + * Create a map for each zone to set of persons living there. + */ + public Long2ObjectMap> createHomeIndex(Population population) { + + Long2ObjectMap> personsByHome = new Long2ObjectOpenHashMap<>(); + for (Person person : population.getPersons().values()) { + + double homeX = (double) person.getAttributes().getAttribute(Attributes.HOME_X); + double homeY = (double) person.getAttributes().getAttribute(Attributes.HOME_Y); + + Long home = zoneIndex.query(new Coord(homeX, homeY)); + + if (home != null) { + personsByHome.computeIfAbsent(home, k -> new ArrayList<>()).add(person); + } + } + + return personsByHome; + } + + /** + * Find the home zone for a person. + * + * @return -1 if not known + */ + public long findHomeZone(String personId) { + + List acts = activities.get(personId); + + Optional home = acts.stream().filter(r -> r.get("type").equals("home")).findFirst(); + if (home.isEmpty()) + return -1; + + Location loc = new Location(home.get().get("location"), home.get().get("zone")); + return features.getOrDefault(loc, -1); + } + + /** + * Assigns location from reference data to a person. + * + * @return whether the assignment was successful + */ + public boolean assignLocationsFromZones(String personId, Plan plan, Coord homeCoord) { + + List acts = activities.get(personId); + List existing = TripStructureUtils.getActivities(plan, TripStructureUtils.StageActivityHandling.ExcludeStageActivities); + + // If activities don't match, this entry is skipped + // this can happen if an end home activity has been added at the end + if (acts.size() != existing.size()) + return false; + + ActLocation home = new ActLocation(null, homeCoord); + + List> possibleLocations = new ArrayList<>(); + + // Distances between activities in meter + DoubleList dists = new DoubleArrayList(); + + for (int i = 0; i < acts.size(); i++) { + + CSVRecord ref = acts.get(i); + Activity activity = existing.get(i); + + String type = activity.getType(); + + dists.add(InitLocationChoice.beelineDist(Double.parseDouble(ref.get("leg_dist")))); + + if (type.equals("home")) { + possibleLocations.add(List.of(home)); + continue; + } + + Location loc = new Location(ref.get("location"), ref.get("zone")); + long id = features.getOrDefault(loc, -1); + if (id == -1) { + return false; + } + + Set facilities = zones.get(id); + + List subSet = facilities.stream().filter(f -> f.getActivityOptions().containsKey(type)).toList(); + + if (subSet.isEmpty()) { + // If there is no location with the correct type, choose from all possible coordinates + possibleLocations.add( + facilities.stream().map(f -> new ActLocation(null, f.getCoord())).toList() + ); + } else { + possibleLocations.add( + subSet.stream().map(f -> new ActLocation(f, f.getCoord())).toList() + ); + } + } + + List chosen = sampleLocation(possibleLocations, dists); + + // No valid locations or matching error was too large + if (chosen == null) + return false; + + for (int i = 0; i < chosen.size(); i++) { + ActLocation loc = chosen.get(i); + Activity activity = existing.get(i); + + activity.setLinkId(null); + if (loc.facility() != null) { + activity.setFacilityId(loc.facility().getId()); + } else { + activity.setCoord(loc.coord()); + } + } + + + return true; + } + + /** + * Chooses from a list of possible locations such that difference to the references distances is minimized. + */ + private List sampleLocation(List> locations, DoubleList dists) { + + double err = Double.POSITIVE_INFINITY; + List best = null; + + for (int k = 0; k < 100; k++) { + List current = new ArrayList<>(); + for (List locs : locations) { + current.add(locs.get(rnd.nextInt(locs.size()))); + } + + double currentErr = 0; + for (int i = 1; i < current.size(); i++) { + double dist = CoordUtils.calcEuclideanDistance(current.get(i - 1).coord(), current.get(i).coord()); + currentErr += Math.abs(dist - dists.getDouble(i)); + } + + if (currentErr < err || best == null) { + err = currentErr; + best = current; + } + } + + double total = dists.doubleStream().sum() / (locations.size() - 1); + double perActErr = err / (locations.size() - 1); + + // threshold for deviation + if (perActErr > Math.max(300, total * 0.03)) + return null; + + return best; + } + + private record Location(String name, String zone) { + } + + private record ActLocation(ActivityFacility facility, Coord coord) { + } + +} diff --git a/src/main/java/org/matsim/prepare/population/RunActivitySampling.java b/src/main/java/org/matsim/prepare/population/RunActivitySampling.java index 245e17ddc..36ccd0749 100644 --- a/src/main/java/org/matsim/prepare/population/RunActivitySampling.java +++ b/src/main/java/org/matsim/prepare/population/RunActivitySampling.java @@ -1,9 +1,5 @@ package org.matsim.prepare.population; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; @@ -24,23 +20,21 @@ import java.nio.file.Path; import java.util.*; -import java.util.stream.IntStream; -import java.util.stream.Stream; @CommandLine.Command( name = "activity-sampling", description = "Create activities by sampling from survey data" ) -public class RunActivitySampling implements MATSimAppCommand, PersonAlgorithm { +public final class RunActivitySampling implements MATSimAppCommand, PersonAlgorithm { private static final Logger log = LogManager.getLogger(RunActivitySampling.class); private final CsvOptions csv = new CsvOptions(CSVFormat.Predefined.Default); - private final Map groups = new HashMap<>(); - private final Int2ObjectMap persons = new Int2ObjectOpenHashMap<>(); + /** * Maps person index to list of activities. */ - private final Int2ObjectMap> activities = new Int2ObjectOpenHashMap<>(); + private final Map> activities = new HashMap<>(); + @CommandLine.Option(names = "--input", description = "Path to input population", required = true) private Path input; @CommandLine.Option(names = "--output", description = "Output path for population", required = true) @@ -54,6 +48,24 @@ public class RunActivitySampling implements MATSimAppCommand, PersonAlgorithm { private ThreadLocal ctxs; private PopulationFactory factory; + private PersonMatcher matcher; + + /** + * Default constructor. Can only be used with command line. + */ + public RunActivitySampling() { + } + + /** + * Constructor that allows to use the run method directly and not as command. + */ + RunActivitySampling(PersonMatcher matcher, Map> activities, PopulationFactory factory, long seed) { + this.matcher = matcher; + this.activities.putAll(activities); + this.factory = factory; + this.seed = seed; + this.ctxs = ThreadLocal.withInitial(() -> new Context(new SplittableRandom(seed))); + } public static void main(String[] args) { new RunActivitySampling().execute(args); @@ -64,9 +76,7 @@ public Integer call() throws Exception { Population population = PopulationUtils.readPopulation(input.toString()); - try (CSVParser parser = csv.createParser(personsPath)) { - buildSubgroups(parser); - } + matcher = new PersonMatcher("idx", personsPath); try (CSVParser parser = csv.createParser(activityPath)) { readActivities(parser); @@ -94,41 +104,17 @@ public Integer call() throws Exception { return 0; } - /** - * Create subpopulations for sampling. - */ - private void buildSubgroups(CSVParser csv) { - - int i = 0; - - for (CSVRecord r : csv) { - - int idx = Integer.parseInt(r.get("idx")); - int regionType = Integer.parseInt(r.get("region_type")); - String gender = r.get("gender"); - String employment = r.get("employment"); - int age = Integer.parseInt(r.get("age")); - - Stream keys = createKey(gender, age, regionType, employment); - keys.forEach(key -> groups.computeIfAbsent(key, (k) -> new IntArrayList()).add(idx)); - persons.put(idx, r); - i++; - } - - log.info("Read {} persons from csv.", i); - } - private void readActivities(CSVParser csv) { - int currentId = -1; + String currentId = null; List current = null; int i = 0; for (CSVRecord r : csv) { - int pId = Integer.parseInt(r.get("p_id")); + String pId = r.get("p_id"); - if (pId != currentId) { + if (!Objects.equals(pId, currentId)) { if (current != null) activities.put(currentId, current); @@ -147,82 +133,16 @@ private void readActivities(CSVParser csv) { log.info("Read {} activities for {} persons", i, activities.size()); } - private Stream createKey(String gender, int age, int regionType, String employment) { - if (age < 6) { - return IntStream.rangeClosed(0, 5).mapToObj(i -> new Key(null, i, regionType, null)); - } - if (age <= 10) { - return IntStream.rangeClosed(6, 10).mapToObj(i -> new Key(null, i, regionType, null)); - } - if (age < 18) { - return IntStream.rangeClosed(11, 18).mapToObj(i -> new Key(gender, i, regionType, null)); - } - - Boolean isEmployed = age > 65 ? null : !employment.equals("unemployed"); - int min = Math.max(18, age - 6); - int max = Math.min(65, age + 6); - - // larger groups for older people - if (age > 65) { - min = Math.max(66, age - 10); - max = Math.min(99, age + 10); - } - - return IntStream.rangeClosed(min, max).mapToObj(i -> new Key(gender, i, regionType, isEmployed)); - } - - private Key createKey(Person person) { - - Integer age = PersonUtils.getAge(person); - String gender = PersonUtils.getSex(person); - if (age <= 10) - gender = null; - - Boolean employed = PersonUtils.isEmployed(person); - if (age < 18 || age > 65) - employed = null; - - int regionType = (int) person.getAttributes().getAttribute(Attributes.RegioStaR7); - - // Region types have been reduced to 1 and 3 - if (regionType != 1) - regionType = 3; - - return new Key(gender, age, regionType, employed); - } @Override public void run(Person person) { SplittableRandom rnd = ctxs.get().rnd; - Key key = createKey(person); - - IntList subgroup = groups.get(key); - if (subgroup == null) { - log.error("No subgroup found for key {}", key); - throw new IllegalStateException("Invalid entry"); - } - - if (subgroup.size() < 30) { - log.warn("Group {} has low sample size: {}", key, subgroup.size()); - } - - int idx = subgroup.getInt(rnd.nextInt(subgroup.size())); - CSVRecord row = persons.get(idx); - - PersonUtils.setCarAvail(person, row.get("car_avail").equals("True") ? "always" : "never"); - PersonUtils.setLicence(person, row.get("driving_license").toLowerCase()); - PersonUtils.setIncome(person, Math.max(499, Double.parseDouble(row.get("income")))); - - person.getAttributes().putAttribute(Attributes.BIKE_AVAIL, row.get("bike_avail").equals("True") ? "always" : "never"); - person.getAttributes().putAttribute(Attributes.PT_ABO_AVAIL, row.get("pt_abo_avail").equals("True") ? "always" : "never"); - - person.getAttributes().putAttribute(Attributes.EMPLOYMENT, row.get("employment")); - person.getAttributes().putAttribute(Attributes.RESTRICTED_MOBILITY, row.get("restricted_mobility").equals("True")); - person.getAttributes().putAttribute(Attributes.ECONOMIC_STATUS, row.get("economic_status")); - person.getAttributes().putAttribute(Attributes.HOUSEHOLD_SIZE, Integer.parseInt(row.get("n_persons"))); + String idx = matcher.matchPerson(person, rnd); + CSVRecord row = matcher.getPerson(idx); + copyAttributes(row, person); String mobile = row.get("mobile_on_day"); @@ -253,10 +173,27 @@ public void run(Person person) { } } + /** + * Copy attributes from csv record to person. + */ + public void copyAttributes(CSVRecord row, Person person) { + PersonUtils.setCarAvail(person, row.get("car_avail").equals("True") ? "always" : "never"); + PersonUtils.setLicence(person, row.get("driving_license").toLowerCase()); + PersonUtils.setIncome(person, Math.max(499, Double.parseDouble(row.get("income")))); + + person.getAttributes().putAttribute(Attributes.BIKE_AVAIL, row.get("bike_avail").equals("True") ? "always" : "never"); + person.getAttributes().putAttribute(Attributes.PT_ABO_AVAIL, row.get("pt_abo_avail").equals("True") ? "always" : "never"); + + person.getAttributes().putAttribute(Attributes.EMPLOYMENT, row.get("employment")); + person.getAttributes().putAttribute(Attributes.RESTRICTED_MOBILITY, row.get("restricted_mobility").equals("True")); + person.getAttributes().putAttribute(Attributes.ECONOMIC_STATUS, row.get("economic_status")); + person.getAttributes().putAttribute(Attributes.HOUSEHOLD_SIZE, Integer.parseInt(row.get("n_persons"))); + } + /** * Randomize the duration slightly, depending on total duration. */ - private int randomizeDuration(int minutes, SplittableRandom rnd) { + private static int randomizeDuration(int minutes, SplittableRandom rnd) { if (minutes <= 10) return minutes * 60; @@ -269,6 +206,13 @@ private int randomizeDuration(int minutes, SplittableRandom rnd) { return minutes * 60 + rnd.nextInt(1200) - 600; } + /** + * Create plan for a person using given id. + */ + public Plan createPlan(Coord homeCoord, String personId) { + return createPlan(homeCoord, activities.get(personId), ctxs.get().rnd); + } + private Plan createPlan(Coord homeCoord, List activities, SplittableRandom rnd) { Plan plan = factory.createPlan(); @@ -377,12 +321,6 @@ private Plan createPlan(Coord homeCoord, List activities, SplittableR return plan; } - /** - * Key used for sampling activities. - */ - private record Key(String gender, int age, int regionType, Boolean employed) { - } - private record Context(SplittableRandom rnd) { } diff --git a/src/main/java/org/matsim/run/OpenBerlinDrtScenario.java b/src/main/java/org/matsim/run/OpenBerlinDrtScenario.java index 072f27f43..7266e862d 100644 --- a/src/main/java/org/matsim/run/OpenBerlinDrtScenario.java +++ b/src/main/java/org/matsim/run/OpenBerlinDrtScenario.java @@ -81,7 +81,7 @@ public class OpenBerlinDrtScenario extends OpenBerlinScenario { private static final Logger log = LogManager.getLogger(OpenBerlinDrtScenario.class); @CommandLine.Option(names = "--drt-config", - defaultValue = "input/v" + OpenBerlinScenario.VERSION + "/berlin-v" + OpenBerlinScenario.VERSION + ".drt-config.xml", + defaultValue = "input/v" + OpenBerlinScenario.VERSION + "/berlin-v" + OpenBerlinScenario.VERSION +".drt-config.xml", description = "Path to drt (only) config. Should contain only additional stuff to base config. Otherwise overrides.") private String drtConfig; diff --git a/src/main/java/org/matsim/run/OpenBerlinScenario.java b/src/main/java/org/matsim/run/OpenBerlinScenario.java index abaeb2243..62b6e1050 100644 --- a/src/main/java/org/matsim/run/OpenBerlinScenario.java +++ b/src/main/java/org/matsim/run/OpenBerlinScenario.java @@ -28,10 +28,11 @@ @CommandLine.Command(header = ":: Open Berlin Scenario ::", version = OpenBerlinScenario.VERSION, mixinStandardHelpOptions = true) public class OpenBerlinScenario extends MATSimApplication { + public static final String VERSION = "6.3"; + public static final String CRS = "EPSG:25832"; + private static final Logger log = LogManager.getLogger(RunOpenBerlinCalibration.class); - public static final String VERSION = "6.2"; - public static final String CRS = "EPSG:25832"; @CommandLine.Mixin private final SampleOptions sample = new SampleOptions(10, 25, 3, 1); diff --git a/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java b/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java index f943a7aac..3d4b93f66 100644 --- a/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java +++ b/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java @@ -2,7 +2,6 @@ import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ReflectiveConfigGroup; -import org.matsim.utils.objectattributes.attributable.Attributes; import java.util.*; @@ -14,27 +13,24 @@ public final class AdvancedScoringConfigGroup extends ReflectiveConfigGroup { private static final String GROUP_NAME = "advancedScoring"; - private final List scoringParameters = new ArrayList<>(); - @Parameter @Comment("The distance groups if marginal utility of distance is adjusted. In meters.") public List distGroups; - @Parameter @Comment("Enable income dependent marginal utility of money.") - public boolean incomeDependent = true; - + public IncomeDependentScoring incomeDependent = IncomeDependentScoring.avgByPersonalIncome; + @Parameter + @Comment("Define how to load existing preferences.") + public LoadPreferences loadPreferences = LoadPreferences.none; - // TODO: maybe option to re-assign variations or use them from attributes - // TODO: could load the random variations from a file, helper function to only generate the variations - // TODO: reference population?, for which the loglikelihood could be calculated + private final List scoringParameters = new ArrayList<>(); public AdvancedScoringConfigGroup() { super(GROUP_NAME); } /** - * Return the defined scoring parameters. + * Return the defined scoring parameters. */ public List getScoringParameters() { return Collections.unmodifiableList(scoringParameters); @@ -59,6 +55,31 @@ public void addParameterSet(ConfigGroup set) { } } + /** + * Different options for income dependent scoring. + */ + public enum IncomeDependentScoring { + none, + avgByPersonalIncome + } + + /** + * Define how existing preferences are loaded. + */ + public enum LoadPreferences { + none, + requireAttribute, + skipMissing, + skipRefPersons + } + + /** + * Variate values with random draw from specific distribution. + */ + public enum VariationType { + fixed, normal, truncatedNormal + } + /** * Scoring parameters for a specific group of agents. * This group allows arbitrary attributes to be defined, which are matched against person attributes. @@ -76,24 +97,6 @@ public ScoringParameters() { super(GROUP_NAME, true); } - /** - * Checks if the given attributes match the config. If true these parameters are applicable to tbe object. - */ - public boolean matchObject(Attributes attr, Map categories) { - - for (Map.Entry e : this.getParams().entrySet()) { - // might be null if not defined - Object objValue = attr.getAttribute(e.getKey()); - String category = categories.get(e.getKey()).categorize(objValue); - - // compare as string - if (!Objects.toString(category).equals(e.getValue())) - return false; - } - - return true; - } - public Map getModeParams() { return modeParams; } @@ -168,11 +171,4 @@ public ModeParams() { super(GROUP_NAME); } } - - /** - * Variate values with random draw from specific distribution. - */ - public enum VariationType { - fixed, normal, truncatedNormal - } } diff --git a/src/main/java/org/matsim/run/scoring/Category.java b/src/main/java/org/matsim/run/scoring/Category.java deleted file mode 100644 index eb4594582..000000000 --- a/src/main/java/org/matsim/run/scoring/Category.java +++ /dev/null @@ -1,199 +0,0 @@ -package org.matsim.run.scoring; - -import org.matsim.core.config.ReflectiveConfigGroup; -import org.matsim.utils.objectattributes.attributable.Attributes; - -import java.util.*; -import java.util.regex.Pattern; - -/** - * Categorize values into groups. - */ -public final class Category { - - private static final Set TRUE = Set.of("true", "yes", "1", "on", "y", "j", "ja"); - private static final Set FALSE = Set.of("false", "no", "0", "off", "n", "nein"); - - /** - * Unique values of the category. - */ - private final Set values; - - /** - * Groups of values that have been subsumed under a single category. - * These are values separated by , - */ - private final Map grouped; - - - /** - * Regular expressions for each category. - */ - private final Map regex; - - /** - * Range categories. - */ - private final List ranges; - - public Category(Set values) { - this.values = values; - this.grouped = new HashMap<>(); - this.regex = new HashMap<>(); - for (String v : values) { - if (v.contains(",")) { - String[] grouped = v.split(","); - for (String g : grouped) { - this.grouped.put(g, v); - } - } - - if (v.startsWith("/") && v.endsWith("/")) { - this.regex.put(v, Pattern.compile(v.substring(1, v.length() - 1), Pattern.CASE_INSENSITIVE)); - } - } - - boolean range = this.values.stream().allMatch(v -> v.contains("-") || v.contains("+")); - if (range) { - ranges = new ArrayList<>(); - for (String value : this.values) { - if (value.contains("-")) { - String[] parts = value.split("-"); - ranges.add(new Range(Double.parseDouble(parts[0]), Double.parseDouble(parts[1]), value)); - } else if (value.contains("+")) { - ranges.add(new Range(Double.parseDouble(value.replace("+", "")), Double.POSITIVE_INFINITY, value)); - } - } - - ranges.sort(Comparator.comparingDouble(r -> r.left)); - } else - ranges = null; - - - // Check if all values are boolean - if (values.stream().allMatch(v -> TRUE.contains(v.toLowerCase()) || FALSE.contains(v.toLowerCase()))) { - for (String value : values) { - Set group = TRUE.contains(value.toLowerCase()) ? TRUE : FALSE; - for (String g : group) { - this.grouped.put(g, value); - } - } - } - } - - /** - * Create categories from config parameters. - */ - public static Map fromConfigParams(Collection params) { - - Map> categories = new HashMap<>(); - - // Collect all values - for (ReflectiveConfigGroup parameter : params) { - for (Map.Entry kv : parameter.getParams().entrySet()) { - categories.computeIfAbsent(kv.getKey(), k -> new HashSet<>()).add(kv.getValue()); - } - } - - return categories.entrySet().stream() - .collect(HashMap::new, (m, e) -> m.put(e.getKey(), new Category(e.getValue())), HashMap::putAll); - } - - /** - * Match attributes from an object with parameters defined in config. - */ - public static boolean matchAttributesWithConfig(Attributes attr, ReflectiveConfigGroup config, Map categories) { - - for (Map.Entry e : config.getParams().entrySet()) { - // might be null if not defined - Object objValue = attr.getAttribute(e.getKey()); - String category = categories.get(e.getKey()).categorize(objValue); - - // compare as string - if (!Objects.toString(category).equals(e.getValue())) - return false; - } - - return true; - } - - /** - * Categorize a single value. - */ - public String categorize(Object value) { - - if (value == null) - return null; - - if (value instanceof Boolean) { - // Booleans and synonyms are in the group map - return categorize(((Boolean) value).toString().toLowerCase()); - } else if (value instanceof Number) { - return categorizeNumber((Number) value); - } else { - String v = value.toString(); - if (values.contains(v)) - return v; - else if (grouped.containsKey(v)) - return grouped.get(v); - else { - for (Map.Entry kv : regex.entrySet()) { - if (kv.getValue().matcher(v).matches()) - return kv.getKey(); - } - } - - try { - double d = Double.parseDouble(v); - return categorizeNumber(d); - } catch (NumberFormatException e) { - return null; - } - } - } - - private String categorizeNumber(Number value) { - - if (ranges != null) { - for (Range r : ranges) { - if (value.doubleValue() >= r.left && value.doubleValue() < r.right) - return r.label; - } - } - - // Match string representation - String v = value.toString(); - if (values.contains(v)) - return v; - else if (grouped.containsKey(v)) - return grouped.get(v); - - - // Convert the number to a whole number, which will have a different string representation - if (value instanceof Float || value instanceof Double) { - return categorizeNumber(value.longValue()); - } - - return null; - } - - @Override - public String toString() { - return "Category{" + - "values=" + values + - (grouped != null && !grouped.isEmpty() ? ", grouped=" + grouped : "") + - (regex != null && !regex.isEmpty() ? ", regex=" + regex : "") + - (ranges != null && !ranges.isEmpty() ? ", ranges=" + ranges : "") + - '}'; - } - - /** - * Number range. - * - * @param left Left bound of the range. - * @param right Right bound of the range. (exclusive) - * @param label Label of this group. - */ - private record Range(double left, double right, String label) { - } -} diff --git a/src/main/java/org/matsim/run/scoring/IndividualPersonScoringParameters.java b/src/main/java/org/matsim/run/scoring/IndividualPersonScoringParameters.java index 10e9138ec..5d29051be 100644 --- a/src/main/java/org/matsim/run/scoring/IndividualPersonScoringParameters.java +++ b/src/main/java/org/matsim/run/scoring/IndividualPersonScoringParameters.java @@ -1,9 +1,14 @@ package org.matsim.run.scoring; +import com.google.common.base.Joiner; +import com.google.common.primitives.Longs; import com.google.inject.Inject; import it.unimi.dsi.fastutil.doubles.DoubleList; import it.unimi.dsi.fastutil.objects.Object2DoubleMap; import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.rng.RestorableUniformRandomProvider; +import org.apache.commons.rng.core.RandomProviderDefaultState; import org.apache.commons.rng.simple.RandomSource; import org.apache.commons.statistics.distribution.ContinuousDistribution; import org.apache.commons.statistics.distribution.NormalDistribution; @@ -15,6 +20,8 @@ import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Population; +import org.matsim.application.analysis.population.Category; +import org.matsim.application.analysis.population.TripAnalysis; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.population.PersonUtils; @@ -64,7 +71,7 @@ public class IndividualPersonScoringParameters implements ScoringParametersForPe /** * Thread-local random number generator. */ - private final ThreadLocal rnd = ThreadLocal.withInitial(Context::new); + private final ThreadLocal rnd; private final Scenario scenario; private final ScoringConfigGroup basicScoring; private final TransitConfigGroup transitConfig; @@ -83,6 +90,7 @@ public IndividualPersonScoringParameters(Scenario scenario) { this.globalAvgIncome = computeAvgIncome(scenario.getPopulation()); this.categories = Category.fromConfigParams(this.scoring.getScoringParameters()); this.cache = new IdMap<>(Person.class, scenario.getPopulation().getPersons().size()); + this.rnd = ThreadLocal.withInitial(() -> new Context(scenario.getConfig().global().getRandomSeed())); } static DistanceGroup[] calcDistanceGroups(List dists, DoubleList distUtils) { @@ -135,7 +143,7 @@ static DistanceGroup[] calcDistanceGroups(List dists, DoubleList distUt } private double computeAvgIncome(Population population) { - if (!scoring.incomeDependent) + if (scoring.incomeDependent == AdvancedScoringConfigGroup.IncomeDependentScoring.none) return Double.NaN; log.info("reading income attribute using " + PersonUtils.class + " of all agents and compute global average.\n" + @@ -199,13 +207,14 @@ public ScoringParameters getScoringParameters(Person person) { if (personalIncome != 0) { builder.setMarginalUtilityOfMoney(scoringParameters.getMarginalUtilityOfMoney() * globalAvgIncome / personalIncome); } else { - log.warn("you have set income to " + personalIncome + " for person " + person + ". This is invalid and gets ignored." + - "Instead, the marginalUtilityOfMoney is derived from the subpopulation's scoring parameters."); + log.warn("You have set income to {} for person {}. This is invalid and gets ignored.Instead, the marginalUtilityOfMoney is derived from the subpopulation's scoring parameters.", personalIncome, person); } } Map deltaParams = new HashMap<>(); + this.rnd.get().setSeed(person); + for (AdvancedScoringConfigGroup.ScoringParameters parameter : scoring.getScoringParameters()) { if (Category.matchAttributesWithConfig(person.getAttributes(), parameter, categories)) { @@ -215,11 +224,21 @@ public ScoringParameters getScoringParameters(Person person) { deltaParams.computeIfAbsent(mode.getKey(), k -> new DistanceGroupModeUtilityParameters.DeltaBuilder()); b.addUtilsDistance(mode.getValue()); - addDeltaParams(b, mode.getValue(), person); + addDeltaParams(this.rnd.get(), b, mode.getValue()); } } } + Object attr = person.getAttributes().getAttribute("utilDelta"); + Object2DoubleMap existing = new Object2DoubleOpenHashMap<>(); + if (attr instanceof String s) { + String[] split = s.split("\\|"); + for (String s1 : split) { + String[] split1 = s1.split("="); + existing.put(split1[0], Double.parseDouble(split1[1])); + } + } + for (Map.Entry mode : deltaParams.entrySet()) { ModeUtilityParameters params = builder.getModeParameters(mode.getKey()); DistanceGroupModeUtilityParameters.DeltaBuilder delta = mode.getValue(); @@ -227,6 +246,9 @@ public ScoringParameters getScoringParameters(Person person) { // These arrays are re-used if possible DistanceGroup[] groups = distGroups.computeIfAbsent(delta.getPerDistGroup(), k -> calcDistanceGroups(scoring.distGroups, k)); + // This may overwrite the preferences with the one stored + loadPreferences(mode.getKey(), delta, person, existing); + DistanceGroupModeUtilityParameters p = new DistanceGroupModeUtilityParameters(params, delta, groups); builder.setModeParameters(mode.getKey(), p); @@ -234,11 +256,15 @@ public ScoringParameters getScoringParameters(Person person) { Object2DoubleMap values = info.computeIfAbsent(person.getId(), k -> new Object2DoubleOpenHashMap<>()); // Write the overall constants, but only if they are different to the base values - if (delta.constant != 0) + if (delta.constant != 0) { values.put(mode.getKey() + "_constant", p.constant); + existing.put(mode.getKey() + "_constant", p.constant); + } - if (delta.dailyUtilityConstant != 0) + if (delta.dailyUtilityConstant != 0) { values.put(mode.getKey() + "_dailyConstant", p.dailyUtilityConstant); + existing.put(mode.getKey() + "_dailyConstant", p.dailyUtilityConstant); + } if (groups != null) { for (DistanceGroup group : groups) { @@ -249,17 +275,47 @@ public ScoringParameters getScoringParameters(Person person) { header.addAll(values.keySet()); } + if (!existing.isEmpty()) { + Joiner.MapJoiner mapJoiner = Joiner.on("|").withKeyValueSeparator("="); + person.getAttributes().putAttribute("utilDelta", mapJoiner.join(existing)); + } + return builder.build(); }); } + private void loadPreferences(String mode, DistanceGroupModeUtilityParameters.DeltaBuilder delta, Person person, Object2DoubleMap existing) { + + boolean isRefPerson = person.getAttributes().getAttribute(TripAnalysis.ATTR_REF_ID) != null; + + if (scoring.loadPreferences == AdvancedScoringConfigGroup.LoadPreferences.none || + (isRefPerson && scoring.loadPreferences == AdvancedScoringConfigGroup.LoadPreferences.skipRefPersons)) { + return; + } + + // Else, require that the attributes are present + if (!existing.containsKey(mode + "constant") && scoring.loadPreferences == AdvancedScoringConfigGroup.LoadPreferences.requireAttribute) { + throw new IllegalArgumentException("Person " + person.getId() + " does not have attribute " + mode + "_constant"); + } + if (!existing.containsKey(mode + "_dailyConstant") && scoring.loadPreferences == AdvancedScoringConfigGroup.LoadPreferences.requireAttribute) { + throw new IllegalArgumentException("Person " + person.getId() + " does not have attribute " + mode + "_dailyConstant"); + } + + // Use attributes if they are present + if (existing.containsKey(mode + "_constant")) + delta.constant = existing.getDouble(mode + "_constant"); + + if (existing.containsKey(mode + "_dailyConstant")) + delta.dailyUtilityConstant = existing.getDouble(mode + "_dailyConstant"); + } + /** * Compute or retrieve delta params for person. */ - private void addDeltaParams(DistanceGroupModeUtilityParameters.DeltaBuilder delta, AdvancedScoringConfigGroup.ModeParams params, Person person) { + private void addDeltaParams(Context ctx, DistanceGroupModeUtilityParameters.DeltaBuilder delta, AdvancedScoringConfigGroup.ModeParams params) { - ContinuousDistribution.Sampler normal = this.rnd.get().normal; - ContinuousDistribution.Sampler tn = this.rnd.get().tn; + ContinuousDistribution.Sampler normal = ctx.normal.createSampler(ctx.rnd()); + ContinuousDistribution.Sampler tn = ctx.tn.createSampler(ctx.rnd()); switch (params.varConstant) { case fixed -> delta.constant += params.deltaConstant; @@ -276,13 +332,34 @@ private void addDeltaParams(DistanceGroupModeUtilityParameters.DeltaBuilder delt } } - private record Context(ContinuousDistribution.Sampler normal, ContinuousDistribution.Sampler tn) { + /** + * Thread-local context for random number generation. This makes generation thread-safe and consistent independently of threads and order of persons. + */ + private record Context(NormalDistribution normal, TruncatedNormalDistribution tn, byte[] seed, RestorableUniformRandomProvider rnd) { + + Context(long seed) { + this(NormalDistribution.of(0, 1), + TruncatedNormalDistribution.of(0, 1, 0, Double.POSITIVE_INFINITY), + // Feed seed into random number generator + Longs.toByteArray(new SplittableRandom(seed).nextLong()), + RandomSource.KISS.create()); + } + + /** + * Set the state of rnd specific to person and configured global seed. + */ + void setSeed(Person p) { + + byte[] state = new byte[20]; + byte[] person = p.getId().toString().getBytes(); + + // Reverse, because the more significant bytes are at the end + ArrayUtils.reverse(person); + + System.arraycopy(seed, 0, state, 0, 8); + System.arraycopy(person, 0, state, 8, Math.min(person.length, 12)); - Context() { - this(NormalDistribution.of(0, 1).createSampler(RandomSource.KISS.create(123L)), - TruncatedNormalDistribution.of(0, 1, 0, Double.POSITIVE_INFINITY) - .createSampler(RandomSource.KISS.create(123L)) - ); + rnd.restoreState(new RandomProviderDefaultState(state)); } } } diff --git a/src/main/python/calibrate.py b/src/main/python/calibrate.py index c1b99ba75..809d42e52 100755 --- a/src/main/python/calibrate.py +++ b/src/main/python/calibrate.py @@ -8,10 +8,10 @@ modes = ["walk", "car", "pt", "bike", "ride"] fixed_mode = "walk" initial = { - "bike": -2.23, - "pt": -0.25, - "car": -0.62, - "ride": -1.37 + "bike": -1.4, + "pt": 0.6, + "car": -1, + "ride": -1.4 } # Original modal split diff --git a/src/main/python/calibrate_dist.py b/src/main/python/calibrate_dist.py index 58c7d1911..69d5e301b 100755 --- a/src/main/python/calibrate_dist.py +++ b/src/main/python/calibrate_dist.py @@ -10,10 +10,10 @@ modes = ["walk", "car", "pt", "bike", "ride"] fixed_mode = "walk" initial = { - "bike": -1.6, - "pt": 0.2, - "car": -0.4, - "ride": -1.2 + "bike": -1.4, + "pt": 0.6, + "car": -1, + "ride": -1.4 } target = pd.read_csv("mode_share_ref.csv") diff --git a/src/main/python/estimate_mixed_plan_choice.py b/src/main/python/estimate_mixed_plan_choice.py index dbd643bd2..1e784d931 100644 --- a/src/main/python/estimate_mixed_plan_choice.py +++ b/src/main/python/estimate_mixed_plan_choice.py @@ -11,7 +11,7 @@ if __name__ == "__main__": parser = argparse.ArgumentParser(description="Estimate the plan choice mixed logit model") - parser.add_argument("--input", help="Path to the input file", type=str, default="../../../plan-choices-random.csv") + parser.add_argument("--input", help="Path to the input file", type=str, default="../../../plan-choices.csv") parser.add_argument("--n-draws", help="Number of draws for the estimation", type=int, default=1500) parser.add_argument("--batch-size", help="Batch size for the estimation", type=int, default=None) parser.add_argument("--sample", help="Use sample of choice data", type=float, default=0.2) @@ -20,14 +20,14 @@ args = parser.parse_args() - df_wide = pd.read_csv(args.input) + df_wide = pd.read_csv(args.input, comment="#") modes = list(df_wide.columns.str.extract(r"_([a-zA-z]+)_usage", expand=False).dropna().unique()) print("Modes: ", modes) k = df_wide.columns.str.extract(r"plan_(\d+)", expand=False).dropna().to_numpy(int).max() - print("Number of choices: ", len(df_wide)) - print("Number of plans per choice: ", k) + print("Number of plans: ", len(df_wide)) + print("Number of choices for plan: ", k) # df_wide["p_id"] = df_wide["p_id"].str.replace(r"_\d+$", "", regex=True) # df_wide["person"] = df_wide["person"].astype('category').cat.codes @@ -62,11 +62,12 @@ # varnames = ["car_used", "car_usage"] # Additive costs - addit = df["costs"] + df["car_fixed_cost"] - df["pt_n_switches"] + # utils contains the price and opportunist costs + addit = df["utils"] - df["pt_n_switches"] if not args.mnl: model = MixedLogit() - model.fit(X=df[varnames], y=df['choice'], varnames=varnames, + model.fit(X=df[varnames], y=df['choice'], weights=df['weight'], varnames=varnames, alts=df['alt'], ids=df['custom_id'], avail=df['valid'], random_state=args.seed, addit=addit, # randvars={"car_used": "tn"}, @@ -79,7 +80,7 @@ #varnames += ["car_usage"] model = MultinomialLogit() - model.fit(X=df[varnames], y=df['choice'], varnames=varnames, + model.fit(X=df[varnames], y=df['choice'], weights=df['weight'], varnames=varnames, alts=df['alt'], ids=df['custom_id'], avail=df['valid'], random_state=args.seed, addit=addit) diff --git a/src/main/python/estimate_plan_choice.py b/src/main/python/estimate_plan_choice.py index 05b52c24e..49ae67f52 100644 --- a/src/main/python/estimate_plan_choice.py +++ b/src/main/python/estimate_plan_choice.py @@ -25,9 +25,9 @@ def tn_generator(sample_size: int, number_of_draws: int) -> np.ndarray: def calc_costs(df, k, modes): # Cost parameter from current berlin model - fixed_costs = defaultdict(lambda: 0.0, car=-15, pt=-3) + daily_costs = defaultdict(lambda: 0.0, car=-14.30, pt=-3) km_costs = defaultdict(lambda: 0.0, car=-0.149, ride=-0.149) - time_cost = -6.88 + util_performing = -6.88 # Normalize activity utilities to be near zero # columns = [f"plan_{i}_act_util" for i in range(1, k + 1)] @@ -35,35 +35,39 @@ def calc_costs(df, k, modes): # utils = df.loc[t.Index, columns] # df.loc[t.Index, columns] -= utils.max() + # Marginal utility of money as factor + util_money = df.util_money for i in range(1, k + 1): - # Costs will also include time costs - df[f"plan_{i}_costs"] = 0 # Price is only monetary costs df[f"plan_{i}_price"] = 0 + # Costs will also include time costs + df[f"plan_{i}_utils"] = 0 + df[f"plan_{i}_tt_hours"] = 0 for mode in modes: - df[f"plan_{i}_{mode}_fixed_cost"] = (df[f"plan_{i}_{mode}_usage"] > 0) * fixed_costs[mode] - df[f"plan_{i}_{mode}_used"] = (df[f"plan_{i}_{mode}_usage"] > 0) * 1 + fixed_costs = (df[f"plan_{i}_{mode}_usage"] > 0) * daily_costs[mode] + distance_costs = df[f"plan_{i}_{mode}_km"] * km_costs[mode] - df[f"plan_{i}_price"] += df[f"plan_{i}_{mode}_fixed_cost"] + df[f"plan_{i}_{mode}_km"] * km_costs[mode] + df[f"plan_{i}_{mode}_fixed_cost"] = fixed_costs + df[f"plan_{i}_price"] += fixed_costs + distance_costs + + df[f"plan_{i}_{mode}_used"] = (df[f"plan_{i}_{mode}_usage"] > 0) * 1 df[f"plan_{i}_tt_hours"] += df[f"plan_{i}_{mode}_hours"] # Add configured time costs - df[f"plan_{i}_costs"] += df[f"plan_{i}_{mode}_km"] * km_costs[mode] + df[f"plan_{i}_utils"] += (fixed_costs + distance_costs) * util_money + # Add time costs the overall costs - df[f"plan_{i}_costs"] += time_cost * df[f"plan_{i}_{mode}_hours"] + df[f"plan_{i}_utils"] += util_performing * df[f"plan_{i}_{mode}_hours"] - # Add additional ride time costs + # Add additional ride time utils for the driver if mode == "ride": - df[f"plan_{i}_costs"] += time_cost * df[f"plan_{i}_{mode}_hours"] - - if mode == "pt": - df[f"plan_{i}_costs"] += (df[f"plan_{i}_pt_usage"] > 0) * fixed_costs[mode] + df[f"plan_{i}_utils"] += util_performing * df[f"plan_{i}_{mode}_hours"] # Defragment df df = df.copy() diff --git a/src/main/python/extract_income.py b/src/main/python/extract_income.py deleted file mode 100644 index 2a20ecac3..000000000 --- a/src/main/python/extract_income.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import argparse -import numpy as np -import os -from matsim.scenariogen.data import read_all - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Converter for survey data") - - parser.add_argument("-d", "--directory", default=os.path.expanduser( - "~/Development/matsim-scenarios/shared-svn/projects/matsim-berlin/data/SrV/")) - - args = parser.parse_args() - - hh, persons, trips = read_all([args.directory + "Berlin+Umland", args.directory + "Brandenburg"]) - - hh = hh[hh.income >= 0] - - # Large households are underrepresented and capped (same operation as in input) - hh.n_persons = np.minimum(hh.n_persons, 5) - - groups = list(sorted(set(hh.income))) - - def calc(x): - counts = x.groupby("income").size() - prob = counts / sum(counts) - return prob.to_frame().transpose() - - - dist = hh.groupby(["economic_status"]).apply(calc).fillna(0).reset_index().drop(columns=["level_1"]) - - print("Income groups:", groups) - - for t in dist[["economic_status"] + groups].itertuples(): - print('"%s", new double[]{%s},' % (t.economic_status, ", ".join("%.3f" % x for x in t[2:]))) diff --git a/src/main/python/extract_ref_data.py b/src/main/python/extract_ref_data.py index 34a6e7f04..c4adc04af 100644 --- a/src/main/python/extract_ref_data.py +++ b/src/main/python/extract_ref_data.py @@ -11,9 +11,12 @@ def person_filter(df): df = df[df.reporting_day <= 4] df = df[df.location == "Berlin"] - df["age"] = preparation.cut(df.age, [0, 18, 66, np.inf]) + df["age"] = preparation.cut(df.age, [0, 12, 18, 25, 35, 66, np.inf]) preparation.fill(df, "economic_status", EconomicStatus.UNKNOWN) + preparation.fill(df, "income", -1) + + df["income"] = preparation.cut(df.income, [0, 500, 900, 1500, 2000, 2600, 3000, 3600, 4600, 5600, np.inf]) return df @@ -35,7 +38,7 @@ def trip_filter(df): d + "Berlin+Umland", person_filter, trip_filter, run_create_ref_data.InvalidHandling.REMOVE_TRIPS, - ref_groups=["age", "economic_status"] + ref_groups=["age", "income", "economic_status", "employment", "car_avail", "bike_avail", "pt_abo_avail"] ) print(result.share) diff --git a/src/main/python/extract_trips.py b/src/main/python/extract_trips.py deleted file mode 100644 index 6eefeb34f..000000000 --- a/src/main/python/extract_trips.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import numpy as np -from matsim.scenariogen.data import read_all - -from extract_ref_data import trip_filter - -if __name__ == "__main__": - hh, persons, trips = read_all("../../../../shared-svn/projects/matsim-berlin/data/SrV/Berlin+Umland") - - trips = trip_filter(trips) - trips = trips[trips.valid] - trips = trips[(~trips.from_zone.isna()) & (~trips.to_zone.isna())] - trips = trips[(~trips.from_location.isna()) & (~trips.to_location.isna())] - - trips_hh = trips.merge(hh, left_on="hh_id", right_index=True) - - trips = trips[trips_hh.location == "Berlin"] - - # Duplication factor - factor = 3 - - repeats = np.maximum(1, np.rint(trips.t_weight * factor)).to_numpy(int) - index = trips.index.repeat(repeats) - df = trips.loc[index] - - # Each sample has a unique sequence number - seq = np.zeros(len(df), dtype=int) - - i = 0 - for r in repeats: - for s in range(r): - seq[i] = s - i += 1 - - df["seq"] = seq - - df = df.drop(columns=["t_weight", "valid"]) - - df_p = df.merge(persons, left_on="p_id", right_index=True) - df["p_age"] = df_p["age"] - - df_hh = df.merge(hh, left_on="hh_id", right_index=True) - df["hh_cars"] = df_hh["n_cars"] - - df = df.sort_values(["p_id", "seq", "n"]) - df.to_csv("trips-scaled.csv", index=False) diff --git a/src/main/resources/mode_share_per_group_dist_ref.csv b/src/main/resources/mode_share_per_group_dist_ref.csv new file mode 100644 index 000000000..2386d7ba7 --- /dev/null +++ b/src/main/resources/mode_share_per_group_dist_ref.csv @@ -0,0 +1,1231 @@ +age,income,economic_status,employment,car_avail,bike_avail,pt_abo_avail,dist_group,main_mode,share +,,,,,,,0 - 1000,bike,0.03993953574665 +,,,,,,,0 - 1000,car,0.010077686113465548 +,,,,,,,0 - 1000,pt,0.0035575461836548946 +,,,,,,,0 - 1000,ride,0.0037969819009802397 +,,,,,,,0 - 1000,walk,0.22881070677001764 +,,,,,,,1000 - 2000,bike,0.04698342341346555 +,,,,,,,1000 - 2000,car,0.023147292190076742 +,,,,,,,1000 - 2000,pt,0.02007970854535886 +,,,,,,,1000 - 2000,ride,0.010215459406613094 +,,,,,,,1000 - 2000,walk,0.05220619945665914 +,,,,,,,2000 - 5000,bike,0.05624516000252578 +,,,,,,,2000 - 5000,car,0.05164117094781402 +,,,,,,,2000 - 5000,pt,0.07035847284671681 +,,,,,,,2000 - 5000,ride,0.019753111426262702 +,,,,,,,2000 - 5000,walk,0.013501281662145714 +,,,,,,,5000 - 10000,bike,0.02693069701653698 +,,,,,,,5000 - 10000,car,0.05215320525856776 +,,,,,,,5000 - 10000,pt,0.08244750747280563 +,,,,,,,5000 - 10000,ride,0.01397122077983446 +,,,,,,,5000 - 10000,walk,0.001517535520605139 +,,,,,,,10000 - 20000,bike,0.007136782872703979 +,,,,,,,10000 - 20000,car,0.04134445948649564 +,,,,,,,10000 - 20000,pt,0.06385899348452925 +,,,,,,,10000 - 20000,ride,0.007926042173942492 +,,,,,,,10000 - 20000,walk,0.0005273153061550004 +,,,,,,,20000+,bike,0.0006419443250597948 +,,,,,,,20000+,car,0.022309302342807483 +,,,,,,,20000+,pt,0.024771142628339887 +,,,,,,,20000+,ride,0.003944497517436229 +,,,,,,,20000+,walk,0.00020561720177352156 +0 - 12,,,,,,,0 - 1000,bike,0.008517923186350437 +0 - 12,,,,,,,0 - 1000,car,3.3087829984082275e-06 +0 - 12,,,,,,,0 - 1000,pt,0.0002096824780535133 +0 - 12,,,,,,,0 - 1000,ride,0.002029469269805064 +0 - 12,,,,,,,0 - 1000,walk,0.03308803116309243 +0 - 12,,,,,,,1000 - 2000,bike,0.008096522930526682 +0 - 12,,,,,,,1000 - 2000,car,1.4482130927893376e-05 +0 - 12,,,,,,,1000 - 2000,pt,0.0022240552516710867 +0 - 12,,,,,,,1000 - 2000,ride,0.00548244643347418 +0 - 12,,,,,,,1000 - 2000,walk,0.0070833714423778225 +0 - 12,,,,,,,2000 - 5000,bike,0.004071005721068593 +0 - 12,,,,,,,2000 - 5000,car,6.301165237870091e-06 +0 - 12,,,,,,,2000 - 5000,pt,0.005203029203728283 +0 - 12,,,,,,,2000 - 5000,ride,0.008538310331494542 +0 - 12,,,,,,,2000 - 5000,walk,0.0008106474785451107 +0 - 12,,,,,,,5000 - 10000,bike,0.00037547491019678696 +0 - 12,,,,,,,5000 - 10000,car,5.398602409648083e-06 +0 - 12,,,,,,,5000 - 10000,pt,0.002694053920261828 +0 - 12,,,,,,,5000 - 10000,ride,0.004394592866035796 +0 - 12,,,,,,,5000 - 10000,walk,8.967264191923621e-05 +0 - 12,,,,,,,10000 - 20000,bike,7.579848095002919e-06 +0 - 12,,,,,,,10000 - 20000,car,0.0 +0 - 12,,,,,,,10000 - 20000,pt,0.0007491330684292405 +0 - 12,,,,,,,10000 - 20000,ride,0.0018407990404998905 +0 - 12,,,,,,,10000 - 20000,walk,4.039646752471745e-05 +0 - 12,,,,,,,20000+,bike,3.3087829984082275e-06 +0 - 12,,,,,,,20000+,car,0.0 +0 - 12,,,,,,,20000+,pt,0.00030231628953346167 +0 - 12,,,,,,,20000+,ride,0.0004951435481063568 +0 - 12,,,,,,,20000+,walk,2.9887238744409086e-05 +12 - 18,,,,,,,0 - 1000,bike,0.0019792792514497825 +12 - 18,,,,,,,0 - 1000,car,0.0 +12 - 18,,,,,,,0 - 1000,pt,0.00026781913936508694 +12 - 18,,,,,,,0 - 1000,ride,0.00010882485081855171 +12 - 18,,,,,,,0 - 1000,walk,0.007339990685009927 +12 - 18,,,,,,,1000 - 2000,bike,0.004346248067065028 +12 - 18,,,,,,,1000 - 2000,car,0.0 +12 - 18,,,,,,,1000 - 2000,pt,0.001538992156359736 +12 - 18,,,,,,,1000 - 2000,ride,0.00041055227571393845 +12 - 18,,,,,,,1000 - 2000,walk,0.0021890484059849395 +12 - 18,,,,,,,2000 - 5000,bike,0.0052400109508453216 +12 - 18,,,,,,,2000 - 5000,car,1.3804108659462701e-05 +12 - 18,,,,,,,2000 - 5000,pt,0.009673748842012386 +12 - 18,,,,,,,2000 - 5000,ride,0.001574332754614814 +12 - 18,,,,,,,2000 - 5000,walk,0.00036801454994234053 +12 - 18,,,,,,,5000 - 10000,bike,0.0006812006400720527 +12 - 18,,,,,,,5000 - 10000,car,1.3804108659462701e-05 +12 - 18,,,,,,,5000 - 10000,pt,0.007624584361236859 +12 - 18,,,,,,,5000 - 10000,ride,0.001409368305114249 +12 - 18,,,,,,,5000 - 10000,walk,1.723169042141231e-05 +12 - 18,,,,,,,10000 - 20000,bike,6.757042351144588e-05 +12 - 18,,,,,,,10000 - 20000,car,7.453909685531387e-06 +12 - 18,,,,,,,10000 - 20000,pt,0.002722903815289597 +12 - 18,,,,,,,10000 - 20000,ride,0.00043451071899363035 +12 - 18,,,,,,,10000 - 20000,walk,0.0 +12 - 18,,,,,,,20000+,bike,1.831017105587331e-05 +12 - 18,,,,,,,20000+,car,0.0 +12 - 18,,,,,,,20000+,pt,0.000484219630018591 +12 - 18,,,,,,,20000+,ride,0.00024232959934533506 +12 - 18,,,,,,,20000+,walk,2.1501580059874272e-05 +18 - 25,,,,,,,0 - 1000,bike,0.001432221646221816 +18 - 25,,,,,,,0 - 1000,car,0.00029110361309750416 +18 - 25,,,,,,,0 - 1000,pt,0.00023561400016997695 +18 - 25,,,,,,,0 - 1000,ride,0.00012385283120214407 +18 - 25,,,,,,,0 - 1000,walk,0.008069002092563936 +18 - 25,,,,,,,1000 - 2000,bike,0.002028124042469899 +18 - 25,,,,,,,1000 - 2000,car,0.0005968294026813628 +18 - 25,,,,,,,1000 - 2000,pt,0.0014637511700461901 +18 - 25,,,,,,,1000 - 2000,ride,0.00015932274468686333 +18 - 25,,,,,,,1000 - 2000,walk,0.00271524800733926 +18 - 25,,,,,,,2000 - 5000,bike,0.0037726492395604477 +18 - 25,,,,,,,2000 - 5000,car,0.0013884089847483213 +18 - 25,,,,,,,2000 - 5000,pt,0.006534172962434739 +18 - 25,,,,,,,2000 - 5000,ride,0.0006266720094108574 +18 - 25,,,,,,,2000 - 5000,walk,0.0009459002061526505 +18 - 25,,,,,,,5000 - 10000,bike,0.0014564852917102107 +18 - 25,,,,,,,5000 - 10000,car,0.0018238841737853375 +18 - 25,,,,,,,5000 - 10000,pt,0.010082997804321684 +18 - 25,,,,,,,5000 - 10000,ride,0.0007481598039209727 +18 - 25,,,,,,,5000 - 10000,walk,7.971861202079858e-05 +18 - 25,,,,,,,10000 - 20000,bike,0.00013635820530129959 +18 - 25,,,,,,,10000 - 20000,car,0.0014058205836528842 +18 - 25,,,,,,,10000 - 20000,pt,0.008349501211167018 +18 - 25,,,,,,,10000 - 20000,ride,0.0005815231393334991 +18 - 25,,,,,,,10000 - 20000,walk,2.749147329899406e-05 +18 - 25,,,,,,,20000+,bike,2.148829801837644e-05 +18 - 25,,,,,,,20000+,car,0.0011849509687035997 +18 - 25,,,,,,,20000+,pt,0.0030998178565597033 +18 - 25,,,,,,,20000+,ride,0.00032491946082011954 +18 - 25,,,,,,,20000+,walk,5.7083633394889036e-06 +25 - 35,,,,,,,0 - 1000,bike,0.0042852561388654785 +25 - 35,,,,,,,0 - 1000,car,0.0009168183383532765 +25 - 35,,,,,,,0 - 1000,pt,0.0002022489929617155 +25 - 35,,,,,,,0 - 1000,ride,0.0001480562319291914 +25 - 35,,,,,,,0 - 1000,walk,0.035267805475233074 +25 - 35,,,,,,,1000 - 2000,bike,0.006146363435432691 +25 - 35,,,,,,,1000 - 2000,car,0.002462073187140846 +25 - 35,,,,,,,1000 - 2000,pt,0.002223103057206319 +25 - 35,,,,,,,1000 - 2000,ride,0.0004270863548432357 +25 - 35,,,,,,,1000 - 2000,walk,0.008342098773894857 +25 - 35,,,,,,,2000 - 5000,bike,0.010681005886217319 +25 - 35,,,,,,,2000 - 5000,car,0.005405348609556078 +25 - 35,,,,,,,2000 - 5000,pt,0.012636152482188533 +25 - 35,,,,,,,2000 - 5000,ride,0.0010950846328671084 +25 - 35,,,,,,,2000 - 5000,walk,0.002296667696036939 +25 - 35,,,,,,,5000 - 10000,bike,0.00714525178845766 +25 - 35,,,,,,,5000 - 10000,car,0.006469468631623288 +25 - 35,,,,,,,5000 - 10000,pt,0.018514209099342118 +25 - 35,,,,,,,5000 - 10000,ride,0.0011792754801178322 +25 - 35,,,,,,,5000 - 10000,walk,0.00018866159641852678 +25 - 35,,,,,,,10000 - 20000,bike,0.0016395575829799966 +25 - 35,,,,,,,10000 - 20000,car,0.0058687029850053455 +25 - 35,,,,,,,10000 - 20000,pt,0.013135330873607598 +25 - 35,,,,,,,10000 - 20000,ride,0.0006755783567638264 +25 - 35,,,,,,,10000 - 20000,walk,7.423688459484545e-05 +25 - 35,,,,,,,20000+,bike,0.00013675095939469654 +25 - 35,,,,,,,20000+,car,0.003561938478892821 +25 - 35,,,,,,,20000+,pt,0.006452657651336276 +25 - 35,,,,,,,20000+,ride,0.0004869189430278995 +25 - 35,,,,,,,20000+,walk,2.4664875835279084e-05 +35 - 66,,,,,,,0 - 1000,bike,0.018767973943631885 +35 - 66,,,,,,,0 - 1000,car,0.005852253062503732 +35 - 66,,,,,,,0 - 1000,pt,0.001222583895659084 +35 - 66,,,,,,,0 - 1000,ride,0.0005929641697055336 +35 - 66,,,,,,,0 - 1000,walk,0.09609966222035016 +35 - 66,,,,,,,1000 - 2000,bike,0.021440930762736468 +35 - 66,,,,,,,1000 - 2000,car,0.013402320471081593 +35 - 66,,,,,,,1000 - 2000,pt,0.006930927457045121 +35 - 66,,,,,,,1000 - 2000,ride,0.00182940376880349 +35 - 66,,,,,,,1000 - 2000,walk,0.02013373771019918 +35 - 66,,,,,,,2000 - 5000,bike,0.02867892858019107 +35 - 66,,,,,,,2000 - 5000,car,0.0306540140499963 +35 - 66,,,,,,,2000 - 5000,pt,0.02511400281828082 +35 - 66,,,,,,,2000 - 5000,ride,0.003962382877969196 +35 - 66,,,,,,,2000 - 5000,walk,0.00586928668118051 +35 - 66,,,,,,,5000 - 10000,bike,0.016246350428763018 +35 - 66,,,,,,,5000 - 10000,car,0.033073853486548795 +35 - 66,,,,,,,5000 - 10000,pt,0.03445283159189375 +35 - 66,,,,,,,5000 - 10000,ride,0.00326402230517573 +35 - 66,,,,,,,5000 - 10000,walk,0.0007866622142723672 +35 - 66,,,,,,,10000 - 20000,bike,0.004941665985263888 +35 - 66,,,,,,,10000 - 20000,car,0.02792464406813406 +35 - 66,,,,,,,10000 - 20000,pt,0.032644342492031235 +35 - 66,,,,,,,10000 - 20000,ride,0.002340623656188577 +35 - 66,,,,,,,10000 - 20000,walk,0.00021950303172812345 +35 - 66,,,,,,,20000+,bike,0.00041830098334830754 +35 - 66,,,,,,,20000+,car,0.014731449973696797 +35 - 66,,,,,,,20000+,pt,0.012346321725763982 +35 - 66,,,,,,,20000+,ride,0.0012749952596997623 +35 - 66,,,,,,,20000+,walk,0.00011121016481649163 +66+,,,,,,,0 - 1000,bike,0.004964309147280085 +66+,,,,,,,0 - 1000,car,0.0030160764667479757 +66+,,,,,,,0 - 1000,pt,0.0014202592753502637 +66+,,,,,,,0 - 1000,ride,0.0007945206733549131 +66+,,,,,,,0 - 1000,walk,0.04895388339532232 +66+,,,,,,,1000 - 2000,bike,0.004901112061815805 +66+,,,,,,,1000 - 2000,car,0.006664368538186043 +66+,,,,,,,1000 - 2000,pt,0.005694241219659483 +66+,,,,,,,1000 - 2000,ride,0.001908547601066374 +66+,,,,,,,1000 - 2000,walk,0.011752403919052076 +66+,,,,,,,2000 - 5000,bike,0.0038120195535065672 +66+,,,,,,,2000 - 5000,car,0.014182897753287854 +66+,,,,,,,2000 - 5000,pt,0.011168004816612568 +66+,,,,,,,2000 - 5000,ride,0.003960002311818075 +66+,,,,,,,2000 - 5000,walk,0.003213275887589475 +66+,,,,,,,5000 - 10000,bike,0.0010309422669415914 +66+,,,,,,,5000 - 10000,car,0.010764972033622977 +66+,,,,,,,5000 - 10000,pt,0.009085791020194418 +66+,,,,,,,5000 - 10000,ride,0.0029652986791929787 +66+,,,,,,,5000 - 10000,walk,0.0003558709820776099 +66+,,,,,,,10000 - 20000,bike,0.0003453780571514728 +66+,,,,,,,10000 - 20000,car,0.006129189784084991 +66+,,,,,,,10000 - 20000,pt,0.00626965789970863 +66+,,,,,,,10000 - 20000,ride,0.002047930484398672 +66+,,,,,,,10000 - 20000,walk,0.000165785513990103 +66+,,,,,,,20000+,bike,4.3904512818103214e-05 +66+,,,,,,,20000+,car,0.0028351117889988684 +66+,,,,,,,20000+,pt,0.0020904161717707347 +66+,,,,,,,20000+,ride,0.0011209242657942713 +66+,,,,,,,20000+,walk,1.2683217669157984e-05 +,0 - 500,,,,,,0 - 1000,bike,0.00035232906322035405 +,0 - 500,,,,,,0 - 1000,car,7.090399671919135e-05 +,0 - 500,,,,,,0 - 1000,pt,7.660995087296602e-05 +,0 - 500,,,,,,0 - 1000,ride,1.0994881991693622e-05 +,0 - 500,,,,,,0 - 1000,walk,0.002062163570854928 +,0 - 500,,,,,,1000 - 2000,bike,0.0005460419853036346 +,0 - 500,,,,,,1000 - 2000,car,0.00013863488911091545 +,0 - 500,,,,,,1000 - 2000,pt,0.00037538887497296587 +,0 - 500,,,,,,1000 - 2000,ride,7.706829309402262e-06 +,0 - 500,,,,,,1000 - 2000,walk,0.0005002007112455454 +,0 - 500,,,,,,2000 - 5000,bike,0.0005903379360366923 +,0 - 500,,,,,,2000 - 5000,car,0.000164129854459031 +,0 - 500,,,,,,2000 - 5000,pt,0.000977491071983108 +,0 - 500,,,,,,2000 - 5000,ride,5.5772576688806945e-05 +,0 - 500,,,,,,2000 - 5000,walk,4.7047029128693725e-05 +,0 - 500,,,,,,5000 - 10000,bike,0.0003415737350324348 +,0 - 500,,,,,,5000 - 10000,car,7.340959103672418e-05 +,0 - 500,,,,,,5000 - 10000,pt,0.0011667747069172992 +,0 - 500,,,,,,5000 - 10000,ride,0.00011349593684832373 +,0 - 500,,,,,,5000 - 10000,walk,0.0 +,0 - 500,,,,,,10000 - 20000,bike,0.0 +,0 - 500,,,,,,10000 - 20000,car,0.00010616828727126032 +,0 - 500,,,,,,10000 - 20000,pt,0.0010196345434890866 +,0 - 500,,,,,,10000 - 20000,ride,8.481671801367754e-05 +,0 - 500,,,,,,10000 - 20000,walk,5.145651420926276e-05 +,0 - 500,,,,,,20000+,bike,0.0 +,0 - 500,,,,,,20000+,car,0.00019659582172983761 +,0 - 500,,,,,,20000+,pt,0.0003546799090771576 +,0 - 500,,,,,,20000+,ride,0.00011803904947862314 +,0 - 500,,,,,,20000+,walk,0.0 +,500 - 900,,,,,,0 - 1000,bike,0.0011553909709452585 +,500 - 900,,,,,,0 - 1000,car,0.0001988346512506266 +,500 - 900,,,,,,0 - 1000,pt,0.00047317266605468607 +,500 - 900,,,,,,0 - 1000,ride,5.176430806876595e-05 +,500 - 900,,,,,,0 - 1000,walk,0.008738137941421167 +,500 - 900,,,,,,1000 - 2000,bike,0.001499650235703195 +,500 - 900,,,,,,1000 - 2000,car,0.0006517772253995121 +,500 - 900,,,,,,1000 - 2000,pt,0.00104620105069389 +,500 - 900,,,,,,1000 - 2000,ride,0.00021087180606797166 +,500 - 900,,,,,,1000 - 2000,walk,0.0028972081761874497 +,500 - 900,,,,,,2000 - 5000,bike,0.0018106193690665612 +,500 - 900,,,,,,2000 - 5000,car,0.0008469950119964699 +,500 - 900,,,,,,2000 - 5000,pt,0.0036489656229842546 +,500 - 900,,,,,,2000 - 5000,ride,0.0003496013110205455 +,500 - 900,,,,,,2000 - 5000,walk,0.0009779684880968755 +,500 - 900,,,,,,5000 - 10000,bike,0.0008595566459149111 +,500 - 900,,,,,,5000 - 10000,car,0.000767425577185238 +,500 - 900,,,,,,5000 - 10000,pt,0.0040375862807297425 +,500 - 900,,,,,,5000 - 10000,ride,0.00015757683182533478 +,500 - 900,,,,,,5000 - 10000,walk,5.37989183824766e-05 +,500 - 900,,,,,,10000 - 20000,bike,0.00020345638834471633 +,500 - 900,,,,,,10000 - 20000,car,0.00046008561008179064 +,500 - 900,,,,,,10000 - 20000,pt,0.002760888506726252 +,500 - 900,,,,,,10000 - 20000,ride,9.296752316542117e-05 +,500 - 900,,,,,,10000 - 20000,walk,3.731758378907825e-05 +,500 - 900,,,,,,20000+,bike,1.7932776108010744e-05 +,500 - 900,,,,,,20000+,car,9.937523469734491e-05 +,500 - 900,,,,,,20000+,pt,0.0010246129298322614 +,500 - 900,,,,,,20000+,ride,3.520290554041116e-05 +,500 - 900,,,,,,20000+,walk,0.0 +,900 - 1500,,,,,,0 - 1000,bike,0.0036030125039514184 +,900 - 1500,,,,,,0 - 1000,car,0.0008096352439403976 +,900 - 1500,,,,,,0 - 1000,pt,0.0005304901168066659 +,900 - 1500,,,,,,0 - 1000,ride,0.00019560944694263635 +,900 - 1500,,,,,,0 - 1000,walk,0.02660624854450333 +,900 - 1500,,,,,,1000 - 2000,bike,0.004065223415465144 +,900 - 1500,,,,,,1000 - 2000,car,0.0018147580719711346 +,900 - 1500,,,,,,1000 - 2000,pt,0.0031016548306686265 +,900 - 1500,,,,,,1000 - 2000,ride,0.0005664064381921502 +,900 - 1500,,,,,,1000 - 2000,walk,0.005282831687822155 +,900 - 1500,,,,,,2000 - 5000,bike,0.004835798726788617 +,900 - 1500,,,,,,2000 - 5000,car,0.004005939569279477 +,900 - 1500,,,,,,2000 - 5000,pt,0.009025057970035024 +,900 - 1500,,,,,,2000 - 5000,ride,0.0009725091552085511 +,900 - 1500,,,,,,2000 - 5000,walk,0.0016667786156981407 +,900 - 1500,,,,,,5000 - 10000,bike,0.0025457362806628335 +,900 - 1500,,,,,,5000 - 10000,car,0.004215234417807464 +,900 - 1500,,,,,,5000 - 10000,pt,0.01043239622170388 +,900 - 1500,,,,,,5000 - 10000,ride,0.0007642456044670178 +,900 - 1500,,,,,,5000 - 10000,walk,0.00020946315748903858 +,900 - 1500,,,,,,10000 - 20000,bike,0.0005494669964205728 +,900 - 1500,,,,,,10000 - 20000,car,0.0028074887081674105 +,900 - 1500,,,,,,10000 - 20000,pt,0.0068694456384684665 +,900 - 1500,,,,,,10000 - 20000,ride,0.0004903398401965092 +,900 - 1500,,,,,,10000 - 20000,walk,3.761382871981281e-05 +,900 - 1500,,,,,,20000+,bike,5.085925810983151e-05 +,900 - 1500,,,,,,20000+,car,0.0010672818968565573 +,900 - 1500,,,,,,20000+,pt,0.002718175621841794 +,900 - 1500,,,,,,20000+,ride,0.00023860154836237305 +,900 - 1500,,,,,,20000+,walk,3.737374683682059e-05 +,1500 - 2000,,,,,,0 - 1000,bike,0.003906483044569717 +,1500 - 2000,,,,,,0 - 1000,car,0.001060725000363174 +,1500 - 2000,,,,,,0 - 1000,pt,0.000609655153955435 +,1500 - 2000,,,,,,0 - 1000,ride,0.00028333230595037974 +,1500 - 2000,,,,,,0 - 1000,walk,0.029497186703420167 +,1500 - 2000,,,,,,1000 - 2000,bike,0.0050197842930531575 +,1500 - 2000,,,,,,1000 - 2000,car,0.0028681812498369277 +,1500 - 2000,,,,,,1000 - 2000,pt,0.0031796396587783207 +,1500 - 2000,,,,,,1000 - 2000,ride,0.000856614121643278 +,1500 - 2000,,,,,,1000 - 2000,walk,0.0073421416410575655 +,1500 - 2000,,,,,,2000 - 5000,bike,0.005655283879068705 +,1500 - 2000,,,,,,2000 - 5000,car,0.0062605520271039146 +,1500 - 2000,,,,,,2000 - 5000,pt,0.00966695728954003 +,1500 - 2000,,,,,,2000 - 5000,ride,0.0017578800571828417 +,1500 - 2000,,,,,,2000 - 5000,walk,0.0020865704579953 +,1500 - 2000,,,,,,5000 - 10000,bike,0.002691588669406727 +,1500 - 2000,,,,,,5000 - 10000,car,0.006121933559301505 +,1500 - 2000,,,,,,5000 - 10000,pt,0.012104026976708796 +,1500 - 2000,,,,,,5000 - 10000,ride,0.0015141964905483394 +,1500 - 2000,,,,,,5000 - 10000,walk,0.00014242852425506544 +,1500 - 2000,,,,,,10000 - 20000,bike,0.0008133399608978236 +,1500 - 2000,,,,,,10000 - 20000,car,0.004168753390349577 +,1500 - 2000,,,,,,10000 - 20000,pt,0.008152228651661163 +,1500 - 2000,,,,,,10000 - 20000,ride,0.0007736839544154396 +,1500 - 2000,,,,,,10000 - 20000,walk,9.602383251572777e-05 +,1500 - 2000,,,,,,20000+,bike,0.00010092296430600491 +,1500 - 2000,,,,,,20000+,car,0.002089253609395865 +,1500 - 2000,,,,,,20000+,pt,0.0028769909408601223 +,1500 - 2000,,,,,,20000+,ride,0.00037695422209180646 +,1500 - 2000,,,,,,20000+,walk,1.9733993436363767e-05 +,2000 - 2600,,,,,,0 - 1000,bike,0.004766926158855704 +,2000 - 2600,,,,,,0 - 1000,car,0.0016774651039110759 +,2000 - 2600,,,,,,0 - 1000,pt,0.00047933120694804664 +,2000 - 2600,,,,,,0 - 1000,ride,0.0005735427906518753 +,2000 - 2600,,,,,,0 - 1000,walk,0.03452167922210745 +,2000 - 2600,,,,,,1000 - 2000,bike,0.005944479382721511 +,2000 - 2600,,,,,,1000 - 2000,car,0.003576361720079578 +,2000 - 2600,,,,,,1000 - 2000,pt,0.0031813927421609307 +,2000 - 2600,,,,,,1000 - 2000,ride,0.0012975105462565144 +,2000 - 2600,,,,,,1000 - 2000,walk,0.007096477896365713 +,2000 - 2600,,,,,,2000 - 5000,bike,0.007824784459075218 +,2000 - 2600,,,,,,2000 - 5000,car,0.007802538183538487 +,2000 - 2600,,,,,,2000 - 5000,pt,0.010344283431731521 +,2000 - 2600,,,,,,2000 - 5000,ride,0.0023245759823558292 +,2000 - 2600,,,,,,2000 - 5000,walk,0.0017995572656591457 +,2000 - 2600,,,,,,5000 - 10000,bike,0.0035725992123135444 +,2000 - 2600,,,,,,5000 - 10000,car,0.008442749504628805 +,2000 - 2600,,,,,,5000 - 10000,pt,0.01155232596588037 +,2000 - 2600,,,,,,5000 - 10000,ride,0.0016149032313883922 +,2000 - 2600,,,,,,5000 - 10000,walk,0.0003091502890405365 +,2000 - 2600,,,,,,10000 - 20000,bike,0.0009285455332542868 +,2000 - 2600,,,,,,10000 - 20000,car,0.005795857865928424 +,2000 - 2600,,,,,,10000 - 20000,pt,0.008778983578839283 +,2000 - 2600,,,,,,10000 - 20000,ride,0.0009886154568520366 +,2000 - 2600,,,,,,10000 - 20000,walk,5.338638259674346e-05 +,2000 - 2600,,,,,,20000+,bike,4.683860177461387e-05 +,2000 - 2600,,,,,,20000+,car,0.0032611234385663494 +,2000 - 2600,,,,,,20000+,pt,0.003491960894045404 +,2000 - 2600,,,,,,20000+,ride,0.0006791418050425238 +,2000 - 2600,,,,,,20000+,walk,2.779398791394461e-05 +,2600 - 3000,,,,,,0 - 1000,bike,0.004084223848754118 +,2600 - 3000,,,,,,0 - 1000,car,0.0011359892211911054 +,2600 - 3000,,,,,,0 - 1000,pt,0.00036942393427873756 +,2600 - 3000,,,,,,0 - 1000,ride,0.00036545268506301196 +,2600 - 3000,,,,,,0 - 1000,walk,0.02263782024262488 +,2600 - 3000,,,,,,1000 - 2000,bike,0.004556881108570422 +,2600 - 3000,,,,,,1000 - 2000,car,0.0023989736518997503 +,2600 - 3000,,,,,,1000 - 2000,pt,0.0017919075730422201 +,2600 - 3000,,,,,,1000 - 2000,ride,0.0010136460995469466 +,2600 - 3000,,,,,,1000 - 2000,walk,0.00515205526145704 +,2600 - 3000,,,,,,2000 - 5000,bike,0.00552167545658979 +,2600 - 3000,,,,,,2000 - 5000,car,0.005039544820023574 +,2600 - 3000,,,,,,2000 - 5000,pt,0.006964572917813233 +,2600 - 3000,,,,,,2000 - 5000,ride,0.0017460713988484088 +,2600 - 3000,,,,,,2000 - 5000,walk,0.0013467572034831475 +,2600 - 3000,,,,,,5000 - 10000,bike,0.0025207374335703358 +,2600 - 3000,,,,,,5000 - 10000,car,0.005392219484126 +,2600 - 3000,,,,,,5000 - 10000,pt,0.007915112634375571 +,2600 - 3000,,,,,,5000 - 10000,ride,0.0014469122270612453 +,2600 - 3000,,,,,,5000 - 10000,walk,0.00014688761137743425 +,2600 - 3000,,,,,,10000 - 20000,bike,0.0007297529398405454 +,2600 - 3000,,,,,,10000 - 20000,car,0.0043480691384510765 +,2600 - 3000,,,,,,10000 - 20000,pt,0.0064139668957580815 +,2600 - 3000,,,,,,10000 - 20000,ride,0.0007561441104438095 +,2600 - 3000,,,,,,10000 - 20000,walk,4.129643578131873e-05 +,2600 - 3000,,,,,,20000+,bike,7.575663124300254e-05 +,2600 - 3000,,,,,,20000+,car,0.002054625813152185 +,2600 - 3000,,,,,,20000+,pt,0.0020783945559327446 +,2600 - 3000,,,,,,20000+,ride,0.00037719425231081435 +,2600 - 3000,,,,,,20000+,walk,8.232158789514617e-06 +,3000 - 3600,,,,,,0 - 1000,bike,0.005047405591037784 +,3000 - 3600,,,,,,0 - 1000,car,0.0011352122449559516 +,3000 - 3600,,,,,,0 - 1000,pt,0.00033272777792851203 +,3000 - 3600,,,,,,0 - 1000,ride,0.0005144972668703102 +,3000 - 3600,,,,,,0 - 1000,walk,0.0262335277178426 +,3000 - 3600,,,,,,1000 - 2000,bike,0.005830855409162586 +,3000 - 3600,,,,,,1000 - 2000,car,0.0027008573210507104 +,3000 - 3600,,,,,,1000 - 2000,pt,0.0018961084062378546 +,3000 - 3600,,,,,,1000 - 2000,ride,0.001276091087977852 +,3000 - 3600,,,,,,1000 - 2000,walk,0.006813891301403424 +,3000 - 3600,,,,,,2000 - 5000,bike,0.006775101378684258 +,3000 - 3600,,,,,,2000 - 5000,car,0.006483144112316924 +,3000 - 3600,,,,,,2000 - 5000,pt,0.007880032355711181 +,3000 - 3600,,,,,,2000 - 5000,ride,0.0029981256362548096 +,3000 - 3600,,,,,,2000 - 5000,walk,0.0015844945335384735 +,3000 - 3600,,,,,,5000 - 10000,bike,0.003258057317522539 +,3000 - 3600,,,,,,5000 - 10000,car,0.007259524622921011 +,3000 - 3600,,,,,,5000 - 10000,pt,0.009132683765721633 +,3000 - 3600,,,,,,5000 - 10000,ride,0.0021101207464436673 +,3000 - 3600,,,,,,5000 - 10000,walk,0.00021477034828702496 +,3000 - 3600,,,,,,10000 - 20000,bike,0.0008793649182361488 +,3000 - 3600,,,,,,10000 - 20000,car,0.005359622298441573 +,3000 - 3600,,,,,,10000 - 20000,pt,0.007992605851177633 +,3000 - 3600,,,,,,10000 - 20000,ride,0.0010075116058614508 +,3000 - 3600,,,,,,10000 - 20000,walk,3.715133502190691e-05 +,3000 - 3600,,,,,,20000+,bike,3.864665093487742e-05 +,3000 - 3600,,,,,,20000+,car,0.003127244180749633 +,3000 - 3600,,,,,,20000+,pt,0.002953911985680946 +,3000 - 3600,,,,,,20000+,ride,0.0004787409885176979 +,3000 - 3600,,,,,,20000+,walk,4.228094193517067e-05 +,3600 - 4600,,,,,,0 - 1000,bike,0.007146309031588047 +,3600 - 4600,,,,,,0 - 1000,car,0.0015229287022055407 +,3600 - 4600,,,,,,0 - 1000,pt,0.0003253808489507266 +,3600 - 4600,,,,,,0 - 1000,ride,0.0006398488134865594 +,3600 - 4600,,,,,,0 - 1000,walk,0.03297491846551036 +,3600 - 4600,,,,,,1000 - 2000,bike,0.0077555808818918414 +,3600 - 4600,,,,,,1000 - 2000,car,0.0037044395043078176 +,3600 - 4600,,,,,,1000 - 2000,pt,0.002434884174048942 +,3600 - 4600,,,,,,1000 - 2000,ride,0.0020357652763356355 +,3600 - 4600,,,,,,1000 - 2000,walk,0.006919201918318882 +,3600 - 4600,,,,,,2000 - 5000,bike,0.009321533342121135 +,3600 - 4600,,,,,,2000 - 5000,car,0.00820999320458536 +,3600 - 4600,,,,,,2000 - 5000,pt,0.00864405340831884 +,3600 - 4600,,,,,,2000 - 5000,ride,0.003420885083365796 +,3600 - 4600,,,,,,2000 - 5000,walk,0.0018178281277228094 +,3600 - 4600,,,,,,5000 - 10000,bike,0.004424215655813713 +,3600 - 4600,,,,,,5000 - 10000,car,0.0072327145208212675 +,3600 - 4600,,,,,,5000 - 10000,pt,0.010589905262758752 +,3600 - 4600,,,,,,5000 - 10000,ride,0.002502551768114926 +,3600 - 4600,,,,,,5000 - 10000,walk,0.0001328241841154802 +,3600 - 4600,,,,,,10000 - 20000,bike,0.0011879856861528161 +,3600 - 4600,,,,,,10000 - 20000,car,0.0069041533478274185 +,3600 - 4600,,,,,,10000 - 20000,pt,0.008765934745098557 +,3600 - 4600,,,,,,10000 - 20000,ride,0.0013666994870583434 +,3600 - 4600,,,,,,10000 - 20000,walk,2.2273001533269444e-05 +,3600 - 4600,,,,,,20000+,bike,9.448855396082501e-05 +,3600 - 4600,,,,,,20000+,car,0.004301941442336025 +,3600 - 4600,,,,,,20000+,pt,0.0034068595865636574 +,3600 - 4600,,,,,,20000+,ride,0.0006720362744198136 +,3600 - 4600,,,,,,20000+,walk,1.6689237934032153e-05 +,4600 - 5600,,,,,,0 - 1000,bike,0.005052233241036812 +,4600 - 5600,,,,,,0 - 1000,car,0.0011080293624990592 +,4600 - 5600,,,,,,0 - 1000,pt,0.00011133555771025316 +,4600 - 5600,,,,,,0 - 1000,ride,0.00046582444191717244 +,4600 - 5600,,,,,,0 - 1000,walk,0.021226433641453317 +,4600 - 5600,,,,,,1000 - 2000,bike,0.005501902963690853 +,4600 - 5600,,,,,,1000 - 2000,car,0.0023090294722739827 +,4600 - 5600,,,,,,1000 - 2000,pt,0.0015501194494462072 +,4600 - 5600,,,,,,1000 - 2000,ride,0.0011960269268397633 +,4600 - 5600,,,,,,1000 - 2000,walk,0.004820069779500147 +,4600 - 5600,,,,,,2000 - 5000,bike,0.006583786031856505 +,4600 - 5600,,,,,,2000 - 5000,car,0.005829460585159519 +,4600 - 5600,,,,,,2000 - 5000,pt,0.006076940519134916 +,4600 - 5600,,,,,,2000 - 5000,ride,0.0028785588212419176 +,4600 - 5600,,,,,,2000 - 5000,walk,0.0009565681582078224 +,4600 - 5600,,,,,,5000 - 10000,bike,0.0033680018925677066 +,4600 - 5600,,,,,,5000 - 10000,car,0.005801717900974979 +,4600 - 5600,,,,,,5000 - 10000,pt,0.00758391604310863 +,4600 - 5600,,,,,,5000 - 10000,ride,0.0017484453882993549 +,4600 - 5600,,,,,,5000 - 10000,walk,0.00010402432512995702 +,4600 - 5600,,,,,,10000 - 20000,bike,0.0007196228628143366 +,4600 - 5600,,,,,,10000 - 20000,car,0.0052079619978355205 +,4600 - 5600,,,,,,10000 - 20000,pt,0.006113842082934254 +,4600 - 5600,,,,,,10000 - 20000,ride,0.0010964706545048006 +,4600 - 5600,,,,,,10000 - 20000,walk,1.9864828182869717e-05 +,4600 - 5600,,,,,,20000+,bike,6.781203403882217e-05 +,4600 - 5600,,,,,,20000+,car,0.0027895751751229676 +,4600 - 5600,,,,,,20000+,pt,0.002926011444635216 +,4600 - 5600,,,,,,20000+,ride,0.0004705938102194283 +,4600 - 5600,,,,,,20000+,walk,3.587801352570508e-05 +,5600+,,,,,,0 - 1000,bike,0.004825222292690779 +,5600+,,,,,,0 - 1000,car,0.0013579625864294262 +,5600+,,,,,,0 - 1000,pt,0.0002494189701488654 +,5600+,,,,,,0 - 1000,ride,0.0006961149600378349 +,5600+,,,,,,0 - 1000,walk,0.02431259072027944 +,5600+,,,,,,1000 - 2000,bike,0.006263023737903204 +,5600+,,,,,,1000 - 2000,car,0.002984279084146409 +,5600+,,,,,,1000 - 2000,pt,0.0015224117853089012 +,5600+,,,,,,1000 - 2000,ride,0.0017548202744435794 +,5600+,,,,,,1000 - 2000,walk,0.005382121083301224 +,5600+,,,,,,2000 - 5000,bike,0.007326239423238306 +,5600+,,,,,,2000 - 5000,car,0.006998873579351267 +,5600+,,,,,,2000 - 5000,pt,0.007130118259464701 +,5600+,,,,,,2000 - 5000,ride,0.003249131404095197 +,5600+,,,,,,2000 - 5000,walk,0.0012177117826153055 +,5600+,,,,,,5000 - 10000,bike,0.0033486301737322326 +,5600+,,,,,,5000 - 10000,car,0.006846276079764767 +,5600+,,,,,,5000 - 10000,pt,0.007932779614900964 +,5600+,,,,,,5000 - 10000,ride,0.001998772554837859 +,5600+,,,,,,5000 - 10000,walk,0.00020418816252812546 +,5600+,,,,,,10000 - 20000,bike,0.0011252475867427323 +,5600+,,,,,,10000 - 20000,car,0.006186298842141585 +,5600+,,,,,,10000 - 20000,pt,0.006991462990376476 +,5600+,,,,,,10000 - 20000,ride,0.0012687928234310039 +,5600+,,,,,,10000 - 20000,walk,0.0001309315638050105 +,5600+,,,,,,20000+,bike,0.0001486868545838068 +,5600+,,,,,,20000+,car,0.0033222857302007175 +,5600+,,,,,,20000+,pt,0.0029395447598705815 +,5600+,,,,,,20000+,ride,0.0004979926614527379 +,5600+,,,,,,20000+,walk,1.7635121401970097e-05 +,,very_low,,,,,0 - 1000,bike,0.003392672613447378 +,,very_low,,,,,0 - 1000,car,0.0006496289233146933 +,,very_low,,,,,0 - 1000,pt,0.0007437680149058938 +,,very_low,,,,,0 - 1000,ride,0.0003054495758613595 +,,very_low,,,,,0 - 1000,walk,0.02391989848698311 +,,very_low,,,,,1000 - 2000,bike,0.004465449398329219 +,,very_low,,,,,1000 - 2000,car,0.0017226549417710916 +,,very_low,,,,,1000 - 2000,pt,0.0031052433795656855 +,,very_low,,,,,1000 - 2000,ride,0.0009106932302638451 +,,very_low,,,,,1000 - 2000,walk,0.00671783308257349 +,,very_low,,,,,2000 - 5000,bike,0.004975576861984718 +,,very_low,,,,,2000 - 5000,car,0.00329910307891031 +,,very_low,,,,,2000 - 5000,pt,0.009273670130406362 +,,very_low,,,,,2000 - 5000,ride,0.0015434064247872776 +,,very_low,,,,,2000 - 5000,walk,0.001717580672977547 +,,very_low,,,,,5000 - 10000,bike,0.002224210778455478 +,,very_low,,,,,5000 - 10000,car,0.002710071666802448 +,,very_low,,,,,5000 - 10000,pt,0.010596207745346182 +,,very_low,,,,,5000 - 10000,ride,0.0011739115425644406 +,,very_low,,,,,5000 - 10000,walk,0.00015048030223031736 +,,very_low,,,,,10000 - 20000,bike,0.000430845849792407 +,,very_low,,,,,10000 - 20000,car,0.0018952048427516065 +,,very_low,,,,,10000 - 20000,pt,0.007177392326197229 +,,very_low,,,,,10000 - 20000,ride,0.00044844448882692836 +,,very_low,,,,,10000 - 20000,walk,0.00011819488715629153 +,,very_low,,,,,20000+,bike,4.017165493929981e-05 +,,very_low,,,,,20000+,car,0.0009336066630243455 +,,very_low,,,,,20000+,pt,0.0026784533100517177 +,,very_low,,,,,20000+,ride,0.00039515547177791703 +,,very_low,,,,,20000+,walk,3.737374683682059e-05 +,,low,,,,,0 - 1000,bike,0.006545901351725986 +,,low,,,,,0 - 1000,car,0.0014141097215581385 +,,low,,,,,0 - 1000,pt,0.0006814515020462688 +,,low,,,,,0 - 1000,ride,0.0005248545687365419 +,,low,,,,,0 - 1000,walk,0.03959295783565192 +,,low,,,,,1000 - 2000,bike,0.007874438604372315 +,,low,,,,,1000 - 2000,car,0.0030677042479769685 +,,low,,,,,1000 - 2000,pt,0.004044244566596204 +,,low,,,,,1000 - 2000,ride,0.0014578293812093533 +,,low,,,,,1000 - 2000,walk,0.008312374797945148 +,,low,,,,,2000 - 5000,bike,0.00832650152914556 +,,low,,,,,2000 - 5000,car,0.006789869630817028 +,,low,,,,,2000 - 5000,pt,0.013382210962491772 +,,low,,,,,2000 - 5000,ride,0.0027025361747107972 +,,low,,,,,2000 - 5000,walk,0.0025209913223077757 +,,low,,,,,5000 - 10000,bike,0.0035982184003339494 +,,low,,,,,5000 - 10000,car,0.007102249724694728 +,,low,,,,,5000 - 10000,pt,0.014660947955024123 +,,low,,,,,5000 - 10000,ride,0.0019606672465484368 +,,low,,,,,5000 - 10000,walk,0.00021397315146220276 +,,low,,,,,10000 - 20000,bike,0.0009626571796784078 +,,low,,,,,10000 - 20000,car,0.004953067683758229 +,,low,,,,,10000 - 20000,pt,0.010367406073155226 +,,low,,,,,10000 - 20000,ride,0.0010965695360368203 +,,low,,,,,10000 - 20000,walk,8.352578059816359e-05 +,,low,,,,,20000+,bike,7.459506028301793e-05 +,,low,,,,,20000+,car,0.0021398745547135697 +,,low,,,,,20000+,pt,0.0037289570867659752 +,,low,,,,,20000+,ride,0.0005172019870130312 +,,low,,,,,20000+,walk,4.21088680698872e-06 +,,medium,,,,,0 - 1000,bike,0.015282132327218758 +,,medium,,,,,0 - 1000,car,0.0043825153382871075 +,,medium,,,,,0 - 1000,pt,0.001432776372852335 +,,medium,,,,,0 - 1000,ride,0.0015630849589145406 +,,medium,,,,,0 - 1000,walk,0.09108651503685507 +,,medium,,,,,1000 - 2000,bike,0.017809117393496856 +,,medium,,,,,1000 - 2000,car,0.009939931793946208 +,,medium,,,,,1000 - 2000,pt,0.007592776793745955 +,,medium,,,,,1000 - 2000,ride,0.003850516611602353 +,,medium,,,,,1000 - 2000,walk,0.021015128012898943 +,,medium,,,,,2000 - 5000,bike,0.02138214291163708 +,,medium,,,,,2000 - 5000,car,0.02103562524067573 +,,medium,,,,,2000 - 5000,pt,0.02585369190378979 +,,medium,,,,,2000 - 5000,ride,0.007486177986014311 +,,medium,,,,,2000 - 5000,walk,0.005094523480838895 +,,medium,,,,,5000 - 10000,bike,0.010058524736992059 +,,medium,,,,,5000 - 10000,car,0.022038308620705886 +,,medium,,,,,5000 - 10000,pt,0.031388138896530644 +,,medium,,,,,5000 - 10000,ride,0.00558258932353683 +,,medium,,,,,5000 - 10000,walk,0.0007195688973310005 +,,medium,,,,,10000 - 20000,bike,0.002479444159819982 +,,medium,,,,,10000 - 20000,car,0.016214590065412083 +,,medium,,,,,10000 - 20000,pt,0.02459414997970121 +,,medium,,,,,10000 - 20000,ride,0.0029223820343375483 +,,medium,,,,,10000 - 20000,walk,0.00015252524487939558 +,,medium,,,,,20000+,bike,0.0002658299540914969 +,,medium,,,,,20000+,car,0.00853933410945757 +,,medium,,,,,20000+,pt,0.009311456990432732 +,,medium,,,,,20000+,ride,0.0015263097558082229 +,,medium,,,,,20000+,walk,7.769899237174301e-05 +,,high,,,,,0 - 1000,bike,0.010503479212315984 +,,high,,,,,0 - 1000,car,0.0023283840087043217 +,,high,,,,,0 - 1000,pt,0.00048199001137843054 +,,high,,,,,0 - 1000,ride,0.0007399243591582074 +,,high,,,,,0 - 1000,walk,0.052626805835954 +,,high,,,,,1000 - 2000,bike,0.011751762392389242 +,,high,,,,,1000 - 2000,car,0.005599747423276816 +,,high,,,,,1000 - 2000,pt,0.004002380035274495 +,,high,,,,,1000 - 2000,ride,0.0024287727294781806 +,,high,,,,,1000 - 2000,walk,0.011595429012370675 +,,high,,,,,2000 - 5000,bike,0.015992335502747403 +,,high,,,,,2000 - 5000,car,0.014185710536307268 +,,high,,,,,2000 - 5000,pt,0.015880762945385242 +,,high,,,,,2000 - 5000,ride,0.005166631012466794 +,,high,,,,,2000 - 5000,walk,0.003261382730332471 +,,high,,,,,5000 - 10000,bike,0.008365498245814847 +,,high,,,,,5000 - 10000,car,0.013829796002619512 +,,high,,,,,5000 - 10000,pt,0.0192382847764826 +,,high,,,,,5000 - 10000,ride,0.0035441935341135452 +,,high,,,,,5000 - 10000,walk,0.000251657439345782 +,,high,,,,,10000 - 20000,bike,0.0023250195687822633 +,,high,,,,,10000 - 20000,car,0.012728031817306105 +,,high,,,,,10000 - 20000,pt,0.015931761652693548 +,,high,,,,,10000 - 20000,ride,0.0024224393712088607 +,,high,,,,,10000 - 20000,walk,5.1736919938093964e-05 +,,high,,,,,20000+,bike,0.00010876241756218597 +,,high,,,,,20000+,car,0.007581742127792979 +,,high,,,,,20000+,pt,0.0063412410097546034 +,,high,,,,,20000+,ride,0.0010977539709170763 +,,high,,,,,20000+,walk,8.188487514146019e-05 +,,very_high,,,,,0 - 1000,bike,0.004215350241941886 +,,very_high,,,,,0 - 1000,car,0.0013030481216012866 +,,very_high,,,,,0 - 1000,pt,0.0002175602824719666 +,,very_high,,,,,0 - 1000,ride,0.0006636684383095902 +,,very_high,,,,,0 - 1000,walk,0.021584529574573557 +,,very_high,,,,,1000 - 2000,bike,0.0050826556248779195 +,,very_high,,,,,1000 - 2000,car,0.0028172537831056537 +,,very_high,,,,,1000 - 2000,pt,0.001335063770176519 +,,very_high,,,,,1000 - 2000,ride,0.0015676474540593606 +,,very_high,,,,,1000 - 2000,walk,0.004565434550870893 +,,very_high,,,,,2000 - 5000,bike,0.005568603197011025 +,,very_high,,,,,2000 - 5000,car,0.006330862461103685 +,,very_high,,,,,2000 - 5000,pt,0.005968136904643643 +,,very_high,,,,,2000 - 5000,ride,0.0028543598282835237 +,,very_high,,,,,2000 - 5000,walk,0.0009068034556890246 +,,very_high,,,,,5000 - 10000,bike,0.002684244854940645 +,,very_high,,,,,5000 - 10000,car,0.006472779243745186 +,,very_high,,,,,5000 - 10000,pt,0.006563928099422097 +,,very_high,,,,,5000 - 10000,ride,0.0017098591330712077 +,,very_high,,,,,5000 - 10000,walk,0.00018185573023583643 +,,very_high,,,,,10000 - 20000,bike,0.0009388161146309195 +,,very_high,,,,,10000 - 20000,car,0.005553565077267616 +,,very_high,,,,,10000 - 20000,pt,0.005788283452782034 +,,very_high,,,,,10000 - 20000,ride,0.001036206743532335 +,,very_high,,,,,10000 - 20000,walk,0.00012133247358305572 +,,very_high,,,,,20000+,bike,0.0001525852381837944 +,,very_high,,,,,20000+,car,0.003114744887819019 +,,very_high,,,,,20000+,pt,0.0027110342313348577 +,,very_high,,,,,20000+,ride,0.0004080763319199827 +,,very_high,,,,,20000+,walk,4.448700616509089e-06 +,,,child,,,,0 - 1000,bike,0.004692866529485639 +,,,child,,,,0 - 1000,car,3.3081677774786484e-06 +,,,child,,,,0 - 1000,pt,0.00013231100373825276 +,,,child,,,,0 - 1000,ride,0.0011997748347578991 +,,,child,,,,0 - 1000,walk,0.01840197515147046 +,,,child,,,,1000 - 2000,bike,0.003961887527723342 +,,,child,,,,1000 - 2000,car,4.606019997009655e-06 +,,,child,,,,1000 - 2000,pt,0.0013201840990155196 +,,,child,,,,1000 - 2000,ride,0.0031581025689356884 +,,,child,,,,1000 - 2000,walk,0.004408459652352422 +,,,child,,,,2000 - 5000,bike,0.0021397845306586616 +,,,child,,,,2000 - 5000,car,0.0 +,,,child,,,,2000 - 5000,pt,0.002554508777092543 +,,,child,,,,2000 - 5000,ride,0.004665859024253609 +,,,child,,,,2000 - 5000,walk,0.0005759960159007431 +,,,child,,,,5000 - 10000,bike,0.00025632080887674384 +,,,child,,,,5000 - 10000,car,0.0 +,,,child,,,,5000 - 10000,pt,0.0013247143312894864 +,,,child,,,,5000 - 10000,ride,0.0023296092882754667 +,,,child,,,,5000 - 10000,walk,6.910684087182668e-05 +,,,child,,,,10000 - 20000,bike,0.0 +,,,child,,,,10000 - 20000,car,0.0 +,,,child,,,,10000 - 20000,pt,0.00033923934533775933 +,,,child,,,,10000 - 20000,ride,0.0012007122956201768 +,,,child,,,,10000 - 20000,walk,2.928850332873569e-05 +,,,child,,,,20000+,bike,0.0 +,,,child,,,,20000+,car,0.0 +,,,child,,,,20000+,pt,0.00019696539216831793 +,,,child,,,,20000+,ride,0.00036551223345497206 +,,,child,,,,20000+,walk,1.2407207687113833e-05 +,,,homemaker,,,,0 - 1000,bike,0.0004125909152603389 +,,,homemaker,,,,0 - 1000,car,0.00021277936985541072 +,,,homemaker,,,,0 - 1000,pt,9.229593173153882e-05 +,,,homemaker,,,,0 - 1000,ride,4.016816717423426e-05 +,,,homemaker,,,,0 - 1000,walk,0.0035907786972401478 +,,,homemaker,,,,1000 - 2000,bike,0.0007368034837866882 +,,,homemaker,,,,1000 - 2000,car,0.000500935705792783 +,,,homemaker,,,,1000 - 2000,pt,0.0003408402984391401 +,,,homemaker,,,,1000 - 2000,ride,7.879225338336706e-05 +,,,homemaker,,,,1000 - 2000,walk,0.0009783322502679476 +,,,homemaker,,,,2000 - 5000,bike,0.0005036590766324672 +,,,homemaker,,,,2000 - 5000,car,0.0011662552263619096 +,,,homemaker,,,,2000 - 5000,pt,0.000752190310465438 +,,,homemaker,,,,2000 - 5000,ride,0.00023219446308315715 +,,,homemaker,,,,2000 - 5000,walk,0.00027790655986467086 +,,,homemaker,,,,5000 - 10000,bike,0.00012533305243492886 +,,,homemaker,,,,5000 - 10000,car,0.0006439551988876109 +,,,homemaker,,,,5000 - 10000,pt,0.0004930148128209032 +,,,homemaker,,,,5000 - 10000,ride,0.0001352589247426541 +,,,homemaker,,,,5000 - 10000,walk,1.3933437314208883e-05 +,,,homemaker,,,,10000 - 20000,bike,1.696408167077197e-05 +,,,homemaker,,,,10000 - 20000,car,0.0004103831410194768 +,,,homemaker,,,,10000 - 20000,pt,0.0002566363687182722 +,,,homemaker,,,,10000 - 20000,ride,0.0001384708892004928 +,,,homemaker,,,,10000 - 20000,walk,4.560943062508568e-06 +,,,homemaker,,,,20000+,bike,0.0 +,,,homemaker,,,,20000+,car,0.00011394892436480222 +,,,homemaker,,,,20000+,pt,0.00015581720908638437 +,,,homemaker,,,,20000+,ride,1.086802538544448e-05 +,,,homemaker,,,,20000+,walk,9.83534852131206e-06 +,,,retiree,,,,0 - 1000,bike,0.005652276047174647 +,,,retiree,,,,0 - 1000,car,0.003214139779060221 +,,,retiree,,,,0 - 1000,pt,0.0017290223064901813 +,,,retiree,,,,0 - 1000,ride,0.000948218998889611 +,,,retiree,,,,0 - 1000,walk,0.057386439186349524 +,,,retiree,,,,1000 - 2000,bike,0.005772858165709162 +,,,retiree,,,,1000 - 2000,car,0.007727904824459909 +,,,retiree,,,,1000 - 2000,pt,0.006643964183294398 +,,,retiree,,,,1000 - 2000,ride,0.002355386288541044 +,,,retiree,,,,1000 - 2000,walk,0.01388936798163634 +,,,retiree,,,,2000 - 5000,bike,0.004865284201097934 +,,,retiree,,,,2000 - 5000,car,0.016741347683255206 +,,,retiree,,,,2000 - 5000,pt,0.013323761508664878 +,,,retiree,,,,2000 - 5000,ride,0.0045800411926804705 +,,,retiree,,,,2000 - 5000,walk,0.004006439218441658 +,,,retiree,,,,5000 - 10000,bike,0.001315313046615281 +,,,retiree,,,,5000 - 10000,car,0.012288208425475344 +,,,retiree,,,,5000 - 10000,pt,0.010687864032158425 +,,,retiree,,,,5000 - 10000,ride,0.0035379605959617406 +,,,retiree,,,,5000 - 10000,walk,0.0004288216837695058 +,,,retiree,,,,10000 - 20000,bike,0.00055462506153584 +,,,retiree,,,,10000 - 20000,car,0.006752719627355881 +,,,retiree,,,,10000 - 20000,pt,0.007151409430753409 +,,,retiree,,,,10000 - 20000,ride,0.002379353808505534 +,,,retiree,,,,10000 - 20000,walk,0.00017252697293602346 +,,,retiree,,,,20000+,bike,4.826864601038086e-05 +,,,retiree,,,,20000+,car,0.003609899643074684 +,,,retiree,,,,20000+,pt,0.0024442786014520143 +,,,retiree,,,,20000+,ride,0.001399376619333296 +,,,retiree,,,,20000+,walk,1.2680859406023705e-05 +,,,unemployed,,,,0 - 1000,bike,0.0005735635409495549 +,,,unemployed,,,,0 - 1000,car,0.00024309159455313252 +,,,unemployed,,,,0 - 1000,pt,0.00013344814107144894 +,,,unemployed,,,,0 - 1000,ride,5.616614877984527e-06 +,,,unemployed,,,,0 - 1000,walk,0.005841992725271934 +,,,unemployed,,,,1000 - 2000,bike,0.001261557681842297 +,,,unemployed,,,,1000 - 2000,car,0.0005820939623851538 +,,,unemployed,,,,1000 - 2000,pt,0.0005358523718174833 +,,,unemployed,,,,1000 - 2000,ride,9.280004953809131e-05 +,,,unemployed,,,,1000 - 2000,walk,0.0012437692375647142 +,,,unemployed,,,,2000 - 5000,bike,0.0009974480251563292 +,,,unemployed,,,,2000 - 5000,car,0.0007992582739544471 +,,,unemployed,,,,2000 - 5000,pt,0.0014954388337952251 +,,,unemployed,,,,2000 - 5000,ride,0.00012600861551444902 +,,,unemployed,,,,2000 - 5000,walk,0.0006020190006907724 +,,,unemployed,,,,5000 - 10000,bike,0.0005106919961364574 +,,,unemployed,,,,5000 - 10000,car,0.0006895081291714719 +,,,unemployed,,,,5000 - 10000,pt,0.0016854518666430459 +,,,unemployed,,,,5000 - 10000,ride,0.00011325369994986607 +,,,unemployed,,,,5000 - 10000,walk,0.0 +,,,unemployed,,,,10000 - 20000,bike,8.406712132483904e-05 +,,,unemployed,,,,10000 - 20000,car,0.00027546191602736937 +,,,unemployed,,,,10000 - 20000,pt,0.001479835498993433 +,,,unemployed,,,,10000 - 20000,ride,0.00012117937428825752 +,,,unemployed,,,,10000 - 20000,walk,6.355971012591362e-05 +,,,unemployed,,,,20000+,bike,0.0 +,,,unemployed,,,,20000+,car,0.00015878151449851233 +,,,unemployed,,,,20000+,pt,0.00040652490765087125 +,,,unemployed,,,,20000+,ride,2.182022954965465e-05 +,,,unemployed,,,,20000+,walk,0.0 +,,,school,,,,0 - 1000,bike,0.006049499262222317 +,,,school,,,,0 - 1000,car,1.8212042323231435e-05 +,,,school,,,,0 - 1000,pt,0.00035302605101887217 +,,,school,,,,0 - 1000,ride,0.0009255429313918094 +,,,school,,,,0 - 1000,walk,0.02308725423827697 +,,,school,,,,1000 - 2000,bike,0.008820567505910154 +,,,school,,,,1000 - 2000,car,6.058872817298557e-05 +,,,school,,,,1000 - 2000,pt,0.0026169897951879937 +,,,school,,,,1000 - 2000,ride,0.0027338004213293032 +,,,school,,,,1000 - 2000,walk,0.0050699524101381855 +,,,school,,,,2000 - 5000,bike,0.00783786206216763 +,,,school,,,,2000 - 5000,car,0.0002235363916981748 +,,,school,,,,2000 - 5000,pt,0.012960773012017854 +,,,school,,,,2000 - 5000,ride,0.005536519541963315 +,,,school,,,,2000 - 5000,walk,0.0006347880777467985 +,,,school,,,,5000 - 10000,bike,0.0009142337436560488 +,,,school,,,,5000 - 10000,car,0.0001639984873911542 +,,,school,,,,5000 - 10000,pt,0.010037942114945454 +,,,school,,,,5000 - 10000,ride,0.003459997783809964 +,,,school,,,,5000 - 10000,walk,3.777761413544874e-05 +,,,school,,,,10000 - 20000,bike,5.8412862520045975e-05 +,,,school,,,,10000 - 20000,car,0.00012899919448776582 +,,,school,,,,10000 - 20000,pt,0.003709530889087389 +,,,school,,,,10000 - 20000,ride,0.0010722642507973706 +,,,school,,,,10000 - 20000,walk,1.7366094470450898e-05 +,,,school,,,,20000+,bike,2.161493431862157e-05 +,,,school,,,,20000+,car,3.758916429970176e-05 +,,,school,,,,20000+,pt,0.0006499783086174258 +,,,school,,,,20000+,ride,0.00039315668844395275 +,,,school,,,,20000+,walk,3.897205610044889e-05 +,,,student,,,,0 - 1000,bike,0.0014222955110270886 +,,,student,,,,0 - 1000,car,0.000263723824025743 +,,,student,,,,0 - 1000,pt,0.00022925297310867214 +,,,student,,,,0 - 1000,ride,9.19875955672688e-05 +,,,student,,,,0 - 1000,walk,0.010307973874572942 +,,,student,,,,1000 - 2000,bike,0.0022564339094027015 +,,,student,,,,1000 - 2000,car,0.000463728212110332 +,,,student,,,,1000 - 2000,pt,0.0013384956804595475 +,,,student,,,,1000 - 2000,ride,0.00016035821281562497 +,,,student,,,,1000 - 2000,walk,0.003294459635506251 +,,,student,,,,2000 - 5000,bike,0.00388312448953792 +,,,student,,,,2000 - 5000,car,0.0011529654277025246 +,,,student,,,,2000 - 5000,pt,0.006771548943256871 +,,,student,,,,2000 - 5000,ride,0.00055246794285061 +,,,student,,,,2000 - 5000,walk,0.0009917076213134363 +,,,student,,,,5000 - 10000,bike,0.002054626919804988 +,,,student,,,,5000 - 10000,car,0.0013792558992513013 +,,,student,,,,5000 - 10000,pt,0.009911578344494618 +,,,student,,,,5000 - 10000,ride,0.0004150711713051344 +,,,student,,,,5000 - 10000,walk,6.451174215808193e-05 +,,,student,,,,10000 - 20000,bike,0.0003891398547186369 +,,,student,,,,10000 - 20000,car,0.0011233726274083645 +,,,student,,,,10000 - 20000,pt,0.007159327283628914 +,,,student,,,,10000 - 20000,ride,0.00035591491894511703 +,,,student,,,,10000 - 20000,walk,2.1220720231769756e-05 +,,,student,,,,20000+,bike,4.084780814030599e-05 +,,,student,,,,20000+,car,0.0008403578981488645 +,,,student,,,,20000+,pt,0.0031346134428065797 +,,,student,,,,20000+,ride,0.0002930796652546498 +,,,student,,,,20000+,walk,0.0 +,,,trainee,,,,0 - 1000,bike,0.0001605695418158462 +,,,trainee,,,,0 - 1000,car,9.359049296552214e-05 +,,,trainee,,,,0 - 1000,pt,2.1375236302883183e-05 +,,,trainee,,,,0 - 1000,ride,3.952943774773782e-05 +,,,trainee,,,,0 - 1000,walk,0.002115925359801069 +,,,trainee,,,,1000 - 2000,bike,0.00022823200148878913 +,,,trainee,,,,1000 - 2000,car,4.039394495269516e-05 +,,,trainee,,,,1000 - 2000,pt,0.00029090873812727034 +,,,trainee,,,,1000 - 2000,ride,5.21455543836348e-05 +,,,trainee,,,,1000 - 2000,walk,0.0005079261899180733 +,,,trainee,,,,2000 - 5000,bike,0.0003343257502448825 +,,,trainee,,,,2000 - 5000,car,0.0004673440660421167 +,,,trainee,,,,2000 - 5000,pt,0.0011490271071859742 +,,,trainee,,,,2000 - 5000,ride,0.0001232743923737029 +,,,trainee,,,,2000 - 5000,walk,5.152141307068502e-05 +,,,trainee,,,,5000 - 10000,bike,0.00017279421429671615 +,,,trainee,,,,5000 - 10000,car,0.0005180294112367801 +,,,trainee,,,,5000 - 10000,pt,0.0019809105546282155 +,,,trainee,,,,5000 - 10000,ride,0.00020199248891230768 +,,,trainee,,,,5000 - 10000,walk,3.5451998359595674e-05 +,,,trainee,,,,10000 - 20000,bike,4.605861494790905e-05 +,,,trainee,,,,10000 - 20000,car,0.0003921446304848578 +,,,trainee,,,,10000 - 20000,pt,0.0023569412630439286 +,,,trainee,,,,10000 - 20000,ride,0.00014410162056371947 +,,,trainee,,,,10000 - 20000,walk,0.0 +,,,trainee,,,,20000+,bike,8.644268399064298e-06 +,,,trainee,,,,20000+,car,0.0002873279812656447 +,,,trainee,,,,20000+,pt,0.0009290302235340364 +,,,trainee,,,,20000+,ride,6.142537028000668e-05 +,,,trainee,,,,20000+,walk,1.2561191363595179e-05 +,,,job_full_time,,,,0 - 1000,bike,0.01086500473217239 +,,,job_full_time,,,,0 - 1000,car,0.0037822424841995987 +,,,job_full_time,,,,0 - 1000,pt,0.0004902268698735909 +,,,job_full_time,,,,0 - 1000,ride,0.0003510166166547789 +,,,job_full_time,,,,0 - 1000,walk,0.0668922563800486 +,,,job_full_time,,,,1000 - 2000,bike,0.013545606671803066 +,,,job_full_time,,,,1000 - 2000,car,0.008775314384230634 +,,,job_full_time,,,,1000 - 2000,pt,0.004371007391038683 +,,,job_full_time,,,,1000 - 2000,ride,0.000916435916012209 +,,,job_full_time,,,,1000 - 2000,walk,0.013385349998172735 +,,,job_full_time,,,,2000 - 5000,bike,0.022853624434314254 +,,,job_full_time,,,,2000 - 5000,car,0.019926358071657317 +,,,job_full_time,,,,2000 - 5000,pt,0.02043190238010268 +,,,job_full_time,,,,2000 - 5000,ride,0.0024791279652634883 +,,,job_full_time,,,,2000 - 5000,walk,0.0038733018975949367 +,,,job_full_time,,,,5000 - 10000,bike,0.01513318742126614 +,,,job_full_time,,,,5000 - 10000,car,0.02710835028311718 +,,,job_full_time,,,,5000 - 10000,pt,0.03169260256159603 +,,,job_full_time,,,,5000 - 10000,ride,0.0024700375546930094 +,,,job_full_time,,,,5000 - 10000,walk,0.0005917630321244554 +,,,job_full_time,,,,10000 - 20000,bike,0.004752892344076316 +,,,job_full_time,,,,10000 - 20000,car,0.025700086755806324 +,,,job_full_time,,,,10000 - 20000,pt,0.029385044880196182 +,,,job_full_time,,,,10000 - 20000,ride,0.0017075200997407904 +,,,job_full_time,,,,10000 - 20000,walk,0.00020140297418339166 +,,,job_full_time,,,,20000+,bike,0.00045025758452577785 +,,,job_full_time,,,,20000+,car,0.01462414171064768 +,,,job_full_time,,,,20000+,pt,0.012691714590245276 +,,,job_full_time,,,,20000+,ride,0.0009468897151677744 +,,,job_full_time,,,,20000+,walk,9.781589130468683e-05 +,,,job_part_time,,,,0 - 1000,bike,0.007970512037215898 +,,,job_part_time,,,,0 - 1000,car,0.0017348529672357208 +,,,job_part_time,,,,0 - 1000,pt,0.00031698431119132626 +,,,job_part_time,,,,0 - 1000,ride,0.00012051413997583627 +,,,job_part_time,,,,0 - 1000,walk,0.028434226993971246 +,,,job_part_time,,,,1000 - 2000,bike,0.00842659833877445 +,,,job_part_time,,,,1000 - 2000,car,0.003605602243449669 +,,,job_part_time,,,,1000 - 2000,pt,0.001882178260432313 +,,,job_part_time,,,,1000 - 2000,ride,0.0005320006410433818 +,,,job_part_time,,,,1000 - 2000,walk,0.00533775098770212 +,,,job_part_time,,,,2000 - 5000,bike,0.010527853898367986 +,,,job_part_time,,,,2000 - 5000,car,0.008614081906706246 +,,,job_part_time,,,,2000 - 5000,pt,0.008264103724331989 +,,,job_part_time,,,,2000 - 5000,ride,0.0011023619749039738 +,,,job_part_time,,,,2000 - 5000,walk,0.0015714917273572395 +,,,job_part_time,,,,5000 - 10000,bike,0.005426736183818503 +,,,job_part_time,,,,5000 - 10000,car,0.0073644988428259615 +,,,job_part_time,,,,5000 - 10000,pt,0.011893870908522557 +,,,job_part_time,,,,5000 - 10000,ride,0.0007686335093330968 +,,,job_part_time,,,,5000 - 10000,walk,0.00023866521363936536 +,,,job_part_time,,,,10000 - 20000,bike,0.0011199450405668426 +,,,job_part_time,,,,10000 - 20000,car,0.005215812695031842 +,,,job_part_time,,,,10000 - 20000,pt,0.010645913173651676 +,,,job_part_time,,,,10000 - 20000,ride,0.0005868314840309652 +,,,job_part_time,,,,10000 - 20000,walk,1.738938781620664e-05 +,,,job_part_time,,,,20000+,bike,7.231108366564436e-05 +,,,job_part_time,,,,20000+,car,0.0020270224453257887 +,,,job_part_time,,,,20000+,pt,0.0035660622013648246 +,,,job_part_time,,,,20000+,ride,0.0002844507851310514 +,,,job_part_time,,,,20000+,walk,1.6131202896261944e-05 +,,,other,,,,0 - 1000,bike,0.002140357629326274 +,,,other,,,,0 - 1000,car,0.0005117453914694895 +,,,other,,,,0 - 1000,pt,5.960335912812815e-05 +,,,other,,,,0 - 1000,ride,7.461256394307926e-05 +,,,other,,,,0 - 1000,walk,0.012751884163014747 +,,,other,,,,1000 - 2000,bike,0.0019728781270249036 +,,,other,,,,1000 - 2000,car,0.0013861241645255675 +,,,other,,,,1000 - 2000,pt,0.0007392877275465098 +,,,other,,,,1000 - 2000,ride,0.00013563750063074829 +,,,other,,,,1000 - 2000,walk,0.004090831113400357 +,,,other,,,,2000 - 5000,bike,0.0023021935343477224 +,,,other,,,,2000 - 5000,car,0.0025500239004360797 +,,,other,,,,2000 - 5000,pt,0.002655218249803366 +,,,other,,,,2000 - 5000,ride,0.0003552563133759286 +,,,other,,,,2000 - 5000,walk,0.0009161101301647727 +,,,other,,,,5000 - 10000,bike,0.0010214596296311722 +,,,other,,,,5000 - 10000,car,0.0019974005812109577 +,,,other,,,,5000 - 10000,pt,0.002739557945706912 +,,,other,,,,5000 - 10000,ride,0.0005394057628512207 +,,,other,,,,5000 - 10000,walk,3.750395823265048e-05 +,,,other,,,,10000 - 20000,bike,0.00011467789134277697 +,,,other,,,,10000 - 20000,car,0.001345478898873757 +,,,other,,,,10000 - 20000,pt,0.0013751153511182815 +,,,other,,,,10000 - 20000,ride,0.00021969343225006888 +,,,other,,,,10000 - 20000,walk,0.0 +,,,other,,,,20000+,bike,0.0 +,,,other,,,,20000+,car,0.0006102330611818056 +,,,other,,,,20000+,pt,0.0005961577514141544 +,,,other,,,,20000+,ride,0.00016791818543542713 +,,,other,,,,20000+,walk,5.213444494079144e-06 +,,,,yes,,,0 - 1000,bike,0.018929755804802963 +,,,,yes,,,0 - 1000,car,0.009768026506833723 +,,,,yes,,,0 - 1000,pt,0.0008301990378947855 +,,,,yes,,,0 - 1000,ride,0.0035401416089951023 +,,,,yes,,,0 - 1000,walk,0.11325481049714482 +,,,,yes,,,1000 - 2000,bike,0.02177562425684126 +,,,,yes,,,1000 - 2000,car,0.02263330163116019 +,,,,yes,,,1000 - 2000,pt,0.006569208805126475 +,,,,yes,,,1000 - 2000,ride,0.009419497489163126 +,,,,yes,,,1000 - 2000,walk,0.02668375499597424 +,,,,yes,,,2000 - 5000,bike,0.023492105348261776 +,,,,yes,,,2000 - 5000,car,0.05054714045725838 +,,,,yes,,,2000 - 5000,pt,0.022446424509950724 +,,,,yes,,,2000 - 5000,ride,0.017737073016303423 +,,,,yes,,,2000 - 5000,walk,0.006491197798211769 +,,,,yes,,,5000 - 10000,bike,0.010910002063736782 +,,,,yes,,,5000 - 10000,car,0.05043701823151539 +,,,,yes,,,5000 - 10000,pt,0.028576100741498255 +,,,,yes,,,5000 - 10000,ride,0.012369479096029538 +,,,,yes,,,5000 - 10000,walk,0.0007546385028848041 +,,,,yes,,,10000 - 20000,bike,0.004131967768658317 +,,,,yes,,,10000 - 20000,car,0.04041677743948118 +,,,,yes,,,10000 - 20000,pt,0.02854040274301633 +,,,,yes,,,10000 - 20000,ride,0.00653150499155608 +,,,,yes,,,10000 - 20000,walk,0.0002836956349746596 +,,,,yes,,,20000+,bike,0.00040115866561361954 +,,,,yes,,,20000+,car,0.021793512183705416 +,,,,yes,,,20000+,pt,0.011100131908396548 +,,,,yes,,,20000+,ride,0.0032033822007237066 +,,,,yes,,,20000+,walk,0.00011053562224261572 +,,,,no,,,0 - 1000,bike,0.004407136642269874 +,,,,no,,,0 - 1000,car,1.0626013166566981e-05 +,,,,no,,,0 - 1000,pt,0.00033032868558137874 +,,,,no,,,0 - 1000,ride,3.142484481937415e-05 +,,,,no,,,0 - 1000,walk,0.02018438237288519 +,,,,no,,,1000 - 2000,bike,0.006130037375169747 +,,,,no,,,1000 - 2000,car,8.127657987569786e-05 +,,,,no,,,1000 - 2000,pt,0.0019386676063516326 +,,,,no,,,1000 - 2000,ride,0.00015440774779759703 +,,,,no,,,1000 - 2000,walk,0.004792205903494368 +,,,,no,,,2000 - 5000,bike,0.006497290066366694 +,,,,no,,,2000 - 5000,car,0.00016493665378920132 +,,,,no,,,2000 - 5000,pt,0.008636943526998841 +,,,,no,,,2000 - 5000,ride,0.00032444913963609754 +,,,,no,,,2000 - 5000,walk,0.0011253234988841323 +,,,,no,,,5000 - 10000,bike,0.0018634548154423556 +,,,,no,,,5000 - 10000,car,0.00030401627611029435 +,,,,no,,,5000 - 10000,pt,0.009890998211336646 +,,,,no,,,5000 - 10000,ride,0.0002846609775431933 +,,,,no,,,5000 - 10000,walk,0.00010270536290327082 +,,,,no,,,10000 - 20000,bike,0.0005083925211017682 +,,,,no,,,10000 - 20000,car,0.00018602034371911237 +,,,,no,,,10000 - 20000,pt,0.007026640766568477 +,,,,no,,,10000 - 20000,ride,0.00017772976295662524 +,,,,no,,,10000 - 20000,walk,1.732569724350586e-05 +,,,,no,,,20000+,bike,1.1366732489071948e-05 +,,,,no,,,20000+,car,8.700924561668775e-05 +,,,,no,,,20000+,pt,0.002739858760973299 +,,,,no,,,20000+,ride,0.00018216425500054305 +,,,,no,,,20000+,walk,2.7957924054035553e-05 +,,,,unknown,,,0 - 1000,bike,0.016602643299577155 +,,,,unknown,,,0 - 1000,car,0.00029903359346525777 +,,,,unknown,,,0 - 1000,pt,0.0023970184601787305 +,,,,unknown,,,0 - 1000,ride,0.00022541544716576307 +,,,,unknown,,,0 - 1000,walk,0.09537151389998763 +,,,,unknown,,,1000 - 2000,bike,0.019077761781454546 +,,,,unknown,,,1000 - 2000,car,0.0004327139790408516 +,,,,unknown,,,1000 - 2000,pt,0.011571832133880753 +,,,,unknown,,,1000 - 2000,ride,0.0006415541696523716 +,,,,unknown,,,1000 - 2000,walk,0.020730238557190535 +,,,,unknown,,,2000 - 5000,bike,0.02625576458789732 +,,,,unknown,,,2000 - 5000,car,0.0009290938367664427 +,,,,unknown,,,2000 - 5000,pt,0.03927510480976725 +,,,,unknown,,,2000 - 5000,ride,0.0016915892703231843 +,,,,unknown,,,2000 - 5000,walk,0.005884760365049813 +,,,,unknown,,,5000 - 10000,bike,0.014157240137357838 +,,,,unknown,,,5000 - 10000,car,0.00141217075094207 +,,,,unknown,,,5000 - 10000,pt,0.04398040851997074 +,,,,unknown,,,5000 - 10000,ride,0.0013170807062617299 +,,,,unknown,,,5000 - 10000,walk,0.000660191654817064 +,,,,unknown,,,10000 - 20000,bike,0.0024964225829438944 +,,,,unknown,,,10000 - 20000,car,0.0007416617032953524 +,,,,unknown,,,10000 - 20000,pt,0.028291949974944445 +,,,,unknown,,,10000 - 20000,ride,0.0012168074194297866 +,,,,unknown,,,10000 - 20000,walk,0.0002262939739368349 +,,,,unknown,,,20000+,bike,0.00022941892695710347 +,,,,unknown,,,20000+,car,0.00042878091348537824 +,,,,unknown,,,20000+,pt,0.010931151958970039 +,,,,unknown,,,20000+,ride,0.0005589510617119798 +,,,,unknown,,,20000+,walk,6.71236554768703e-05 +,,,,,yes,,0 - 1000,bike,0.03526153988314146 +,,,,,yes,,0 - 1000,car,0.007767216636451483 +,,,,,yes,,0 - 1000,pt,0.0015046490351480813 +,,,,,yes,,0 - 1000,ride,0.0019471572578235753 +,,,,,yes,,0 - 1000,walk,0.15483964431577996 +,,,,,yes,,1000 - 2000,bike,0.042569029973730285 +,,,,,yes,,1000 - 2000,car,0.017521684422207355 +,,,,,yes,,1000 - 2000,pt,0.010633913525598832 +,,,,,yes,,1000 - 2000,ride,0.005678228929608481 +,,,,,yes,,1000 - 2000,walk,0.03530808073907148 +,,,,,yes,,2000 - 5000,bike,0.05389478611833032 +,,,,,yes,,2000 - 5000,car,0.04057557797063311 +,,,,,yes,,2000 - 5000,pt,0.046350892222815084 +,,,,,yes,,2000 - 5000,ride,0.012018317393160503 +,,,,,yes,,2000 - 5000,walk,0.009919712152485364 +,,,,,yes,,5000 - 10000,bike,0.02650536373908755 +,,,,,yes,,5000 - 10000,car,0.04142871541416054 +,,,,,yes,,5000 - 10000,pt,0.06128105025341791 +,,,,,yes,,5000 - 10000,ride,0.009353670334295405 +,,,,,yes,,5000 - 10000,walk,0.0010967016132781512 +,,,,,yes,,10000 - 20000,bike,0.00710775947494979 +,,,,,yes,,10000 - 20000,car,0.03277682228670882 +,,,,,yes,,10000 - 20000,pt,0.050013911969984386 +,,,,,yes,,10000 - 20000,ride,0.004969995335343994 +,,,,,yes,,10000 - 20000,walk,0.00026401832076371375 +,,,,,yes,,20000+,bike,0.0006419443250597948 +,,,,,yes,,20000+,car,0.01694731584305633 +,,,,,yes,,20000+,pt,0.01935612289498037 +,,,,,yes,,20000+,ride,0.002736809474265735 +,,,,,yes,,20000+,walk,0.00014853383561765716 +,,,,,no,,0 - 1000,bike,0.00017381551629732777 +,,,,,no,,0 - 1000,car,0.0004927075557295723 +,,,,,no,,0 - 1000,pt,0.0002444389863711156 +,,,,,no,,0 - 1000,ride,0.0002454264159013551 +,,,,,no,,0 - 1000,walk,0.010564171844486419 +,,,,,no,,1000 - 2000,bike,0.0003343615222094387 +,,,,,no,,1000 - 2000,car,0.0011067798630987466 +,,,,,no,,1000 - 2000,pt,0.0014689799752443844 +,,,,,no,,1000 - 2000,ride,0.00037261013579593813 +,,,,,no,,1000 - 2000,walk,0.002669100953126748 +,,,,,no,,2000 - 5000,bike,0.00013525011033367883 +,,,,,no,,2000 - 5000,car,0.0024218933796065823 +,,,,,no,,2000 - 5000,pt,0.004976660770868682 +,,,,,no,,2000 - 5000,ride,0.0008817691326068465 +,,,,,no,,2000 - 5000,walk,0.0006562864007710937 +,,,,,no,,5000 - 10000,bike,7.686754018846836e-05 +,,,,,no,,5000 - 10000,car,0.0024929411197459283 +,,,,,no,,5000 - 10000,pt,0.0049475400840298936 +,,,,,no,,5000 - 10000,ride,0.0008317128185086237 +,,,,,no,,5000 - 10000,walk,4.2448186827222066e-05 +,,,,,no,,10000 - 20000,bike,4.799545110977397e-06 +,,,,,no,,10000 - 20000,car,0.0020980241411930394 +,,,,,no,,10000 - 20000,pt,0.003677482258433107 +,,,,,no,,10000 - 20000,ride,0.0005851372776740185 +,,,,,no,,10000 - 20000,walk,5.053633750356802e-06 +,,,,,no,,20000+,bike,0.0 +,,,,,no,,20000+,car,0.0013520453325610638 +,,,,,no,,20000+,pt,0.0013575191025121322 +,,,,,no,,20000+,ride,0.0003210068566224953 +,,,,,no,,20000+,walk,1.724236554325946e-05 +,,,,,unknown,,0 - 1000,bike,0.004504180347211205 +,,,,,unknown,,0 - 1000,car,0.001817761921284492 +,,,,,unknown,,0 - 1000,pt,0.0018084581621356976 +,,,,,unknown,,0 - 1000,ride,0.0016043982272553096 +,,,,,unknown,,0 - 1000,walk,0.06340689060975126 +,,,,,unknown,,1000 - 2000,bike,0.004080031917525824 +,,,,,unknown,,1000 - 2000,car,0.004518827904770637 +,,,,,unknown,,1000 - 2000,pt,0.007976815044515643 +,,,,,unknown,,1000 - 2000,ride,0.004164620341208673 +,,,,,unknown,,1000 - 2000,walk,0.014229017764460918 +,,,,,unknown,,2000 - 5000,bike,0.0022151237738617905 +,,,,,unknown,,2000 - 5000,car,0.008643699597574335 +,,,,,unknown,,2000 - 5000,pt,0.019030919853033037 +,,,,,unknown,,2000 - 5000,ride,0.006853024900495355 +,,,,,unknown,,2000 - 5000,walk,0.002925283108889256 +,,,,,unknown,,5000 - 10000,bike,0.0003484657372609636 +,,,,,unknown,,5000 - 10000,car,0.008231548724661293 +,,,,,unknown,,5000 - 10000,pt,0.01621891713535783 +,,,,,unknown,,5000 - 10000,ride,0.00378583762703043 +,,,,,unknown,,5000 - 10000,walk,0.0003783857204997658 +,,,,,unknown,,10000 - 20000,bike,2.422385264321117e-05 +,,,,,unknown,,10000 - 20000,car,0.006469613058593776 +,,,,,unknown,,10000 - 20000,pt,0.010167599256111758 +,,,,,unknown,,10000 - 20000,ride,0.0023709095609244788 +,,,,,unknown,,10000 - 20000,walk,0.0002582433516409299 +,,,,,unknown,,20000+,bike,0.0 +,,,,,unknown,,20000+,car,0.004009941167190085 +,,,,,unknown,,20000+,pt,0.004057500630847384 +,,,,,unknown,,20000+,ride,0.0008866811865479991 +,,,,,unknown,,20000+,walk,3.984100061260495e-05 +,,,,,,yes,0 - 1000,bike,0.013321700888520232 +,,,,,,yes,0 - 1000,car,0.0022847742089234686 +,,,,,,yes,0 - 1000,pt,0.0027812474751549177 +,,,,,,yes,0 - 1000,ride,0.0009960436060376069 +,,,,,,yes,0 - 1000,walk,0.10757698281908094 +,,,,,,yes,1000 - 2000,bike,0.0155364133579487 +,,,,,,yes,1000 - 2000,car,0.00589129390102087 +,,,,,,yes,1000 - 2000,pt,0.01620246662898983 +,,,,,,yes,1000 - 2000,ride,0.0024216475162077127 +,,,,,,yes,1000 - 2000,walk,0.024304962641621592 +,,,,,,yes,2000 - 5000,bike,0.02057687018839181 +,,,,,,yes,2000 - 5000,car,0.01252170273825969 +,,,,,,yes,2000 - 5000,pt,0.05862793584566103 +,,,,,,yes,2000 - 5000,ride,0.00660171094035656 +,,,,,,yes,2000 - 5000,walk,0.006267391125244222 +,,,,,,yes,5000 - 10000,bike,0.009917615537323106 +,,,,,,yes,5000 - 10000,car,0.012371713669786753 +,,,,,,yes,5000 - 10000,pt,0.07127683356040514 +,,,,,,yes,5000 - 10000,ride,0.004891555156239355 +,,,,,,yes,5000 - 10000,walk,0.0007928088722503891 +,,,,,,yes,10000 - 20000,bike,0.0025545207017572237 +,,,,,,yes,10000 - 20000,car,0.009172860693407035 +,,,,,,yes,10000 - 20000,pt,0.056610722542005915 +,,,,,,yes,10000 - 20000,ride,0.0031824095717129506 +,,,,,,yes,10000 - 20000,walk,0.00025106742703714696 +,,,,,,yes,20000+,bike,0.00022320068190049555 +,,,,,,yes,20000+,car,0.0056370341062884285 +,,,,,,yes,20000+,pt,0.02110823659629495 +,,,,,,yes,20000+,ride,0.001612270200139276 +,,,,,,yes,20000+,walk,0.0001247575354904969 +,,,,,,no,0 - 1000,bike,0.022306953556190626 +,,,,,,no,0 - 1000,car,0.0077896037367646 +,,,,,,no,0 - 1000,pt,0.0006653518212564869 +,,,,,,no,0 - 1000,ride,0.0016950254315340235 +,,,,,,no,0 - 1000,walk,0.10352062303505469 +,,,,,,no,1000 - 2000,bike,0.02764009841393322 +,,,,,,no,1000 - 2000,car,0.017239871242866762 +,,,,,,no,1000 - 2000,pt,0.002638470488856684 +,,,,,,no,1000 - 2000,ride,0.00486316902167795 +,,,,,,no,1000 - 2000,walk,0.023565719254479672 +,,,,,,no,2000 - 5000,bike,0.033648315815233934 +,,,,,,no,2000 - 5000,car,0.03911946820955432 +,,,,,,no,2000 - 5000,pt,0.009276170589732876 +,,,,,,no,2000 - 5000,ride,0.00874865044210388 +,,,,,,no,2000 - 5000,walk,0.006657894521000746 +,,,,,,no,5000 - 10000,bike,0.016767638374136376 +,,,,,,no,5000 - 10000,car,0.039758964228332985 +,,,,,,no,5000 - 10000,pt,0.009910652884025836 +,,,,,,no,5000 - 10000,ride,0.0068449883427132464 +,,,,,,no,5000 - 10000,walk,0.0006556198074829231 +,,,,,,no,10000 - 20000,bike,0.004582262170946754 +,,,,,,no,10000 - 20000,car,0.03212840663363432 +,,,,,,no,10000 - 20000,pt,0.006911322381026511 +,,,,,,no,10000 - 20000,ride,0.0035654776077440986 +,,,,,,no,10000 - 20000,walk,0.00024695937578911764 +,,,,,,no,20000+,bike,0.0004187436431592994 +,,,,,,no,20000+,car,0.01667226823651905 +,,,,,,no,20000+,pt,0.0034659406398766138 +,,,,,,no,20000+,ride,0.0019853858511924448 +,,,,,,no,20000+,walk,6.845245859591084e-05 +,,,,,,unknown,0 - 1000,bike,0.004310881301939126 +,,,,,,unknown,0 - 1000,car,3.308167777478648e-06 +,,,,,,unknown,0 - 1000,pt,0.00011094688724348958 +,,,,,,unknown,0 - 1000,ride,0.0011059128634086088 +,,,,,,unknown,0 - 1000,walk,0.017713100915881987 +,,,,,,unknown,1000 - 2000,bike,0.003806911641583625 +,,,,,,unknown,1000 - 2000,car,1.612704618910499e-05 +,,,,,,unknown,1000 - 2000,pt,0.0012387714275123413 +,,,,,,unknown,1000 - 2000,ride,0.002930642868727429 +,,,,,,unknown,1000 - 2000,walk,0.004335517560557874 +,,,,,,unknown,2000 - 5000,bike,0.00201997399890004 +,,,,,,unknown,2000 - 5000,car,0.0 +,,,,,,unknown,2000 - 5000,pt,0.002454366411322895 +,,,,,,unknown,2000 - 5000,ride,0.004402750043802261 +,,,,,,unknown,2000 - 5000,walk,0.0005759960159007431 +,,,,,,unknown,5000 - 10000,bike,0.00024544310507749166 +,,,,,,unknown,5000 - 10000,car,2.252736044801564e-05 +,,,,,,unknown,5000 - 10000,pt,0.0012600210283746445 +,,,,,,unknown,5000 - 10000,ride,0.002234677280881857 +,,,,,,unknown,5000 - 10000,walk,6.910684087182666e-05 +,,,,,,unknown,10000 - 20000,bike,0.0 +,,,,,,unknown,10000 - 20000,car,4.319215945427999e-05 +,,,,,,unknown,10000 - 20000,pt,0.0003369485614968173 +,,,,,,unknown,10000 - 20000,ride,0.0011781549944854426 +,,,,,,unknown,10000 - 20000,walk,2.9288503328735686e-05 +,,,,,,unknown,20000+,bike,0.0 +,,,,,,unknown,20000+,car,0.0 +,,,,,,unknown,20000+,pt,0.0001969653921683179 +,,,,,,unknown,20000+,ride,0.00034684146610450836 +,,,,,,unknown,20000+,walk,1.2407207687113833e-05 diff --git a/src/main/sh/runEval.sh b/src/main/sh/runEval.sh new file mode 100755 index 000000000..b8eef7d3d --- /dev/null +++ b/src/main/sh/runEval.sh @@ -0,0 +1,38 @@ +#!/bin/bash --login +#SBATCH --time=100:00:00 +#SBATCH --output=logfile_%x-%j.log +#SBATCH --partition=smp +#SBATCH --nodes=1 +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=16 +#SBATCH --mem=32G +#SBATCH --job-name=eval6.3-berlin +#SBATCH --constraint=intel +#SBATCH --mail-type=END,FAIL + +date +hostname + +echo "Task Id: $SLURM_ARRAY_TASK_ID" +echo "Task Count: $SLURM_ARRAY_TASK_COUNT" + +JAR=matsim-berlin-6.3-SNAPSHOT-v6.0-158-gcdbb04f.jar +CONFIG="../input/v6.3/berlin-v6.3.config.xml" +ARGS="--3pct --iterations 200 --yaml params/run10.yaml --config:plans.inputPlansFile=/net/ils/matsim-berlin/calibration-v6.3/mode-choice-3pct-default-v1/runs/008/008.output_plans.xml.gz" +JVM_ARGS="-Xmx30G -Xms30G -XX:+AlwaysPreTouch -XX:+UseParallelGC" +RUNS=12 + +# Start job as array with --array=0-5 +idx=$SLURM_ARRAY_TASK_ID +total=$SLURM_ARRAY_TASK_COUNT + +source env/bin/activate + +module add java/21 +java -version + +# Runs simulation multiple times (simultaneously) with different random seeds + +python -u -m matsim.calibration run-simulations\ + --jar $JAR --config $CONFIG --args "$ARGS" --jvm-args "$JVM_ARGS" --runs $RUNS\ + --worker-id $idx --workers $total diff --git a/src/main/sh/setup.sh b/src/main/sh/setup.sh index a28e23684..8a161adbb 100644 --- a/src/main/sh/setup.sh +++ b/src/main/sh/setup.sh @@ -5,5 +5,5 @@ source env/bin/activate pip install --upgrade pip -#pip install "matsim-tools[calibration]==0.0.16" +#pip install "matsim-tools[calibration]==0.0.19" pip install --force-reinstall "matsim-tools[calibration] @ git+https://github.com/matsim-vsp/matsim-python-tools.git@dist-calibration" \ No newline at end of file