Skip to content
Snippets Groups Projects
main.rs 5.63 KiB
Newer Older
Florian Franzmann's avatar
Florian Franzmann committed
extern crate chrono;
Florian Franzmann's avatar
Florian Franzmann committed
extern crate csv;
Florian Franzmann's avatar
Florian Franzmann committed

use chrono::prelude::*;
Florian Franzmann's avatar
Florian Franzmann committed
use chrono::Utc;
Florian Franzmann's avatar
Florian Franzmann committed

mod curves {
    mod exponential;
Florian Franzmann's avatar
Florian Franzmann committed
}
pub mod estimators {
    pub mod ratios;
    pub mod exponential;
    pub mod gompertz;
Florian Franzmann's avatar
Florian Franzmann committed
}

mod datamodel;
mod data;
Florian Franzmann's avatar
Florian Franzmann committed

pub mod search {
    pub mod ternary;
    pub mod golden_section_search;
Florian Franzmann's avatar
Florian Franzmann committed
use estimators::ratios::{estimate_growth_rate};
use estimators::exponential::estimate_exponential;
Florian Franzmann's avatar
Florian Franzmann committed
use estimators::gompertz::{estimate_gompertz_with_recovery};
use search::golden_section_search::golden_section_search;
Florian Franzmann's avatar
Florian Franzmann committed

Florian Franzmann's avatar
Florian Franzmann committed
fn main() -> std::io::Result<()> {
    let saturation_percentage = 66.0;
    let population = 83019213;
    let plateau = population as f64 / 100.0 * saturation_percentage;
    let days = crate::data::berliner_morgenpost_deutschland();
    /*
     * https://www.imperial.ac.uk/media/imperial-college/medicine/sph/ide/gida-fellowships/Imperial-College-2019-nCoV-severity-10-02-2020.pdf
     */
    let recovery_period = 12.0;

    let gompertz_reconstruction = |rate: f64| {
        let initial_infected = days[0].infected as f64;
        let mut ret = Vec::new();
        let mut day: f64 = 0.0;

        for _ in &days {
            ret.push(estimate_gompertz_with_recovery(initial_infected, rate, plateau, recovery_period, day));
            day = day + 1.0;
Florian Franzmann's avatar
Florian Franzmann committed

Florian Franzmann's avatar
Florian Franzmann committed

    let gompertz_squared_errors = |rate: f64| {
        let mut ret = Vec::new();
Florian Franzmann's avatar
Florian Franzmann committed

        let reconstruction = gompertz_reconstruction(rate);
        let mut actual = Vec::new();
        for each in &days {
            actual.push(each.infected as f64);
        }
        for (a, b) in reconstruction.iter().zip(actual.iter()) {
            ret.push((a - b).powi(2));
        }
Florian Franzmann's avatar
Florian Franzmann committed

Florian Franzmann's avatar
Florian Franzmann committed

    let gompertz_root_mean_squared_error = |rate: f64| -> f64 {
        let errors = gompertz_squared_errors(rate);
        let sample_size = errors.len() as f64;
        let sum: f64 = errors.iter().sum();

        (sum / sample_size).sqrt()
Florian Franzmann's avatar
Florian Franzmann committed

    let find_gompertz_rate = |start: f64, end:f64| -> f64 {
        golden_section_search(&gompertz_root_mean_squared_error, start, end, 0.00000000000001)
Florian Franzmann's avatar
Florian Franzmann committed

    let gompertz_rate = find_gompertz_rate(0.0, 0.1);
    let gompertz_squared_error = gompertz_root_mean_squared_error(gompertz_rate);
Florian Franzmann's avatar
Florian Franzmann committed
    println!("gompertz rate: {}, rms: {}", gompertz_rate, gompertz_squared_error.sqrt());
Florian Franzmann's avatar
Florian Franzmann committed

    let mean = estimate_growth_rate(&days);

    println!("estimated growth rate: {:.0}%±{:.0}%\n\n", (mean.geometric_mean * 100.0).round(), (mean.geometric_mean_stderr * 100.0).round());

Florian Franzmann's avatar
Florian Franzmann committed
    use std::fs::File;

    let buffer = File::create("corona.csv")?;
    let mut writer = csv::Writer::from_writer(buffer);

    for day in 0 .. 400 {
        let day = day as f64;
        let initial_infected = days[0].infected as f64;
        let estimate_exponential = |rate| { estimate_exponential(initial_infected, rate, day) };
Florian Franzmann's avatar
Florian Franzmann committed
        let rate = mean.geometric_mean;

        let exponential_estimate             = estimate_exponential(rate);
Florian Franzmann's avatar
Florian Franzmann committed

        let gompertz_estimate = estimate_gompertz_with_recovery(initial_infected, gompertz_rate, plateau, recovery_period, day).round();
Florian Franzmann's avatar
Florian Franzmann committed

Florian Franzmann's avatar
Florian Franzmann committed
        let mut write_record = |date: Date<Utc>| {
            let date = date.format("%Y-%m-%d").to_string();
Florian Franzmann's avatar
Florian Franzmann committed
            let exponential_estimate = exponential_estimate.to_string();
            let gompertz_estimate = gompertz_estimate.to_string();
            let actual_infected = if (day as usize) < days.len() {
                days[day as usize].infected.to_string()
            } else {
                "".to_string()
            };

            writer.write_record(&[date, actual_infected, exponential_estimate, gompertz_estimate]).unwrap();
Florian Franzmann's avatar
Florian Franzmann committed
        if (day as usize) < days.len() {
            let date = days[day as usize].day;
Florian Franzmann's avatar
Florian Franzmann committed
            write_record(date);
Florian Franzmann's avatar
Florian Franzmann committed
        } else {
            let date = days[days.len() - 1].day;
            let date = date.checked_add_signed(chrono::Duration::days(day as i64 - days.len() as i64 + 1)).unwrap();

Florian Franzmann's avatar
Florian Franzmann committed
            write_record(date);
Florian Franzmann's avatar
Florian Franzmann committed
        }
    }
    let mut conf = ini::Ini::new();
    let last_day_index = days.len() - 1;
    let infected = days[last_day_index].infected;
    let dead = days[last_day_index].dead;
    let mortality_rate_percent = 0.6;
    let severe_cases_rate_percent = 5.0;
    // https://www.medicalnewstoday.com/articles/coronavirus-81-of-cases-are-mild-study-says
    let medically_relevant_cases_percent = 19.1;
    let available_icus = 28000 + 8000;
    let available_beds = 497000;
    conf.with_section(Some("most_recent_values"))
        .set("infected", infected.to_string())
        .set("dead", dead.to_string())
        .set("current_day", last_day_index.to_string())
        .set("data_source", "Berliner Morgenpost");
    conf.with_section(Some("assumptions"))
        .set("mortality_rate_percent", mortality_rate_percent.to_string())
        .set("severe_cases_rate_percent", severe_cases_rate_percent.to_string())
        .set("medically_relevant_cases_percent", medically_relevant_cases_percent.to_string())
        .set("available_icus", available_icus.to_string())
        .set("available_beds", available_beds.to_string())
        .set("recovery_period", recovery_period.to_string());
    conf.with_section(Some("exponential"))
        .set("rate", mean.geometric_mean.to_string())
        .set("rate_stderr", mean.geometric_mean_stderr.to_string());
    conf.with_section(Some("gompertz"))
        .set("rate", gompertz_rate.to_string())
        .set("rmserror", gompertz_squared_error.sqrt().to_string())
        .set("saturation_percentage", saturation_percentage.to_string())
        .set("population", population.to_string());
    conf.write_to_file("estimates.ini").unwrap();

Florian Franzmann's avatar
Florian Franzmann committed
    Ok(())
Florian Franzmann's avatar
Florian Franzmann committed
}