You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
4.7 KiB

3 years ago
3 years ago
3 years ago
  1. #!/usr/bin/env python3
  2. #
  3. # Usage: ./update_plots.py
  4. # Updates plots from the Plotly section so they show the latest data.
  5. from pathlib import Path
  6. from datetime import date, time, datetime, timedelta
  7. import pandas as pd
  8. from plotly.express import line
  9. import plotly.graph_objects as go
  10. import re
  11. def main():
  12. print('Updating covid deaths...')
  13. update_covid_deaths()
  14. print('Updating covid cases...')
  15. update_confirmed_cases()
  16. def update_covid_deaths():
  17. def update_readme(date_treshold):
  18. lines = read_file(Path('..') / 'README.md')
  19. out = [re.sub("df.date < '\d{4}-\d{2}-\d{2}'", f"df.date < '{date_treshold}'", line)
  20. for line in lines]
  21. write_to_file(Path('..') / 'README.md', out)
  22. covid = pd.read_csv('https://covid.ourworldindata.org/data/owid-covid-data.csv',
  23. usecols=['iso_code', 'date', 'total_deaths', 'population'])
  24. continents = pd.read_csv('https://datahub.io/JohnSnowLabs/country-and-continent-codes-' + \
  25. 'list/r/country-and-continent-codes-list-csv.csv',
  26. usecols=['Three_Letter_Country_Code', 'Continent_Name'])
  27. df = pd.merge(covid, continents, left_on='iso_code', right_on='Three_Letter_Country_Code')
  28. df = df.groupby(['Continent_Name', 'date']).sum().reset_index()
  29. df['Total Deaths per Million'] = df.total_deaths * 1e6 / df.population
  30. date_treshold = str(date.today() - timedelta(days=2)) # '2020-11-25'
  31. df = df[('2020-03-14' < df.date) & (df.date < date_treshold)]
  32. df = df.rename({'date': 'Date', 'Continent_Name': 'Continent'}, axis='columns')
  33. f = line(df, x='Date', y='Total Deaths per Million', color='Continent')
  34. f.update_layout(margin=dict(t=24, b=0), paper_bgcolor='rgba(0, 0, 0, 0)')
  35. update_file('covid_deaths.js', f)
  36. update_readme(date_treshold)
  37. write_to_png_file('covid_deaths.png', f, width=960, height=340)
  38. def update_confirmed_cases():
  39. def main():
  40. df = wrangle_data(*scrape_data())
  41. f = get_figure(df)
  42. update_file('covid_cases.js', f)
  43. write_to_png_file('covid_cases.png', f, width=960, height=315)
  44. def scrape_data():
  45. def scrape_yahoo(id_):
  46. BASE_URL = 'https://query1.finance.yahoo.com/v7/finance/download/'
  47. now = int(datetime.now().timestamp())
  48. url = f'{BASE_URL}{id_}?period1=1579651200&period2={now}&interval=1d&' + \
  49. 'events=history'
  50. return pd.read_csv(url, usecols=['Date', 'Close']).set_index('Date').Close
  51. covid = pd.read_csv('https://covid.ourworldindata.org/data/owid-covid-data.csv',
  52. usecols=['location', 'date', 'total_cases'])
  53. covid = covid[covid.location == 'World'].set_index('date').total_cases
  54. dow, gold, bitcoin = [scrape_yahoo(id_) for id_ in ('^DJI', 'GC=F', 'BTC-USD')]
  55. dow.name, gold.name, bitcoin.name = 'Dow Jones', 'Gold', 'Bitcoin'
  56. return covid, dow, gold, bitcoin
  57. def wrangle_data(covid, dow, gold, bitcoin):
  58. df = pd.concat([dow, gold, bitcoin], axis=1)
  59. df = df.sort_index().interpolate()
  60. df = df.rolling(10, min_periods=1, center=True).mean()
  61. df = df.loc['2020-02-23':].iloc[:-2]
  62. df = (df / df.iloc[0]) * 100
  63. return pd.concat([covid, df], axis=1, join='inner')
  64. def get_figure(df):
  65. def get_trace(col_name):
  66. return go.Scatter(x=df.index, y=df[col_name], name=col_name, yaxis='y2')
  67. traces = [get_trace(col_name) for col_name in df.columns[1:]]
  68. traces.append(go.Scatter(x=df.index, y=df.total_cases, name='Total Cases', yaxis='y1'))
  69. figure = go.Figure()
  70. figure.add_traces(traces)
  71. figure.update_layout(
  72. yaxis1=dict(title='Total Cases', rangemode='tozero'),
  73. yaxis2=dict(title='%', rangemode='tozero', overlaying='y', side='right'),
  74. legend=dict(x=1.1),
  75. margin=dict(t=24, b=0),
  76. paper_bgcolor='rgba(0, 0, 0, 0)'
  77. )
  78. return figure
  79. main()
  80. def update_file(filename, figure):
  81. lines = read_file(filename)
  82. f_json = figure.to_json(pretty=True).replace('\n', '\n ')
  83. out = lines[:6] + [f' {f_json}\n', ' )\n', '};\n']
  84. write_to_file(filename, out)
  85. ###
  86. ## UTIL
  87. #
  88. def read_file(filename):
  89. p = Path(__file__).resolve().parent / filename
  90. with open(p, encoding='utf-8') as file:
  91. return file.readlines()
  92. def write_to_file(filename, lines):
  93. p = Path(__file__).resolve().parent / filename
  94. with open(p, 'w', encoding='utf-8') as file:
  95. file.writelines(lines)
  96. def write_to_png_file(filename, figure, width, height):
  97. p = Path(__file__).resolve().parent / filename
  98. figure.write_image(str(p), width=width, height=height)
  99. if __name__ == '__main__':
  100. main()