Erweiterte Todesuhr

Habe die Funktion der Todesuhr noch etwas erweitert. Es lassen sich jetzt die Charakteristiken einer zweiten Person eingeben und es wird berechnet, basierend auf der Annahme, dass die Todesjahre normalverteilt sind mit einer Standardabweichung von 11 Jahren (Mittelwert dieser beiden Publikationen), wie wahrscheinlich es ist, dass Person 2 vor Person 1 stirbt. Dies einmal gemäß den unveränderten Lebenserwartungen aus den Sterbetafeln 2019 des Statistischen Bundesamts wie auch den veränderten Lebenserwartungen gemäß den Inputs zu Bewegung, Übergewicht, Rauchen, etc … Es lässt sich jetzt ebenfalls ein Testjahr (im Beispiel-Output 2030) eingeben und gemäß der Normalverteilung wird angezeigt, wie wahrscheinlich es ist, dass die jeweiligen Personen vor diesem Testjahr sterben.

Beispiel-Output für mich und eine gewisse andere Person:

------ Values for Person 1 ------

Birth Year: 1983
Gender: Male

Expected Remaining Lifespan (Table): 42
Expected Remaining Lifespan (Adjusted): 36

Expected Death Year (Table): 2063
Expected Death Year (Adjusted): 2057

Probability of Dying before 2030 (Table): 0.13 %
Probability of Dying before 2030 (Adjusted): 0.74 %


------ Values for Person 2 ------

Birth Year: 1958
Gender: Female

Expected Remaining Lifespan (Table): 22
Expected Remaining Lifespan (Adjusted): 26

Expected Death Year (Table): 2043
Expected Death Year (Adjusted): 2047

Probability of Dying before 2030 (Table): 11.86 %
Probability of Dying before 2030 (Adjusted): 5.79 %


------ Simulation Based on Normally Distributed Death Years ------

Probability of Person 2 Dying Before Person 1 (Table): 90.06 %
Probability of Person 2 Dying Before Person 1 (Adjusted): 73.00 %

Wie wissenschaftlich sind die Berechnungen?

  • Grundlage für alles ist ein fast perfekter Fit der Sterbetafeln 2019, separat für Männer und Frauen, da die Unterschiede der Verläufe recht deutlich sind
def funcm(x):
    return (79.02-1.317*x**0.8806)/(1+0.000004896*x**2.776)

def funcf(x):
    return (83.82-1.334*x**0.8906)/(1+0.000000820*x**3.136)
  • Die Annahme der Normalverteilung der Todesjahre ist in der Praxis gut erfüllt
  • Die Standardabweichung müsste sich exakt aus den Sterbetafeln bestimmen lassen. Aber solange ich nicht weiß wie, bleiben die 11 Jahre als Annahme. Dürfte keine großen Verzerrungen bringen
  • Die maximale Erhöhung / Reduktion der Lebenserwartung durch einen gegebenen Lifestyle-Faktor ist akademischen Quellen entnommen
  • Größtes Problem ist die lineare Addition der Erhöhung / Reduktion der Lebenserwartungen. Rauchen bringt eine Reduktion bis zu 9 Jahre (bei smoking_scale = 1), Übergewicht bis zu 5 Jahre (bei obesity_scale = 1), das heißt aber nicht, dass es bei übergewichtigen Rauchern bis zu 9 + 5 = 14 Jahren sind. So funktioniert das nicht, aber ohne die jeweiligen Korrelationen zu kennen, bleibt es die beste Annahme
  • Die Wahl der Skalenwerte ist subjektiv. Wer nie geraucht hat, setzt smoking_scale = 0. Wer seit dem Alter von 10 Jahren drei Packungen pro Tag raucht, setzt smoking_scale = 1. Wer irgendwo dazwischen liegt, setzt irgendwas wohlpassendes dazwischen. Was ist wohlpassend? Das muss jeder selbst entscheiden.

Der Code mit ausführlichen Kommentaranweisungen zum Input und Links zu den Quellen. Funktioniert auch in diesem Online-Compiler.

import scipy
from scipy import stats
import numpy as np


# USER INPUT: SPECIFY CURRENT YEAR AND TEST YEAR
currentyear = 2021
testyear = 2030


# USER INPUT: SPECIFY GENDER AND BIRTH YEAR FOR PERSON 1
# if male set to 1, otherwise to 0
male = 1
birthyear = 1983

# USER INPUT: SPECIFY CHARACTERISTICS FOR PERSON 1
# USER INPUT: ONLY ADJUST SCALE VALUES

# from 0 to 1
# https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3395188/

exercise_scale = 0.3
exercise_add = 7

# from 0 to 1
# https://www.acpjournals.org/doi/abs/10.7326/0003-4819-138-1-200301070-00008

obesity_scale = 0.3
obesity_red = 5

# from 0 to 1
# https://www.bmj.com/content/345/bmj.e7093.full.pdf+html

smoking_scale = 0.7
smoking_red = 9

# from 0 to 1
# https://jamanetwork.com/journals/jama/article-abstract/2513561

wealth_scale = 0.6
wealth_add = 12

# from 0 to 1
# https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2366041/

education_scale = 0.8
education_add = 3.5

# from 0 to 1
# https://pubmed.ncbi.nlm.nih.gov/21593516/

mental_disorder_scale = 0.7
mental_disorder_red = 15




# USER INPUT: SPECIFY IF COMPARISON TO SECOND PERSON SHALL BE MADE
# To Compare to Another Person, set to 1. Otherwise 0
testperson = 1


# USER INPUT: SPECIFY GENDER AND BIRTH YEAR FOR PERSON 2
# if male set to 1, otherwise to 0
tp_male = 0
tp_birthyear = 1958


