Technische Details

Genauere Erklärung einiger Funktionen

Aus Quantilen eine Verteilung bestimmten

Problem: Für eine gewisse Anzahl an Agenten relative Werte festlegen, wobei vorher definierte Quantiele mit relativen nicht-kumulierten Werten erfüllt sein müssen.

Dafür gibt es mehrere Lösungen. Wir haben uns entschieden wie bei linearer Regression vorzugehen, nur mit dem Ziel, das die Integrale im Bereich der Quantile $Q$ gleich dem Wert der Quantile sind und nicht möglichst viele Punkte getroffen werden sollen. Als Ergebnis erhält man eine Dichtefunktion, die angibt, wie viele Einkommen an eine Stelle liegt. Die Dichtefunktion $f$ ist dann wie folgt definiert:

$$f(x) = \beta_1 \cdot x^g + \beta_0 $$

und hat folgende Parameter $\beta_1$ und $\beta_0$, die Verändert werden können. Der Grad der Funktion $g$ muss vorher gegeben sein. Für diese Parameter soll nun ein Wert gefunden werden, der ermöglicht, das die Integralle im Bereich der Quantile $Q$ der Funktion $d$ dem Wert der Quantile entspricht.

Also das: $$ Quantilwert \approx \int_{x_{von}}^{x_{bis}} f(x) \, dx $$

Dazu muss eine Loss-Funktion definiert werden, die für jede Eingabe an Parametern ausgibt, wie Groß der Abstand zwischen dem Wert des Integrals und dem gewühntschtem Wert im Qunatil $Q$ ist. Die Lossfunktion $l$ lässt sich definieren als:

$$ l(\beta_0, \beta_1) = \sum_{i=1}^{n} \left( d[i] - \int_{\frac{i-1}{n}}^{\frac{i}{n}} \beta_0 + \beta_1 \cdot x^g \,dx \right)^2$$

wobei:

Alle Parameter, bis auf $\beta_0$ und $\beta_1$, müssen vorher gegeben sein!

Da es sich um eine Polynomenfunktion handelt, kann das Integral symbolisch gelöst weren (Quelle ChatGPT). Dadurch gilt folgendes: \begin{align*} \int_{a}^{b} \beta_1 \cdot x^g + \beta_0 \, dx &= \beta^1 \cdot \int_{a}^{b} x^g \, dx \cdot \int_{a}^{b} 1 \, dx = \beta_1 \cdot \left[ \frac{x^{g+1}}{g+1} \right]^b_a + \beta_0 \cdot (b-a) \\ &= \beta_1 \cdot \left( \frac{b^{g+1} - a^{g+1}}{g+1} \right) + \beta_0 \cdot (b-a) \end{align*}

Dadurch kann die Loss-Funktion wie folgt geschrieben werden: $$ l(\beta_0, \beta_1) = \sum_{i=1}^{n} \left(d[i] - \left[ \beta_1 \cdot \left( \frac{ \left(\frac{i}{n}\right)^{g+1} - \left(\frac{i-1}{n}\right)^{g+1}}{g+1} \right) + \beta_0 \cdot \frac{1}{n} \right] \right)^2 $$

Nun kann mit der Funktion minimize aus scipy.optimize das Problem, die besten Werte für $\beta_0$ und $\beta_1$ zu finden, gelöst werden. Wie genau die Lösungsalgorithmen funktionieren weiß ich auch nicht.

    import matplotlib.pyplot as plt
    import numpy as np
    from scipy.optimize import minimize
 
    # Quantile - Hier Dezile als Beispiel
    quantile = np.array([3,5,6,7,8,9,10,12,15,25])/100
    # Grad der Funktion
    g = 2
 
    # Weitere Variablen, die berechnet werden
    n = len(quantile)
    b_0 = 0
    b_1 = 0
 
    def l(xy):
        b_0, b_1 = xy
        error = 0
 
        for i, element in enumerate(quantile):
            integral = (b_1 * (((((i+1) / n)**(g+1)) - (((i)/n)**(g+1)))/(g+1)) + b_0 * (1/n))
            error += (element - integral)**2
        return error 
 
    def f(x):
        return b_1 * (x**g) + b_0
 
    res = minimize(l, (1,1), method='L-BFGS-B')     # (1,1) ist der Startwert
    b_0, b_1 = res.x
 
    print(f'Die Funktion lautet: f(x) = {b_1} * x^{g} + {b_0}')
    print(f'Der Fehlerwert ist : {l(res.x)}')
 
    #  Darstellung der Werte
    x = np.linspace(0, 1, 10)
    x_quantile = np.linspace(0.05, 0.95, len(quantile))
    plt.figure()
    plt.bar(x_quantile, quantile, width=0.1, alpha=0.6, label='Dezile', color='orange', edgecolor='black')
    plt.plot(x, f(x), label='f(x)')
    plt.title(rf"$f(x) = {b_1}\, \cdot x^{g} + {b_0}$")
    plt.legend()
    plt.show()

