2
\$\begingroup\$

I am a self-learned programmer and this is my first program. I would really appreciate any critique on my approach.

The program should automatically download the who data file to the folder these files are in.

file: who_report.py

# https://covid19.who.int/
# https://covid19.who.int/WHO-COVID-19-global-data.csv
# https://www.kaggle.com/tanuprabhu/population-by-country-2020


import numpy as np
import matplotlib.pyplot as plt
import math
import os

# local imports
from who_report_helper import get_who_statistics, get_range_date


"""
This script will download the latest Covid statistics directly from WHO and write it to a local csv.
Then parse 9 countries statistics to compare.
Then it will generate a mat plot chart saving to a png after.
"""


def create_report(cdf, ytd, end):
    # from pprint import pprint  # Uncomment for list of country codes to compare other country stats
    # countries = dict(zip(cdf.Country.unique(), cdf.Country_code.unique()))
    # pprint(countries)
    US = cdf[(cdf['Country_code'] == "US") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # US
    MX = cdf[(cdf['Country_code'] == "MX") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # Mexico
    BR = cdf[(cdf['Country_code'] == "BR") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # Brazil
    GB = cdf[(cdf['Country_code'] == "GB") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # UK
    DE = cdf[(cdf['Country_code'] == "DE") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # Germany
    IT = cdf[(cdf['Country_code'] == "IT") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # Italy
    FR = cdf[(cdf['Country_code'] == "FR") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # France
    ES = cdf[(cdf['Country_code'] == "ES") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # Spain
    IN = cdf[(cdf['Country_code'] == "IN") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # India
    RU = cdf[(cdf['Country_code'] == "RU") & (cdf['Date_reported'] <= end) & (cdf['Date_reported'] >= ytd)]  # Russia

    # Setting up the figure and its grids to plot
    grid_size = (13, 3)
    plt.style.use('seaborn-darkgrid')
    fig = plt.figure(figsize=(13, 13))
    ax1 = plt.subplot2grid(grid_size, (0, 0), colspan=1, rowspan=4)
    ax2 = plt.subplot2grid(grid_size, (0, 1), colspan=1, rowspan=2)
    ax3 = plt.subplot2grid(grid_size, (2, 1), colspan=1, rowspan=2)
    ax4 = plt.subplot2grid(grid_size, (0, 2), colspan=1, rowspan=4)
    ax5 = plt.subplot2grid(grid_size, (4, 0), colspan=3, rowspan=3)
    ax6 = plt.subplot2grid(grid_size, (7, 0), colspan=3, rowspan=3)
    ax7 = plt.subplot2grid(grid_size, (10, 0), colspan=3, rowspan=3)
    fig.tight_layout(pad=3)
    fig.suptitle(f"United States and World Covid Statistics: {ytd[5:]} to {end[5:]}-2020\n", size=12, weight='bold')

    # This is all of the information needed for the graphs/charts
    world_dfs = (US, MX, BR, DE, GB, IT, FR, ES, RU, IN)
    populations = [country.Population.iloc[0] for country in world_dfs]
    cases_per50k = [country['New_cases'].sum() / populations[i] * 50000 for i, country in enumerate(world_dfs)]
    cases_per100k = [country['New_cases'].sum() / populations[i] * 100000 for i, country in enumerate(world_dfs)]
    deaths_per100k = [country['New_deaths'].sum() / populations[i] * 100000 for i, country in enumerate(world_dfs)]
    deaths_per1m = [country['New_deaths'].sum() / populations[i] * 1000000 for i, country in enumerate(world_dfs)]
    us_total_cases = US['New_cases'].sum()
    us_total_deaths = US['New_deaths'].sum()
    world_td = cdf[cdf['Date_reported'] >= ytd]
    world_total_cases = world_td['New_cases'].sum()
    world_total_deaths = world_td['New_deaths'].sum()
    us_world_cases = (us_total_cases, world_total_cases)
    us_world_deaths = (us_total_deaths, world_total_deaths)
    us_world_labels = ('$US$', '$World$')
    country_labels = ['$US$', '$Mexico$', '$Brazil$', '$Germany$', '$UK$', '$Italy$',
                      '$France$', '$Spain$', '$Russia$', '$India$']  # toggle this if you change countries
    # country_labels = [f"${country.Country.iloc[0]}$" for country in world_dfs]  # toggle this if you change countries
    label_cols = math.ceil(len(country_labels) / 2)
    explode = (0.025, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    pos = np.arange(len(country_labels))
    width = 0.25

    # \\...Pie chart for Cases per capita of 100,000 for selected countries...\\
    ax1.pie(cases_per100k, explode=explode, labels=country_labels, shadow=False, startangle=90, pctdistance=0.8,
            autopct=lambda temp: '${:.0f}$'.format(temp * sum(cases_per100k) / 100))
    ax1.set_title('Cases per capita (100,000)')

    # \\...Pie chart for Deaths per capita of 100,000 for selected countries...\\
    ax4.pie(deaths_per100k, explode=explode, labels=country_labels, shadow=False, startangle=90, pctdistance=0.8,
            autopct=lambda temp: '${:.0f}$'.format(temp * sum(deaths_per100k) / 100))
    ax4.set_title('Deaths per capita (100,000)')

    # \\...Pie Donut chart for total cases US vs World...\\
    ax2.pie(us_world_cases, labels=us_world_labels, textprops={'fontsize': 8}, colors=['#ff0000', '#7ab6ff'],
            wedgeprops=dict(width=.1), startangle=115, pctdistance=0.45,
            autopct=lambda temp: '${:.1f}$%\n$({:.0f})$'.format(temp, (temp / 100) * sum(us_world_cases)))
    ax2.set_title('USA vs World: Total Cases\n_______________________', y=-0.3)

    # \\...Pie Donut chart for total deaths US vs World...\\
    ax3.pie(us_world_deaths, labels=us_world_labels, textprops={'fontsize': 8}, colors=['#ff0000', '#7ab6ff'],
            wedgeprops=dict(width=.1), startangle=115, pctdistance=0.45,
            autopct=lambda temp: '${:.1f}$%\n$({:.0f})$'.format(temp, (temp / 100) * sum(us_world_deaths)))
    ax3.set_title('USA vs World: Total Deaths')

    # \\...Line chart for Cases for selected countries...\\
    for country in world_dfs:
        ax5.plot(country['Date_reported'], country['New_cases'] / country['Population'] * 10000000)

    ax5.set_title('Daily Covid Cases per capita (1,000,000)')
    ax5.set_ylabel('Cases')
    ax5.set_xticks(ax5.get_xticks()[::31])
    leg = ax5.legend(country_labels, loc='upper center', ncol=label_cols, fontsize=8, bbox_to_anchor=(0.5, 1.3))
    for line in leg.get_lines():
        line.set_linewidth(3.0)

    # \\...Line chart for Deaths per Capita 1,000,000 for selected countries...\\
    for country in world_dfs:
        ax6.plot(country['Date_reported'], country['New_deaths'] / country['Population'] * 10000000)

    ax6.set_title('Daily Covid Deaths per capita (1,000,000)')
    ax6.set_ylabel('Deaths')
    ax6.set_xticks(ax6.get_xticks()[::31])

    # \\...Bar chart for Deaths per Capita 50k cases/1m deaths, for selected countries...\\
    ax7.bar(pos, cases_per50k, width, alpha=0.5, color='#EE3224')
    ax7.bar([p + width for p in pos], deaths_per1m, width, alpha=0.5, color='#FFC222')
    ax7.set_ylabel('Cases/Deaths')
    ax7.set_title('Countries: Cases and Deaths per capita')
    ax7.set_xticks([p + .5 * width for p in pos])
    ax7.set_xticklabels(country_labels)
    ax7.set_xlim(min(pos) - width, max(pos) + width * 2)
    ax7.set_ylim([0, max((cases_per50k + deaths_per1m))])
    ax7.legend(['Cases per Capita (50,000)', 'Deaths per capita (1,000,000)'], loc='upper right')

    return plt


def save_plot(figure, ytd, end):
    if not os.path.exists('covid_charts'):
        os.makedirs('covid_charts')
    if ytd <= '2020-01-03':
        figure.savefig(f'covid_charts/COVID-19-chart-{end}', dpi=300)
    else:
        figure.savefig(f'covid_charts/COVID-19-chart-{ytd[5:]}-{end[5:]}', dpi=300)
    figure.show()


def main():
    stats = get_who_statistics()
    dates = get_range_date()
    figure = create_report(stats, *dates)
    save_plot(figure, *dates)


if __name__ == "__main__":
    main()

file: who_report_helper.py

# Programmer: Jeffrey Carvalho
# https://covid19.who.int/
# https://covid19.who.int/WHO-COVID-19-global-data.csv
# https://www.kaggle.com/tanuprabhu/population-by-country-2020


import requests
import pandas as pd
import datetime as dt
import os
import calendar


replacements = {
    'Bolivia': 'Bolivia (Plurinational State of)',
    'Brunei': 'Brunei Darussalam',
    'Caribbean Netherlands': 'Bonaire, Sint Eustatius and Saba',
    'Channel Islands': 'Jersey',
    'Czech Republic (Czechia)': 'Czechia',
    'Côte d\'Ivoire': 'Côte d’Ivoire',
    'DR Congo': 'Democratic Republic of the Congo',
    'Faeroe Islands': 'Faroe Islands',
    'Falkland Islands': 'Falkland Islands (Malvinas)',  #
    'Iran': 'Iran (Islamic Republic of)',
    'Laos': "Lao People's Democratic Republic",  #
    'Micronesia': 'Micronesia (Federated States of)',
    'Moldova': 'Republic of Moldova',
    'North Korea': "Democratic People's Republic of Korea",
    'Northern Mariana Islands': 'Northern Mariana Islands (Commonwealth of the)',
    'Russia': 'Russian Federation',
    'Saint Barthelemy': 'Saint Barthélemy',
    'Saint Kitts & Nevis': 'Saint Kitts and Nevis',
    'Saint Pierre & Miquelon': 'Saint Pierre and Miquelon',
    'Sao Tome & Principe': 'Sao Tome and Principe',
    'South Korea': 'Republic of Korea',
    'St. Vincent & Grenadines': 'Saint Vincent and the Grenadines',
    'State of Palestine': 'occupied Palestinian territory, including east Jerusalem',
    'Syria': 'Syrian Arab Republic',  #
    'Tanzania': 'United Republic of Tanzania',
    'Turks and Caicos': 'Turks and Caicos Islands',
    'U.S. Virgin Islands': 'United States Virgin Islands',
    'United Kingdom': 'The United Kingdom',
    'United States': 'United States of America',
    'Venezuela': 'Venezuela (Bolivarian Republic of)',
    'Vietnam': 'Viet Nam',
    'Wallis & Futuna': 'Wallis and Futuna',
}


def get_who_statistics():
    # Returns the latest version WHO report or passes on a Panda Data Frame from an already downloaded CSV file
    PATH = 'https://covid19.who.int/WHO-COVID-19-global-data.csv'
    FILE = 'WHO-COVID-19-global-data.csv'

    def dl_data():
        data_source = requests.get(PATH)
        data = data_source.content
        if not os.path.exists('data'):
            os.makedirs('data')
        covid_data = open(f'data/{FILE}', 'wb')
        covid_data.write(data)
        covid_data.close()

    who = input('Would you like to get today\'s COVID stats from WHO?\n\nType Yes to download\nPress ENTER to skip\n')
    if who.lower() == 'yes':
        dl_data()
    try:
        cdf = pd.read_csv(f'data/{FILE}')
    except FileNotFoundError:
        print('ERROR: File not found!\nDownloading new file now.\n')
        dl_data()
        cdf = pd.read_csv(f'data/{FILE}')
    finally:
        cdf = cdf[['Date_reported', 'Country_code', 'Country', 'New_cases', 'New_deaths', 'Cumulative_deaths']]
        world_pop = pd.read_csv('data/population_by_country_2020.csv')
        world_pop = world_pop[['Country (or dependency)', 'Population (2020)']]
        world_pop['Country (or dependency)'].replace(replacements, inplace=True)
        cdf = cdf.join(world_pop.set_index('Country (or dependency)'), on='Country')
        cdf.rename(columns={'Population (2020)': 'Population'}, inplace=True)
    return cdf


def get_range_date():
    # Returns the range of dates to use on the charts
    msg1 = '''When would you like to start the graphs at?\n\n0: for Complete data\
    \n1: for the past week\n2: for past 30 days\n3: for past 90 days\
    \n4: Select number of days to look back\n5: Select a month in 2020\n'''
    msg2 = "How many days would you like to set the chart back to?\n"
    msg3 = "What month of 2020 would you like to look at?\nEnter numeric month value (1-12)"
    today = end = dt.date.today()
    ytd = dt.date(2020, 1, 3)
    try:
        date_range = int(input(msg1))
        while date_range < 0 or date_range > 5:
            date_range = int(input(msg1))
        if date_range == 1: ytd = today - dt.timedelta(days=7)
        if date_range == 2: ytd = today - dt.timedelta(days=30)
        if date_range == 3: ytd = today - dt.timedelta(days=90)
        if date_range == 4:
            days = int(input(msg2))
            while days < 0:
                days = int(input(msg2))
            ytd = today - dt.timedelta(days=days)
        if date_range == 5:
            month = int(input(msg3))
            while month < 1 or date_range > 12:
                month = int(input(msg3))
            month_range = calendar.monthrange(2020, month)
            ytd = dt.date(2020, month, 1)
            end = dt.date(2020, month, month_range[1])
    except ValueError:
        print('PLEASE ENTER NUMBERS ONLY!\n')
        return get_range_date()
    else:
        return str(ytd), str(end)


def main():
    print('This file is used for collecting the covid data from WHO for the report')


if __name__ == "__main__":
    main()
\$\endgroup\$
1
  • \$\begingroup\$ It would help to know what the code is supposed to do -- in English, not just in code. \$\endgroup\$ – Rick James Mar 31 at 0:55
2
\$\begingroup\$
    US = cdf[(cdf['Country_code'] == "US") &
        (cdf['Date_reported'] <= end) & 
        (cdf['Date_reported'] >= ytd)]  # US

You have a lot of lines like that.

  • Build an array of 'US', 'MX', ...
  • Have an associative array for the left part
  • Use a loop to perform those 'identical' statements

(Sorry, I don't know python, so I can't write it for you.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.