diff --git a/src/main/java/org/matsim/prepare/population/CalcIncome.java b/src/main/java/org/matsim/prepare/population/CalcIncome.java index 7510f846..d663a56f 100644 --- a/src/main/java/org/matsim/prepare/population/CalcIncome.java +++ b/src/main/java/org/matsim/prepare/population/CalcIncome.java @@ -1,83 +1,53 @@ package org.matsim.prepare.population; -import org.apache.commons.math3.distribution.EnumeratedIntegerDistribution; -import org.apache.commons.math3.random.Well19937c; import org.matsim.api.core.v01.population.Person; import org.matsim.core.population.PersonUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.PersonAlgorithm; -import java.util.HashMap; -import java.util.Map; import java.util.SplittableRandom; -import java.util.stream.IntStream; /** - * Draw income from distribution, according to household size and income group. Based on SrV income groups. + * Draw income from german wide distribution. */ public class CalcIncome implements PersonAlgorithm { - /** - * Income groups in Euro. The last element is the maximum income in the model, which is not known but defined. - * The minimum is also defined as well. - */ - private static final int[] INCOME_GROUPS = new int[]{450, 500, 900, 1500, 2000, 2600, 3000, 3600, 4600, 5600, 8000}; - - /** - * Distribution per economic status. See python file extract income. - */ - private static final Map INCOME_DIST = Map.of( - "very_low", new double[]{0.086, 0.342, 0.343, 0.165, 0.058, 0.004, 0.002, 0.000, 0.000, 0.000}, - "low", new double[]{0.000, 0.000, 0.443, 0.343, 0.123, 0.031, 0.056, 0.005, 0.000, 0.000}, - "medium", new double[]{0.000, 0.000, 0.000, 0.154, 0.324, 0.196, 0.237, 0.084, 0.005, 0.000}, - "high", new double[]{0.000, 0.000, 0.000, 0.000, 0.000, 0.066, 0.069, 0.433, 0.377, 0.055}, - "very_high", new double[]{0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.025, 0.975} - ); - private final SplittableRandom rnd = new SplittableRandom(1234); - private final Map dists = new HashMap<>(); - - - public CalcIncome() { - - for (Map.Entry e : INCOME_DIST.entrySet()) { - EnumeratedIntegerDistribution d = new EnumeratedIntegerDistribution( - new Well19937c(0), - IntStream.range(0, e.getValue().length).toArray(), e.getValue()); - dists.put(e.getKey(), d); - } - - } - @Override public void run(Person person) { - // Only handle persons if (!PopulationUtils.getSubpopulation(person).equals("person")) return; - int hh = (int) person.getAttributes().getAttribute(Attributes.HOUSEHOLD_SIZE); - String economicStatus = (String) person.getAttributes().getAttribute(Attributes.ECONOMIC_STATUS); - - // This is only approximate correct at best, finer grained income data is not available - // Economic status is normally per household and defined here: - // https://tu-dresden.de/bu/verkehr/ivs/srv/ressourcen/dateien/SrV2018_Tabellenbericht_Oberzentren_500TEW-_flach.pdf?lang=de - // page 17 - - EnumeratedIntegerDistribution dist = dists.get(economicStatus); - - int idx = dist.sample(); - - // income between lower and upper bound is uniformly sampled - int income = rnd.nextInt(INCOME_GROUPS[idx], INCOME_GROUPS[idx + 1]); - - // Income is divided equally to household - double perPerson = (double) income / hh; + // The income is german wide and not related to other attributes + // even though srv contains the (household) economic status, this can not be easily back calculated to the income + person.getAttributes().removeAttribute(Attributes.ECONOMIC_STATUS); + + // https://de.wikipedia.org/wiki/Einkommensverteilung_in_Deutschland + // besser https://www.destatis.de/DE/Themen/Gesellschaft-Umwelt/Einkommen-Konsum-Lebensbedingungen/Einkommen-Einnahmen-Ausgaben/Publikationen/Downloads-Einkommen/einkommensverteilung-2152606139004.pdf?__blob=publicationFile + // Anteil der Personen (%) an allen Personen 10 20 30 40 50 60 70 80 90 100 + // Nettoäquivalenzeinkommen(€) 826 1.142 1.399 1.630 1.847 2.070 2.332 2.659 3.156 4.329 + + double income = 0.; + double rndDouble = rnd.nextDouble(); + + if (rndDouble <= 0.1) income = 826.; + else if (rndDouble > 0.1 && rndDouble <= 0.2) income = 1142.; + else if (rndDouble > 0.2 && rndDouble <= 0.3) income = 1399.; + else if (rndDouble > 0.3 && rndDouble <= 0.4) income = 1630.; + else if (rndDouble > 0.4 && rndDouble <= 0.5) income = 1847.; + else if (rndDouble > 0.5 && rndDouble <= 0.6) income = 2070.; + else if (rndDouble > 0.6 && rndDouble <= 0.7) income = 2332.; + else if (rndDouble > 0.7 && rndDouble <= 0.8) income = 2659.; + else if (rndDouble > 0.8 && rndDouble <= 0.9) income = 3156.; + else if (rndDouble > 0.9) income = 4329.; + else { + throw new RuntimeException("Aborting..." + rndDouble); + } - // Minimum income is "Regelsatz" - PersonUtils.setIncome(person, Math.max(450, perPerson)); + PersonUtils.setIncome(person, income); } }