diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/population/Category.java b/contribs/application/src/main/java/org/matsim/application/analysis/population/Category.java new file mode 100644 index 00000000000..67194e881b4 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/population/Category.java @@ -0,0 +1,199 @@ +package org.matsim.application.analysis.population; + +import org.matsim.core.config.ReflectiveConfigGroup; +import org.matsim.utils.objectattributes.attributable.Attributes; + +import java.util.*; +import java.util.regex.Pattern; + +/** + * Helper class to 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/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java index 4c6da13627c..4da59a48294 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java @@ -27,29 +27,49 @@ import tech.tablesaw.joining.DataFrameJoiner; import tech.tablesaw.selection.Selection; -import java.io.*; +import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import java.nio.file.Files; import java.util.*; -import java.util.zip.GZIPInputStream; import static tech.tablesaw.aggregate.AggregateFunctions.count; @CommandLine.Command(name = "trips", description = "Calculates various trip related metrics.") @CommandSpec( requires = {"trips.csv", "persons.csv"}, - produces = {"mode_share.csv", "mode_share_per_dist.csv", "mode_users.csv", "trip_stats.csv", "population_trip_stats.csv", "trip_purposes_by_hour.csv"} + produces = { + "mode_share.csv", "mode_share_per_dist.csv", "mode_users.csv", "trip_stats.csv", + "mode_share_per_%s.csv", "population_trip_stats.csv", "trip_purposes_by_hour.csv", + "mode_choices.csv", "mode_choice_evaluation.csv", "mode_choice_evaluation_per_mode.csv", + "mode_confusion_matrix.csv", "mode_prediction_error.csv" + } ) public class TripAnalysis implements MATSimAppCommand { private static final Logger log = LogManager.getLogger(TripAnalysis.class); + /** + * Attributes which relates this person to a reference person. + */ + public static String ATTR_REF_ID = "ref_id"; + /** + * Person attribute that contains the reference modes of a person. Multiple modes are delimited by "-". + */ + public static String ATTR_REF_MODES = "ref_modes"; + /** + * Person attribute containing its weight for analysis purposes. + */ + public static String ATTR_REF_WEIGHT = "ref_weight"; + @CommandLine.Mixin private InputOptions input = InputOptions.ofCommand(TripAnalysis.class); @CommandLine.Mixin private OutputOptions output = OutputOptions.ofCommand(TripAnalysis.class); + @CommandLine.Option(names = "--input-ref-data", description = "Optional path to reference data", required = false) + private String refData; + @CommandLine.Option(names = "--match-id", description = "Pattern to filter agents by id") private String matchId; @@ -95,7 +115,7 @@ public Integer call() throws Exception { Table persons = Table.read().csv(CsvReadOptions.builder(IOUtils.getBufferedReader(input.getPath("persons.csv"))) .columnTypesPartial(Map.of("person", ColumnType.TEXT)) .sample(false) - .separator(new CsvOptions().detectDelimiter(input.getPath("persons.csv"))).build()); + .separator(CsvOptions.detectDelimiter(input.getPath("persons.csv"))).build()); int total = persons.rowCount(); @@ -132,6 +152,7 @@ public Integer call() throws Exception { // Map.of only has 10 argument max columnTypes.put("traveled_distance", ColumnType.LONG); + columnTypes.put("euclidean_distance", ColumnType.LONG); Table trips = Table.read().csv(CsvReadOptions.builder(IOUtils.getBufferedReader(input.getPath("trips.csv"))) .columnTypesPartial(columnTypes) @@ -172,6 +193,12 @@ public Integer call() throws Exception { trips = trips.where(Selection.with(idx.toIntArray())); } + TripByGroupAnalysis groups = null; + if (refData != null) { + groups = new TripByGroupAnalysis(refData); + groups.groupPersons(persons); + } + // Use longest_distance_mode where main_mode is not present trips.stringColumn("main_mode") .set(trips.stringColumn("main_mode").isMissing(), @@ -196,6 +223,24 @@ public Integer call() throws Exception { writeModeShare(joined, labels); + if (groups != null) { + groups.analyzeModeShare(joined, labels, modeOrder, (g) -> output.getPath("mode_share_per_%s.csv", g)); + } + + if (persons.containsColumn(ATTR_REF_MODES)) { + try { + TripChoiceAnalysis choices = new TripChoiceAnalysis(persons, trips, modeOrder); + + choices.writeChoices(output.getPath("mode_choices.csv")); + choices.writeChoiceEvaluation(output.getPath("mode_choice_evaluation.csv")); + choices.writeChoiceEvaluationPerMode(output.getPath("mode_choice_evaluation_per_mode.csv")); + choices.writeConfusionMatrix(output.getPath("mode_confusion_matrix.csv")); + choices.writeModePredictionError(output.getPath("mode_prediction_error.csv")); + } catch (RuntimeException e) { + log.error("Error while analyzing mode choices", e); + } + } + writePopulationStats(persons, joined); writeTripStats(joined); @@ -345,7 +390,6 @@ private void writePopulationStats(Table persons, Table trips) throws IOException table.write().csv(output.getPath("mode_users.csv").toFile()); try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(output.getPath("population_trip_stats.csv")), CSVFormat.DEFAULT)) { - printer.printRecord("Info", "Value"); printer.printRecord("Persons", tripsPerPerson.size()); printer.printRecord("Mobile persons [%]", new BigDecimal(100 * totalMobile / tripsPerPerson.size()).setScale(2, RoundingMode.HALF_UP)); @@ -386,8 +430,7 @@ private void writeTripPurposes(Table trips) { TextColumn purpose = trips.textColumn("end_activity_type"); // Remove suffix durations like _345 - Selection withDuration = purpose.matchesRegex("^.+_[0-9]+$"); - purpose.set(withDuration, purpose.where(withDuration).replaceAll("_[0-9]+$", "")); + purpose.set(Selection.withRange(0, purpose.size()), purpose.replaceAll("_[0-9]{2,}$", "")); Table tArrival = trips.summarize("trip_id", count).by("end_activity_type", "arrival_h"); diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/population/TripByGroupAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripByGroupAnalysis.java new file mode 100644 index 00000000000..47edfec0824 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripByGroupAnalysis.java @@ -0,0 +1,157 @@ +package org.matsim.application.analysis.population; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.core.utils.io.IOUtils; +import tech.tablesaw.api.*; +import tech.tablesaw.columns.Column; +import tech.tablesaw.io.csv.CsvReadOptions; +import tech.tablesaw.joining.DataFrameJoiner; +import tech.tablesaw.selection.Selection; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Function; + +import static tech.tablesaw.aggregate.AggregateFunctions.count; + +/** + * Helper class to analyze trips by groups. + * This class can not be used on its own, but will be called by {@link TripAnalysis}. + */ +final class TripByGroupAnalysis { + + private final static Logger log = LogManager.getLogger(TripByGroupAnalysis.class); + + /** + * Contains detected groups and their reference data. + */ + private final List groups; + + private final Map categories; + + TripByGroupAnalysis(String refData) throws IOException { + + try (BufferedReader reader = IOUtils.getBufferedReader(refData)) { + Table ref = Table.read().csv(CsvReadOptions.builder(reader) + .columnTypes((column) -> column.equals("share") ? ColumnType.DOUBLE : ColumnType.STRING) + .sample(false) + .build()); + + List columns = new ArrayList<>(ref.columnNames()); + // remove non group columns + columns.removeAll(Set.of("dist_group", "main_mode", "share")); + + // Collect all contained groups + Set> groups = new HashSet<>(); + for (Row row : ref) { + + List g = new ArrayList<>(); + for (String c : columns) { + if (!row.getString(c).isEmpty()) + g.add(c); + } + if (!g.isEmpty()) + groups.add(g); + } + + log.info("Detected groups: {}", groups); + + this.groups = new ArrayList<>(); + + for (List g : groups) { + + Selection sel = Selection.withRange(0, ref.rowCount()); + for (String c : g) { + sel = sel.and(ref.stringColumn(c).isNotEqualTo("")); + } + + Table gRef = ref.where(sel); + this.groups.add(new Group(g, gRef)); + } + + this.categories = new HashMap<>(); + for (List group : groups) { + for (String g : group) { + if (!this.categories.containsKey(g)) { + this.categories.put(g, new Category(ref.column(g).asStringColumn().removeMissing().asSet())); + } + } + } + + } + } + + void analyzeModeShare(Table trips, List dists, List modeOrder, Function output) { + + for (Group group : groups) { + + List columns = new ArrayList<>(List.of("dist_group", "main_mode")); + columns.addAll(group.columns); + + String[] join = columns.toArray(new String[0]); + + Table aggr = trips.summarize("trip_id", count).by(join); + + int idx = aggr.columnCount() - 1; + DoubleColumn share = aggr.numberColumn(idx).divide(aggr.numberColumn(idx).sum()).setName("sim_share"); + aggr.replaceColumn(idx, share); + + // Sort by dist_group and mode + Comparator cmp = Comparator.comparingInt(row -> dists.indexOf(row.getString("dist_group"))); + aggr = aggr.sortOn(cmp.thenComparingInt(row -> modeOrder.indexOf(row.getString("main_mode")))); + + // Norm each group to 1 + String norm = group.columns.get(0); + if (group.columns.size() > 1) + throw new UnsupportedOperationException("Multiple columns not supported yet"); + + for (String label : aggr.stringColumn(norm).asSet()) { + DoubleColumn dist_group = aggr.doubleColumn("sim_share"); + Selection sel = aggr.stringColumn(norm).isEqualTo(label); + + double total = dist_group.where(sel).sum(); + if (total > 0) + dist_group.set(sel, dist_group.divide(total)); + } + + Table joined = new DataFrameJoiner(group.data, join).leftOuter(aggr); + joined.column("share").setName("ref_share"); + + joined.removeColumns( + joined.columnNames().stream() + .filter(c -> !columns.contains(c) && !c.equals("sim_share") && !c.equals("ref_share")) + .toArray(String[]::new) + ); + + String name = String.join("_", group.columns); + joined.write().csv(output.apply(name).toFile()); + } + } + + void groupPersons(Table persons) { + + for (Group g : groups) { + for (String c : g.columns) { + + if (!persons.columnNames().contains(c)) { + log.error("Column {} not found in persons table", c); + persons.addColumns(StringColumn.create(c, persons.rowCount())); + continue; + } + + Column column = persons.column(c); + + StringColumn copy = column.emptyCopy(column.size()).asStringColumn().setName(c); + column.mapInto((Object value) -> categories.get(c).categorize(value), copy); + persons.replaceColumn(c, copy); + } + } + } + + private record Group(List columns, Table data) { + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/population/TripChoiceAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripChoiceAnalysis.java new file mode 100644 index 00000000000..529798d09b6 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripChoiceAnalysis.java @@ -0,0 +1,289 @@ +package org.matsim.application.analysis.population; + +import it.unimi.dsi.fastutil.doubles.DoubleArrayList; +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.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import tech.tablesaw.api.Row; +import tech.tablesaw.api.Table; +import tech.tablesaw.joining.DataFrameJoiner; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.IntStream; + +/** + * Helper class to analyze trip choices from persons against reference data. + * Metrics for binary classification + * Evaluation multi-class classifiers + * more + * more + * ... + */ +final class TripChoiceAnalysis { + + private static final Logger log = LogManager.getLogger(TripChoiceAnalysis.class); + + private final List modeOrder; + + /** + * Contains trip data with true and predicated (simulated) modes. + */ + private final List data = new ArrayList<>(); + + /** + * Contains predication result for each mode. + */ + private final Map counts = new HashMap<>(); + private final Object2DoubleMap pairs = new Object2DoubleOpenHashMap<>(); + + /** + * Contains confusion matrix for each mode. + */ + private final List confusionMatrix = new ArrayList<>(); + + public TripChoiceAnalysis(Table persons, Table trips, List modeOrder) { + persons = persons.where(persons.stringColumn("ref_modes").isNotEqualTo("")); + trips = new DataFrameJoiner(trips, "person").inner(persons); + this.modeOrder = modeOrder; + + log.info("Analyzing mode choices for {} persons", persons.rowCount()); + + boolean hasWeight = persons.containsColumn(TripAnalysis.ATTR_REF_WEIGHT); + + for (Row trip : trips) { + + String person = trip.getText("person"); + int n = trip.getInt("trip_number") - 1; + double weight = hasWeight ? trip.getDouble(TripAnalysis.ATTR_REF_WEIGHT) : 1; + + String predMode = trip.getString("main_mode"); + String[] split = trip.getString(TripAnalysis.ATTR_REF_MODES).split("-"); + + if (n < split.length) { + String trueMode = split[n]; + data.add(new Entry(trip.getObject(TripAnalysis.ATTR_REF_ID), + person, weight, n, trip.getLong("euclidean_distance"), trueMode, predMode)); + } else + log.warn("Person {} trip {} does not match ref data ({})", person, n, split.length); + } + + for (Entry e : data) { + pairs.mergeDouble(new Pair(e.trueMode(), e.predMode()), e.weight(), Double::sum); + } + + for (String mode : modeOrder) { + counts.put(mode, countPredictions(mode, data)); + DoubleArrayList preds = new DoubleArrayList(); + + for (String predMode : modeOrder) { + double c = pairs.getDouble(new Pair(mode, predMode)); + preds.add(c); + } + + confusionMatrix.add(preds); + } + } + + private static double round(double d) { + if (Double.isNaN(d)) + return Double.NaN; + + return BigDecimal.valueOf(d).setScale(5, RoundingMode.HALF_UP).doubleValue(); + } + + private static double precision(Counts c) { + return c.tp / (c.tp + c.fp); + } + + private static double recall(Counts c) { + return c.tp / (c.tp + c.fn); + } + + private static double f1(Counts c) { + return 2 * c.tp / (2 * c.tp + c.fp + c.fn); + } + + /** + * Implemented as in sklearn.metrics.cohen_kappa_score. + * See Wikipedia + * + * @param cm confusion matrix + */ + static double computeCohenKappa(List cm) { + DoubleList sumRows = cm.stream().mapToDouble(l -> l.doubleStream().sum()).collect(DoubleArrayList::new, DoubleList::add, DoubleList::addAll); + DoubleList sumCols = IntStream.range(0, cm.size()).mapToDouble(i -> cm.stream().mapToDouble(l -> l.getDouble(i)).sum()).collect(DoubleArrayList::new, DoubleList::add, DoubleList::addAll); + double sumTotal = sumRows.doubleStream().sum(); + double expected = 0; + + for (int i = 0; i < cm.size(); i++) { + for (int j = 0; j < cm.size(); j++) { + if (i != j) + expected += sumRows.getDouble(i) * sumCols.getDouble(j) / sumTotal; + } + } + + double k = 0; + for (int i = 0; i < cm.size(); i++) { + for (int j = 0; j < cm.size(); j++) { + if (i != j) + k += cm.get(i).getDouble(j) / expected; + } + } + return 1 - k; + } + + private Counts countPredictions(String mode, List data) { + double tp = 0, fp = 0, fn = 0, tn = 0; + double total = 0; + for (Entry e : data) { + if (e.trueMode.equals(mode)) { + if (e.predMode.equals(mode)) + tp += e.weight; + else + fn += e.weight; + } else { + if (e.predMode.equals(mode)) + fp += e.weight; + else + tn += e.weight; + } + total += e.weight; + } + + return new Counts(tp, fp, fn, tn, total); + } + + /** + * Writes all choices to csv. + */ + public void writeChoices(Path path) throws IOException { + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(path), CSVFormat.DEFAULT)) { + csv.printRecord("ref_id", "person", "weight", "n", "euclidean_distance", "true_mode", "pred_mode"); + for (Entry e : data) { + csv.printRecord(e.refId, e.person, e.weight, e.n, e.dist, e.trueMode, e.predMode); + } + } + } + + /** + * Writes aggregated choices metrics. + */ + public void writeChoiceEvaluation(Path path) throws IOException { + + double tp = 0; + double total = 0; + double tpfp = 0; + double tpfn = 0; + for (Counts c : counts.values()) { + tp += c.tp; + tpfp += c.tp + c.fp; + tpfn += c.tp + c.fn; + total = c.total; + } + + OptionalDouble precision = counts.values().stream().mapToDouble(TripChoiceAnalysis::precision).filter(Double::isFinite).average(); + OptionalDouble recall = counts.values().stream().mapToDouble(TripChoiceAnalysis::recall).filter(Double::isFinite).average(); + OptionalDouble f1 = counts.values().stream().mapToDouble(TripChoiceAnalysis::f1).filter(Double::isFinite).average(); + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(path), CSVFormat.DEFAULT)) { + + csv.printRecord("Info", "Value"); + + csv.printRecord("Accuracy", round(tp / total)); + csv.printRecord("Precision (macro avg.)", round(precision.orElse(0))); + csv.printRecord("Recall (macro avg.)", round(recall.orElse(0))); + csv.printRecord("F1 Score (macro avg.)", round(f1.orElse(0))); + csv.printRecord("Cohen’s Kappa", round(computeCohenKappa(confusionMatrix))); + } + } + + /** + * Writes metrics per mode. + */ + public void writeChoiceEvaluationPerMode(Path path) throws IOException { + + // Precision in multi-class classification is the fraction of instances correctly classified as belonging to a specific class out of all instances the model predicted to belong to that class. + + // Recall in multi-class classification is the fraction of instances in a class that the model correctly classified out of all instances in that class. + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(path), CSVFormat.DEFAULT)) { + + csv.printRecord("Mode", "Precision", "Recall", "F1 Score"); + for (String m : modeOrder) { + csv.print(m); + + Counts c = counts.get(m); + + csv.print(round(precision(c))); + csv.print(round(recall(c))); + csv.print(round(f1(c))); + csv.println(); + } + } + } + + /** + * Write confusion matrix. This normalizes per row. + */ + public void writeConfusionMatrix(Path path) throws IOException { + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(path), CSVFormat.DEFAULT)) { + csv.print("True/Pred"); + for (String s : modeOrder) { + csv.print(s); + } + csv.println(); + + for (int i = 0; i < modeOrder.size(); i++) { + csv.print(modeOrder.get(i)); + DoubleList row = confusionMatrix.get(i); + double sum = row.doubleStream().sum(); + for (int j = 0; j < row.size(); j++) { + csv.print(row.getDouble(j) / sum); + } + csv.println(); + } + } + } + + public void writeModePredictionError(Path path) throws IOException { + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(path), CSVFormat.DEFAULT)) { + csv.printRecord("true_mode", "predicted_mode", "count"); + for (String trueMode : modeOrder) { + for (String predMode : modeOrder) { + double c = pairs.getDouble(new Pair(trueMode, predMode)); + if (c > 0) + csv.printRecord(trueMode, predMode, c); + } + } + } + } + + record Entry(Object refId, String person, double weight, int n, long dist, String trueMode, String predMode) { + } + + record Pair(String trueMode, String predMode) { + } + + /** + * Contains true positive, false positive, false negative and true negative counts. + * + * @param tp correctly predicted this class + * @param fp incorrectly predicted this class + * @param fn incorrectly predicted different class + * @param tn correctly predicated different class + */ + record Counts(double tp, double fp, double fn, double tn, double total) { + + } + +} diff --git a/contribs/application/src/test/java/org/matsim/application/analysis/population/CategoryTest.java b/contribs/application/src/test/java/org/matsim/application/analysis/population/CategoryTest.java new file mode 100644 index 00000000000..7395864e575 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/analysis/population/CategoryTest.java @@ -0,0 +1,65 @@ +package org.matsim.application.analysis.population; + +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class CategoryTest { + + @Test + void standard() { + + Category c = new Category(Set.of("a", "b", "c")); + + assertThat(c.categorize("a")).isEqualTo("a"); + assertThat(c.categorize("b")).isEqualTo("b"); + assertThat(c.categorize("c")).isEqualTo("c"); + assertThat(c.categorize("d")).isNull(); + + } + + @Test + void ranges() { + + Category c = new Category(Set.of("1-2", "2-4", "4+")); + + assertThat(c.categorize("1")).isEqualTo("1-2"); + assertThat(c.categorize(1)).isEqualTo("1-2"); + assertThat(c.categorize(1.0)).isEqualTo("1-2"); + + assertThat(c.categorize("2")).isEqualTo("2-4"); + assertThat(c.categorize("3")).isEqualTo("2-4"); + assertThat(c.categorize("5")).isEqualTo("4+"); + assertThat(c.categorize(5)).isEqualTo("4+"); + assertThat(c.categorize(5.0)).isEqualTo("4+"); + + } + + @Test + void grouped() { + + Category c = new Category(Set.of("a,b", "101,102")); + + assertThat(c.categorize("a")).isEqualTo("a,b"); + assertThat(c.categorize("b")).isEqualTo("a,b"); + assertThat(c.categorize(101)).isEqualTo("101,102"); + assertThat(c.categorize(102)).isEqualTo("101,102"); + + } + + @Test + void bool() { + + Category c = new Category(Set.of("y", "n")); + + assertThat(c.categorize("y")).isEqualTo("y"); + assertThat(c.categorize("yes")).isEqualTo("y"); + assertThat(c.categorize("1")).isEqualTo("y"); + + assertThat(c.categorize(true)).isEqualTo("y"); + assertThat(c.categorize(false)).isEqualTo("n"); + + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/analysis/population/TripChoiceAnalysisTest.java b/contribs/application/src/test/java/org/matsim/application/analysis/population/TripChoiceAnalysisTest.java new file mode 100644 index 00000000000..bbc75c738a6 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/analysis/population/TripChoiceAnalysisTest.java @@ -0,0 +1,87 @@ +package org.matsim.application.analysis.population; + +import it.unimi.dsi.fastutil.doubles.DoubleArrayList; +import it.unimi.dsi.fastutil.doubles.DoubleList; +import it.unimi.dsi.fastutil.objects.Object2DoubleMap; +import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap; +import org.assertj.core.data.Offset; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class TripChoiceAnalysisTest { + + /** + * Create confusion matrix. + */ + public static List cm(String... entries) { + List rows = new ArrayList<>(); + List distinct = Arrays.stream(entries).distinct().toList(); + Object2DoubleMap pairs = new Object2DoubleOpenHashMap<>(); + + for (int i = 0; i < entries.length; i += 2) { + TripChoiceAnalysis.Pair pair = new TripChoiceAnalysis.Pair(entries[i], entries[i + 1]); + pairs.mergeDouble(pair, 1, Double::sum); + } + + for (String d1 : distinct) { + DoubleArrayList row = new DoubleArrayList(); + for (String d2 : distinct) { + row.add(pairs.getDouble(new TripChoiceAnalysis.Pair(d1, d2))); + } + rows.add(row); + } + + return rows; + } + + @Test + void cohenKappa() { + + double ck = TripChoiceAnalysis.computeCohenKappa(List.of()); + assertThat(ck).isEqualTo(1); + + ck = TripChoiceAnalysis.computeCohenKappa(cm( + "a", "a", + "b", "b", + "b", "b", + "c", "c") + ); + + assertThat(ck).isEqualTo(1.0); + ck = TripChoiceAnalysis.computeCohenKappa(cm( + "a", "c", + "d", "e", + "a", "b", + "b", "d" + )); + + assertThat(ck).isLessThan(0.0); + + // These have been verified with sklearn + ck = TripChoiceAnalysis.computeCohenKappa(cm( + "negative", "negative", + "positive", "positive", + "negative", "negative", + "neutral", "neutral", + "positive", "negative" + )); + + assertThat(ck).isEqualTo(0.6875); + + ck = TripChoiceAnalysis.computeCohenKappa(cm( + "negative", "positive", + "positive", "neutral", + "negative", "negative", + "neutral", "neutral", + "positive", "negative" + )); + + assertThat(ck).isEqualTo( 0.11764705882352955, Offset.offset(1e-5)); + + } +} diff --git a/contribs/simwrapper/src/main/java/org/matsim/simwrapper/dashboard/TripDashboard.java b/contribs/simwrapper/src/main/java/org/matsim/simwrapper/dashboard/TripDashboard.java index 30c4a140e6b..7ca920bfa12 100644 --- a/contribs/simwrapper/src/main/java/org/matsim/simwrapper/dashboard/TripDashboard.java +++ b/contribs/simwrapper/src/main/java/org/matsim/simwrapper/dashboard/TripDashboard.java @@ -1,24 +1,33 @@ package org.matsim.simwrapper.dashboard; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.matsim.application.analysis.population.TripAnalysis; +import org.matsim.application.options.CsvOptions; +import org.matsim.core.utils.io.IOUtils; import org.matsim.simwrapper.Dashboard; import org.matsim.simwrapper.Header; import org.matsim.simwrapper.Layout; -import org.matsim.simwrapper.viz.ColorScheme; -import org.matsim.simwrapper.viz.Plotly; -import org.matsim.simwrapper.viz.Table; +import org.matsim.simwrapper.viz.*; import tech.tablesaw.plotly.components.Axis; import tech.tablesaw.plotly.traces.BarTrace; import javax.annotation.Nullable; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Objects; /** * Shows trip information, optionally against reference data. */ public class TripDashboard implements Dashboard { + private static final Logger log = LogManager.getLogger(TripDashboard.class); + @Nullable private final String modeShareRefCsv; @Nullable @@ -26,8 +35,15 @@ public class TripDashboard implements Dashboard { @Nullable private final String modeUsersRefCsv; + @Nullable + private String groupedRefCsv; + @Nullable + private String[] categories; + private String[] args; + private boolean choiceEvaluation; + /** * Default trip dashboard constructor. */ @@ -51,6 +67,47 @@ public TripDashboard(@Nullable String modeShareRefCsv, @Nullable String modeShar args = new String[0]; } + private static String[] detectCategories(String groupedRefCsv) { + try { + Character c = CsvOptions.detectDelimiter(groupedRefCsv); + try (BufferedReader reader = IOUtils.getBufferedReader(groupedRefCsv)) { + String header = reader.readLine(); + return Arrays.stream(header.split(String.valueOf(c))) + .filter(s -> !s.equals("main_mode") && !s.equals("share") && !s.equals("dist_group")) + .toArray(String[]::new); + } + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Set grouped reference data. Will enable additional tab with analysis for subgroups of the population. + * + * @param groupedRefCsv resource containing the grouped reference data + * @param categories categories to show on dashboard, if empty all categories will be used + */ + public TripDashboard withGroupedRefData(String groupedRefCsv, String... categories) { + this.groupedRefCsv = groupedRefCsv; + if (categories.length == 0) { + categories = detectCategories(groupedRefCsv); + log.info("Detected categories from reference data: {}", Arrays.toString(categories)); + } + this.categories = categories; + return this; + } + + /** + * Enable choice evaluation tab. This only produces valid data if choice reference data was set in the population. + * + * @see TripAnalysis#ATTR_REF_MODES + */ + public TripDashboard withChoiceEvaluation(boolean enable) { + this.choiceEvaluation = enable; + return this; + } + /** * Set argument that will be passed to the analysis script. See {@link TripAnalysis}. */ @@ -65,7 +122,19 @@ public void configure(Header header, Layout layout) { header.title = "Trips"; header.description = "General information about modal share and trip distributions."; - Layout.Row first = layout.row("first"); + String[] args = new String[this.groupedRefCsv == null ? this.args.length : this.args.length + 2]; + System.arraycopy(this.args, 0, args, 0, this.args.length); + + // Add ref data to the argument if set + if (groupedRefCsv != null) { + args[this.args.length] = "--input-ref-data"; + args[this.args.length + 1] = groupedRefCsv; + } + + // A tab will only be present if one of the other tabs is used as well + String tab = (groupedRefCsv != null || choiceEvaluation) ? header.title : null; + + Layout.Row first = layout.row("first", tab); first.el(Plotly.class, (viz, data) -> { viz.title = "Modal split"; @@ -117,7 +186,7 @@ public void configure(Header header, Layout layout) { } }); - layout.row("second") + layout.row("second", tab) .el(Table.class, (viz, data) -> { viz.title = "Mode Statistics"; viz.description = "by main mode, over whole trip (including access & egress)"; @@ -155,7 +224,7 @@ public void configure(Header header, Layout layout) { }); - layout.row("third") + layout.row("third", tab) .el(Table.class, (viz, data) -> { viz.title = "Population statistics"; viz.description = "over simulated persons (not scaled by sample size)"; @@ -188,7 +257,7 @@ public void configure(Header header, Layout layout) { }); - layout.row("departures").el(Plotly.class, (viz, data) -> { + layout.row("departures", tab).el(Plotly.class, (viz, data) -> { viz.title = "Departures"; viz.description = "by hour and purpose"; @@ -207,7 +276,7 @@ public void configure(Header header, Layout layout) { }); - layout.row("arrivals").el(Plotly.class, (viz, data) -> { + layout.row("arrivals", tab).el(Plotly.class, (viz, data) -> { viz.title = "Arrivals"; viz.description = "by hour and purpose"; @@ -226,5 +295,147 @@ public void configure(Header header, Layout layout) { }); + if (groupedRefCsv != null) { + createGroupedTab(layout, args); + } + + if (choiceEvaluation) { + createChoiceTab(layout, args); + } + } + + private void createChoiceTab(Layout layout, String[] args) { + + layout.row("choice-intro", "Mode Choice").el(TextBlock.class, (viz, data) -> { + viz.title = "Information"; + viz.content = """ + Note that these metrics are based on a single run and may have limited interpretability. For a more robust evaluation, consider running multiple simulations with different seeds and use metrics that consider probabilities as well. + (log-likelihood, Brier score, etc.) + For policy cases, these metrics do not have any meaning. They are solely for the base-case. + + - Precision is the fraction of instances correctly classified as belonging to a specific class out of all instances the model predicted to belong to that class. + - Recall is the fraction of instances in a class that the model correctly classified out of all instances in that class. + - The macro-average computes the metric independently for each class and then take the average (hence treating all classes equally). + - The micro-averages of Precision, Recall and F1 score are identical to the accuracy. + - Cohen's Kappa is a measure of agreement between two raters that corrects for chance agreement. 1.0 indicates perfect agreement, 0.0 or less indicates agreement by chance. + """; + }); + + layout.row("choice", "Mode Choice").el(Table.class, (viz, data) -> { + viz.title = "Choice Evaluation"; + viz.description = "Metrics for mode choice."; + viz.showAllRows = true; + viz.dataset = data.compute(TripAnalysis.class, "mode_choice_evaluation.csv", args); + }); + + layout.row("choice", "Mode Choice").el(Table.class, (viz, data) -> { + viz.title = "Choice Evaluation per Mode"; + viz.description = "Metrics for choices per mode."; + viz.showAllRows = true; + viz.dataset = data.compute(TripAnalysis.class, "mode_choice_evaluation_per_mode.csv", args); + }); + + layout.row("choice-plots", "Mode Choice").el(Heatmap.class, (viz, data) -> { + viz.title = "Confusion Matrix"; + viz.description = "Share of (mis)classified modes."; + viz.xAxisTitle = "Predicted"; + viz.yAxisTitle = "True"; + viz.y = "True/Pred"; + viz.flipAxes = false; + viz.showLabels = true; + viz.dataset = data.compute(TripAnalysis.class, "mode_confusion_matrix.csv", args); + }); + + layout.row("choice-plots", "Mode Choice").el(Plotly.class, (viz, data) -> { + viz.title = "Mode Prediction Error"; + viz.description = "Plot showing the number of (mis)classified modes."; + + viz.layout = tech.tablesaw.plotly.components.Layout.builder() + .xAxis(Axis.builder().title("True mode").build()) + .yAxis(Axis.builder().title("Predicted mode count").build()) + .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) + .build(); + + Plotly.DataMapping ds = viz.addDataset(data.compute(TripAnalysis.class, "mode_prediction_error.csv", args)) + .mapping() + .x("true_mode") + .y("count") + .name("predicted_mode"); + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT).build(), ds); + }); + } + + private void createGroupedTab(Layout layout, String[] args) { + + for (String cat : Objects.requireNonNull(categories, "Categories not set")) { + + layout.row("category_" + cat, "By Groups") + .el(Plotly.class, (viz, data) -> { + + viz.title = "Mode share"; + viz.description = "by " + cat; + viz.height = 6d; + viz.layout = tech.tablesaw.plotly.components.Layout.builder() + .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) + .build(); + + Plotly.DataMapping ds = viz.addDataset(data.computeWithPlaceholder(TripAnalysis.class, "mode_share_per_%s.csv", cat)) + .pivot(List.of("main_mode", "dist_group", cat), "source", "share") + .aggregate(List.of("main_mode", "source", cat), "share", Plotly.AggrFunc.SUM) + .rename("sim_share", "Sim") + .rename("ref_share", "Ref") + .mapping() + .facetCol(cat) + .name("main_mode") + .x("source") + .y("share"); + + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) + .orientation(BarTrace.Orientation.VERTICAL) + .build(), ds); + + }); + + /* + TODO: This part needs some more work in simwrapper and is not yet ready + + .el(Plotly.class, (viz, data) -> { + viz.title = "Modal distance distribution"; + viz.description = "by " + cat; + viz.layout = tech.tablesaw.plotly.components.Layout.builder() + .xAxis(Axis.builder().title("Distance group").build()) + .yAxis(Axis.builder().title("Share").build()) + .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) + .build(); + + viz.interactive = Plotly.Interactive.dropdown; + + // TODO: modes are not separated into different traces + // probably dropdown config in plotly needs to be extended + Plotly.DataMapping ds = viz.addDataset(data.computeWithPlaceholder(TripAnalysis.class, "mode_share_per_%s.csv", cat)) + .pivot(List.of("main_mode", "dist_group", cat), "source", "share") + .normalize(List.of("dist_group", "source", cat), "share") + .rename("sim_share", "Sim") + .rename("ref_share", "Ref") + .mapping() + .name("main_mode") + .facetCol(cat) + .x("dist_group") + .y("share"); + + viz.multiIndex = Map.of("dist_group", "source"); + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) + .orientation(BarTrace.Orientation.VERTICAL) + .build(), ds); + + }); + */ + + } + } + } diff --git a/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/Heatmap.java b/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/Heatmap.java index cd689d49a29..73ac0d689fe 100644 --- a/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/Heatmap.java +++ b/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/Heatmap.java @@ -43,7 +43,12 @@ public class Heatmap extends Viz { * and y axes. Can be useful if your data is stored * one way, but you want it displayed the other. */ - public String flipAxes; + public Boolean flipAxes; + + /** + * Show labels on the heatmap. + */ + public Boolean showLabels; public Heatmap() { super("heatmap"); diff --git a/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/Plotly.java b/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/Plotly.java index 4514035af4f..36d01346f58 100644 --- a/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/Plotly.java +++ b/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/Plotly.java @@ -73,11 +73,11 @@ public final class Plotly extends Viz { public Map multiIndex; @JsonIgnore - private List traces = new ArrayList<>(); + private final List traces = new ArrayList<>(); @JsonIgnore - private List data = new ArrayList<>(); + private final List data = new ArrayList<>(); @JsonIgnore - private List mappings = new ArrayList<>(); + private final List mappings = new ArrayList<>(); public Plotly() { super("plotly"); @@ -138,7 +138,7 @@ public DataSet addDataset(String path) { Objects.requireNonNull(path, "Path argument can not be null"); - String name = data.size() == 0 ? "dataset" : FilenameUtils.removeExtension(FilenameUtils.getName(path)).replace("_", ""); + String name = data.isEmpty() ? "dataset" : FilenameUtils.removeExtension(FilenameUtils.getName(path)).replace("_", ""); DataSet ds = new DataSet(path, name); data.add(ds); return ds; @@ -167,7 +167,7 @@ public Plotly fromFigure(Figure figure) { @JsonProperty(value = "datasets", index = 20) Map getDataSets() { - if (data.size() == 0) + if (data.isEmpty()) return null; Map result = new LinkedHashMap<>(); @@ -322,7 +322,9 @@ enum ColumnType { TEXT, SIZE, COLOR, - OPACITY + OPACITY, + FACET_COL, + FACET_ROW, } /** @@ -353,6 +355,8 @@ public static final class DataSet { private Map pivot; private Map constant; private Map aggregate; + private Map normalize; + private Map rename; private DataSet(String file, String name) { this.file = file; @@ -362,7 +366,9 @@ private DataSet(String file, String name) { private Object toJSON() { if (pivot == null && constant == null && - aggregate == null) + aggregate == null && + normalize == null && + rename == null) return file; return this; @@ -416,6 +422,30 @@ public DataSet aggregate(List groupBy, String target, AggrFunc func) { ); return this; } + + /** + * Normalize data to 100% for each group. + * @param groupBy group columns to group by. + * @param target target column. + */ + public DataSet normalize(List groupBy, String target) { + normalize = Map.of( + "groupBy", groupBy, + "target", target + ); + return this; + } + + /** + * Rename values in the dataset. Useful after pivot or to rename certain entries. + */ + public DataSet rename(String oldName, String newName) { + if (rename == null) + rename = new LinkedHashMap<>(); + + rename.put(oldName, newName); + return this; + } } /** @@ -424,7 +454,7 @@ public DataSet aggregate(List groupBy, String target, AggrFunc func) { public static final class DataMapping { private final String ref; - private Map columns = new EnumMap<>(ColumnType.class); + private final Map columns = new EnumMap<>(ColumnType.class); private String colorRamp; @@ -507,6 +537,22 @@ public DataMapping opacity(String columnName) { return this; } + /** + * Mapping for facet_col column. + */ + public DataMapping facetCol(String columnName) { + columns.put(ColumnType.FACET_COL, columnName); + return this; + } + + /** + * Mapping for facet_row column. + */ + public DataMapping facetRow(String columnName) { + columns.put(ColumnType.FACET_ROW, columnName); + return this; + } + private void insert(Map obj) { // None standard attribute name to preserve its meaning diff --git a/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/TextBlock.java b/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/TextBlock.java index aaa21607c5c..e84ddc494b3 100644 --- a/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/TextBlock.java +++ b/contribs/simwrapper/src/main/java/org/matsim/simwrapper/viz/TextBlock.java @@ -10,9 +10,13 @@ public class TextBlock extends Viz { /** * The filepath containing the data. */ - @JsonProperty(required = true) public String file; + /** + * Content of the text block. Can be used instead of file to directly include content. + */ + public String content; + public TextBlock() { super("text"); } diff --git a/contribs/simwrapper/src/test/java/org/matsim/simwrapper/TestScenario.java b/contribs/simwrapper/src/test/java/org/matsim/simwrapper/TestScenario.java index a58095cc3e1..0ba046d7d05 100644 --- a/contribs/simwrapper/src/test/java/org/matsim/simwrapper/TestScenario.java +++ b/contribs/simwrapper/src/test/java/org/matsim/simwrapper/TestScenario.java @@ -3,19 +3,27 @@ import com.google.common.collect.Sets; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.population.Person; import org.matsim.application.MATSimApplication; +import org.matsim.application.analysis.population.TripAnalysis; import org.matsim.contrib.vsp.scenario.SnzActivities; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.router.DefaultAnalysisMainModeIdentifier; +import org.matsim.core.router.TripStructureUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; import org.matsim.testcases.MatsimTestUtils; import java.net.URL; import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.SplittableRandom; +import java.util.function.Function; +import java.util.stream.Collectors; /** * A test scenario based on kelheim example. @@ -63,6 +71,35 @@ protected void prepareScenario(Scenario scenario) { link.setAllowedModes(newModes); } } + + SplittableRandom rnd = new SplittableRandom(0); + DefaultAnalysisMainModeIdentifier mmi = new DefaultAnalysisMainModeIdentifier(); + + // Generate reference modes randomly + Function genMode = t -> { + double r = rnd.nextDouble(); + if (r < 0.1) + return "car"; + else if (r < 0.2) + return "pt"; + else if (r < 0.3) + return "bike"; + + return mmi.identifyMainMode(t.getLegsOnly()); + }; + + // Assign reference modes to persons + for (Person person : scenario.getPopulation().getPersons().values()) { + + if (rnd.nextDouble() < 0.5) + continue; + + List trips = TripStructureUtils.getTrips(person.getSelectedPlan()); + String ref = trips.stream().map(genMode).collect(Collectors.joining("-")); + + person.getAttributes().putAttribute(TripAnalysis.ATTR_REF_ID, person.getId().toString()); + person.getAttributes().putAttribute(TripAnalysis.ATTR_REF_MODES, ref); + } } @Override diff --git a/contribs/simwrapper/src/test/java/org/matsim/simwrapper/dashboard/DashboardTests.java b/contribs/simwrapper/src/test/java/org/matsim/simwrapper/dashboard/DashboardTests.java index f35fdb8b5ea..0f5b273b809 100644 --- a/contribs/simwrapper/src/test/java/org/matsim/simwrapper/dashboard/DashboardTests.java +++ b/contribs/simwrapper/src/test/java/org/matsim/simwrapper/dashboard/DashboardTests.java @@ -77,10 +77,17 @@ void tripRef() { Path out = Path.of(utils.getOutputDirectory(), "analysis", "population"); - run(new TripDashboard("mode_share_ref.csv", "mode_share_per_dist_ref.csv", "mode_users_ref.csv")); + TripDashboard dashboard = new TripDashboard("mode_share_ref.csv", "mode_share_per_dist_ref.csv", "mode_users_ref.csv") + .withGroupedRefData("mode_share_per_group_dist_ref.csv") + .withChoiceEvaluation(true); + + run(dashboard); Assertions.assertThat(out) .isDirectoryContaining("glob:**trip_stats.csv") - .isDirectoryContaining("glob:**mode_share.csv"); + .isDirectoryContaining("glob:**mode_share.csv") + .isDirectoryContaining("glob:**mode_choices.csv") + .isDirectoryContaining("glob:**mode_choice_evaluation.csv") + .isDirectoryContaining("glob:**mode_confusion_matrix.csv"); } diff --git a/contribs/simwrapper/src/test/resources/mode_share_per_group_dist_ref.csv b/contribs/simwrapper/src/test/resources/mode_share_per_group_dist_ref.csv new file mode 100644 index 00000000000..a293d24b2c2 --- /dev/null +++ b/contribs/simwrapper/src/test/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.08835438432558193 +0 - 12,,,,,,,0 - 1000,car,3.432121636877185e-05 +0 - 12,,,,,,,0 - 1000,pt,0.0021749863020563654 +0 - 12,,,,,,,0 - 1000,ride,0.021051200382818012 +0 - 12,,,,,,,0 - 1000,walk,0.3432142504695775 +0 - 12,,,,,,,1000 - 2000,bike,0.08398330004325194 +0 - 12,,,,,,,1000 - 2000,car,0.000150219687811569 +0 - 12,,,,,,,1000 - 2000,pt,0.02306959433284934 +0 - 12,,,,,,,1000 - 2000,ride,0.05686810841448048 +0 - 12,,,,,,,1000 - 2000,walk,0.07347412145528517 +0 - 12,,,,,,,2000 - 5000,bike,0.04222757075895274 +0 - 12,,,,,,,2000 - 5000,car,6.536048317715672e-05 +0 - 12,,,,,,,2000 - 5000,pt,0.05396978017600571 +0 - 12,,,,,,,2000 - 5000,ride,0.08856585531656834 +0 - 12,,,,,,,2000 - 5000,walk,0.008408652825927461 +0 - 12,,,,,,,5000 - 10000,bike,0.0038947116326784196 +0 - 12,,,,,,,5000 - 10000,car,5.599841436553611e-05 +0 - 12,,,,,,,5000 - 10000,pt,0.02794477835232046 +0 - 12,,,,,,,5000 - 10000,ride,0.04558406298643193 +0 - 12,,,,,,,5000 - 10000,walk,0.0009301529133672708 +0 - 12,,,,,,,10000 - 20000,bike,7.862395528391248e-05 +0 - 12,,,,,,,10000 - 20000,car,0.0 +0 - 12,,,,,,,10000 - 20000,pt,0.007770578530816594 +0 - 12,,,,,,,10000 - 20000,ride,0.019094169122247644 +0 - 12,,,,,,,10000 - 20000,walk,0.0004190229166182497 +0 - 12,,,,,,,20000+,bike,3.432121636877185e-05 +0 - 12,,,,,,,20000+,car,0.0 +0 - 12,,,,,,,20000+,pt,0.0031358547205645646 +0 - 12,,,,,,,20000+,ride,0.00513600585361295 +0 - 12,,,,,,,20000+,walk,0.00031001319461127424 +12 - 18,,,,,,,0 - 1000,bike,0.04056261263021194 +12 - 18,,,,,,,0 - 1000,car,0.0 +12 - 18,,,,,,,0 - 1000,pt,0.00548858580569948 +12 - 18,,,,,,,0 - 1000,ride,0.0022302160066904092 +12 - 18,,,,,,,0 - 1000,walk,0.15042303841024 +12 - 18,,,,,,,1000 - 2000,bike,0.089070390956725 +12 - 18,,,,,,,1000 - 2000,car,0.0 +12 - 18,,,,,,,1000 - 2000,pt,0.03153953270294178 +12 - 18,,,,,,,1000 - 2000,ride,0.008413705601187106 +12 - 18,,,,,,,1000 - 2000,walk,0.044861543643076415 +12 - 18,,,,,,,2000 - 5000,bike,0.10738683499133322 +12 - 18,,,,,,,2000 - 5000,car,0.0002828962673402461 +12 - 18,,,,,,,2000 - 5000,pt,0.19825020985446604 +12 - 18,,,,,,,2000 - 5000,ride,0.032263789775858934 +12 - 18,,,,,,,2000 - 5000,walk,0.007541953274485525 +12 - 18,,,,,,,5000 - 10000,bike,0.013960272491340344 +12 - 18,,,,,,,5000 - 10000,car,0.0002828962673402461 +12 - 18,,,,,,,5000 - 10000,pt,0.15625539533377433 +12 - 18,,,,,,,5000 - 10000,ride,0.02888306971933014 +12 - 18,,,,,,,5000 - 10000,walk,0.00035313985281031226 +12 - 18,,,,,,,10000 - 20000,bike,0.0013847631212960674 +12 - 18,,,,,,,10000 - 20000,car,0.0001527576520257711 +12 - 18,,,,,,,10000 - 20000,pt,0.055802177791747724 +12 - 18,,,,,,,10000 - 20000,ride,0.008904701024528816 +12 - 18,,,,,,,10000 - 20000,walk,0.0 +12 - 18,,,,,,,20000+,bike,0.00037524183370703695 +12 - 18,,,,,,,20000+,car,0.0 +12 - 18,,,,,,,20000+,pt,0.009923416953924949 +12 - 18,,,,,,,20000+,ride,0.004966212655379154 +12 - 18,,,,,,,20000+,walk,0.000440645382538785 +18 - 25,,,,,,,0 - 1000,bike,0.02398561235781499 +18 - 25,,,,,,,0 - 1000,car,0.0048751521373352445 +18 - 25,,,,,,,0 - 1000,pt,0.003945859978488251 +18 - 25,,,,,,,0 - 1000,ride,0.0020741803522305053 +18 - 25,,,,,,,0 - 1000,walk,0.1351326848167619 +18 - 25,,,,,,,1000 - 2000,bike,0.03396527152384182 +18 - 25,,,,,,,1000 - 2000,car,0.009995183869916416 +18 - 25,,,,,,,1000 - 2000,pt,0.024513641618001722 +18 - 25,,,,,,,1000 - 2000,ride,0.0026681998585366875 +18 - 25,,,,,,,1000 - 2000,walk,0.0454726308118391 +18 - 25,,,,,,,2000 - 5000,bike,0.06318107428470454 +18 - 25,,,,,,,2000 - 5000,car,0.023251875706620243 +18 - 25,,,,,,,2000 - 5000,pt,0.10942869085194808 +18 - 25,,,,,,,2000 - 5000,ride,0.010494962098131744 +18 - 25,,,,,,,2000 - 5000,walk,0.015841120495424313 +18 - 25,,,,,,,5000 - 10000,bike,0.024391958956895745 +18 - 25,,,,,,,5000 - 10000,car,0.0305448384287256 +18 - 25,,,,,,,5000 - 10000,pt,0.16886134724827598 +18 - 25,,,,,,,5000 - 10000,ride,0.012529534856484127 +18 - 25,,,,,,,5000 - 10000,walk,0.001335058529996402 +18 - 25,,,,,,,10000 - 20000,bike,0.002283609567549982 +18 - 25,,,,,,,10000 - 20000,car,0.02354347014171086 +18 - 25,,,,,,,10000 - 20000,pt,0.13983024203024946 +18 - 25,,,,,,,10000 - 20000,ride,0.009738847778169043 +18 - 25,,,,,,,10000 - 20000,walk,0.00046040347416503706 +18 - 25,,,,,,,20000+,bike,0.0003598674743239825 +18 - 25,,,,,,,20000+,car,0.019844536404905074 +18 - 25,,,,,,,20000+,pt,0.05191307482568158 +18 - 25,,,,,,,20000+,ride,0.0054414707774460185 +18 - 25,,,,,,,20000+,walk,9.559874382553337e-05 +25 - 35,,,,,,,0 - 1000,bike,0.02710671281214892 +25 - 35,,,,,,,0 - 1000,car,0.0057994039547034845 +25 - 35,,,,,,,0 - 1000,pt,0.001279341348825648 +25 - 35,,,,,,,0 - 1000,ride,0.0009365409275199195 +25 - 35,,,,,,,0 - 1000,walk,0.22308917916513984 +25 - 35,,,,,,,1000 - 2000,bike,0.038879288211573826 +25 - 35,,,,,,,1000 - 2000,car,0.015574030733198638 +25 - 35,,,,,,,1000 - 2000,pt,0.014062407046561295 +25 - 35,,,,,,,1000 - 2000,ride,0.0027015671389454218 +25 - 35,,,,,,,1000 - 2000,walk,0.05276857867693636 +25 - 35,,,,,,,2000 - 5000,bike,0.06756351305973912 +25 - 35,,,,,,,2000 - 5000,car,0.0341919427125718 +25 - 35,,,,,,,2000 - 5000,pt,0.07993094118194079 +25 - 35,,,,,,,2000 - 5000,ride,0.006927040925022773 +25 - 35,,,,,,,2000 - 5000,walk,0.014527745750548082 +25 - 35,,,,,,,5000 - 10000,bike,0.04519783227041666 +25 - 35,,,,,,,5000 - 10000,car,0.04092311464281495 +25 - 35,,,,,,,5000 - 10000,pt,0.11711303425909293 +25 - 35,,,,,,,5000 - 10000,ride,0.007459596516540125 +25 - 35,,,,,,,5000 - 10000,walk,0.0011933932411686548 +25 - 35,,,,,,,10000 - 20000,bike,0.010371145878011873 +25 - 35,,,,,,,10000 - 20000,car,0.037122925967374674 +25 - 35,,,,,,,10000 - 20000,pt,0.0830885319675897 +25 - 35,,,,,,,10000 - 20000,ride,0.00427342214921809 +25 - 35,,,,,,,10000 - 20000,walk,0.00046959104556907043 +25 - 35,,,,,,,20000+,bike,0.0008650285684158126 +25 - 35,,,,,,,20000+,car,0.022531312078687672 +25 - 35,,,,,,,20000+,pt,0.04081677551162561 +25 - 35,,,,,,,20000+,ride,0.003080042714773812 +25 - 35,,,,,,,20000+,walk,0.00015601954332448127 +35 - 66,,,,,,,0 - 1000,bike,0.040332771508063456 +35 - 66,,,,,,,0 - 1000,car,0.01257661515762158 +35 - 66,,,,,,,0 - 1000,pt,0.002627358555652047 +35 - 66,,,,,,,0 - 1000,ride,0.001274292496410711 +35 - 66,,,,,,,0 - 1000,walk,0.20652019924881693 +35 - 66,,,,,,,1000 - 2000,bike,0.046077012040347726 +35 - 66,,,,,,,1000 - 2000,car,0.028801869123512337 +35 - 66,,,,,,,1000 - 2000,pt,0.014894709162723204 +35 - 66,,,,,,,1000 - 2000,ride,0.00393142725073833 +35 - 66,,,,,,,1000 - 2000,walk,0.04326782662357003 +35 - 66,,,,,,,2000 - 5000,bike,0.061631621878577604 +35 - 66,,,,,,,2000 - 5000,car,0.06587612217476327 +35 - 66,,,,,,,2000 - 5000,pt,0.053970521291472305 +35 - 66,,,,,,,2000 - 5000,ride,0.008515244305250143 +35 - 66,,,,,,,2000 - 5000,walk,0.012613220763112581 +35 - 66,,,,,,,5000 - 10000,bike,0.034913749435674296 +35 - 66,,,,,,,5000 - 10000,car,0.07107640811792384 +35 - 66,,,,,,,5000 - 10000,pt,0.07403986112593337 +35 - 66,,,,,,,5000 - 10000,ride,0.0070144527175531445 +35 - 66,,,,,,,5000 - 10000,walk,0.0016905536760423883 +35 - 66,,,,,,,10000 - 20000,bike,0.010619744339555919 +35 - 66,,,,,,,10000 - 20000,car,0.06001064856690114 +35 - 66,,,,,,,10000 - 20000,pt,0.07015337993949035 +35 - 66,,,,,,,10000 - 20000,ride,0.0050300495618203845 +35 - 66,,,,,,,10000 - 20000,walk,0.0004717166408375984 +35 - 66,,,,,,,20000+,bike,0.0008989376281988117 +35 - 66,,,,,,,20000+,car,0.03165819643378093 +35 - 66,,,,,,,20000+,pt,0.02653250556644344 +35 - 66,,,,,,,20000+,ride,0.0027399916814560116 +35 - 66,,,,,,,20000+,walk,0.00023899298775612217 +66+,,,,,,,0 - 1000,bike,0.028917781379748875 +66+,,,,,,,0 - 1000,car,0.01756905891685044 +66+,,,,,,,0 - 1000,pt,0.008273204993617672 +66+,,,,,,,0 - 1000,ride,0.004628191849485549 +66+,,,,,,,0 - 1000,walk,0.2851630822571291 +66+,,,,,,,1000 - 2000,bike,0.02854964969272559 +66+,,,,,,,1000 - 2000,car,0.03882086040651397 +66+,,,,,,,1000 - 2000,pt,0.0331697357735842 +66+,,,,,,,1000 - 2000,ride,0.011117551434265606 +66+,,,,,,,1000 - 2000,walk,0.06845936405951983 +66+,,,,,,,2000 - 5000,bike,0.022205536519422444 +66+,,,,,,,2000 - 5000,car,0.08261732385978052 +66+,,,,,,,2000 - 5000,pt,0.0650551591678631 +66+,,,,,,,2000 - 5000,ride,0.023067556374726784 +66+,,,,,,,2000 - 5000,walk,0.01871777257889785 +66+,,,,,,,5000 - 10000,bike,0.0060053800450549635 +66+,,,,,,,5000 - 10000,car,0.0627074379519613 +66+,,,,,,,5000 - 10000,pt,0.05292597833638581 +66+,,,,,,,5000 - 10000,ride,0.0172732713428096 +66+,,,,,,,5000 - 10000,walk,0.0020729972598010397 +66+,,,,,,,10000 - 20000,bike,0.0020118745335472974 +66+,,,,,,,10000 - 20000,car,0.03570337079193992 +66+,,,,,,,10000 - 20000,pt,0.03652161682334497 +66+,,,,,,,10000 - 20000,ride,0.011929475838790412 +66+,,,,,,,10000 - 20000,walk,0.0009657233478543096 +66+,,,,,,,20000+,bike,0.00025574980638623377 +66+,,,,,,,20000+,car,0.01651491485906698 +66+,,,,,,,20000+,pt,0.012176960792435333 +66+,,,,,,,20000+,ride,0.006529537524723665 +66+,,,,,,,20000+,walk,7.388148176658781e-05 +,0 - 500,,,,,,0 - 1000,bike,0.03669177865113303 +,0 - 500,,,,,,0 - 1000,car,0.00738398850586487 +,0 - 500,,,,,,0 - 1000,pt,0.007978210296398438 +,0 - 500,,,,,,0 - 1000,ride,0.0011450141882909117 +,0 - 500,,,,,,0 - 1000,walk,0.21475506048990617 +,0 - 500,,,,,,1000 - 2000,bike,0.05686516881650415 +,0 - 500,,,,,,1000 - 2000,car,0.014437527855602178 +,0 - 500,,,,,,1000 - 2000,pt,0.0390932425009502 +,0 - 500,,,,,,1000 - 2000,ride,0.0008025942354514118 +,0 - 500,,,,,,1000 - 2000,walk,0.0520912286100064 +,0 - 500,,,,,,2000 - 5000,bike,0.061478178043115554 +,0 - 500,,,,,,2000 - 5000,car,0.01709259019057143 +,0 - 500,,,,,,2000 - 5000,pt,0.10179655836178256 +,0 - 500,,,,,,2000 - 5000,ride,0.005808192545811038 +,0 - 500,,,,,,2000 - 5000,walk,0.004899508326691209 +,0 - 500,,,,,,5000 - 10000,bike,0.03557171175235263 +,0 - 500,,,,,,5000 - 10000,car,0.007644922733794155 +,0 - 500,,,,,,5000 - 10000,pt,0.12150867967192143 +,0 - 500,,,,,,5000 - 10000,ride,0.011819540955771745 +,0 - 500,,,,,,5000 - 10000,walk,0.0 +,0 - 500,,,,,,10000 - 20000,bike,0.0 +,0 - 500,,,,,,10000 - 20000,car,0.011056434745182113 +,0 - 500,,,,,,10000 - 20000,pt,0.10618540699650501 +,0 - 500,,,,,,10000 - 20000,ride,0.008832868383971678 +,0 - 500,,,,,,10000 - 20000,walk,0.0053587149815805465 +,0 - 500,,,,,,20000+,bike,0.0 +,0 - 500,,,,,,20000+,car,0.020473617216577353 +,0 - 500,,,,,,20000+,pt,0.036936597273339124 +,0 - 500,,,,,,20000+,ride,0.012292663670924705 +,0 - 500,,,,,,20000+,walk,0.0 +,500 - 900,,,,,,0 - 1000,bike,0.032856330403264675 +,500 - 900,,,,,,0 - 1000,car,0.005654343128338344 +,500 - 900,,,,,,0 - 1000,pt,0.013455806601091204 +,500 - 900,,,,,,0 - 1000,ride,0.0014720430155450257 +,500 - 900,,,,,,0 - 1000,walk,0.24849003889804464 +,500 - 900,,,,,,1000 - 2000,bike,0.04264617334968981 +,500 - 900,,,,,,1000 - 2000,car,0.0185348582476192 +,500 - 900,,,,,,1000 - 2000,pt,0.029751251528059353 +,500 - 900,,,,,,1000 - 2000,ride,0.005996648673162164 +,500 - 900,,,,,,1000 - 2000,walk,0.08238910591968335 +,500 - 900,,,,,,2000 - 5000,bike,0.05148933107546342 +,500 - 900,,,,,,2000 - 5000,car,0.024086347101453732 +,500 - 900,,,,,,2000 - 5000,pt,0.1037671430311061 +,500 - 900,,,,,,2000 - 5000,ride,0.009941756923120151 +,500 - 900,,,,,,2000 - 5000,walk,0.02781089395445394 +,500 - 900,,,,,,5000 - 10000,bike,0.024443567475169733 +,500 - 900,,,,,,5000 - 10000,car,0.021823598208739102 +,500 - 900,,,,,,5000 - 10000,pt,0.11481850923831592 +,500 - 900,,,,,,5000 - 10000,ride,0.0044810774712191965 +,500 - 900,,,,,,5000 - 10000,walk,0.001529902069657657 +,500 - 900,,,,,,10000 - 20000,bike,0.0057857733756045155 +,500 - 900,,,,,,10000 - 20000,car,0.013083644583328781 +,500 - 900,,,,,,10000 - 20000,pt,0.07851252715724369 +,500 - 900,,,,,,10000 - 20000,ride,0.002643755866810356 +,500 - 900,,,,,,10000 - 20000,walk,0.0010612155483804289 +,500 - 900,,,,,,20000+,bike,0.000509961763307295 +,500 - 900,,,,,,20000+,car,0.0028259746070602053 +,500 - 900,,,,,,20000+,pt,0.029137341215747518 +,500 - 900,,,,,,20000+,ride,0.001001079569320493 +,500 - 900,,,,,,20000+,walk,0.0 +,900 - 1500,,,,,,0 - 1000,bike,0.03598849459141938 +,900 - 1500,,,,,,0 - 1000,car,0.008086997634789332 +,900 - 1500,,,,,,0 - 1000,pt,0.005298771702446347 +,900 - 1500,,,,,,0 - 1000,ride,0.001953834330467959 +,900 - 1500,,,,,,0 - 1000,walk,0.2657550676806446 +,900 - 1500,,,,,,1000 - 2000,bike,0.04060526316240379 +,900 - 1500,,,,,,1000 - 2000,car,0.018126612379569047 +,900 - 1500,,,,,,1000 - 2000,pt,0.03098071071792005 +,900 - 1500,,,,,,1000 - 2000,ride,0.005657519926746878 +,900 - 1500,,,,,,1000 - 2000,walk,0.05276727722039848 +,900 - 1500,,,,,,2000 - 5000,bike,0.04830211278294567 +,900 - 1500,,,,,,2000 - 5000,car,0.04001310968653559 +,900 - 1500,,,,,,2000 - 5000,pt,0.09014630107046426 +,900 - 1500,,,,,,2000 - 5000,ride,0.009713854846172548 +,900 - 1500,,,,,,2000 - 5000,walk,0.016648527622471675 +,900 - 1500,,,,,,5000 - 10000,bike,0.025427948492362438 +,900 - 1500,,,,,,5000 - 10000,car,0.04210363990701105 +,900 - 1500,,,,,,5000 - 10000,pt,0.1042034227160141 +,900 - 1500,,,,,,5000 - 10000,ride,0.007633625687591608 +,900 - 1500,,,,,,5000 - 10000,walk,0.0020922113653862396 +,900 - 1500,,,,,,10000 - 20000,bike,0.005488321233178787 +,900 - 1500,,,,,,10000 - 20000,car,0.028042448389659042 +,900 - 1500,,,,,,10000 - 20000,pt,0.0686150844425172 +,900 - 1500,,,,,,10000 - 20000,ride,0.004897732846476809 +,900 - 1500,,,,,,10000 - 20000,walk,0.00037570368405910224 +,900 - 1500,,,,,,20000+,bike,0.0005080049357036462 +,900 - 1500,,,,,,20000+,car,0.010660487225736238 +,900 - 1500,,,,,,20000+,pt,0.02715034948058022 +,900 - 1500,,,,,,20000+,ride,0.002383258599110134 +,900 - 1500,,,,,,20000+,walk,0.00037330563921799005 +,1500 - 2000,,,,,,0 - 1000,bike,0.031995950241218714 +,1500 - 2000,,,,,,0 - 1000,car,0.008687841197317943 +,1500 - 2000,,,,,,0 - 1000,pt,0.00499336506717366 +,1500 - 2000,,,,,,0 - 1000,ride,0.0023206260617257162 +,1500 - 2000,,,,,,0 - 1000,walk,0.24159595914041004 +,1500 - 2000,,,,,,1000 - 2000,bike,0.0411144158645314 +,1500 - 2000,,,,,,1000 - 2000,car,0.02349176574057982 +,1500 - 2000,,,,,,1000 - 2000,pt,0.02604275793509365 +,1500 - 2000,,,,,,1000 - 2000,ride,0.007016076224911017 +,1500 - 2000,,,,,,1000 - 2000,walk,0.06013562478859629 +,1500 - 2000,,,,,,2000 - 5000,bike,0.04631945909663589 +,1500 - 2000,,,,,,2000 - 5000,car,0.05127689250314956 +,1500 - 2000,,,,,,2000 - 5000,pt,0.07917696836034209 +,1500 - 2000,,,,,,2000 - 5000,ride,0.014397871998404659 +,1500 - 2000,,,,,,2000 - 5000,walk,0.01709000238503994 +,1500 - 2000,,,,,,5000 - 10000,bike,0.022045388691979202 +,1500 - 2000,,,,,,5000 - 10000,car,0.05014154146035286 +,1500 - 2000,,,,,,5000 - 10000,pt,0.09913772578726267 +,1500 - 2000,,,,,,5000 - 10000,ride,0.01240198792987441 +,1500 - 2000,,,,,,5000 - 10000,walk,0.0011665572134838835 +,1500 - 2000,,,,,,10000 - 20000,bike,0.006661640309499392 +,1500 - 2000,,,,,,10000 - 20000,car,0.0341440688526599 +,1500 - 2000,,,,,,10000 - 20000,pt,0.06677062189126137 +,1500 - 2000,,,,,,10000 - 20000,ride,0.006336838794761074 +,1500 - 2000,,,,,,10000 - 20000,walk,0.0007864807634107508 +,1500 - 2000,,,,,,20000+,bike,0.0008266069780131096 +,1500 - 2000,,,,,,20000+,car,0.01711197866849559 +,1500 - 2000,,,,,,20000+,pt,0.02356392129134062 +,1500 - 2000,,,,,,20000+,ride,0.0030874339900264993 +,1500 - 2000,,,,,,20000+,walk,0.00016163077244841305 +,2000 - 2600,,,,,,0 - 1000,bike,0.033392386287582995 +,2000 - 2600,,,,,,0 - 1000,car,0.011750667173578672 +,2000 - 2600,,,,,,0 - 1000,pt,0.0033577219970918923 +,2000 - 2600,,,,,,0 - 1000,ride,0.004017675495628648 +,2000 - 2600,,,,,,0 - 1000,walk,0.2418248593465435 +,2000 - 2600,,,,,,1000 - 2000,bike,0.04164116355308919 +,2000 - 2600,,,,,,1000 - 2000,car,0.025052465274538565 +,2000 - 2600,,,,,,1000 - 2000,pt,0.022285701904862766 +,2000 - 2600,,,,,,1000 - 2000,ride,0.009089080033748823 +,2000 - 2600,,,,,,1000 - 2000,walk,0.049710929706035606 +,2000 - 2600,,,,,,2000 - 5000,bike,0.05481272765031414 +,2000 - 2600,,,,,,2000 - 5000,car,0.05465689217029469 +,2000 - 2600,,,,,,2000 - 5000,pt,0.07246185418226436 +,2000 - 2600,,,,,,2000 - 5000,ride,0.016283688182049984 +,2000 - 2600,,,,,,2000 - 5000,walk,0.012605924522216998 +,2000 - 2600,,,,,,5000 - 10000,bike,0.025026108853535477 +,2000 - 2600,,,,,,5000 - 10000,car,0.05914158168028776 +,2000 - 2600,,,,,,5000 - 10000,pt,0.08092420950471652 +,2000 - 2600,,,,,,5000 - 10000,ride,0.011312420356964784 +,2000 - 2600,,,,,,5000 - 10000,walk,0.0021656022200766834 +,2000 - 2600,,,,,,10000 - 20000,bike,0.006504474812229927 +,2000 - 2600,,,,,,10000 - 20000,car,0.040600067690888426 +,2000 - 2600,,,,,,10000 - 20000,pt,0.06149690620492426 +,2000 - 2600,,,,,,10000 - 20000,ride,0.006925265490792309 +,2000 - 2600,,,,,,10000 - 20000,walk,0.000373972377746059 +,2000 - 2600,,,,,,20000+,bike,0.0003281050789348971 +,2000 - 2600,,,,,,20000+,car,0.022844216579649936 +,2000 - 2600,,,,,,20000+,pt,0.024461236274549023 +,2000 - 2600,,,,,,20000+,ride,0.004757398109869234 +,2000 - 2600,,,,,,20000+,walk,0.00019469728499374168 +,2600 - 3000,,,,,,0 - 1000,bike,0.04149356389552307 +,2600 - 3000,,,,,,0 - 1000,car,0.011541052371186121 +,2600 - 3000,,,,,,0 - 1000,pt,0.0037531526647850847 +,2600 - 3000,,,,,,0 - 1000,ride,0.003712806863678218 +,2600 - 3000,,,,,,0 - 1000,walk,0.22998833449818365 +,2600 - 3000,,,,,,1000 - 2000,bike,0.04629551279380722 +,2600 - 3000,,,,,,1000 - 2000,car,0.02437230920610378 +,2600 - 3000,,,,,,1000 - 2000,pt,0.01820483747470896 +,2600 - 3000,,,,,,1000 - 2000,ride,0.010298110670851011 +,2600 - 3000,,,,,,1000 - 2000,walk,0.05234216881862283 +,2600 - 3000,,,,,,2000 - 5000,bike,0.05609731539035026 +,2600 - 3000,,,,,,2000 - 5000,car,0.05119912197216824 +,2600 - 3000,,,,,,2000 - 5000,pt,0.07075639388827012 +,2600 - 3000,,,,,,2000 - 5000,ride,0.017739166078363386 +,2600 - 3000,,,,,,2000 - 5000,walk,0.01368234409862979 +,2600 - 3000,,,,,,5000 - 10000,bike,0.025609365117338957 +,2600 - 3000,,,,,,5000 - 10000,car,0.05478211087071505 +,2600 - 3000,,,,,,5000 - 10000,pt,0.08041337693449645 +,2600 - 3000,,,,,,5000 - 10000,ride,0.014699866404994835 +,2600 - 3000,,,,,,5000 - 10000,walk,0.0014923007929669577 +,2600 - 3000,,,,,,10000 - 20000,bike,0.007413905642428567 +,2600 - 3000,,,,,,10000 - 20000,car,0.0441740931201671 +,2600 - 3000,,,,,,10000 - 20000,pt,0.06516252660688335 +,2600 - 3000,,,,,,10000 - 20000,ride,0.0076820260403931 +,2600 - 3000,,,,,,10000 - 20000,walk,0.0004195500443180223 +,2600 - 3000,,,,,,20000+,bike,0.0007696474863761435 +,2600 - 3000,,,,,,20000+,car,0.020873916468958396 +,2600 - 3000,,,,,,20000+,pt,0.021115394380993573 +,2600 - 3000,,,,,,20000+,ride,0.0038320950047968515 +,2600 - 3000,,,,,,20000+,walk,8.363439894094326e-05 +,3000 - 3600,,,,,,0 - 1000,bike,0.04157860120113375 +,3000 - 3600,,,,,,0 - 1000,car,0.009351445284182618 +,3000 - 3600,,,,,,0 - 1000,pt,0.0027408844677735806 +,3000 - 3600,,,,,,0 - 1000,ride,0.0042382321555965025 +,3000 - 3600,,,,,,0 - 1000,walk,0.21610179079244526 +,3000 - 3600,,,,,,1000 - 2000,bike,0.04803236184338368 +,3000 - 3600,,,,,,1000 - 2000,car,0.022248631980859034 +,3000 - 3600,,,,,,1000 - 2000,pt,0.015619417507692595 +,3000 - 3600,,,,,,1000 - 2000,ride,0.010511951434527544 +,3000 - 3600,,,,,,1000 - 2000,walk,0.05613023640342644 +,3000 - 3600,,,,,,2000 - 5000,bike,0.05581069982205347 +,3000 - 3600,,,,,,2000 - 5000,car,0.053405667270753264 +,3000 - 3600,,,,,,2000 - 5000,pt,0.06491269957617579 +,3000 - 3600,,,,,,2000 - 5000,ride,0.024697414925814096 +,3000 - 3600,,,,,,2000 - 5000,walk,0.013052461334264796 +,3000 - 3600,,,,,,5000 - 10000,bike,0.02683863292782313 +,3000 - 3600,,,,,,5000 - 10000,car,0.05980119365525028 +,3000 - 3600,,,,,,5000 - 10000,pt,0.07523156388803975 +,3000 - 3600,,,,,,5000 - 10000,ride,0.017382369500561722 +,3000 - 3600,,,,,,5000 - 10000,walk,0.0017691961741910986 +,3000 - 3600,,,,,,10000 - 20000,bike,0.007243872636375718 +,3000 - 3600,,,,,,10000 - 20000,car,0.044150523296818574 +,3000 - 3600,,,,,,10000 - 20000,pt,0.06584003707449934 +,3000 - 3600,,,,,,10000 - 20000,ride,0.008299496149072898 +,3000 - 3600,,,,,,10000 - 20000,walk,0.00030603852119757605 +,3000 - 3600,,,,,,20000+,bike,0.00031835636308559577 +,3000 - 3600,,,,,,20000+,car,0.025761044224548002 +,3000 - 3600,,,,,,20000+,pt,0.02433319974403415 +,3000 - 3600,,,,,,20000+,ride,0.003943685578895834 +,3000 - 3600,,,,,,20000+,walk,0.00034829426552370624 +,3600 - 4600,,,,,,0 - 1000,bike,0.04812497069835277 +,3600 - 4600,,,,,,0 - 1000,car,0.010255769635116858 +,3600 - 4600,,,,,,0 - 1000,pt,0.0021911932092977438 +,3600 - 4600,,,,,,0 - 1000,ride,0.004308896419719163 +,3600 - 4600,,,,,,0 - 1000,walk,0.222061063678996 +,3600 - 4600,,,,,,1000 - 2000,bike,0.05222795446432146 +,3600 - 4600,,,,,,1000 - 2000,car,0.024946590164324003 +,3600 - 4600,,,,,,1000 - 2000,pt,0.016397098000105494 +,3600 - 4600,,,,,,1000 - 2000,ride,0.013709334964290706 +,3600 - 4600,,,,,,1000 - 2000,walk,0.04659557655612417 +,3600 - 4600,,,,,,2000 - 5000,bike,0.06277345647528074 +,3600 - 4600,,,,,,2000 - 5000,car,0.05528807677612364 +,3600 - 4600,,,,,,2000 - 5000,pt,0.058211143004250746 +,3600 - 4600,,,,,,2000 - 5000,ride,0.023037066221417945 +,3600 - 4600,,,,,,2000 - 5000,walk,0.012241693578406765 +,3600 - 4600,,,,,,5000 - 10000,bike,0.02979373657899519 +,3600 - 4600,,,,,,5000 - 10000,car,0.04870684612791301 +,3600 - 4600,,,,,,5000 - 10000,pt,0.07131497927334177 +,3600 - 4600,,,,,,5000 - 10000,ride,0.016852787918811663 +,3600 - 4600,,,,,,5000 - 10000,walk,0.000894470130011945 +,3600 - 4600,,,,,,10000 - 20000,bike,0.008000182483496954 +,3600 - 4600,,,,,,10000 - 20000,car,0.04649423584852916 +,3600 - 4600,,,,,,10000 - 20000,pt,0.05903192135784184 +,3600 - 4600,,,,,,10000 - 20000,ride,0.00920368437432668 +,3600 - 4600,,,,,,10000 - 20000,walk,0.00014999177077495683 +,3600 - 4600,,,,,,20000+,bike,0.0006363087393218901 +,3600 - 4600,,,,,,20000+,car,0.02897031249884871 +,3600 - 4600,,,,,,20000+,pt,0.022942615139097094 +,3600 - 4600,,,,,,20000+,ride,0.004525654554221921 +,3600 - 4600,,,,,,20000+,walk,0.00011238935833910546 +,4600 - 5600,,,,,,0 - 1000,bike,0.048710281958938 +,4600 - 5600,,,,,,0 - 1000,car,0.01068288420014341 +,4600 - 5600,,,,,,0 - 1000,pt,0.0010734236028677698 +,4600 - 5600,,,,,,0 - 1000,ride,0.0044911703056080415 +,4600 - 5600,,,,,,0 - 1000,walk,0.20465119449744432 +,4600 - 5600,,,,,,1000 - 2000,bike,0.05304569917621215 +,4600 - 5600,,,,,,1000 - 2000,car,0.022262130681615536 +,4600 - 5600,,,,,,1000 - 2000,pt,0.014945223597211262 +,4600 - 5600,,,,,,1000 - 2000,ride,0.011531298350131435 +,4600 - 5600,,,,,,1000 - 2000,walk,0.04647191584785695 +,4600 - 5600,,,,,,2000 - 5000,bike,0.06347649814821983 +,4600 - 5600,,,,,,2000 - 5000,car,0.05620379250609639 +,4600 - 5600,,,,,,2000 - 5000,pt,0.058589829885607216 +,4600 - 5600,,,,,,2000 - 5000,ride,0.027753154917548345 +,4600 - 5600,,,,,,2000 - 5000,walk,0.009222595726733103 +,4600 - 5600,,,,,,5000 - 10000,bike,0.03247204038258978 +,4600 - 5600,,,,,,5000 - 10000,car,0.05593631594583977 +,4600 - 5600,,,,,,5000 - 10000,pt,0.0731190883691118 +,4600 - 5600,,,,,,5000 - 10000,ride,0.016857350757699483 +,4600 - 5600,,,,,,5000 - 10000,walk,0.0010029335475866892 +,4600 - 5600,,,,,,10000 - 20000,bike,0.006938126345210255 +,4600 - 5600,,,,,,10000 - 20000,car,0.050211715343122625 +,4600 - 5600,,,,,,10000 - 20000,pt,0.058945610288378684 +,4600 - 5600,,,,,,10000 - 20000,ride,0.010571442804107264 +,4600 - 5600,,,,,,10000 - 20000,walk,0.00019152349776608256 +,4600 - 5600,,,,,,20000+,bike,0.000653798655099752 +,4600 - 5600,,,,,,20000+,car,0.026895233621084425 +,4600 - 5600,,,,,,20000+,pt,0.028210661638814544 +,4600 - 5600,,,,,,20000+,ride,0.004537153391440641 +,4600 - 5600,,,,,,20000+,walk,0.00034591200991445915 +,5600+,,,,,,0 - 1000,bike,0.04081231520885232 +,5600+,,,,,,0 - 1000,car,0.011485812208722163 +,5600+,,,,,,0 - 1000,pt,0.002109615891521199 +,5600+,,,,,,0 - 1000,ride,0.005887824735804848 +,5600+,,,,,,0 - 1000,walk,0.205638840209066 +,5600+,,,,,,1000 - 2000,bike,0.05297341416560734 +,5600+,,,,,,1000 - 2000,car,0.025241394336974542 +,5600+,,,,,,1000 - 2000,pt,0.012876743472278455 +,5600+,,,,,,1000 - 2000,ride,0.014842482652864112 +,5600+,,,,,,1000 - 2000,walk,0.045522632703706566 +,5600+,,,,,,2000 - 5000,bike,0.061966221346868716 +,5600+,,,,,,2000 - 5000,car,0.05919732134622661 +,5600+,,,,,,2000 - 5000,pt,0.06030740476401822 +,5600+,,,,,,2000 - 5000,ride,0.02748154737239418 +,5600+,,,,,,2000 - 5000,walk,0.010299553904679378 +,5600+,,,,,,5000 - 10000,bike,0.02832312003019088 +,5600+,,,,,,5000 - 10000,car,0.05790663319230656 +,5600+,,,,,,5000 - 10000,pt,0.0670964118308329 +,5600+,,,,,,5000 - 10000,ride,0.016905860619605945 +,5600+,,,,,,5000 - 10000,walk,0.0017270482364382673 +,5600+,,,,,,10000 - 20000,bike,0.009517480524723213 +,5600+,,,,,,10000 - 20000,car,0.05232446569437673 +,5600+,,,,,,10000 - 20000,pt,0.05913464168614534 +,5600+,,,,,,10000 - 20000,ride,0.010731603541464917 +,5600+,,,,,,10000 - 20000,walk,0.0011074350420896743 +,5600+,,,,,,20000+,bike,0.0012576114443223207 +,5600+,,,,,,20000+,car,0.028100295532542622 +,5600+,,,,,,20000+,pt,0.02486302599822139 +,5600+,,,,,,20000+,ride,0.0042120823120815505 +,5600+,,,,,,20000+,walk,0.00014915999507293675 +,,very_low,,,,,0 - 1000,bike,0.03470681238249625 +,,very_low,,,,,0 - 1000,car,0.00664566014131732 +,,very_low,,,,,0 - 1000,pt,0.00760869670923257 +,,very_low,,,,,0 - 1000,ride,0.003124728592942877 +,,very_low,,,,,0 - 1000,walk,0.24469895082287613 +,,very_low,,,,,1000 - 2000,bike,0.04568124665405379 +,,very_low,,,,,1000 - 2000,car,0.01762264405555989 +,,very_low,,,,,1000 - 2000,pt,0.03176643067456601 +,,very_low,,,,,1000 - 2000,ride,0.009316330422067991 +,,very_low,,,,,1000 - 2000,walk,0.06872298007466462 +,,very_low,,,,,2000 - 5000,bike,0.050899816256695075 +,,very_low,,,,,2000 - 5000,car,0.033749602344892396 +,,very_low,,,,,2000 - 5000,pt,0.09486902097108738 +,,very_low,,,,,2000 - 5000,ride,0.015788943796908508 +,,very_low,,,,,2000 - 5000,walk,0.017570734627488975 +,,very_low,,,,,5000 - 10000,bike,0.02275352649147605 +,,very_low,,,,,5000 - 10000,car,0.027723850662754297 +,,very_low,,,,,5000 - 10000,pt,0.10839849171594336 +,,very_low,,,,,5000 - 10000,ride,0.012009036032519245 +,,very_low,,,,,5000 - 10000,walk,0.0015394033588941112 +,,very_low,,,,,10000 - 20000,bike,0.004407524031423659 +,,very_low,,,,,10000 - 20000,car,0.019387817923563564 +,,very_low,,,,,10000 - 20000,pt,0.0734242401915033 +,,very_low,,,,,10000 - 20000,ride,0.004587556923703756 +,,very_low,,,,,10000 - 20000,walk,0.001209125736696242 +,,very_low,,,,,20000+,bike,0.00041095332498231995 +,,very_low,,,,,20000+,car,0.009550733296281531 +,,very_low,,,,,20000+,pt,0.027400397002285904 +,,very_low,,,,,20000+,ride,0.004042413867625509 +,,very_low,,,,,20000+,walk,0.00038233091349725186 +,,low,,,,,0 - 1000,bike,0.041246470043080165 +,,low,,,,,0 - 1000,car,0.008910466432937744 +,,low,,,,,0 - 1000,pt,0.004293903536684401 +,,low,,,,,0 - 1000,ride,0.0033071684223682056 +,,low,,,,,0 - 1000,walk,0.24947973724879838 +,,low,,,,,1000 - 2000,bike,0.04961773460207695 +,,low,,,,,1000 - 2000,car,0.019329953900366764 +,,low,,,,,1000 - 2000,pt,0.025483245683043533 +,,low,,,,,1000 - 2000,ride,0.009185948988387805 +,,low,,,,,1000 - 2000,walk,0.05237722044190244 +,,low,,,,,2000 - 5000,bike,0.052466234584333846 +,,low,,,,,2000 - 5000,car,0.04278374195939727 +,,low,,,,,2000 - 5000,pt,0.08432283560598662 +,,low,,,,,2000 - 5000,ride,0.01702898827541261 +,,low,,,,,2000 - 5000,walk,0.015885053481139822 +,,low,,,,,5000 - 10000,bike,0.022672784003795167 +,,low,,,,,5000 - 10000,car,0.04475208451328922 +,,low,,,,,5000 - 10000,pt,0.09238030305339276 +,,low,,,,,5000 - 10000,ride,0.012354387654785704 +,,low,,,,,5000 - 10000,walk,0.0013482692004642123 +,,low,,,,,10000 - 20000,bike,0.00606581254281994 +,,low,,,,,10000 - 20000,car,0.031209843665855812 +,,low,,,,,10000 - 20000,pt,0.0653262065900355 +,,low,,,,,10000 - 20000,ride,0.006909609553827318 +,,low,,,,,10000 - 20000,walk,0.0005263054577439737 +,,low,,,,,20000+,bike,0.00047003197176413144 +,,low,,,,,20000+,car,0.013483593316552238 +,,low,,,,,20000+,pt,0.023496583359092254 +,,low,,,,,20000+,ride,0.0032589486332435533 +,,low,,,,,20000+,walk,2.6533277421522046e-05 +,,medium,,,,,0 - 1000,bike,0.03911700063927027 +,,medium,,,,,0 - 1000,car,0.011217731375356293 +,,medium,,,,,0 - 1000,pt,0.0036674145395910263 +,,medium,,,,,0 - 1000,ride,0.0040009596846765074 +,,medium,,,,,0 - 1000,walk,0.2331501383860879 +,,medium,,,,,1000 - 2000,bike,0.04558521294999408 +,,medium,,,,,1000 - 2000,car,0.02544280536333108 +,,medium,,,,,1000 - 2000,pt,0.019434896147692903 +,,medium,,,,,1000 - 2000,ride,0.009855997679676024 +,,medium,,,,,1000 - 2000,walk,0.0537914970446092 +,,medium,,,,,2000 - 5000,bike,0.05473092890667934 +,,medium,,,,,2000 - 5000,car,0.05384396289524313 +,,medium,,,,,2000 - 5000,pt,0.06617655580215061 +,,medium,,,,,2000 - 5000,ride,0.01916203987731777 +,,medium,,,,,2000 - 5000,walk,0.013040227239873697 +,,medium,,,,,5000 - 10000,bike,0.02574636249329226 +,,medium,,,,,5000 - 10000,car,0.05641048735516864 +,,medium,,,,,5000 - 10000,pt,0.08034283586815048 +,,medium,,,,,5000 - 10000,ride,0.01428950786852113 +,,medium,,,,,5000 - 10000,walk,0.0018418487953256962 +,,medium,,,,,10000 - 20000,bike,0.006346523947575605 +,,medium,,,,,10000 - 20000,car,0.04150377161860774 +,,medium,,,,,10000 - 20000,pt,0.0629525618466731 +,,medium,,,,,10000 - 20000,ride,0.0074802925048469545 +,,medium,,,,,10000 - 20000,walk,0.0003904121475787536 +,,medium,,,,,20000+,bike,0.000680433218446468 +,,medium,,,,,20000+,car,0.021857757194240046 +,,medium,,,,,20000+,pt,0.023834126105462355 +,,medium,,,,,20000+,ride,0.003906827817956772 +,,medium,,,,,20000+,walk,0.00019888268660406687 +,,high,,,,,0 - 1000,bike,0.041939214472666166 +,,high,,,,,0 - 1000,car,0.009296976205872368 +,,high,,,,,0 - 1000,pt,0.0019245320576424141 +,,high,,,,,0 - 1000,ride,0.0029544349795922243 +,,high,,,,,0 - 1000,walk,0.21013293332150743 +,,high,,,,,1000 - 2000,bike,0.046923469208975645 +,,high,,,,,1000 - 2000,car,0.022359163419126016 +,,high,,,,,1000 - 2000,pt,0.01598105459224138 +,,high,,,,,1000 - 2000,ride,0.00969781710878335 +,,high,,,,,1000 - 2000,walk,0.04629924755619724 +,,high,,,,,2000 - 5000,bike,0.06385560203538197 +,,high,,,,,2000 - 5000,car,0.05664195116716653 +,,high,,,,,2000 - 5000,pt,0.06341010532730178 +,,high,,,,,2000 - 5000,ride,0.02062977816711411 +,,high,,,,,2000 - 5000,walk,0.013022335460471058 +,,high,,,,,5000 - 10000,bike,0.03340249625957814 +,,high,,,,,5000 - 10000,car,0.05522082435189495 +,,high,,,,,5000 - 10000,pt,0.0768163134346059 +,,high,,,,,5000 - 10000,ride,0.014151567281204704 +,,high,,,,,5000 - 10000,walk,0.0010048399305621642 +,,high,,,,,10000 - 20000,bike,0.009283542374603758 +,,high,,,,,10000 - 20000,car,0.05082160352876232 +,,high,,,,,10000 - 20000,pt,0.06361373744580245 +,,high,,,,,10000 - 20000,ride,0.009672528719534406 +,,high,,,,,10000 - 20000,walk,0.000206579718736878 +,,high,,,,,20000+,bike,0.00043427613503130015 +,,high,,,,,20000+,car,0.0302730459828126 +,,high,,,,,20000+,pt,0.025319864147407976 +,,high,,,,,20000+,ride,0.004383208486815366 +,,high,,,,,20000+,walk,0.00032695712260737283 +,,very_high,,,,,0 - 1000,bike,0.04115639089698632 +,,very_high,,,,,0 - 1000,car,0.012722254325776056 +,,very_high,,,,,0 - 1000,pt,0.0021241404664278096 +,,very_high,,,,,0 - 1000,ride,0.006479698270689627 +,,very_high,,,,,0 - 1000,walk,0.21073962672422653 +,,very_high,,,,,1000 - 2000,bike,0.04962428972352181 +,,very_high,,,,,1000 - 2000,car,0.02750613621612044 +,,very_high,,,,,1000 - 2000,pt,0.013034837734497939 +,,very_high,,,,,1000 - 2000,ride,0.015305658534843499 +,,very_high,,,,,1000 - 2000,walk,0.044574423999390184 +,,very_high,,,,,2000 - 5000,bike,0.054368817956349784 +,,very_high,,,,,2000 - 5000,car,0.06181110351680053 +,,very_high,,,,,2000 - 5000,pt,0.05826964813749158 +,,very_high,,,,,2000 - 5000,ride,0.02786841949326947 +,,very_high,,,,,2000 - 5000,walk,0.008853536562096663 +,,very_high,,,,,5000 - 10000,bike,0.02620750926316137 +,,very_high,,,,,5000 - 10000,car,0.06319670192405069 +,,very_high,,,,,5000 - 10000,pt,0.06408662985856173 +,,very_high,,,,,5000 - 10000,ride,0.016694136150129876 +,,very_high,,,,,5000 - 10000,walk,0.0017755406053744846 +,,very_high,,,,,10000 - 20000,bike,0.00916609078166196 +,,very_high,,,,,10000 - 20000,car,0.05422199392062498 +,,very_high,,,,,10000 - 20000,pt,0.05651365669095907 +,,very_high,,,,,10000 - 20000,ride,0.010116960000757621 +,,very_high,,,,,10000 - 20000,walk,0.0011846243905422437 +,,very_high,,,,,20000+,bike,0.001489759414370527 +,,very_high,,,,,20000+,car,0.03041067782980122 +,,very_high,,,,,20000+,pt,0.026469066188086976 +,,very_high,,,,,20000+,ride,0.003984235726180184 +,,very_high,,,,,20000+,walk,4.343469724887364e-05 +,,,child,,,,0 - 1000,bike,0.08797447704562544 +,,,child,,,,0 - 1000,car,6.201632379149975e-05 +,,,child,,,,0 - 1000,pt,0.0024803584947749153 +,,,child,,,,0 - 1000,ride,0.022491490648019048 +,,,child,,,,0 - 1000,walk,0.34497127297047747 +,,,child,,,,1000 - 2000,bike,0.07427123298204033 +,,,child,,,,1000 - 2000,car,8.634641491562534e-05 +,,,child,,,,1000 - 2000,pt,0.024748734059472668 +,,,child,,,,1000 - 2000,ride,0.05920313740289035 +,,,child,,,,1000 - 2000,walk,0.08264286445303023 +,,,child,,,,2000 - 5000,bike,0.040113313236642925 +,,,child,,,,2000 - 5000,car,0.0 +,,,child,,,,2000 - 5000,pt,0.04788791080274093 +,,,child,,,,2000 - 5000,ride,0.08746818283628316 +,,,child,,,,2000 - 5000,walk,0.01079786692437333 +,,,child,,,,5000 - 10000,bike,0.004805099180886954 +,,,child,,,,5000 - 10000,car,0.0 +,,,child,,,,5000 - 10000,pt,0.02483365972541551 +,,,child,,,,5000 - 10000,ride,0.04367184908604869 +,,,child,,,,5000 - 10000,walk,0.0012955063068117055 +,,,child,,,,10000 - 20000,bike,0.0 +,,,child,,,,10000 - 20000,car,0.0 +,,,child,,,,10000 - 20000,pt,0.006359525422654798 +,,,child,,,,10000 - 20000,ride,0.022509064689085725 +,,,child,,,,10000 - 20000,walk,0.0005490547723028881 +,,,child,,,,20000+,bike,0.0 +,,,child,,,,20000+,car,0.0 +,,,child,,,,20000+,pt,0.003692397229544375 +,,,child,,,,20000+,ride,0.006852048186314848 +,,,child,,,,20000+,walk,0.00023259080585655162 +,,,homemaker,,,,0 - 1000,bike,0.033151806966215495 +,,,homemaker,,,,0 - 1000,car,0.017096887825047154 +,,,homemaker,,,,0 - 1000,pt,0.00741600650756041 +,,,homemaker,,,,0 - 1000,ride,0.003227524589354174 +,,,homemaker,,,,0 - 1000,walk,0.2885201729519205 +,,,homemaker,,,,1000 - 2000,bike,0.059202386584587534 +,,,homemaker,,,,1000 - 2000,car,0.04025033806294192 +,,,homemaker,,,,1000 - 2000,pt,0.0273866228320414 +,,,homemaker,,,,1000 - 2000,ride,0.006330981798157926 +,,,homemaker,,,,1000 - 2000,walk,0.07860929727267789 +,,,homemaker,,,,2000 - 5000,bike,0.04046916174769916 +,,,homemaker,,,,2000 - 5000,car,0.09370896621243806 +,,,homemaker,,,,2000 - 5000,pt,0.06043872284166353 +,,,homemaker,,,,2000 - 5000,ride,0.01865689653854778 +,,,homemaker,,,,2000 - 5000,walk,0.022329877577321945 +,,,homemaker,,,,5000 - 10000,bike,0.01007054931926354 +,,,homemaker,,,,5000 - 10000,car,0.0517419983300954 +,,,homemaker,,,,5000 - 10000,pt,0.03961389187595275 +,,,homemaker,,,,5000 - 10000,ride,0.010868096212678242 +,,,homemaker,,,,5000 - 10000,walk,0.001119555974530009 +,,,homemaker,,,,10000 - 20000,bike,0.0013630691808948023 +,,,homemaker,,,,10000 - 20000,car,0.03297441162678622 +,,,homemaker,,,,10000 - 20000,pt,0.02062081117537517 +,,,homemaker,,,,10000 - 20000,ride,0.011126178544959902 +,,,homemaker,,,,10000 - 20000,walk,0.00036647317815220574 +,,,homemaker,,,,20000+,bike,0.0 +,,,homemaker,,,,20000+,car,0.009155831126737714 +,,,homemaker,,,,20000+,pt,0.012519960684027233 +,,,homemaker,,,,20000+,ride,0.0008732491830433166 +,,,homemaker,,,,20000+,walk,0.0007902732793286338 +,,,retiree,,,,0 - 1000,bike,0.02803433594931146 +,,,retiree,,,,0 - 1000,car,0.015941591246107053 +,,,retiree,,,,0 - 1000,pt,0.008575659044152376 +,,,retiree,,,,0 - 1000,ride,0.004703006319317826 +,,,retiree,,,,0 - 1000,walk,0.2846270602599153 +,,,retiree,,,,1000 - 2000,bike,0.028632402921317548 +,,,retiree,,,,1000 - 2000,car,0.03832910463414241 +,,,retiree,,,,1000 - 2000,pt,0.032952941858310617 +,,,retiree,,,,1000 - 2000,ride,0.01168231876013348 +,,,retiree,,,,1000 - 2000,walk,0.06888892277570885 +,,,retiree,,,,2000 - 5000,bike,0.024130989117319465 +,,,retiree,,,,2000 - 5000,car,0.08303426111525544 +,,,retiree,,,,2000 - 5000,pt,0.06608361005813324 +,,,retiree,,,,2000 - 5000,ride,0.022716231901212754 +,,,retiree,,,,2000 - 5000,walk,0.019871262845775835 +,,,retiree,,,,5000 - 10000,bike,0.006523730886384607 +,,,retiree,,,,5000 - 10000,car,0.06094744140940063 +,,,retiree,,,,5000 - 10000,pt,0.053010003113323496 +,,,retiree,,,,5000 - 10000,ride,0.017547687886227004 +,,,retiree,,,,5000 - 10000,walk,0.0021268832316059514 +,,,retiree,,,,10000 - 20000,bike,0.0027508467688472844 +,,,retiree,,,,10000 - 20000,car,0.033492350519474676 +,,,retiree,,,,10000 - 20000,pt,0.03546978470611497 +,,,retiree,,,,10000 - 20000,ride,0.011801193617084637 +,,,retiree,,,,10000 - 20000,walk,0.0008557046894452218 +,,,retiree,,,,20000+,bike,0.00023940434380408774 +,,,retiree,,,,20000+,car,0.017904493427535573 +,,,retiree,,,,20000+,pt,0.012123209640667017 +,,,retiree,,,,20000+,ride,0.006940672029918143 +,,,retiree,,,,20000+,walk,6.289492405314406e-05 +,,,unemployed,,,,0 - 1000,bike,0.028473036460034625 +,,,unemployed,,,,0 - 1000,car,0.01206763565093488 +,,,unemployed,,,,0 - 1000,pt,0.0066246780260138475 +,,,unemployed,,,,0 - 1000,ride,0.0002788219068772566 +,,,unemployed,,,,0 - 1000,walk,0.2900101906591626 +,,,unemployed,,,,1000 - 2000,bike,0.06262667569850242 +,,,unemployed,,,,1000 - 2000,car,0.028896506543494183 +,,,unemployed,,,,1000 - 2000,pt,0.02660096577041169 +,,,unemployed,,,,1000 - 2000,ride,0.004606811635231683 +,,,unemployed,,,,1000 - 2000,walk,0.06174361569499453 +,,,unemployed,,,,2000 - 5000,bike,0.049515654255582325 +,,,unemployed,,,,2000 - 5000,car,0.039677051190551225 +,,,unemployed,,,,2000 - 5000,pt,0.074237083423941 +,,,unemployed,,,,2000 - 5000,ride,0.006255362566946953 +,,,unemployed,,,,2000 - 5000,walk,0.029885632074737396 +,,,unemployed,,,,5000 - 10000,bike,0.0253519458398073 +,,,unemployed,,,,5000 - 10000,car,0.03422879715974879 +,,,unemployed,,,,5000 - 10000,pt,0.08366977505423692 +,,,unemployed,,,,5000 - 10000,ride,0.005622178708513779 +,,,unemployed,,,,5000 - 10000,walk,0.0 +,,,unemployed,,,,10000 - 20000,bike,0.00417328864141108 +,,,unemployed,,,,10000 - 20000,car,0.013674574163855544 +,,,unemployed,,,,10000 - 20000,pt,0.0734624973685337 +,,,unemployed,,,,10000 - 20000,ride,0.0060156277308030595 +,,,unemployed,,,,10000 - 20000,walk,0.003155252756840654 +,,,unemployed,,,,20000+,bike,0.0 +,,,unemployed,,,,20000+,car,0.007882285969591084 +,,,unemployed,,,,20000+,pt,0.020180847789405588 +,,,unemployed,,,,20000+,ride,0.0010832072598357227 +,,,unemployed,,,,20000+,walk,0.0 +,,,school,,,,0 - 1000,bike,0.0619376780579444 +,,,school,,,,0 - 1000,car,0.00018646363364950395 +,,,school,,,,0 - 1000,pt,0.0036144502125357576 +,,,school,,,,0 - 1000,ride,0.009476152922497092 +,,,school,,,,0 - 1000,walk,0.23637839402380614 +,,,school,,,,1000 - 2000,bike,0.09030920523969665 +,,,school,,,,1000 - 2000,car,0.0006203364901544102 +,,,school,,,,1000 - 2000,pt,0.02679399804666396 +,,,school,,,,1000 - 2000,ride,0.02798996132264418 +,,,school,,,,1000 - 2000,walk,0.051908607065914554 +,,,school,,,,2000 - 5000,bike,0.0802477950696995 +,,,school,,,,2000 - 5000,car,0.0022886729071440455 +,,,school,,,,2000 - 5000,pt,0.13269861709274064 +,,,school,,,,2000 - 5000,ride,0.056685545379448206 +,,,school,,,,2000 - 5000,walk,0.006499265127616391 +,,,school,,,,5000 - 10000,bike,0.009360364028456122 +,,,school,,,,5000 - 10000,car,0.0016790952562727782 +,,,school,,,,5000 - 10000,pt,0.10277327099819801 +,,,school,,,,5000 - 10000,ride,0.035425118596691396 +,,,school,,,,5000 - 10000,walk,0.00038678535209195385 +,,,school,,,,10000 - 20000,bike,0.0005980589328777765 +,,,school,,,,10000 - 20000,car,0.00132075569093999 +,,,school,,,,10000 - 20000,pt,0.03797995833954222 +,,,school,,,,10000 - 20000,ride,0.010978356237460405 +,,,school,,,,10000 - 20000,walk,0.00017780241335867242 +,,,school,,,,20000+,bike,0.00022130407576553514 +,,,school,,,,20000+,car,0.0003848559121911246 +,,,school,,,,20000+,pt,0.006654790004719217 +,,,school,,,,20000+,ride,0.004025326946849407 +,,,school,,,,20000+,walk,0.0003990146224299257 +,,,student,,,,0 - 1000,bike,0.02356220023873468 +,,,student,,,,0 - 1000,car,0.004368932828123815 +,,,student,,,,0 - 1000,pt,0.0037978777376658055 +,,,student,,,,0 - 1000,ride,0.001523895793406923 +,,,student,,,,0 - 1000,walk,0.17076517686042786 +,,,student,,,,1000 - 2000,bike,0.03738080250314786 +,,,student,,,,1000 - 2000,car,0.007682269194679312 +,,,student,,,,1000 - 2000,pt,0.02217394556697622 +,,,student,,,,1000 - 2000,ride,0.0026565452052639033 +,,,student,,,,1000 - 2000,walk,0.0545770671484238 +,,,student,,,,2000 - 5000,bike,0.06432907652809451 +,,,student,,,,2000 - 5000,car,0.019100392334253705 +,,,student,,,,2000 - 5000,pt,0.11217963558936651 +,,,student,,,,2000 - 5000,ride,0.009152359825369636 +,,,student,,,,2000 - 5000,walk,0.01642894417545653 +,,,student,,,,5000 - 10000,bike,0.03403760366604839 +,,,student,,,,5000 - 10000,car,0.022849192327934077 +,,,student,,,,5000 - 10000,pt,0.1641983623123737 +,,,student,,,,5000 - 10000,ride,0.006876201166208598 +,,,student,,,,5000 - 10000,walk,0.001068722058597145 +,,,student,,,,10000 - 20000,bike,0.006446614720123388 +,,,student,,,,10000 - 20000,car,0.01861014858339467 +,,,student,,,,10000 - 20000,pt,0.1186036950293715 +,,,student,,,,10000 - 20000,ride,0.005896199856583919 +,,,student,,,,10000 - 20000,walk,0.00035154920720382153 +,,,student,,,,20000+,bike,0.0006766977939909812 +,,,student,,,,20000+,car,0.013921636477701456 +,,,student,,,,20000+,pt,0.051929004231407894 +,,,student,,,,20000+,ride,0.004855251039669398 +,,,student,,,,20000+,walk,0.0 +,,,trainee,,,,0 - 1000,bike,0.012521503764836246 +,,,trainee,,,,0 - 1000,car,0.007298356193634069 +,,,trainee,,,,0 - 1000,pt,0.0016668796511095385 +,,,trainee,,,,0 - 1000,ride,0.0030825771686377942 +,,,trainee,,,,0 - 1000,walk,0.16500369285009003 +,,,trainee,,,,1000 - 2000,bike,0.01779794495007991 +,,,trainee,,,,1000 - 2000,car,0.003149993006655295 +,,,trainee,,,,1000 - 2000,pt,0.02268559042076619 +,,,trainee,,,,1000 - 2000,ride,0.0040664047997533695 +,,,trainee,,,,1000 - 2000,walk,0.03960900446868207 +,,,trainee,,,,2000 - 5000,bike,0.026071327681647948 +,,,trainee,,,,2000 - 5000,car,0.03644433692867858 +,,,trainee,,,,2000 - 5000,pt,0.08960321544062727 +,,,trainee,,,,2000 - 5000,ride,0.009613160445992438 +,,,trainee,,,,2000 - 5000,walk,0.004017733129450831 +,,,trainee,,,,5000 - 10000,bike,0.013474805871587335 +,,,trainee,,,,5000 - 10000,car,0.040396871970504115 +,,,trainee,,,,5000 - 10000,pt,0.15447499374463067 +,,,trainee,,,,5000 - 10000,ride,0.015751740222842803 +,,,trainee,,,,5000 - 10000,walk,0.0027646110583024472 +,,,trainee,,,,10000 - 20000,bike,0.003591734235218891 +,,,trainee,,,,10000 - 20000,car,0.030580148709696862 +,,,trainee,,,,10000 - 20000,pt,0.18379854961876674 +,,,trainee,,,,10000 - 20000,ride,0.011237305431667793 +,,,trainee,,,,10000 - 20000,walk,0.0 +,,,trainee,,,,20000+,bike,0.0006740957100523827 +,,,trainee,,,,20000+,car,0.02240635651365801 +,,,trainee,,,,20000+,pt,0.07244745989852527 +,,,trainee,,,,20000+,ride,0.0047900616550283995 +,,,trainee,,,,20000+,walk,0.0009795444588767178 +,,,job_full_time,,,,0 - 1000,bike,0.03013933778567867 +,,,job_full_time,,,,0 - 1000,car,0.010491876131548051 +,,,job_full_time,,,,0 - 1000,pt,0.001359880974463405 +,,,job_full_time,,,,0 - 1000,ride,0.0009737141067612931 +,,,job_full_time,,,,0 - 1000,walk,0.18555797811248625 +,,,job_full_time,,,,1000 - 2000,bike,0.037575281838997145 +,,,job_full_time,,,,1000 - 2000,car,0.024342572407602656 +,,,job_full_time,,,,1000 - 2000,pt,0.012125099939636425 +,,,job_full_time,,,,1000 - 2000,ride,0.002542177597938074 +,,,job_full_time,,,,1000 - 2000,walk,0.037130732560095164 +,,,job_full_time,,,,2000 - 5000,bike,0.06339556433079593 +,,,job_full_time,,,,2000 - 5000,car,0.0552753773757432 +,,,job_full_time,,,,2000 - 5000,pt,0.05667774866351121 +,,,job_full_time,,,,2000 - 5000,ride,0.006877058685280377 +,,,job_full_time,,,,2000 - 5000,walk,0.01074447339096398 +,,,job_full_time,,,,5000 - 10000,bike,0.0419792037561615 +,,,job_full_time,,,,5000 - 10000,car,0.07519810125586637 +,,,job_full_time,,,,5000 - 10000,pt,0.08791473887561047 +,,,job_full_time,,,,5000 - 10000,ride,0.00685184204142721 +,,,job_full_time,,,,5000 - 10000,walk,0.0016415405564862843 +,,,job_full_time,,,,10000 - 20000,bike,0.013184442284954296 +,,,job_full_time,,,,10000 - 20000,car,0.07129160225405813 +,,,job_full_time,,,,10000 - 20000,pt,0.08151361323102535 +,,,job_full_time,,,,10000 - 20000,ride,0.004736631628841786 +,,,job_full_time,,,,10000 - 20000,walk,0.0005586884147394083 +,,,job_full_time,,,,20000+,bike,0.0012490068587271434 +,,,job_full_time,,,,20000+,car,0.040567119638494215 +,,,job_full_time,,,,20000+,pt,0.03520659977092769 +,,,job_full_time,,,,20000+,ride,0.0026266559172975616 +,,,job_full_time,,,,20000+,walk,0.00027133961388065865 +,,,job_part_time,,,,0 - 1000,bike,0.05788930935252188 +,,,job_part_time,,,,0 - 1000,car,0.012600124011170702 +,,,job_part_time,,,,0 - 1000,pt,0.0023022363889259505 +,,,job_part_time,,,,0 - 1000,ride,0.0008752863426891199 +,,,job_part_time,,,,0 - 1000,walk,0.20651593711522592 +,,,job_part_time,,,,1000 - 2000,bike,0.06120183442984339 +,,,job_part_time,,,,1000 - 2000,car,0.026187254055777424 +,,,job_part_time,,,,1000 - 2000,pt,0.013670138011963498 +,,,job_part_time,,,,1000 - 2000,ride,0.0038638859763716928 +,,,job_part_time,,,,1000 - 2000,walk,0.03876773747170083 +,,,job_part_time,,,,2000 - 5000,bike,0.0764631165846227 +,,,job_part_time,,,,2000 - 5000,car,0.06256351536224046 +,,,job_part_time,,,,2000 - 5000,pt,0.0600216466376844 +,,,job_part_time,,,,2000 - 5000,ride,0.008006383164056257 +,,,job_part_time,,,,2000 - 5000,walk,0.011413641974962621 +,,,job_part_time,,,,5000 - 10000,bike,0.03941403115041603 +,,,job_part_time,,,,5000 - 10000,car,0.05348787502585056 +,,,job_part_time,,,,5000 - 10000,pt,0.08638440908282295 +,,,job_part_time,,,,5000 - 10000,ride,0.0055825350733728395 +,,,job_part_time,,,,5000 - 10000,walk,0.001733409888793159 +,,,job_part_time,,,,10000 - 20000,bike,0.008134087823778351 +,,,job_part_time,,,,10000 - 20000,car,0.03788210760082822 +,,,job_part_time,,,,10000 - 20000,pt,0.07732057340507803 +,,,job_part_time,,,,10000 - 20000,ride,0.0042621188147322865 +,,,job_part_time,,,,10000 - 20000,walk,0.0001262979901470656 +,,,job_part_time,,,,20000+,bike,0.0005251906869208823 +,,,job_part_time,,,,20000+,car,0.014722131884886766 +,,,job_part_time,,,,20000+,pt,0.025900077307611968 +,,,job_part_time,,,,20000+,ride,0.002065947509913174 +,,,job_part_time,,,,20000+,walk,0.00011715987509080905 +,,,other,,,,0 - 1000,bike,0.04930384107342923 +,,,other,,,,0 - 1000,car,0.011788223194744126 +,,,other,,,,0 - 1000,pt,0.0013729829565074935 +,,,other,,,,0 - 1000,ride,0.0017187249197642756 +,,,other,,,,0 - 1000,walk,0.2937438405365747 +,,,other,,,,1000 - 2000,bike,0.04544589572290246 +,,,other,,,,1000 - 2000,car,0.03192982545506678 +,,,other,,,,1000 - 2000,pt,0.01702973565121598 +,,,other,,,,1000 - 2000,ride,0.00312445438232703 +,,,other,,,,1000 - 2000,walk,0.09423363848630212 +,,,other,,,,2000 - 5000,bike,0.053031784306758735 +,,,other,,,,2000 - 5000,car,0.0587406381989171 +,,,other,,,,2000 - 5000,pt,0.06116382459167897 +,,,other,,,,2000 - 5000,ride,0.00818344587606724 +,,,other,,,,2000 - 5000,walk,0.021102897779573446 +,,,other,,,,5000 - 10000,bike,0.02352965810583334 +,,,other,,,,5000 - 10000,car,0.04601077851041131 +,,,other,,,,5000 - 10000,pt,0.06310661719139919 +,,,other,,,,5000 - 10000,ride,0.012425388935623692 +,,,other,,,,5000 - 10000,walk,0.0008639159974911152 +,,,other,,,,10000 - 20000,bike,0.0026416428973974806 +,,,other,,,,10000 - 20000,car,0.030993548409293373 +,,,other,,,,10000 - 20000,pt,0.03167623382196632 +,,,other,,,,10000 - 20000,ride,0.005060710378546935 +,,,other,,,,10000 - 20000,walk,0.0 +,,,other,,,,20000+,bike,0.0 +,,,other,,,,20000+,car,0.014056919018589648 +,,,other,,,,20000+,pt,0.01373268963452078 +,,,other,,,,20000+,ride,0.003868050560621929 +,,,other,,,,20000+,walk,0.00012009340647531543 +,,,,yes,,,0 - 1000,bike,0.03299714650207197 +,,,,yes,,,0 - 1000,car,0.017027002619882433 +,,,,yes,,,0 - 1000,pt,0.001447150167269608 +,,,,yes,,,0 - 1000,ride,0.006170949721414438 +,,,,yes,,,0 - 1000,walk,0.19741858334434997 +,,,,yes,,,1000 - 2000,bike,0.037957883407812 +,,,,yes,,,1000 - 2000,car,0.0394529320636818 +,,,,yes,,,1000 - 2000,pt,0.011451027027536226 +,,,,yes,,,1000 - 2000,ride,0.01641946900059602 +,,,,yes,,,1000 - 2000,walk,0.04651342478512875 +,,,,yes,,,2000 - 5000,bike,0.040949944088661795 +,,,,yes,,,2000 - 5000,car,0.08811056075566344 +,,,,yes,,,2000 - 5000,pt,0.039127179750233054 +,,,,yes,,,2000 - 5000,ride,0.03091813771250092 +,,,,yes,,,2000 - 5000,walk,0.011315043201306118 +,,,,yes,,,5000 - 10000,bike,0.019017621788004747 +,,,,yes,,,5000 - 10000,car,0.08791860269484927 +,,,,yes,,,5000 - 10000,pt,0.049812041547093816 +,,,,yes,,,5000 - 10000,ride,0.02156168933687161 +,,,,yes,,,5000 - 10000,walk,0.0013154378478287684 +,,,,yes,,,10000 - 20000,bike,0.007202583446410026 +,,,,yes,,,10000 - 20000,car,0.07045195617229356 +,,,,yes,,,10000 - 20000,pt,0.049749815066314804 +,,,,yes,,,10000 - 20000,ride,0.01138530413745259 +,,,,yes,,,10000 - 20000,walk,0.0004945201895780406 +,,,,yes,,,20000+,bike,0.0006992742746565023 +,,,,yes,,,20000+,car,0.03798906450435869 +,,,,yes,,,20000+,pt,0.0193490440421187 +,,,,yes,,,20000+,ride,0.005583932136757436 +,,,,yes,,,20000+,walk,0.0001926786673028211 +,,,,no,,,0 - 1000,bike,0.05634302463139612 +,,,,no,,,0 - 1000,car,0.00013584823212312846 +,,,,no,,,0 - 1000,pt,0.004223086048582885 +,,,,no,,,0 - 1000,ride,0.0004017508303949193 +,,,,no,,,0 - 1000,walk,0.2580471733726988 +,,,,no,,,1000 - 2000,bike,0.07836944366732389 +,,,,no,,,1000 - 2000,car,0.0010390801814425925 +,,,,no,,,1000 - 2000,pt,0.024784889955329636 +,,,,no,,,1000 - 2000,ride,0.001974025369215151 +,,,,no,,,1000 - 2000,walk,0.06126594139823154 +,,,,no,,,2000 - 5000,bike,0.0830645845503165 +,,,,no,,,2000 - 5000,car,0.0021086321349634155 +,,,,no,,,2000 - 5000,pt,0.11041897753164191 +,,,,no,,,2000 - 5000,ride,0.004147919011818219 +,,,,no,,,2000 - 5000,walk,0.014386694755001183 +,,,,no,,,5000 - 10000,bike,0.023823332264979735 +,,,,no,,,5000 - 10000,car,0.00388669513192251 +,,,,no,,,5000 - 10000,pt,0.1264514357248081 +,,,,no,,,5000 - 10000,ride,0.003639247377874085 +,,,,no,,,5000 - 10000,walk,0.001313036391096564 +,,,,no,,,10000 - 20000,bike,0.006499542597368016 +,,,,no,,,10000 - 20000,car,0.0023781765029886976 +,,,,no,,,10000 - 20000,pt,0.0898320669228972 +,,,,no,,,10000 - 20000,ride,0.002272185599137619 +,,,,no,,,10000 - 20000,walk,0.0002215003222691513 +,,,,no,,,20000+,bike,0.000145317955987046 +,,,,no,,,20000+,car,0.0011123694286944764 +,,,,no,,,20000+,pt,0.03502771576797686 +,,,,no,,,20000+,ride,0.0023288783488159014 +,,,,no,,,20000+,walk,0.00035742799270410425 +,,,,unknown,,,0 - 1000,bike,0.04769480779566642 +,,,,unknown,,,0 - 1000,car,0.0008590409073678141 +,,,,unknown,,,0 - 1000,pt,0.006885971870744252 +,,,,unknown,,,0 - 1000,ride,0.0006475563097244331 +,,,,unknown,,,0 - 1000,walk,0.27397601349162803 +,,,,unknown,,,1000 - 2000,bike,0.054805139453978344 +,,,,unknown,,,1000 - 2000,car,0.0012430677265334652 +,,,,unknown,,,1000 - 2000,pt,0.033242677055117804 +,,,,unknown,,,1000 - 2000,ride,0.001843007903016114 +,,,,unknown,,,1000 - 2000,walk,0.05955224874154208 +,,,,unknown,,,2000 - 5000,bike,0.0754255586265544 +,,,,unknown,,,2000 - 5000,car,0.0026690299351213756 +,,,,unknown,,,2000 - 5000,pt,0.11282652655099872 +,,,,unknown,,,2000 - 5000,ride,0.004859468679865614 +,,,,unknown,,,2000 - 5000,walk,0.016905290890743576 +,,,,unknown,,,5000 - 10000,bike,0.040669840042011456 +,,,,unknown,,,5000 - 10000,car,0.0040567764617673455 +,,,,unknown,,,5000 - 10000,pt,0.12634356429185695 +,,,,unknown,,,5000 - 10000,ride,0.0037836090315891847 +,,,,unknown,,,5000 - 10000,walk,0.0018965482493745298 +,,,,unknown,,,10000 - 20000,bike,0.007171532455515839 +,,,,unknown,,,10000 - 20000,car,0.002130589192925642 +,,,,unknown,,,10000 - 20000,pt,0.08127495675667207 +,,,,unknown,,,10000 - 20000,ride,0.003495551578556325 +,,,,unknown,,,10000 - 20000,walk,0.0006500800744487337 +,,,,unknown,,,20000+,bike,0.0006590572012220345 +,,,,unknown,,,20000+,car,0.0012317691156839004 +,,,,unknown,,,20000+,pt,0.03140217989755744 +,,,,unknown,,,20000+,ride,0.0016057119926328554 +,,,,unknown,,,20000+,walk,0.00019282771958326642 +,,,,,yes,,0 - 1000,bike,0.04698912487219602 +,,,,,yes,,0 - 1000,car,0.010350504080342545 +,,,,,yes,,0 - 1000,pt,0.002005078100267684 +,,,,,yes,,0 - 1000,ride,0.0025947594982208538 +,,,,,yes,,0 - 1000,walk,0.20633753959789913 +,,,,,yes,,1000 - 2000,bike,0.05672700261397835 +,,,,,yes,,1000 - 2000,car,0.02334919632026469 +,,,,,yes,,1000 - 2000,pt,0.014170631577363242 +,,,,,yes,,1000 - 2000,ride,0.007566742947430185 +,,,,,yes,,1000 - 2000,walk,0.04705114468466599 +,,,,,yes,,2000 - 5000,bike,0.07181957575497981 +,,,,,yes,,2000 - 5000,car,0.05407055126753428 +,,,,,yes,,2000 - 5000,pt,0.06176666900576435 +,,,,,yes,,2000 - 5000,ride,0.016015472342173622 +,,,,,yes,,2000 - 5000,walk,0.013218894993642248 +,,,,,yes,,5000 - 10000,bike,0.03532074465966296 +,,,,,yes,,5000 - 10000,car,0.055207432470111104 +,,,,,yes,,5000 - 10000,pt,0.08166242688776854 +,,,,,yes,,5000 - 10000,ride,0.01246459413877433 +,,,,,yes,,5000 - 10000,walk,0.001461452030304094 +,,,,,yes,,10000 - 20000,bike,0.00947171900707687 +,,,,,yes,,10000 - 20000,car,0.043678018613143035 +,,,,,yes,,10000 - 20000,pt,0.0666479672383282 +,,,,,yes,,10000 - 20000,ride,0.006622958957568501 +,,,,,yes,,10000 - 20000,walk,0.0003518277954969559 +,,,,,yes,,20000+,bike,0.0008554476676628036 +,,,,,yes,,20000+,car,0.022583799318947207 +,,,,,yes,,20000+,pt,0.02579374805433979 +,,,,,yes,,20000+,ride,0.003647041013066067 +,,,,,yes,,20000+,walk,0.0001979344910266161 +,,,,,no,,0 - 1000,bike,0.0039012262016867227 +,,,,,no,,0 - 1000,car,0.011058642330257716 +,,,,,no,,0 - 1000,pt,0.005486344364754511 +,,,,,no,,0 - 1000,ride,0.005508506862313705 +,,,,,no,,0 - 1000,walk,0.23710900428666265 +,,,,,no,,1000 - 2000,bike,0.0075046230570577285 +,,,,,no,,1000 - 2000,car,0.024841272479000517 +,,,,,no,,1000 - 2000,pt,0.032970722587121595 +,,,,,no,,1000 - 2000,ride,0.008363099312115381 +,,,,,no,,1000 - 2000,walk,0.0599070025225657 +,,,,,no,,2000 - 5000,bike,0.0030356396566586616 +,,,,,no,,2000 - 5000,car,0.054358518223715485 +,,,,,no,,2000 - 5000,pt,0.11169934543132519 +,,,,,no,,2000 - 5000,ride,0.01979098826873427 +,,,,,no,,2000 - 5000,walk,0.014730110159550966 +,,,,,no,,5000 - 10000,bike,0.001725264051395107 +,,,,,no,,5000 - 10000,car,0.05595315897447628 +,,,,,no,,5000 - 10000,pt,0.11104574218043785 +,,,,,no,,5000 - 10000,ride,0.01866749245961559 +,,,,,no,,5000 - 10000,walk,0.0009527341528081855 +,,,,,no,,10000 - 20000,bike,0.00010772404870398959 +,,,,,no,,10000 - 20000,car,0.04708939066977534 +,,,,,no,,10000 - 20000,pt,0.08253975507167001 +,,,,,no,,10000 - 20000,ride,0.013133193905086457 +,,,,,no,,10000 - 20000,walk,0.00011342697602954677 +,,,,,no,,20000+,bike,0.0 +,,,,,no,,20000+,car,0.030346166956882623 +,,,,,no,,20000+,pt,0.030469023737508497 +,,,,,no,,20000+,ride,0.007204882433134221 +,,,,,no,,20000+,walk,0.00038699863895556185 +,,,,,unknown,,0 - 1000,bike,0.02196874356601975 +,,,,,unknown,,0 - 1000,car,0.008865973925200346 +,,,,,unknown,,0 - 1000,pt,0.008820595658083127 +,,,,,unknown,,0 - 1000,ride,0.007825311269823323 +,,,,,unknown,,0 - 1000,walk,0.30926153323029304 +,,,,,unknown,,1000 - 2000,bike,0.019899996897947855 +,,,,,unknown,,1000 - 2000,car,0.02204018573997509 +,,,,,unknown,,1000 - 2000,pt,0.03890621393413655 +,,,,,unknown,,1000 - 2000,ride,0.020312569496132766 +,,,,,unknown,,1000 - 2000,walk,0.06940078290988638 +,,,,,unknown,,2000 - 5000,bike,0.010804071418919524 +,,,,,unknown,,2000 - 5000,car,0.0421588846988312 +,,,,,unknown,,2000 - 5000,pt,0.09282163808907376 +,,,,,unknown,,2000 - 5000,ride,0.033425026327763716 +,,,,,unknown,,2000 - 5000,walk,0.014267811127275818 +,,,,,unknown,,5000 - 10000,bike,0.0016996109909697531 +,,,,,unknown,,5000 - 10000,car,0.04014865505890483 +,,,,,unknown,,5000 - 10000,pt,0.07910634210857279 +,,,,,unknown,,5000 - 10000,ride,0.018465090116188528 +,,,,,unknown,,5000 - 10000,walk,0.0018455430781873152 +,,,,,unknown,,10000 - 20000,bike,0.00011814971112985102 +,,,,,unknown,,10000 - 20000,car,0.03155496878441373 +,,,,,unknown,,10000 - 20000,pt,0.04959157127841764 +,,,,,unknown,,10000 - 20000,ride,0.011563902896211451 +,,,,,unknown,,10000 - 20000,walk,0.0012595592388616737 +,,,,,unknown,,20000+,bike,0.0 +,,,,,unknown,,20000+,car,0.01955813542665283 +,,,,,unknown,,20000+,pt,0.019790102528474483 +,,,,,unknown,,20000+,ride,0.004324709516604467 +,,,,,unknown,,20000+,walk,0.00019432097704832695 +,,,,,,yes,0 - 1000,bike,0.026877735272587144 +,,,,,,yes,0 - 1000,car,0.004609738415460028 +,,,,,,yes,0 - 1000,pt,0.0056114180907021445 +,,,,,,yes,0 - 1000,ride,0.002009607976268384 +,,,,,,yes,0 - 1000,walk,0.2170462833410825 +,,,,,,yes,1000 - 2000,bike,0.0313461177979366 +,,,,,,yes,1000 - 2000,car,0.011886217774270638 +,,,,,,yes,1000 - 2000,pt,0.03268994045589096 +,,,,,,yes,1000 - 2000,ride,0.004885892680583904 +,,,,,,yes,1000 - 2000,walk,0.049037458291423135 +,,,,,,yes,2000 - 5000,bike,0.04151569490188568 +,,,,,,yes,2000 - 5000,car,0.02526366671773831 +,,,,,,yes,2000 - 5000,pt,0.11828715810574975 +,,,,,,yes,2000 - 5000,ride,0.013319548343406455 +,,,,,,yes,2000 - 5000,walk,0.012645027907752997 +,,,,,,yes,5000 - 10000,bike,0.02000968548822276 +,,,,,,yes,5000 - 10000,car,0.02496105021929467 +,,,,,,yes,5000 - 10000,pt,0.14380745218170304 +,,,,,,yes,5000 - 10000,ride,0.009869154521698925 +,,,,,,yes,5000 - 10000,walk,0.0015995635368503834 +,,,,,,yes,10000 - 20000,bike,0.0051539763386626285 +,,,,,,yes,10000 - 20000,car,0.018507075295630705 +,,,,,,yes,10000 - 20000,pt,0.11421724799309253 +,,,,,,yes,10000 - 20000,ride,0.006420798868946036 +,,,,,,yes,10000 - 20000,walk,0.0005065512201440519 +,,,,,,yes,20000+,bike,0.00045032754383129167 +,,,,,,yes,20000+,car,0.011373225663843514 +,,,,,,yes,20000+,pt,0.042587774643345226 +,,,,,,yes,20000+,ride,0.0032529008112295254 +,,,,,,yes,20000+,walk,0.0002517096007660357 +,,,,,,no,0 - 1000,bike,0.049203787113537144 +,,,,,,no,0 - 1000,car,0.017181996770518737 +,,,,,,no,0 - 1000,pt,0.0014676064701637842 +,,,,,,no,0 - 1000,ride,0.0037388193898886695 +,,,,,,no,0 - 1000,walk,0.22834165520840394 +,,,,,,no,1000 - 2000,bike,0.060967425010797105 +,,,,,,no,1000 - 2000,car,0.03802701934901353 +,,,,,,no,1000 - 2000,pt,0.0058198328118644485 +,,,,,,no,1000 - 2000,ride,0.010726983971031032 +,,,,,,no,1000 - 2000,walk,0.051980322210022724 +,,,,,,no,2000 - 5000,bike,0.07422011095918414 +,,,,,,no,2000 - 5000,car,0.08628816036798163 +,,,,,,no,2000 - 5000,pt,0.020461006554586395 +,,,,,,no,2000 - 5000,ride,0.019297423684489126 +,,,,,,no,2000 - 5000,walk,0.01468571778797613 +,,,,,,no,5000 - 10000,bike,0.036985386950286615 +,,,,,,no,5000 - 10000,car,0.08769873514183764 +,,,,,,no,5000 - 10000,pt,0.021860522255242685 +,,,,,,no,5000 - 10000,ride,0.015098401866535355 +,,,,,,no,5000 - 10000,walk,0.0014461399829227438 +,,,,,,no,10000 - 20000,bike,0.010107370860379439 +,,,,,,no,10000 - 20000,car,0.07086755599846502 +,,,,,,no,10000 - 20000,pt,0.01524471883856497 +,,,,,,no,10000 - 20000,ride,0.007864588085845437 +,,,,,,no,10000 - 20000,walk,0.0005447331264401868 +,,,,,,no,20000+,bike,0.0009236480015640312 +,,,,,,no,20000+,car,0.036775023310245354 +,,,,,,no,20000+,pt,0.007645033418080438 +,,,,,,no,20000+,ride,0.0043792848052615985 +,,,,,,no,20000+,walk,0.00015098969886978098 +,,,,,,unknown,0 - 1000,bike,0.08452578986227006 +,,,,,,unknown,0 - 1000,car,6.486504146205827e-05 +,,,,,,unknown,0 - 1000,pt,0.002175395846041459 +,,,,,,unknown,0 - 1000,ride,0.021684233861045763 +,,,,,,unknown,0 - 1000,walk,0.34731038526891134 +,,,,,,unknown,1000 - 2000,bike,0.07464418315020238 +,,,,,,unknown,1000 - 2000,car,0.000316211749246317 +,,,,,,unknown,1000 - 2000,pt,0.02428926385010706 +,,,,,,unknown,1000 - 2000,ride,0.05746270563561737 +,,,,,,unknown,1000 - 2000,walk,0.08500884635887647 +,,,,,,unknown,2000 - 5000,bike,0.03960672674551991 +,,,,,,unknown,2000 - 5000,car,0.0 +,,,,,,unknown,2000 - 5000,pt,0.04812409458714954 +,,,,,,unknown,2000 - 5000,ride,0.08632711015520911 +,,,,,,unknown,2000 - 5000,walk,0.01129386656497147 +,,,,,,unknown,5000 - 10000,bike,0.004812536200797506 +,,,,,,unknown,5000 - 10000,car,0.00044170618535102315 +,,,,,,unknown,5000 - 10000,pt,0.024705916309625302 +,,,,,,unknown,5000 - 10000,ride,0.043816530547673184 +,,,,,,unknown,5000 - 10000,walk,0.0013550153438346378 +,,,,,,unknown,10000 - 20000,bike,0.0 +,,,,,,unknown,10000 - 20000,car,0.0008468921174164319 +,,,,,,unknown,10000 - 20000,pt,0.006606733358829172 +,,,,,,unknown,10000 - 20000,ride,0.023100724541931888 +,,,,,,unknown,10000 - 20000,walk,0.0005742755841204714 +,,,,,,unknown,20000+,bike,0.0 +,,,,,,unknown,20000+,car,0.0 +,,,,,,unknown,20000+,pt,0.003862007367512052 +,,,,,,unknown,20000+,ride,0.006800708909865809 +,,,,,,unknown,20000+,walk,0.00024327485641202528