# USER INPUT: SPECIFY CHARACTERISTICS FOR PERSON 2
# USER INPUT: ONLY ADJUST SCALE VALUES

# Set to 1 to copy all following characteristics from person 1
copyall = 0

# from 0 to 1
# https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3395188/

tp_exercise_scale = 0.3
exercise_add = 7

# from 0 to 1
# https://www.acpjournals.org/doi/abs/10.7326/0003-4819-138-1-200301070-00008

tp_obesity_scale = 0.0
obesity_red = 5

# from 0 to 1
# https://www.bmj.com/content/345/bmj.e7093.full.pdf+html

tp_smoking_scale = 0.1
smoking_red = 9

# from 0 to 1
# https://jamanetwork.com/journals/jama/article-abstract/2513561

tp_wealth_scale = 0.7
wealth_add = 12

# from 0 to 1
# https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2366041/

tp_education_scale = 0.2
education_add = 3.5

# from 0 to 1
# https://pubmed.ncbi.nlm.nih.gov/21593516/

tp_mental_disorder_scale = 0.4
mental_disorder_red = 15


# ----------------------
# END OF ALL USER INPUTS
# ----------------------

    
# Functions are fits from Sterbetafeln 2019 for males and females

def funcm(x):
    return (79.02-1.317*x**0.8806)/(1+0.000004896*x**2.776)

def funcf(x):
    return (83.82-1.334*x**0.8906)/(1+0.000000820*x**3.136)


if copyall > 0.5:

    tp_exercise_scale = exercise_scale
    tp_obesity_scale = obesity_scale
    tp_smoking_scale = smoking_scale
    tp_wealth_scale = wealth_scale
    tp_education_scale = education_scale
    tp_mental_disorder_scale = mental_disorder_scale


print('------ Values for Person 1 ------')

age = currentyear-birthyear

if male > 0.5:
    years_left = round(funcm(age),0)
else:
    years_left = round(funcf(age),0)

deathyear_table = currentyear+years_left

deathyear = deathyear_table + exercise_scale*exercise_add - obesity_scale*obesity_red - smoking_scale*smoking_red + wealth_scale*wealth_add + education_scale*education_add - mental_disorder_scale*mental_disorder_red

years_left_adj = deathyear-currentyear


print()
print('Birth Year:', birthyear)

if male > 0.5:
    print('Gender: Male')
else:
    print('Gender: Female')

print()
print('Expected Remaining Lifespan (Table):',  "%.0f" % years_left)
print('Expected Remaining Lifespan (Adjusted):',  "%.0f" % years_left_adj)
print()
print('Expected Death Year (Table):',  "%.0f" % deathyear_table)
print('Expected Death Year (Adjusted):',  "%.0f" % deathyear)

z1 = (testyear-deathyear_table)/11
p1 = 100*stats.norm.cdf(z1)

z2 = (testyear-deathyear)/11
p2 = 100*stats.norm.cdf(z2)

print()

print('Probability of Dying before', testyear, '(Table):',  "%.2f" % p1, '%')
print('Probability of Dying before', testyear, '(Adjusted):',  "%.2f" % p2, '%')


if testperson > 0.5:

    print()
    print()
    print('------ Values for Person 2 ------')

    tp_age = currentyear-tp_birthyear

    if tp_male > 0.5:
        tp_years_left = round(funcm(tp_age),0)
    else:
        tp_years_left = round(funcf(tp_age),0)

    tp_deathyear_table = currentyear+tp_years_left

    tp_deathyear = tp_deathyear_table + tp_exercise_scale*exercise_add - tp_obesity_scale*obesity_red - tp_smoking_scale*smoking_red + tp_wealth_scale*wealth_add + tp_education_scale*education_add - tp_mental_disorder_scale*mental_disorder_red

    tp_years_left_adj = tp_deathyear-currentyear


    print()
    print('Birth Year:', tp_birthyear)

    if tp_male > 0.5:
        print('Gender: Male')
    else:
        print('Gender: Female')

    print()
    print('Expected Remaining Lifespan (Table):',  "%.0f" % tp_years_left)
    print('Expected Remaining Lifespan (Adjusted):',  "%.0f" % tp_years_left_adj)
    print()
    print('Expected Death Year (Table):',  "%.0f" % tp_deathyear_table)
    print('Expected Death Year (Adjusted):',  "%.0f" % tp_deathyear)

    tp_z1 = (testyear-tp_deathyear_table)/11
    tp_p1 = 100*stats.norm.cdf(tp_z1)

    tp_z2 = (testyear-tp_deathyear)/11
    tp_p2 = 100*stats.norm.cdf(tp_z2)

    print()

    print('Probability of Dying before', testyear, '(Table):',  "%.2f" % tp_p1, '%')
    print('Probability of Dying before', testyear, '(Adjusted):',  "%.2f" % tp_p2, '%')

    print()
    print()
    print('------ Simulation Based on Normally Distributed Death Years ------')

    year1_table = np.random.normal(deathyear_table,11,50000)
    year2_table = np.random.normal(tp_deathyear_table,11,50000)

    count = 0
    i = 0
    while i < len(year1_table):
        if year2_table[i] < year1_table[i]:
            count = count+1
        i += 1

    prob = 100*count/i

    print()
    print('Probability of Person 2 Dying Before Person 1 (Table):',  "%.2f" % prob, '%')

    year1 = np.random.normal(deathyear,11,50000)
    year2 = np.random.normal(tp_deathyear,11,50000)

    count = 0
    i = 0
    while i < len(year1):
        if year2[i] < year1[i]:
            count = count+1
        i += 1

    prob = 100*count/i

    print('Probability of Person 2 Dying Before Person 1 (Adjusted):',  "%.2f" % prob, '%')

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s