Aus der Dichtefunktion lassen sich aber noch keine relativen Werte für die Agenten berechnen. Erstmal kann mit der Dichtefunktion $f$ nur ein absoluter Wert für jeden Agenten berechnet werden. Dazu braucht man einen konstanten Wert, der mit dem Funktoinswert von $f$ multipliziert wird.

Also für das absolute Einkommen des Agenten ergibt sich: $$ Einkommen = f(x) + C $$ Dabei ist $x$ die realtive Position des Agenten. Wenn der Agent die Position $i$ hat ist $x = \frac{i}{n}$, wobei $n$ die Anzahl von Agenten ist. $C$ ist eine Konstante, die z.B. das durchschnittliche Einkommen pro Person sein könnte.

Um nun das relative Einkommen zu erhalten, muss nur das absolute Einkommen durch das Gesamteinkommen gerechnet werden. Dadruch ergibt sich das Einkommen der Agenten aus:

$$ Einkommen_i = \frac{Absolutes Einkommen}{Gesamteinkommen} = \frac{f(x_i) + C}{\sum_{i=0}^{n} f(x_j) +C } = \frac{f(x_i)}{\sum_{i=0}^{n} f(x_j)} $$

Das lässt sich sehr einfach mit numpy.arrays umsetzen. Die gesamte Funktion sieht nun so aus

def g(n, Q) -> np.ndarray:
    """Die Funktion berechnet für eine Anzalh an Agenten n und Quantilen Q die passende Verteilung
    und gibt als Rückgabewert einen Array mit den relativen Werten für jeden Agenten
 
    :param n: Anzahl an Agenten bzw. Werten, die Zurückgegeben werden sollen
    :type n: int
    :param Q: Liste mit realtiven nicht-kumulierten Quantilwerten
    :type Q: list
    :return: Liste mit relativen Werten für jeden Agenten bzw. mit Länge n
    :rtype: array
    """
 
    array = np.linspace(0.5/n, 1 - 0.5/n, n)  # relative Positionen von jedem Agenten
    # Variablen zuschreiben
    g = 2
    quantile = np.array(Q)/100
    n = len(quantile)
    b_0 = 0
    b_1 = 0
 
    # ALTER CODE - VON OBEN KOPIERT
    def l(xy):
        b_0, b_1 = xy
        error = 0
 
        for i, element in enumerate(quantile):
            integral = (b_1 * (((((i+1) / n)**(g+1)) - (((i)/n)**(g+1)))/(g+1)) + b_0 * (1/n))
            error += (element - integral)**2
        return error 
    def f(x):
        return b_1 * (x**g) + b_0
 
    # Dichtefunktion f erstellen
    res = minimize(l, (1,1), method='L-BFGS-B')     # (1,1) ist der Startwert
    b_0, b_1 = res.x
 
    print(f'Die Funktion lautet: f(x) = {b_1} * x^{g} + {b_0}')
    print(f'Der Fehlerwert ist : {l(res.x)}')
 
    # NEUER CODE
    # relative Werte für jeden Agenten Ermitteln
    array = f(array)            # Für jedes ELement im Array wird die Funktion angewendet
    array = array/np.sum(array)    # relative Werte erghalten
 
    return array