From 0eba03f74feba215c936371fc9f5cbe10f0e5a9e Mon Sep 17 00:00:00 2001 From: AaronCooke2718 <121969609+AaronCooke2718@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:29:13 -0500 Subject: [PATCH 01/19] Add dev container --- .devcontainer/devcontainer.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..8d96444 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,26 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile +{ + "name": "Existing Dockerfile", + "build": { + // Sets the run context to one level up instead of the .devcontainer folder. + "context": "..", + // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. + "dockerfile": "../Dockerfile" + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Uncomment the next line to run commands after the container is created. + // "postCreateCommand": "cat /etc/os-release", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "devcontainer" +} From 9ebe41de5fe40903294efa6152ca069c954dc2f0 Mon Sep 17 00:00:00 2001 From: AaronCooke2718 <121969609+AaronCooke2718@users.noreply.github.com> Date: Thu, 23 Nov 2023 12:13:33 -0500 Subject: [PATCH 02/19] Update processing.py --- covid19_drdfm/processing.py | 91 +++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index bfadd4f..ed81356 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -1,3 +1,6 @@ +# %% + + """I/O and processing module Converts all input files into single consolidated dataframe that can be used @@ -129,3 +132,91 @@ def add_datetime(df: pd.DataFrame) -> pd.DataFrame: df = df.assign(Month=pd.to_numeric(df.Period.apply(lambda x: x[1:]))).assign(Day=1) df["Time"] = pd.to_datetime({"year": df.Year, "month": df.Month, "day": df.Day}) return df.drop(columns=["Period", "Month", "Year", "Day"]) + +# %% +def fix_names(df: pd.DataFrame) -> pd.DataFrame: + df = df.rename(columns={'Cases1': 'Pandemic_1', 'Cases2': 'Pandemic_2',\ + 'Cases3': 'Pandemic_3', 'Cases4': 'Pandemic_4', \ + 'Cases5': 'Pandemic_5', 'Hosp1': 'Pandemic_6',\ + 'Hosp2': 'Pandemic_7', 'Deaths1': 'Pandemic_8', \ + 'Deaths2': 'Pandemic_9', 'Deaths3': 'Pandemic_10'\ + 'Deaths4': 'Pandemic_11', 'Deaths5': 'Pandemic_12'\ + 'Vax1': 'Pandemic_Response_1', 'Vax2': 'Pandemic_Response_2',\ + 'Vax3': 'Pandemic_Response_3', 'Gather1': 'Pandemic_Response_4', \ + 'Gather2': 'Pandemic_Response_5', 'Gather3': 'Pandemic_Response_6',\ + 'Gather4': 'Pandemic_Response_7', 'SaH': 'Pandemic_Response_8', \ + 'Curfew': 'Pandemic_Response_9', 'Mask1': 'Pandemic_Response_10'\ + 'Mask2': 'Pandemic_Response_11', 'School': 'Pandemic_Response_12'\ + 'Cons1': 'Demand_1', 'Cons2': 'Demand_2',\ + 'Cons3': 'Demand_3', 'Cons4': 'Demand_4',\ + 'Cons5': 'Demand_5', 'Employment1': 'Demand_6',\ + 'Employment2': 'Demand_7', 'GDP': 'Supply_1',\ + 'UI': 'Supply_2', 'PartR': 'Supply_3',\ + 'UR': 'Supply_4', 'RPFI': 'Supply_5',\ + 'FixAss': 'Supply_6', 'Prod': 'Supply_7',\ + 'CPI': 'Monetary_1', 'CPIU': 'Monetary_2',\ + 'PCE': 'Monetary_3', 'PCEC': 'Monetary_4',\ + 'TBill1mo': 'Monetary_5', 'TBill6mo': 'Monetary_6',\ + 'TBill1yr': 'Monetary_7', 'TBill5yr': 'Monetary_8',\ + 'TBill10yr': 'Monetary_9', 'TBill30yr': 'Monetary_10',\ + 'FFR': 'Monetary_11'}) + """ + #write the code that will change the dataframes names + # Look at other pandemic functions + # return modified version of the dataframe + # Have test code to validate""" + +def factordic() + factors = {} + factors[Cases1] = 'Global', 'Pandemic' + factors[Cases2] = 'Global', 'Pandemic' + factors[Cases3] = 'Global', 'Pandemic' + factors[Cases4] = 'Global', 'Pandemic' + factors[Cases5] = 'Global', 'Pandemic' + factors[Hosp1] = 'Global', 'Pandemic' + factors[Hosp2] = 'Global', 'Pandemic' + factors[Deaths1] = 'Global', 'Pandemic' + factors[Deaths2] = 'Global', 'Pandemic' + factors[Deaths3] = 'Global', 'Pandemic' + factors[Deaths4] = 'Global', 'Pandemic' + factors[Deaths5] = 'Global', 'Pandemic' + factors[Vax1] = 'Global', 'Response' + factors[Vax2] = 'Global', 'Response' + factors[Vax3] = 'Global', 'Response' + factors[Gather1] = 'Global', 'Response' + factors[Gather2] = 'Global', 'Response' + factors[Gather3] = 'Global', 'Response' + factors[Gather4] = 'Global', 'Response' + factors[SaH] = 'Global', 'Response' + factors[Curfew] = 'Global', 'Response' + factors[Mask1] = 'Global', 'Response' + factors[Mask2] = 'Global', 'Response' + factors[School] = 'Global', 'Response' + factors[Cons1] = 'Global', 'Consumption' + factors[Cons2] = 'Global', 'Consumption' + factors[Cons3] = 'Global', 'Consumption' + factors[Cons4] = 'Global', 'Consumption' + factors[Cons5] = 'Global', 'Consumption' + factors[Employment1] = 'Global', 'Employment' + factors[Employment2] = 'Global', 'Employment' + factors[UI] = 'Global', 'Employment' + factors[PartR] = 'Global', 'Employment' + factors[UR] = 'Global', 'Employment' + factors[CPI] = 'Global', 'Inflation' + factors[CPIU] = 'Global', 'Inflation' + factors[PCE] = 'Global', 'Inflation' + factors[PCEC] = 'Global', 'Inflation' + factors[RPFI] = 'Global', 'Uncat' + factors[FixAss] = 'Global', 'Uncat' + factors[Prod] = 'Global', 'Uncat' + factors[GDP] = 'Global', 'Uncat' + factors[TBill1mo] = 'Global', 'Uncat' + factors[TBill6mo] = 'Global', 'Uncat' + factors[TBill1yr] = 'Global', 'Uncat' + factors[TBill5yr] = 'Global', 'Uncat' + factors[TBill10tr] = 'Global', 'Uncat' + factors[TBill30yr] = 'Global', 'Uncat' + factors[TBillFFR] = 'Global', 'Uncat' + + + From 064a282dc0237716d21b136466f2f91c2fb9ab72 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Thu, 30 Nov 2023 17:45:21 -0800 Subject: [PATCH 03/19] Fix syntax errors, fix dictionary mapping, name mapping --- .coverage | Bin 53248 -> 69632 bytes coverage.xml | 168 +++++++++++++++++---------- covid19_drdfm/processing.py | 218 ++++++++++++++++++++---------------- 3 files changed, 228 insertions(+), 158 deletions(-) diff --git a/.coverage b/.coverage index dee71bc5687fb946cc37a7d0e4fc0617475b64cd..ca404e2d7a584fc263f999f2e5d93cbc58fdcfd0 100644 GIT binary patch literal 69632 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 Db8Moi diff --git a/coverage.xml b/coverage.xml index eeb38d0..7cb79f9 100644 --- a/coverage.xml +++ b/coverage.xml @@ -1,12 +1,12 @@ - + /home/jvivian/covid19-drDFM/covid19_drdfm - + @@ -32,64 +32,103 @@ - + - + - + - - - - + + + + + + + - - + + + - + - - - + + - + - - - - + + + - - - + + + - - + + - + + - + + - - - - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -98,44 +137,49 @@ - + - + - - + + - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index ed81356..4181f99 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -1,6 +1,3 @@ -# %% - - """I/O and processing module Converts all input files into single consolidated dataframe that can be used @@ -39,22 +36,20 @@ def get_df() -> pd.DataFrame: .drop( columns=["Proportion", "proportion_vax2", "Pandemic_Response_8"] ) #! Columns removed per discussion with AC - # .assign(Pandemic_Response_4=lambda x: x[['Pandemic_Response_4', 'Pandemic_Response_5', 'Pandemic_Response_6', 'Pandemic_Response_7']].max(axis=1)) - # .assign(Pandemic_Response_10=lambda x: x[['Pandemic_Response_10', 'Pandemic_Response_11']].max(axis=1)) - # .drop(columns=['Pandemic_Response_5','Pandemic_Response_6', 'Pandemic_Response_7', 'Pandemic_Response_11']) .pipe(adjust_inflation) .pipe(add_datetime) + .pipe(fix_names) ) -def get_factors() -> dict[str, (str, str)]: - """Fetch pre-defined factors for model +# def get_factors() -> dict[str, (str, str)]: +# """Fetch pre-defined factors for model - Returns: - dict[str, (str, str)]: Factors from `./data/processed/factors.yaml` - """ - with open(DATA_DIR / "factors.json") as f: - return json.load(f) +# Returns: +# dict[str, (str, str)]: Factors from `./data/processed/factors.yaml` +# """ +# with open(DATA_DIR / "factors.json") as f: +# return json.load(f) def write(df: pd.DataFrame, outpath: Path) -> Path: @@ -133,90 +128,121 @@ def add_datetime(df: pd.DataFrame) -> pd.DataFrame: df["Time"] = pd.to_datetime({"year": df.Year, "month": df.Month, "day": df.Day}) return df.drop(columns=["Period", "Month", "Year", "Day"]) -# %% + def fix_names(df: pd.DataFrame) -> pd.DataFrame: - df = df.rename(columns={'Cases1': 'Pandemic_1', 'Cases2': 'Pandemic_2',\ - 'Cases3': 'Pandemic_3', 'Cases4': 'Pandemic_4', \ - 'Cases5': 'Pandemic_5', 'Hosp1': 'Pandemic_6',\ - 'Hosp2': 'Pandemic_7', 'Deaths1': 'Pandemic_8', \ - 'Deaths2': 'Pandemic_9', 'Deaths3': 'Pandemic_10'\ - 'Deaths4': 'Pandemic_11', 'Deaths5': 'Pandemic_12'\ - 'Vax1': 'Pandemic_Response_1', 'Vax2': 'Pandemic_Response_2',\ - 'Vax3': 'Pandemic_Response_3', 'Gather1': 'Pandemic_Response_4', \ - 'Gather2': 'Pandemic_Response_5', 'Gather3': 'Pandemic_Response_6',\ - 'Gather4': 'Pandemic_Response_7', 'SaH': 'Pandemic_Response_8', \ - 'Curfew': 'Pandemic_Response_9', 'Mask1': 'Pandemic_Response_10'\ - 'Mask2': 'Pandemic_Response_11', 'School': 'Pandemic_Response_12'\ - 'Cons1': 'Demand_1', 'Cons2': 'Demand_2',\ - 'Cons3': 'Demand_3', 'Cons4': 'Demand_4',\ - 'Cons5': 'Demand_5', 'Employment1': 'Demand_6',\ - 'Employment2': 'Demand_7', 'GDP': 'Supply_1',\ - 'UI': 'Supply_2', 'PartR': 'Supply_3',\ - 'UR': 'Supply_4', 'RPFI': 'Supply_5',\ - 'FixAss': 'Supply_6', 'Prod': 'Supply_7',\ - 'CPI': 'Monetary_1', 'CPIU': 'Monetary_2',\ - 'PCE': 'Monetary_3', 'PCEC': 'Monetary_4',\ - 'TBill1mo': 'Monetary_5', 'TBill6mo': 'Monetary_6',\ - 'TBill1yr': 'Monetary_7', 'TBill5yr': 'Monetary_8',\ - 'TBill10yr': 'Monetary_9', 'TBill30yr': 'Monetary_10',\ - 'FFR': 'Monetary_11'}) - """ - #write the code that will change the dataframes names - # Look at other pandemic functions - # return modified version of the dataframe - # Have test code to validate""" - -def factordic() - factors = {} - factors[Cases1] = 'Global', 'Pandemic' - factors[Cases2] = 'Global', 'Pandemic' - factors[Cases3] = 'Global', 'Pandemic' - factors[Cases4] = 'Global', 'Pandemic' - factors[Cases5] = 'Global', 'Pandemic' - factors[Hosp1] = 'Global', 'Pandemic' - factors[Hosp2] = 'Global', 'Pandemic' - factors[Deaths1] = 'Global', 'Pandemic' - factors[Deaths2] = 'Global', 'Pandemic' - factors[Deaths3] = 'Global', 'Pandemic' - factors[Deaths4] = 'Global', 'Pandemic' - factors[Deaths5] = 'Global', 'Pandemic' - factors[Vax1] = 'Global', 'Response' - factors[Vax2] = 'Global', 'Response' - factors[Vax3] = 'Global', 'Response' - factors[Gather1] = 'Global', 'Response' - factors[Gather2] = 'Global', 'Response' - factors[Gather3] = 'Global', 'Response' - factors[Gather4] = 'Global', 'Response' - factors[SaH] = 'Global', 'Response' - factors[Curfew] = 'Global', 'Response' - factors[Mask1] = 'Global', 'Response' - factors[Mask2] = 'Global', 'Response' - factors[School] = 'Global', 'Response' - factors[Cons1] = 'Global', 'Consumption' - factors[Cons2] = 'Global', 'Consumption' - factors[Cons3] = 'Global', 'Consumption' - factors[Cons4] = 'Global', 'Consumption' - factors[Cons5] = 'Global', 'Consumption' - factors[Employment1] = 'Global', 'Employment' - factors[Employment2] = 'Global', 'Employment' - factors[UI] = 'Global', 'Employment' - factors[PartR] = 'Global', 'Employment' - factors[UR] = 'Global', 'Employment' - factors[CPI] = 'Global', 'Inflation' - factors[CPIU] = 'Global', 'Inflation' - factors[PCE] = 'Global', 'Inflation' - factors[PCEC] = 'Global', 'Inflation' - factors[RPFI] = 'Global', 'Uncat' - factors[FixAss] = 'Global', 'Uncat' - factors[Prod] = 'Global', 'Uncat' - factors[GDP] = 'Global', 'Uncat' - factors[TBill1mo] = 'Global', 'Uncat' - factors[TBill6mo] = 'Global', 'Uncat' - factors[TBill1yr] = 'Global', 'Uncat' - factors[TBill5yr] = 'Global', 'Uncat' - factors[TBill10tr] = 'Global', 'Uncat' - factors[TBill30yr] = 'Global', 'Uncat' - factors[TBillFFR] = 'Global', 'Uncat' + """Map sensible names to the merged input dataframe + Args: + df (pd.DataFrame): Input DataFrame after merging all input data + + Returns: + pd.DataFrame: DataFrame with mapped names + """ + return df.rename( + columns={ + "Pandemic_1": "Cases1", + "Pandemic_2": "Cases2", + "Pandemic_3": "Cases3", + "Pandemic_4": "Cases4", + "Pandemic_5": "Cases5", + "Pandemic_6": "Hosp1", + "Pandemic_7": "Hosp2", + "Pandemic_8": "Deaths1", + "Pandemic_9": "Deaths2", + "Pandemic_10": "Deaths3", + "Pandemic_11": "Deaths4", + "Pandemic_12": "Deaths5", + "Pandemic_Response_1": "Vax1", + "Pandemic_Response_2": "Vax2", + "Pandemic_Response_3": "Vax3", + "Pandemic_Response_4": "Gather1", + "Pandemic_Response_5": "Gather2", + "Pandemic_Response_6": "Gather3", + "Pandemic_Response_7": "Gather4", + "Pandemic_Response_8": "SaH", + "Pandemic_Response_9": "Curfew", + "Pandemic_Response_10": "Mask1", + "Pandemic_Response_11": "Mask2", + "Pandemic_Response_12": "School", + "Demand_1": "Cons1", + "Demand_2": "Cons2", + "Demand_3": "Cons3", + "Demand_4": "Cons4", + "Demand_5": "Cons5", + "Demand_6": "Employment1", + "Demand_7": "Employment2", + "Supply_1": "GDP", + "Supply_2": "UI", + "Supply_3": "PartR", + "Supply_4": "UR", + "Supply_5": "RPFI", + "Supply_6": "FixAss", + "Supply_7": "Prod", + "Monetary_1": "CPI", + "Monetary_2": "CPIU", + "Monetary_3": "PCE", + "Monetary_4": "PCEC", + "Monetary_5": "TBill1mo", + "Monetary_6": "TBill6mo", + "Monetary_7": "TBill1yr", + "Monetary_8": "TBill5yr", + "Monetary_9": "TBill10yr", + "Monetary_10": "TBill30yr", + "Monetary_11": "FFR", + } + ) +def get_factors(): + """Returns the pre-assigned factors for the model""" + return { + "Cases1": ("Global", "Pandemic"), + "Cases2": ("Global", "Pandemic"), + "Cases3": ("Global", "Pandemic"), + "Cases4": ("Global", "Pandemic"), + "Cases5": ("Global", "Pandemic"), + "Hosp1": ("Global", "Pandemic"), + "Hosp2": ("Global", "Pandemic"), + "Deaths1": ("Global", "Pandemic"), + "Deaths2": ("Global", "Pandemic"), + "Deaths3": ("Global", "Pandemic"), + "Deaths4": ("Global", "Pandemic"), + "Deaths5": ("Global", "Pandemic"), + "Vax1": ("Global", "Response"), + "Vax2": ("Global", "Response"), + "Vax3": ("Global", "Response"), + "Gather1": ("Global", "Response"), + "Gather2": ("Global", "Response"), + "Gather3": ("Global", "Response"), + "Gather4": ("Global", "Response"), + "SaH": ("Global", "Response"), + "Curfew": ("Global", "Response"), + "Mask1": ("Global", "Response"), + "Mask2": ("Global", "Response"), + "School": ("Global", "Response"), + "Cons1": ("Global", "Consumption"), + "Cons2": ("Global", "Consumption"), + "Cons3": ("Global", "Consumption"), + "Cons4": ("Global", "Consumption"), + "Cons5": ("Global", "Consumption"), + "Employment1": ("Global", "Employment"), + "Employment2": ("Global", "Employment"), + "UI": ("Global", "Employment"), + "PartR": ("Global", "Employment"), + "UR": ("Global", "Employment"), + "CPI": ("Global", "Inflation"), + "CPIU": ("Global", "Inflation"), + "PCE": ("Global", "Inflation"), + "PCEC": ("Global", "Inflation"), + "RPFI": ("Global", "Uncat"), + "FixAss": ("Global", "Uncat"), + "Prod": ("Global", "Uncat"), + "GDP": ("Global", "Uncat"), + "TBill1mo": ("Global", "Uncat"), + "TBill6mo": ("Global", "Uncat"), + "TBill1yr": ("Global", "Uncat"), + "TBill5yr": ("Global", "Uncat"), + "TBill10tr": ("Global", "Uncat"), + "TBill30yr": ("Global", "Uncat"), + "TBillFFR": ("Global", "Uncat"), + } From 1a7d9ae45630524e8e0d76a09345dee252bc9d5d Mon Sep 17 00:00:00 2001 From: AaronCooke2718 <121969609+AaronCooke2718@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:25:22 -0500 Subject: [PATCH 04/19] Create scm.py --- scm.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scm.py diff --git a/scm.py b/scm.py new file mode 100644 index 0000000..e69de29 From d483b2ff9f2959fe78ed1be5dc0c6b3ed113e464 Mon Sep 17 00:00:00 2001 From: AaronCooke2718 <121969609+AaronCooke2718@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:38:59 -0500 Subject: [PATCH 05/19] Update processing.py --- covid19_drdfm/processing.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index 4181f99..0214108 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -164,6 +164,9 @@ def fix_names(df: pd.DataFrame) -> pd.DataFrame: "Pandemic_Response_10": "Mask1", "Pandemic_Response_11": "Mask2", "Pandemic_Response_12": "School", + "Pandemic_Response_13": "ARP", + "Pandemic_Response_14": "PPP", + "Pandemic_Response_15": "CARES", "Demand_1": "Cons1", "Demand_2": "Cons2", "Demand_3": "Cons3", @@ -220,6 +223,11 @@ def get_factors(): "Mask1": ("Global", "Response"), "Mask2": ("Global", "Response"), "School": ("Global", "Response"), + "ARP": ("Global", "Response"), + "PPP": ("Global", "Response"), + "CARES": ("Global", "Response"), + "School": ("Global", "Response"), + "School": ("Global", "Response"), "Cons1": ("Global", "Consumption"), "Cons2": ("Global", "Consumption"), "Cons3": ("Global", "Consumption"), @@ -242,7 +250,7 @@ def get_factors(): "TBill6mo": ("Global", "Uncat"), "TBill1yr": ("Global", "Uncat"), "TBill5yr": ("Global", "Uncat"), - "TBill10tr": ("Global", "Uncat"), + "TBill10yr": ("Global", "Uncat"), "TBill30yr": ("Global", "Uncat"), - "TBillFFR": ("Global", "Uncat"), + "FFR": ("Global", "Uncat"), } From d6cb8c50b62b761f7a0d5792cd77518dd73d4066 Mon Sep 17 00:00:00 2001 From: AaronCooke2718 <121969609+AaronCooke2718@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:39:39 -0500 Subject: [PATCH 06/19] Update scm.py --- scm.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/scm.py b/scm.py index e69de29..b6289e6 100644 --- a/scm.py +++ b/scm.py @@ -0,0 +1,21 @@ +import pandas as pd +import numpy as np + +from SyntheticControlMethods import Synth, DiffSynth + +#Import data +data_dir = "https://raw.githubusercontent.com/OscarEngelbrektson/SyntheticControlMethods/master/examples/datasets/" +df = pd.read_csv(data_dir + "smoking_data" + ".csv") + +from os import rename +#Fit Differenced Synthetic Control +df = df.rename(columns={'cigsale': 'Pandemic'}) +sc = Synth(df, "Pandemic", "state", "year", 1989, "California", n_optim=10, pen="auto") + +print(sc.original_data.weight_df) +print(sc.original_data.comparison_df) +print(sc.original_data.pen) + +#Visualize +sc.plot(["original", "pointwise", "cumulative"], treated_label="California", + synth_label="Synthetic California", treatment_label="Pandemic Response") From 019f787a23615c7c7cea76f5230671db3eac3408 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Sat, 2 Dec 2023 16:12:44 -0800 Subject: [PATCH 07/19] Push fix for adjust_pandemic_response Add poetry dependency for SCM and move SCM to root --- covid19_drdfm/processing.py | 5 +- scm.py => covid19_drdfm/scm.py | 17 ++- poetry.lock | 246 ++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 4 files changed, 260 insertions(+), 9 deletions(-) rename scm.py => covid19_drdfm/scm.py (62%) diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index 0214108..74d9a65 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -6,7 +6,7 @@ This model input DataFrame can be generated with a single function: - `df = run()` """ -import json + from fractions import Fraction from functools import reduce from pathlib import Path @@ -105,7 +105,8 @@ def adjust_pandemic_response(df: pd.DataFrame) -> pd.DataFrame: pd.DataFrame: Adjusted DataFrame """ govt_fund_dist = get_govt_fund_dist() - responses = [f"Pandemic_Response_{x}" for x in [13, 14, 15]] + # responses = [f"Pandemic_Response_{x}" for x in [13, 14, 15]] + responses = ["ARP", "PPP", "CARES"] for r in responses: df[r] = df[r].astype(float) i = df.index[df[r] > 0][0] diff --git a/scm.py b/covid19_drdfm/scm.py similarity index 62% rename from scm.py rename to covid19_drdfm/scm.py index b6289e6..4c0589a 100644 --- a/scm.py +++ b/covid19_drdfm/scm.py @@ -3,19 +3,24 @@ from SyntheticControlMethods import Synth, DiffSynth -#Import data +# Import data data_dir = "https://raw.githubusercontent.com/OscarEngelbrektson/SyntheticControlMethods/master/examples/datasets/" df = pd.read_csv(data_dir + "smoking_data" + ".csv") from os import rename -#Fit Differenced Synthetic Control -df = df.rename(columns={'cigsale': 'Pandemic'}) + +# Fit Differenced Synthetic Control +df = df.rename(columns={"cigsale": "Pandemic"}) sc = Synth(df, "Pandemic", "state", "year", 1989, "California", n_optim=10, pen="auto") print(sc.original_data.weight_df) print(sc.original_data.comparison_df) print(sc.original_data.pen) -#Visualize -sc.plot(["original", "pointwise", "cumulative"], treated_label="California", - synth_label="Synthetic California", treatment_label="Pandemic Response") +# Visualize +sc.plot( + ["original", "pointwise", "cumulative"], + treated_label="California", + synth_label="Synthetic California", + treatment_label="Pandemic Response", +) diff --git a/poetry.lock b/poetry.lock index 56f83f8..2065adc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -256,6 +256,27 @@ files = [ {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] +[[package]] +name = "clarabel" +version = "0.6.0" +description = "Clarabel Conic Interior Point Solver for Rust / Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "clarabel-0.6.0-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:4f366de79b8bc66bef8dc170987840b672ccab9222e710c09536d78ef47f606d"}, + {file = "clarabel-0.6.0-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:edcebbfc14073cd32bfb664317fd2555716c96be8b2a54efdb2b728453582bea"}, + {file = "clarabel-0.6.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e737d2818b9ca10e92ccd3fa9ad1a805b039976016415a0c45adef3427d70792"}, + {file = "clarabel-0.6.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e0b1891d8e507eb0bfc7e0b981584c388b2ab28658056e600997dbbc23f1ab4"}, + {file = "clarabel-0.6.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9946d3b5db346421b6d839d868e7b1151b590f871344fe95113bfd55b5be2433"}, + {file = "clarabel-0.6.0-cp37-abi3-win32.whl", hash = "sha256:73ed408c975a8ea021c3d8262d5d023a18e1ac3f6bb59a37cd69a11dba8f86ed"}, + {file = "clarabel-0.6.0-cp37-abi3-win_amd64.whl", hash = "sha256:5a6be4df9fed98b6f73f034836def913a1ecd52e8b79ca230ddf7cd66ebcdee7"}, + {file = "clarabel-0.6.0.tar.gz", hash = "sha256:ef909a393e72981ca10b1d866d9cc7fb6295ece20ae035def764338894961184"}, +] + +[package.dependencies] +numpy = "*" +scipy = "*" + [[package]] name = "click" version = "8.1.7" @@ -482,6 +503,64 @@ files = [ [package.extras] dev = ["black (==22.3.0)", "hypothesis", "numpy", "pytest (>=5.30)", "pytest-xdist"] +[[package]] +name = "cvxpy" +version = "1.4.1" +description = "A domain-specific language for modeling convex optimization problems in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cvxpy-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:03588055b660c043848f5281fe24dbd21f005b34bd8bd3b56906d8ad457c14ae"}, + {file = "cvxpy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:315609ff96adeda4970471b349bc19d44ff4043e15630cf5ac70c029658fe8fc"}, + {file = "cvxpy-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55e08ffb973d62b3fabc675ad464cb6013ea5ce69799f330b33a084a2e580d8d"}, + {file = "cvxpy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f1482558b785f2db51c76b9c6e91cc85dbd146675b126a799e7d7aab5b15354"}, + {file = "cvxpy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:2f84687d15d11f9b49ca902f20103a2076efd47773c399cace71237ef53cdadc"}, + {file = "cvxpy-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d6bfbd535fdaabc5fa55f28de7a1d40f3a803a27fe3fec86e90700fa159a3afc"}, + {file = "cvxpy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:71a95aaccf22431fd25a63bcb12d583e1b0baeaeb4fafa3e25857cec03b9e2f3"}, + {file = "cvxpy-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bae3bf31e4eb6ed6407f78c6bc3c7bc4b4145cdbbb9ba8c61c3fc541d7067"}, + {file = "cvxpy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41cfaecf86f85162ca53c7be7377b4143e316204fb9b6a7df8b7a08c826e3806"}, + {file = "cvxpy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:edf66010e49b64d3f2dd1a7abde8fa3e615ce7a2b3eb185ab744b0beb3a6adb9"}, + {file = "cvxpy-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6b0f17dca85b2a410e73f5d84b28f35f57a20cfec1b0adc9b16f0f8aabff9961"}, + {file = "cvxpy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9318c4e679b3db470e76e7f23cce362b038bd2d68c4a7326a7c21577ddbdc542"}, + {file = "cvxpy-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a46ef722c8d1590875e86360d5781703dfcbd08be73eb98a2fc91a280870064"}, + {file = "cvxpy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57593a852c563ce77bdb075a3e75f23d36d4b3162ebf3199b54cc7fe75088ef2"}, + {file = "cvxpy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:db89b55025514bad821b1f1781bed373cbb6aa22fe84420431efd510dbe7f858"}, + {file = "cvxpy-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:372c0825cc6e6bb03ecc550d83718761a1bbdbbb48010fec6f9718581ebd45b5"}, + {file = "cvxpy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163caffd7f7f27b6cb151f4ccff283068e063c3673158793048761690cbe4bbe"}, + {file = "cvxpy-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f24067c54979b09910aea0a03256247121d8a8169538facf087c1923e9e2701a"}, + {file = "cvxpy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a3ec054279880a9ebf5fd9d2ac4109acf944b8c45ea8b24e461680e34f3d7b5"}, + {file = "cvxpy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:d220a7ee55907da9b55b98e5238d03735118d03b82855ba87b872cb2e6977367"}, + {file = "cvxpy-1.4.1.tar.gz", hash = "sha256:7a9ef34e3c57ff8c844d86f0a3834fb5575af19233947639de0ba577c6122e3e"}, +] + +[package.dependencies] +clarabel = ">=0.5.0" +ecos = ">=2" +numpy = ">=1.15" +osqp = ">=0.6.2" +pybind11 = "*" +scipy = ">=1.1.0" +scs = ">=3.0" + +[package.extras] +cbc = ["cylp (>=0.91.5)"] +clarabel = ["clarabel"] +cvxopt = ["cvxopt"] +diffcp = ["diffcp"] +glop = ["ortools (>=9.5,<9.8)"] +glpk = ["cvxopt"] +glpk-mi = ["cvxopt"] +gurobi = ["gurobipy"] +highs = ["scipy (>=1.6.1)"] +mosek = ["Mosek"] +pdlp = ["ortools (>=9.5,<9.8)"] +piqp = ["piqp"] +proxqp = ["proxsuite"] +scip = ["PySCIPOpt"] +scipy = ["scipy"] +scs = ["setuptools (>65.5.1)"] +xpress = ["xpress"] + [[package]] name = "cycler" version = "0.12.1" @@ -524,6 +603,35 @@ files = [ {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, ] +[[package]] +name = "ecos" +version = "2.0.12" +description = "This is the Python package for ECOS: Embedded Cone Solver. See Github page for more information." +optional = false +python-versions = "*" +files = [ + {file = "ecos-2.0.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:835298a299c88c207b3402fba60ad9b5688b59bbbf2ac34a46de5b37165d773a"}, + {file = "ecos-2.0.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608bc822ee8e070927ab3519169b13a1a0fe88f3d562212d6b5dbb1039776360"}, + {file = "ecos-2.0.12-cp310-cp310-win_amd64.whl", hash = "sha256:5184a9d8521ad1af90ffcd9902a6fa75c7bc473f37d30d86f97beda1033dfca2"}, + {file = "ecos-2.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eba07599084724eedc20b2862d5580eebebb09609f4740baadc78401cb99827c"}, + {file = "ecos-2.0.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4979dc2d1cb6667e371a45a61887068505c1305437eef104ed6ef16f4b6aa0e3"}, + {file = "ecos-2.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:da8fbbca3feb83a9e27075d29b3765417d0c80af8ea83cbdc4a558cae7b564af"}, + {file = "ecos-2.0.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f70e4547966f530fd7715756f7a65d5b9b90b312b9d37f243ef9356c05e7d74c"}, + {file = "ecos-2.0.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:617be25d74222849622b0f82b94a11abcf1fae78ccaf69977b328321ee6ffa0b"}, + {file = "ecos-2.0.12-cp37-cp37m-win_amd64.whl", hash = "sha256:29d00164eaea66ed54697a3b361c575284a8bca54f2623381a0635806c7303a7"}, + {file = "ecos-2.0.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4e86671397d1d2cd7cccff8a9c45be0541b0c60af8b92a0ff3581c9ed869db67"}, + {file = "ecos-2.0.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:858a4dd3177bdc8cc6e362031732f5177b62138a1e4ef91c0dc3c6bd7d2d1248"}, + {file = "ecos-2.0.12-cp38-cp38-win_amd64.whl", hash = "sha256:528b02f53835bd1baeb2e23f8153b8d6cc2b3704e1768be6a1a972f542241670"}, + {file = "ecos-2.0.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e42bd4c19af6e04f76ccc85d941b1f1adc7faeee4d06d482395a6beb7bec895"}, + {file = "ecos-2.0.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6def54336a15b5a49bc3bfcaa36035e8557cae8a4853b17ca84f5a29c93bcaea"}, + {file = "ecos-2.0.12-cp39-cp39-win_amd64.whl", hash = "sha256:7af08941552fce108bd80145cdb6be7fa74477a20bacdac170800442cc7027d4"}, + {file = "ecos-2.0.12.tar.gz", hash = "sha256:f48816d73b87ae325556ea537b7c8743187311403c80e3832035224156337c4e"}, +] + +[package.dependencies] +numpy = ">=1.6" +scipy = ">=0.9" + [[package]] name = "et-xmlfile" version = "1.1.0" @@ -1561,6 +1669,45 @@ files = [ [package.dependencies] et-xmlfile = "*" +[[package]] +name = "osqp" +version = "0.6.3" +description = "OSQP: The Operator Splitting QP Solver" +optional = false +python-versions = "*" +files = [ + {file = "osqp-0.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7d923c836f1d07115057e595245ccc1694ecae730a1affda78fc6f3c8d239"}, + {file = "osqp-0.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dfda08c38c3521012740a73ef782f97dfc54a41deae4b0bc4afd18d0e74da0"}, + {file = "osqp-0.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7eafa3f3e82dd36c52f3f4ef19a95142405c807c272c4b53c5971c53535d7804"}, + {file = "osqp-0.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:3cbb6efdaffb7387dc0037dfe3259d4803e5ad7217e6f20fb605c92953214b9d"}, + {file = "osqp-0.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1b2049b2c42565dcaa63ddca1c4028b1fb20aab141453f5d77e8ff5b1a99a2cf"}, + {file = "osqp-0.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:146b89f2cfbf59eaeb2c47e3a312f2034138df78d80ce052364810dc0ef70fc4"}, + {file = "osqp-0.6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0084e3d733c75687d68bc133bc380ce471dfe6f7724af2718a43491782eec8d6"}, + {file = "osqp-0.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:1b573fe1cd0e82239a279c58817c1d365187ef862e928b2b9c828c3c516ad3c2"}, + {file = "osqp-0.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c3951ef505177b858c6cd34de980346014cae3d2234c93db960b12c5885f9a2"}, + {file = "osqp-0.6.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc18f87c9549032c163ce590a5e32079df94ee656c8fb357ba607aa9d78fab81"}, + {file = "osqp-0.6.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b1a4b538aab629b0fae69f644b7e76f81f94d65230014d482e296dacd046b"}, + {file = "osqp-0.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:60abec3593870990b16f00bd5017096a7091fb00b68d0db3383fc048ca8e55c9"}, + {file = "osqp-0.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b73bdd9589901841af83c5ed6a4092b4fac5a0beff9e32682d8526d1f16a728c"}, + {file = "osqp-0.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d9f611823af4a8b241c86805920e5382cd65c7f94fd3615b4eef999ed94c7c"}, + {file = "osqp-0.6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30fbc3b3c028c06a6c5f1e66be7b7106ad48a29e0dc5bd82393f82dd68235ef8"}, + {file = "osqp-0.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fe57e4bde071b388518ecb068f26319506dd9cb107363d3d80c12d2e59fc1e81"}, + {file = "osqp-0.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41f304d1d7f91af07d8f0b01e5af29ec3bb8824f0102c7fd8b13b497be120da4"}, + {file = "osqp-0.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea7d8c92bcdf4fef98d777f13d39060d425ef2e8778ed487c96a6fa10848cdea"}, + {file = "osqp-0.6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f3a3c6d2708868e5e3fe2da300d6523cbf68a3d8734ce9c5043db37391969f5"}, + {file = "osqp-0.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:1c548a0b3691850e7e22f3624a128d8af33416d70a9b5976a47d4d832028dcd8"}, + {file = "osqp-0.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:387e7abd737dfe32c9ec00ad74af25328cdd0d0f634d79530655c040a5cb9590"}, + {file = "osqp-0.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1445e10a94e01698e13c87a7debf6ac1a15f3acd1f8f6340cb1ad945db4732b"}, + {file = "osqp-0.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0441c10f7fe5f46692a9b44a57138977bb112ae3f8127151671968c5d9ec5dbb"}, + {file = "osqp-0.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:b15e65a307fbbabf60248bb9bc204e61d5d4ae64e00427a69e2dad9622f4c29d"}, + {file = "osqp-0.6.3.tar.gz", hash = "sha256:03e460e683ec2ce0f839353ddfa3c4c8ffa509ab8cf6a2b2afbb586fa453e180"}, +] + +[package.dependencies] +numpy = ">=1.7" +qdldl = "*" +scipy = ">=0.13.2" + [[package]] name = "packaging" version = "23.1" @@ -1907,6 +2054,20 @@ files = [ [package.dependencies] numpy = ">=1.16.6" +[[package]] +name = "pybind11" +version = "2.11.1" +description = "Seamless operability between C++11 and Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pybind11-2.11.1-py3-none-any.whl", hash = "sha256:33cdd02a6453380dd71cc70357ce388ad1ee8d32bd0e38fc22b273d050aa29b3"}, + {file = "pybind11-2.11.1.tar.gz", hash = "sha256:00cd59116a6e8155aecd9174f37ba299d1d397ed4a6b86ac1dfe01b3e40f2cc4"}, +] + +[package.extras] +global = ["pybind11-global (==2.11.1)"] + [[package]] name = "pycparser" version = "2.21" @@ -2249,6 +2410,38 @@ files = [ [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} +[[package]] +name = "qdldl" +version = "0.1.7.post0" +description = "QDLDL, a free LDL factorization routine." +optional = false +python-versions = "*" +files = [ + {file = "qdldl-0.1.7.post0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ab02e8b9ff86bd644a1935718387c82fbe04c31e3309cf9f7a121d02b1deda8"}, + {file = "qdldl-0.1.7.post0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e5d6753310377451ed4dc09b1ef28faf40108b713e7f55c8a8ae94d679a672"}, + {file = "qdldl-0.1.7.post0-cp310-cp310-win_amd64.whl", hash = "sha256:718d8e141832e96ba71ca1807a74813836c6403110faaa3d33a67de1af3b29c4"}, + {file = "qdldl-0.1.7.post0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e3f06e8a49ddd834b24fc3d7afbba4fec0923101045aa2666e18d2a9980e329"}, + {file = "qdldl-0.1.7.post0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a81c46522dd6b3042e2348fa98128bb5c0e466f42bce214e80cfb766ff40930"}, + {file = "qdldl-0.1.7.post0-cp311-cp311-win_amd64.whl", hash = "sha256:4a86155f3de66c5db0e21544b7a2421c671028fa20da407686d2a8d0e9b57e51"}, + {file = "qdldl-0.1.7.post0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:717cb1892b033c01a0aae84ededcfa1f05bcb97013095d779c497e6c32f90dac"}, + {file = "qdldl-0.1.7.post0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc35432913085d94b2327242cf51388467ef7a37ac0d71eb31b594b575dd498"}, + {file = "qdldl-0.1.7.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:fd5cfd8c50f33ddacb830594a63b8c1093a24aea45312b9d2ed826cea5ece08a"}, + {file = "qdldl-0.1.7.post0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:981ca8672e9506976c663552c1eb6f6daf9726d62650b3bf5900260946156166"}, + {file = "qdldl-0.1.7.post0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8ec670d97cf756f9159dc0a11de5cf054e88aefe84bea1c7282f00334642843"}, + {file = "qdldl-0.1.7.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:aa208703b44337a7e77f6f2663f7a452144becb4421970d534ff8297b92e1e10"}, + {file = "qdldl-0.1.7.post0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b42649484f7c0d8ee659224ecaac0a3e97f12531018207f4d7323e4071320eb1"}, + {file = "qdldl-0.1.7.post0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26aa3d6f0da7779265d72e8f418094003e75fa53c515a53bc03fd8b9bcfbf7de"}, + {file = "qdldl-0.1.7.post0-cp38-cp38-win_amd64.whl", hash = "sha256:e55bcd6962178029faf543addd49db145302dd51e19855fefa71b5fd55840eea"}, + {file = "qdldl-0.1.7.post0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1dd0e570e65aaf35e10b7fb345f7ac763fd05a2227b9c06ce65e07993fc4984"}, + {file = "qdldl-0.1.7.post0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae161342529852b6248ace4642bc4ee371a7c1e0707b7bc43a43ef7e73c06ca3"}, + {file = "qdldl-0.1.7.post0-cp39-cp39-win_amd64.whl", hash = "sha256:092f6606690a2b9bd3c939f3147887e02de13bb068fbed5ffdc7459034def623"}, + {file = "qdldl-0.1.7.post0.tar.gz", hash = "sha256:f346a114c8342ee6d4dbd6471eef314199fb268d3bf7b95885ca351fde2b023f"}, +] + +[package.dependencies] +numpy = ">=1.7" +scipy = ">=0.13.2" + [[package]] name = "referencing" version = "0.30.2" @@ -2597,6 +2790,39 @@ dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyl doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +[[package]] +name = "scs" +version = "3.2.4.post1" +description = "Splitting conic solver" +optional = false +python-versions = ">=3.7" +files = [ + {file = "scs-3.2.4.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51fed30d2a4a1e6fbfc1e52b4cb3adeecbe89d7c47f3539b49afbb852415fe19"}, + {file = "scs-3.2.4.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb0524c0b9c3ed0d65dae161475accf3efa8e170938eb93251a60e9709b156ee"}, + {file = "scs-3.2.4.post1-cp310-cp310-win_amd64.whl", hash = "sha256:534519819eea96f18902a9fce15c4ec562b99d23b38dc843a48cb137b5641613"}, + {file = "scs-3.2.4.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d04ee4d19ac6d0f5053663bc48fcd5c5faed534272f13b10a4e173c814eea69"}, + {file = "scs-3.2.4.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c23b4299ab77ff5f654573d5667dc982292a8ef2b979053b38c40663919f13"}, + {file = "scs-3.2.4.post1-cp311-cp311-win_amd64.whl", hash = "sha256:ae4624938d3e3a8b7e508029275c6ad7a978fd48c158d0818f69f4ae764bf945"}, + {file = "scs-3.2.4.post1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:40294e22bfe509bdf7fd65a6b77c38cec22dcb3567ff5a75f3c41a1faf2ef1d5"}, + {file = "scs-3.2.4.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a2337acb0604770b6df1473254065a51c210ff9c82fc7c4490595510287a337"}, + {file = "scs-3.2.4.post1-cp312-cp312-win_amd64.whl", hash = "sha256:8689e75a57e59846e65d1c4b9d57e9964b00fcbb8e67fc77f98cf6e0a0530abd"}, + {file = "scs-3.2.4.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ad991b00d0a87c85db57bf2f1863c21bdc4e2f13837f6c35e809f5936bc6f165"}, + {file = "scs-3.2.4.post1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a28af160a44268e726a59d6cf340629b82940c1a643c4c87fe777e9cbe550d75"}, + {file = "scs-3.2.4.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:f6283f725f3fee63d4631c2532d01a5b2ea65883b04d3da3be06084b1c60171b"}, + {file = "scs-3.2.4.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b3a622cf2120ae765f0f3ad5c6f4f86796d317e29132bab2ad4af3c14d9bf4d"}, + {file = "scs-3.2.4.post1-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:4b5259137c263304effa2b28d0125437ac23569e6e7753c115ae1206ec5033fd"}, + {file = "scs-3.2.4.post1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424710bc19b0506feee7e05e6d2b7af98acf09af5bd5353126164cbd46ac923f"}, + {file = "scs-3.2.4.post1-cp38-cp38-win_amd64.whl", hash = "sha256:e21bdc8046648846e2c204a6c5cf24eaaedd2b8f5e0a2ab41a647b0247b8d592"}, + {file = "scs-3.2.4.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cea0f7e9473f43f7edf1641d020ead7e39653a81c540fbdba8f3b7b8480038c9"}, + {file = "scs-3.2.4.post1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6126f1d7ed5ff368cb8c1836715b17a50074314579eefc6d511995a3ab93d70"}, + {file = "scs-3.2.4.post1-cp39-cp39-win_amd64.whl", hash = "sha256:18788befa5284bb1f49149bac7f813703de60ef5b6bf7698a9f1c3a5a49b78e4"}, + {file = "scs-3.2.4.post1.tar.gz", hash = "sha256:7015d7a56d1d5b53264fd277289ea169949309e26101677ff88cd0e5030d032f"}, +] + +[package.dependencies] +numpy = "*" +scipy = "*" + [[package]] name = "seaborn" version = "0.13.0" @@ -2740,6 +2966,24 @@ watchdog = {version = ">=2.1.5", markers = "platform_system != \"Darwin\""} [package.extras] snowflake = ["snowflake-connector-python (>=2.8.0)", "snowflake-snowpark-python (>=0.9.0)"] +[[package]] +name = "syntheticcontrolmethods" +version = "1.1.17" +description = "A Python package for causal inference using various Synthetic Control Methods" +optional = false +python-versions = "*" +files = [ + {file = "SyntheticControlMethods-1.1.17.tar.gz", hash = "sha256:941c77427ef1a87d5cf5863047c5d0c77c280a6b3aea04de3302e56fa76fe505"}, +] + +[package.dependencies] +cvxpy = ">=1.1.7" +jinja2 = ">=2.10" +matplotlib = ">=2.2.3" +numpy = ">=1.17" +pandas = ">=1.1.2" +scipy = ">=1.4.1" + [[package]] name = "tenacity" version = "8.2.3" @@ -3034,4 +3278,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.9.7 || >3.9.7,<3.13" -content-hash = "810bd9867f57e51b26369ed4149bd8d7497693e1b02b1cef1bdc20b02aa63869" +content-hash = "afc8b3ec58977cc258171ff816043f526daf261c308246efc484b33d87e196b9" diff --git a/pyproject.toml b/pyproject.toml index e15a59d..177ca8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ streamlit = "^1.28.1" nbclient = "^0.9.0" matplotlib = "^3.8.1" seaborn = "^0.13.0" +syntheticcontrolmethods = "^1.1.17" [tool.poetry.group.dev.dependencies] pytest = "^7.2.0" From aeff69232296e21aba244a573613ad14c670bd76 Mon Sep 17 00:00:00 2001 From: AaronCooke2718 <121969609+AaronCooke2718@users.noreply.github.com> Date: Sat, 2 Dec 2023 19:27:01 -0500 Subject: [PATCH 08/19] Update processing.py --- covid19_drdfm/processing.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index 0214108..b081e8c 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -254,3 +254,40 @@ def get_factors(): "TBill30yr": ("Global", "Uncat"), "FFR": ("Global", "Uncat"), } +""" +Diff + "Cases1" + "Cases2" + "Cases3" + "Cases4" + "Cases5" + "Hosp1" + "Hosp2" + "Deaths1" + "Deaths2" + "Deaths3" + "Deaths4" + "Deaths5" + +Log-Diff + "Cons1" + "Cons2" + "Cons3" + "Cons4" + "Cons5" + "Employment1" + "Employment2" + "CPI" + "CPIU" + "PCE" + "PCEC" + "RPFI" + "FixAss" + "Prod" + "GDP" + +Nothing + + Everything Else + + """ \ No newline at end of file From afc00634ae584d9f50b2d4206b31ed00ec21642c Mon Sep 17 00:00:00 2001 From: AaronCooke2718 <121969609+AaronCooke2718@users.noreply.github.com> Date: Sat, 2 Dec 2023 19:35:00 -0500 Subject: [PATCH 09/19] Update processing.py --- covid19_drdfm/processing.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index 2a86876..9fa9ed8 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -286,9 +286,33 @@ def get_factors(): "FixAss" "Prod" "GDP" + "UI" Nothing - Everything Else + "Vax1" + "Vax2" + "Vax3" + "Gather1" + "Gather2" + "Gather3" + "Gather4" + "SaH" + "Curfew" + "Mask1" + "Mask2" + "School" + "ARP" + "PPP" + "CARES" + "TBill1mo" + "TBill6mo" + "TBill1yr" + "TBill5yr" + "TBill10yr" + "TBill30yr" + "FFR" + "PartR" + "UR" - """ \ No newline at end of file + """ From 763dcd28238e616bd06f8010a750a794635f900a Mon Sep 17 00:00:00 2001 From: John Vivian Date: Wed, 6 Dec 2023 19:03:14 -0800 Subject: [PATCH 10/19] Refactor out constants --- covid19_drdfm/constants.py | 111 +++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 covid19_drdfm/constants.py diff --git a/covid19_drdfm/constants.py b/covid19_drdfm/constants.py new file mode 100644 index 0000000..8c07e88 --- /dev/null +++ b/covid19_drdfm/constants.py @@ -0,0 +1,111 @@ +NAME_MAP = { + "Pandemic_1": "Cases1", + "Pandemic_2": "Cases2", + "Pandemic_3": "Cases3", + "Pandemic_4": "Cases4", + "Pandemic_5": "Cases5", + "Pandemic_6": "Hosp1", + "Pandemic_7": "Hosp2", + "Pandemic_8": "Deaths1", + "Pandemic_9": "Deaths2", + "Pandemic_10": "Deaths3", + "Pandemic_11": "Deaths4", + "Pandemic_12": "Deaths5", + "Pandemic_Response_1": "Vax1", + "Pandemic_Response_2": "Vax2", + "Pandemic_Response_3": "Vax3", + "Pandemic_Response_4": "Gather1", + "Pandemic_Response_5": "Gather2", + "Pandemic_Response_6": "Gather3", + "Pandemic_Response_7": "Gather4", + "Pandemic_Response_8": "SaH", + "Pandemic_Response_9": "Curfew", + "Pandemic_Response_10": "Mask1", + "Pandemic_Response_11": "Mask2", + "Pandemic_Response_12": "School", + "Pandemic_Response_13": "ARP", + "Pandemic_Response_14": "PPP", + "Pandemic_Response_15": "CARES", + "Demand_1": "Cons1", + "Demand_2": "Cons2", + "Demand_3": "Cons3", + "Demand_4": "Cons4", + "Demand_5": "Cons5", + "Demand_6": "Employment1", + "Demand_7": "Employment2", + "Supply_1": "GDP", + "Supply_2": "UI", + "Supply_3": "PartR", + "Supply_4": "UR", + "Supply_5": "RPFI", + "Supply_6": "FixAss", + "Supply_7": "Prod", + "Monetary_1": "CPI", + "Monetary_2": "CPIU", + "Monetary_3": "PCE", + "Monetary_4": "PCEC", + "Monetary_5": "TBill1mo", + "Monetary_6": "TBill6mo", + "Monetary_7": "TBill1yr", + "Monetary_8": "TBill5yr", + "Monetary_9": "TBill10yr", + "Monetary_10": "TBill30yr", + "Monetary_11": "FFR", +} + +FACTORS = { + "Cases1": ("Global", "Pandemic"), + "Cases2": ("Global", "Pandemic"), + "Cases3": ("Global", "Pandemic"), + "Cases4": ("Global", "Pandemic"), + "Cases5": ("Global", "Pandemic"), + "Hosp1": ("Global", "Pandemic"), + "Hosp2": ("Global", "Pandemic"), + "Deaths1": ("Global", "Pandemic"), + "Deaths2": ("Global", "Pandemic"), + "Deaths3": ("Global", "Pandemic"), + "Deaths4": ("Global", "Pandemic"), + "Deaths5": ("Global", "Pandemic"), + "Vax1": ("Global", "Response"), + "Vax2": ("Global", "Response"), + "Vax3": ("Global", "Response"), + "Gather1": ("Global", "Response"), + "Gather2": ("Global", "Response"), + "Gather3": ("Global", "Response"), + "Gather4": ("Global", "Response"), + "SaH": ("Global", "Response"), + "Curfew": ("Global", "Response"), + "Mask1": ("Global", "Response"), + "Mask2": ("Global", "Response"), + "School": ("Global", "Response"), + "ARP": ("Global", "Response"), + "PPP": ("Global", "Response"), + "CARES": ("Global", "Response"), + "School": ("Global", "Response"), + "School": ("Global", "Response"), + "Cons1": ("Global", "Consumption"), + "Cons2": ("Global", "Consumption"), + "Cons3": ("Global", "Consumption"), + "Cons4": ("Global", "Consumption"), + "Cons5": ("Global", "Consumption"), + "Employment1": ("Global", "Employment"), + "Employment2": ("Global", "Employment"), + "UI": ("Global", "Employment"), + "PartR": ("Global", "Employment"), + "UR": ("Global", "Employment"), + "CPI": ("Global", "Inflation"), + "CPIU": ("Global", "Inflation"), + "PCE": ("Global", "Inflation"), + "PCEC": ("Global", "Inflation"), + "RPFI": ("Global", "Uncat"), + "FixAss": ("Global", "Uncat"), + "Prod": ("Global", "Uncat"), + "GDP": ("Global", "Uncat"), + "TBill1mo": ("Global", "Uncat"), + "TBill6mo": ("Global", "Uncat"), + "TBill1yr": ("Global", "Uncat"), + "TBill5yr": ("Global", "Uncat"), + "TBill10yr": ("Global", "Uncat"), + "TBill30yr": ("Global", "Uncat"), + "FFR": ("Global", "Uncat"), +} From b263140a4cc01d539a81b3a847dd5ab4f479b851 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Wed, 6 Dec 2023 19:03:40 -0800 Subject: [PATCH 11/19] Change scaling to (0,1) --- covid19_drdfm/dfm.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/covid19_drdfm/dfm.py b/covid19_drdfm/dfm.py index 310a572..6317541 100644 --- a/covid19_drdfm/dfm.py +++ b/covid19_drdfm/dfm.py @@ -1,4 +1,3 @@ -# %% """Module for Dynamic Factor Model specification Main command to run model @@ -60,9 +59,10 @@ def normalize(df: pd.DataFrame) -> pd.DataFrame: df = df.drop(columns=["Time"]) if "Time" in df.columns else df # Normalize data scaler = MinMaxScaler() - norm_df = pd.DataFrame(scaler.fit_transform(df), columns=df.columns) * 100 + norm_df = pd.DataFrame(scaler.fit_transform(df), columns=df.columns) # * 100 # stationary_df = norm_df.diff() - stationary_df = norm_df.diff().drop(0, axis=0) #! Dropping first after diff + # TODO: Remove Diff logic from here to separate out processing per column + stationary_df = norm_df.diff().drop(0, axis=0) #! Dropping first row after diff # stationary_df = stationary_df.fillna(0) non_stationary_columns = [] @@ -170,13 +170,3 @@ def test_model(df: pd.DataFrame, state: str, outdir: Path) -> sm.tsa.DynamicFact with open(out / "results.csv", "w") as f: f.write(results.summary().as_csv()) return model, results - - -# #%% -# from covid19_drdfm.processing import get_df - - -# df = get_df() -# model, results = run_model(df, 'AL', outdir=Path('./test-delete-NY-foo')) - -# # %% From 747982fd7c5512f4104288e082d1fb15d833fd4f Mon Sep 17 00:00:00 2001 From: John Vivian Date: Wed, 6 Dec 2023 19:04:02 -0800 Subject: [PATCH 12/19] Use refactors constants --- covid19_drdfm/processing.py | 62 +++---------------------------------- 1 file changed, 5 insertions(+), 57 deletions(-) diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index 9fa9ed8..588f898 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -14,6 +14,7 @@ import fastparquet import pandas as pd import yaml +from covid19_drdfm.constants import NAME_MAP ROOT_DIR = Path(__file__).parent.absolute() DATA_DIR = ROOT_DIR / "data/processed" @@ -139,62 +140,7 @@ def fix_names(df: pd.DataFrame) -> pd.DataFrame: Returns: pd.DataFrame: DataFrame with mapped names """ - return df.rename( - columns={ - "Pandemic_1": "Cases1", - "Pandemic_2": "Cases2", - "Pandemic_3": "Cases3", - "Pandemic_4": "Cases4", - "Pandemic_5": "Cases5", - "Pandemic_6": "Hosp1", - "Pandemic_7": "Hosp2", - "Pandemic_8": "Deaths1", - "Pandemic_9": "Deaths2", - "Pandemic_10": "Deaths3", - "Pandemic_11": "Deaths4", - "Pandemic_12": "Deaths5", - "Pandemic_Response_1": "Vax1", - "Pandemic_Response_2": "Vax2", - "Pandemic_Response_3": "Vax3", - "Pandemic_Response_4": "Gather1", - "Pandemic_Response_5": "Gather2", - "Pandemic_Response_6": "Gather3", - "Pandemic_Response_7": "Gather4", - "Pandemic_Response_8": "SaH", - "Pandemic_Response_9": "Curfew", - "Pandemic_Response_10": "Mask1", - "Pandemic_Response_11": "Mask2", - "Pandemic_Response_12": "School", - "Pandemic_Response_13": "ARP", - "Pandemic_Response_14": "PPP", - "Pandemic_Response_15": "CARES", - "Demand_1": "Cons1", - "Demand_2": "Cons2", - "Demand_3": "Cons3", - "Demand_4": "Cons4", - "Demand_5": "Cons5", - "Demand_6": "Employment1", - "Demand_7": "Employment2", - "Supply_1": "GDP", - "Supply_2": "UI", - "Supply_3": "PartR", - "Supply_4": "UR", - "Supply_5": "RPFI", - "Supply_6": "FixAss", - "Supply_7": "Prod", - "Monetary_1": "CPI", - "Monetary_2": "CPIU", - "Monetary_3": "PCE", - "Monetary_4": "PCEC", - "Monetary_5": "TBill1mo", - "Monetary_6": "TBill6mo", - "Monetary_7": "TBill1yr", - "Monetary_8": "TBill5yr", - "Monetary_9": "TBill10yr", - "Monetary_10": "TBill30yr", - "Monetary_11": "FFR", - } - ) + return df.rename(columns=NAME_MAP) def get_factors(): @@ -255,6 +201,8 @@ def get_factors(): "TBill30yr": ("Global", "Uncat"), "FFR": ("Global", "Uncat"), } + + """ Diff "Cases1" @@ -314,5 +262,5 @@ def get_factors(): "FFR" "PartR" "UR" - + """ From 6c0f4f739366385dcdf7967142b6f8406b6e5646 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Wed, 6 Dec 2023 19:04:08 -0800 Subject: [PATCH 13/19] Fix imports --- covid19_drdfm/scm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/covid19_drdfm/scm.py b/covid19_drdfm/scm.py index 4c0589a..d73d4fe 100644 --- a/covid19_drdfm/scm.py +++ b/covid19_drdfm/scm.py @@ -1,13 +1,13 @@ -import pandas as pd -import numpy as np +from os import rename -from SyntheticControlMethods import Synth, DiffSynth +import numpy as np +import pandas as pd +from SyntheticControlMethods import DiffSynth, Synth # Import data data_dir = "https://raw.githubusercontent.com/OscarEngelbrektson/SyntheticControlMethods/master/examples/datasets/" df = pd.read_csv(data_dir + "smoking_data" + ".csv") -from os import rename # Fit Differenced Synthetic Control df = df.rename(columns={"cigsale": "Pandemic"}) From a2cdf42daefc02150b26cb6aa5c08fab49c800e5 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Wed, 6 Dec 2023 19:04:20 -0800 Subject: [PATCH 14/19] Use updated constants in runner --- covid19_drdfm/streamlit/runner.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/covid19_drdfm/streamlit/runner.py b/covid19_drdfm/streamlit/runner.py index e8a7634..b424f04 100644 --- a/covid19_drdfm/streamlit/runner.py +++ b/covid19_drdfm/streamlit/runner.py @@ -12,6 +12,7 @@ from covid19_drdfm.dfm import state_process from covid19_drdfm.processing import get_df, get_factors +from covid19_drdfm.processing import NAME_MAP st.set_page_config(layout="wide") pio.templates.default = "plotly_white" @@ -31,6 +32,8 @@ "Inflation": ["Monetary_2", "Monetary_3", "Monetary_1"], "Pandemic": ["Pandemic_1", "Pandemic_2", "Pandemic_6", "Pandemic_9", "Pandemic_7", "Pandemic_10"], } +DEFAULTS = {NAME_MAP[x]: [NAME_MAP[z] for z in y] for x, y in DEFAULTS.items() if x in NAME_MAP in NAME_MAP} +print(DEFAULTS) def center_title(text): From 3850ef3cf4e542c9885cd27d314aa440d836fc85 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Thu, 7 Dec 2023 09:36:40 -0800 Subject: [PATCH 15/19] Update constants.py --- covid19_drdfm/constants.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/covid19_drdfm/constants.py b/covid19_drdfm/constants.py index 8c07e88..8f81179 100644 --- a/covid19_drdfm/constants.py +++ b/covid19_drdfm/constants.py @@ -109,3 +109,39 @@ "TBill30yr": ("Global", "Uncat"), "FFR": ("Global", "Uncat"), } + +DIFF_COLS = [ + "Cases1", + "Cases2", + "Cases3", + "Cases4", + "Cases5", + "Hosp1", + "Hosp2", + "Deaths1", + "Deaths2", + "Deaths3", + "Deaths4", + "Deaths5", +] + +LOG_DIFF_COLS = [ + "Cons1", + "Cons2", + "Cons3", + "Cons4", + "Cons5", + "Employment1", + "Employment2", + "CPI", + "CPIU", + "PCE", + "PCEC", + "RPFI", + "FixAss", + "Prod", + "GDP", + "UI", +] + +UNMODIFIED_COLS = set(FACTORS).difference(DIFF_COLS).difference(LOG_DIFF_COLS) From ebd76655fe97f6fc8868768d95a656862e262598 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Thu, 7 Dec 2023 14:00:36 -0800 Subject: [PATCH 16/19] Refactor DFM module --- covid19_drdfm/cli.py | 1 + covid19_drdfm/dfm.py | 139 +++++++++++++++++-------------------------- 2 files changed, 56 insertions(+), 84 deletions(-) diff --git a/covid19_drdfm/cli.py b/covid19_drdfm/cli.py index 01eecf1..de04df8 100644 --- a/covid19_drdfm/cli.py +++ b/covid19_drdfm/cli.py @@ -9,6 +9,7 @@ Process data and generate parquet DataFrame - `c19_dfm process ./outfile.xlsx` """ + from pathlib import Path import typer diff --git a/covid19_drdfm/dfm.py b/covid19_drdfm/dfm.py index 6317541..d3aa3a5 100644 --- a/covid19_drdfm/dfm.py +++ b/covid19_drdfm/dfm.py @@ -3,16 +3,16 @@ Main command to run model - `c19_dfm run` """ + from dataclasses import dataclass from pathlib import Path import pandas as pd import statsmodels.api as sm from rich import print as pprint -from sklearn.preprocessing import MinMaxScaler from statsmodels.tsa.stattools import adfuller -from covid19_drdfm.processing import adjust_pandemic_response, get_factors +from covid19_drdfm.constants import FACTORS @dataclass @@ -43,40 +43,30 @@ def state_process(df: pd.DataFrame, state: str) -> pd.DataFrame: const_cols = [x for x in df.columns if is_constant(df[x])] pprint(f"Constant Columns...dropping\n{const_cols}") df = df.drop(columns=const_cols) - df = adjust_pandemic_response(df) - return normalize(df) + return df -def normalize(df: pd.DataFrame) -> pd.DataFrame: - """Normalize data and make stationary - scaling for post-DFM Synthetic Control Model +def get_nonstationary_columns(df: pd.DataFrame) -> list[str]: + """Run AD-Fuller on tests and report failures Args: - df (pd.DataFrame): State data, pre-normalization + df (pd.DataFrame): Input DataFrame Returns: - pd.DataFrame: Normalized and stationary DataFrame + list[str]: List of columns that failed AD-Fuller test """ - df = df.drop(columns=["Time"]) if "Time" in df.columns else df - # Normalize data - scaler = MinMaxScaler() - norm_df = pd.DataFrame(scaler.fit_transform(df), columns=df.columns) # * 100 - # stationary_df = norm_df.diff() - # TODO: Remove Diff logic from here to separate out processing per column - stationary_df = norm_df.diff().drop(0, axis=0) #! Dropping first row after diff - # stationary_df = stationary_df.fillna(0) - non_stationary_columns = [] - for col in stationary_df.columns: - result = adfuller(stationary_df[col]) + for col in df.columns: + result = adfuller(df[col]) p_value = result[1] if p_value > 0.25: non_stationary_columns.append(col) - pprint("Columns that fail the ADF test (non-stationary):", non_stationary_columns) - return stationary_df + pprint(f"Columns that fail the ADF test (non-stationary)\n{non_stationary_columns}") + return non_stationary_columns -def run_model(df: pd.DataFrame, state: str, outdir: Path) -> sm.tsa.DynamicFactor: +def run_model(df: pd.DataFrame, state: str, outdir: Path): # -> sm.tsa.DynamicFactor: """Run DFM for a given state Args: @@ -88,85 +78,66 @@ def run_model(df: pd.DataFrame, state: str, outdir: Path) -> sm.tsa.DynamicFacto sm.tsa.DynamicFactor: Dynamic Factor Model """ - # Factors - factors = get_factors() - factor_multiplicities = {"Global": 2} - # Run model on a given state and print results df = state_process(df, state) + save_df(df, outdir, state) # Remove factors without an associated column + factors = FACTORS.copy() factor_keys = list(factors.keys()) [factors.pop(var) for var in factor_keys if var not in df.columns] - outdir.mkdir(exist_ok=True) - out = outdir / state - pprint(f"Saving state input information to {out}") - out.mkdir(exist_ok=True) - df.to_excel(out / "df.xlsx") - df.to_csv(out / "df.tsv", sep="\t") - if (out / "model.csv").exists(): - return + # Load cached model if exists + if (outdir / state / "model.csv").exists(): + model = sm.load(outdir / state / "model.csv") + return model, model.fit(disp=10) + # Try to run model, if it fails, note failure and return. Rust handles this so much better try: - model = sm.tsa.DynamicFactorMQ(df, factors=factors, factor_multiplicities=factor_multiplicities) - pprint(model.summary()) + factor_multiplicities = {"Global": 2} + model = sm.tsa.DynamicFactorMQ(df, factors=FACTORS, factor_multiplicities=factor_multiplicities) results = model.fit(disp=10) except Exception as e: with open(outdir / "failed_convergence.txt", "a") as f: f.write(f"{state}\t{e}\n") - return - pprint(results.summary()) - # Output - pprint(f"Saving output to {outdir}") - with open(out / "model.csv", "w") as f: - f.write(model.summary().as_csv()) - with open(out / "results.csv", "w") as f: - f.write(results.summary().as_csv()) + return None, None + # Save output + save_results(df, model, results, outdir=outdir / state, verbose=True) return model, results -def test_model(df: pd.DataFrame, state: str, outdir: Path) -> sm.tsa.DynamicFactor: - """Run DFM for a given state +def save_df(df: pd.DataFrame, outdir: Path, state: str): + """Save DataFrame as CSV / Excel Args: - df (pd.DataFrame): DataFrame processed via `covid19_drdfm.run` - state (str): Two-letter state code to process - outdir (str): Output directory for model CSV files + df (pd.DataFrame): Input DataFrame to model + outdir (Path): output directory + state (str): State to subset by + """ + outdir.mkdir(exist_ok=True) + state_dir = outdir / state + pprint(f"Saving state input information to {state_dir}") + state_dir.mkdir(exist_ok=True) + df.to_excel(state_dir / "df.xlsx") + df.to_csv(state_dir / "df.tsv", sep="\t") - Returns: - sm.tsa.DynamicFactor: Dynamic Factor Model +def save_results(df: pd.DataFrame, model, results, outdir: Path, verbose: bool = False): + """Save model and results to given directory + + Args: + df pd.DataFrame: _description_ + model (_type_): _description_ + results (_type_): _description_ + outdir (Path): _description_ + verbose (bool, optional): _description_. Defaults to False. """ - # Factors - factors = get_factors() - # factors = - # x[:-1]: y for x, y in factors.items() - # } # TODO: Fix in config to remove this now that multindex is removed - factor_multiplicities = {"Global": 2} - # Run model on a given state and print results - df = state_process(df, state) - drop_vars = ["proportion_vax2", "Proportion"] - new = df.drop(columns=drop_vars) - # [factors.pop(var) for var in drop_vars] - #! COLUMN REMOVAL - outdir.mkdir(exist_ok=True) - out = outdir / state - pprint(f"Saving state input information to {out}") - out.mkdir(exist_ok=True) - new.to_excel(out / "df.xlsx") - new.to_csv(out / "df.tsv", sep="\t") - if (out / "model.csv").exists(): - return - try: - model = sm.tsa.DynamicFactorMQ(new, factors=factors, factor_multiplicities=factor_multiplicities) + if verbose is True: pprint(model.summary()) - results = model.fit(disp=10) - except Exception as e: - with open(outdir / "failed.txt", "a") as f: - f.write(f"{state}\t{e}\n") - return - pprint(results.summary()) - # Output - pprint(f"Saving output to {outdir}") - with open(out / "model.csv", "w") as f: + pprint(results.summary()) + # Output + pprint(f"Saving output to {outdir}") + with open(outdir / "model.csv", "w") as f: f.write(model.summary().as_csv()) - with open(out / "results.csv", "w") as f: + with open(outdir / "results.csv", "w") as f: f.write(results.summary().as_csv()) - return model, results + non_stationary_cols = get_nonstationary_columns(df) + if non_stationary_cols: + with open(outdir / "non-stationary-columns.txt", "w") as f: + f.write("\n".join(non_stationary_cols)) From 6a076450d0a648a5edb136df407b7a9af25ba6cf Mon Sep 17 00:00:00 2001 From: John Vivian Date: Thu, 7 Dec 2023 14:01:02 -0800 Subject: [PATCH 17/19] Consolidate data processing to single `pipe` function --- covid19_drdfm/processing.py | 181 ++++++++++-------------------------- 1 file changed, 48 insertions(+), 133 deletions(-) diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index 588f898..0c947d9 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -12,9 +12,12 @@ from pathlib import Path import fastparquet +import numpy as np import pandas as pd import yaml -from covid19_drdfm.constants import NAME_MAP +from sklearn.preprocessing import MinMaxScaler + +from covid19_drdfm.constants import DIFF_COLS, LOG_DIFF_COLS, NAME_MAP ROOT_DIR = Path(__file__).parent.absolute() DATA_DIR = ROOT_DIR / "data/processed" @@ -40,20 +43,16 @@ def get_df() -> pd.DataFrame: .pipe(adjust_inflation) .pipe(add_datetime) .pipe(fix_names) + .pipe(adjust_pandemic_response) + .pipe(diff_vars, cols=DIFF_COLS) + .pipe(diff_vars, cols=LOG_DIFF_COLS, log=True) + .fillna(0) + .pipe(normalize) + .drop(index=0) # Drop first row with NaNs from diff ) -# def get_factors() -> dict[str, (str, str)]: -# """Fetch pre-defined factors for model - -# Returns: -# dict[str, (str, str)]: Factors from `./data/processed/factors.yaml` -# """ -# with open(DATA_DIR / "factors.json") as f: -# return json.load(f) - - -def write(df: pd.DataFrame, outpath: Path) -> Path: +def write(df: pd.DataFrame, outpath: Path): """Write dataframe given the extension""" ext = outpath.suffix if ext == ".xlsx": @@ -106,7 +105,6 @@ def adjust_pandemic_response(df: pd.DataFrame) -> pd.DataFrame: pd.DataFrame: Adjusted DataFrame """ govt_fund_dist = get_govt_fund_dist() - # responses = [f"Pandemic_Response_{x}" for x in [13, 14, 15]] responses = ["ARP", "PPP", "CARES"] for r in responses: df[r] = df[r].astype(float) @@ -118,7 +116,7 @@ def adjust_pandemic_response(df: pd.DataFrame) -> pd.DataFrame: def add_datetime(df: pd.DataFrame) -> pd.DataFrame: - """Sets `Time` column to `DateTime` dtype + """Set `Time` column to `DateTime` dtype Args: df (pd.DataFrame): Input DataFrame @@ -143,124 +141,41 @@ def fix_names(df: pd.DataFrame) -> pd.DataFrame: return df.rename(columns=NAME_MAP) -def get_factors(): - """Returns the pre-assigned factors for the model""" - return { - "Cases1": ("Global", "Pandemic"), - "Cases2": ("Global", "Pandemic"), - "Cases3": ("Global", "Pandemic"), - "Cases4": ("Global", "Pandemic"), - "Cases5": ("Global", "Pandemic"), - "Hosp1": ("Global", "Pandemic"), - "Hosp2": ("Global", "Pandemic"), - "Deaths1": ("Global", "Pandemic"), - "Deaths2": ("Global", "Pandemic"), - "Deaths3": ("Global", "Pandemic"), - "Deaths4": ("Global", "Pandemic"), - "Deaths5": ("Global", "Pandemic"), - "Vax1": ("Global", "Response"), - "Vax2": ("Global", "Response"), - "Vax3": ("Global", "Response"), - "Gather1": ("Global", "Response"), - "Gather2": ("Global", "Response"), - "Gather3": ("Global", "Response"), - "Gather4": ("Global", "Response"), - "SaH": ("Global", "Response"), - "Curfew": ("Global", "Response"), - "Mask1": ("Global", "Response"), - "Mask2": ("Global", "Response"), - "School": ("Global", "Response"), - "ARP": ("Global", "Response"), - "PPP": ("Global", "Response"), - "CARES": ("Global", "Response"), - "School": ("Global", "Response"), - "School": ("Global", "Response"), - "Cons1": ("Global", "Consumption"), - "Cons2": ("Global", "Consumption"), - "Cons3": ("Global", "Consumption"), - "Cons4": ("Global", "Consumption"), - "Cons5": ("Global", "Consumption"), - "Employment1": ("Global", "Employment"), - "Employment2": ("Global", "Employment"), - "UI": ("Global", "Employment"), - "PartR": ("Global", "Employment"), - "UR": ("Global", "Employment"), - "CPI": ("Global", "Inflation"), - "CPIU": ("Global", "Inflation"), - "PCE": ("Global", "Inflation"), - "PCEC": ("Global", "Inflation"), - "RPFI": ("Global", "Uncat"), - "FixAss": ("Global", "Uncat"), - "Prod": ("Global", "Uncat"), - "GDP": ("Global", "Uncat"), - "TBill1mo": ("Global", "Uncat"), - "TBill6mo": ("Global", "Uncat"), - "TBill1yr": ("Global", "Uncat"), - "TBill5yr": ("Global", "Uncat"), - "TBill10yr": ("Global", "Uncat"), - "TBill30yr": ("Global", "Uncat"), - "FFR": ("Global", "Uncat"), - } +def diff_vars(df: pd.DataFrame, cols: list[str], log: bool = False) -> pd.DataFrame: + """Differences the set of variables within the dataframe + NOTE: Leaves a row with Nas -""" -Diff - "Cases1" - "Cases2" - "Cases3" - "Cases4" - "Cases5" - "Hosp1" - "Hosp2" - "Deaths1" - "Deaths2" - "Deaths3" - "Deaths4" - "Deaths5" - -Log-Diff - "Cons1" - "Cons2" - "Cons3" - "Cons4" - "Cons5" - "Employment1" - "Employment2" - "CPI" - "CPIU" - "PCE" - "PCEC" - "RPFI" - "FixAss" - "Prod" - "GDP" - "UI" - -Nothing - - "Vax1" - "Vax2" - "Vax3" - "Gather1" - "Gather2" - "Gather3" - "Gather4" - "SaH" - "Curfew" - "Mask1" - "Mask2" - "School" - "ARP" - "PPP" - "CARES" - "TBill1mo" - "TBill6mo" - "TBill1yr" - "TBill5yr" - "TBill10yr" - "TBill30yr" - "FFR" - "PartR" - "UR" - - """ + Args: + df (pd.DataFrame): Input DataFrame + vars (List[str]): List of columns to difference + log bool: Whether to take the log(difference) or not + + Returns: + pd.DataFrame: DataFrame with given vars differenced + """ + if log: + # df[cols] = np.log(df[cols]).diff().fillna(0).apply(lambda x: np.log(x + 0)) + df[cols] = df[cols].apply(lambda x: np.log(x + 1)).diff() + else: + df[cols] = df[cols].diff() + return df + + +def normalize(df: pd.DataFrame) -> pd.DataFrame: + """Normalize data and make stationary - scaling for post-DFM Synthetic Control Model + + Args: + df (pd.DataFrame): State data, pre-normalization + + Returns: + pd.DataFrame: Normalized and stationary DataFrame + """ + meta_cols = df[["State", "Time"]] + # df = df.drop(columns=["Time"]) if "Time" in df.columns else df + df = df.drop(columns=["State", "Time"]) + # Normalize data + scaler = MinMaxScaler() + new = pd.DataFrame(scaler.fit_transform(df), columns=df.columns) + new["State"] = meta_cols["State"] + return new From b61c8876372b92159fa2d34d25579f6740067d6a Mon Sep 17 00:00:00 2001 From: John Vivian Date: Thu, 7 Dec 2023 14:01:17 -0800 Subject: [PATCH 18/19] Fix tests and update coverage --- .coverage | Bin 69632 -> 69632 bytes coverage.xml | 209 +++++++++++++++++++++------------------ tests/test_processing.py | 11 ++- 3 files changed, 117 insertions(+), 103 deletions(-) diff --git a/.coverage b/.coverage index ca404e2d7a584fc263f999f2e5d93cbc58fdcfd0..6463e1a2ed556aaa96134685b740a247bd255976 100644 GIT binary patch literal 69632 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 P54{>*p!lIzo7R5=8Ur3O literal 69632 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 - + - /home/jvivian/covid19-drDFM/covid19_drdfm + /Users/jvivian/Library/CloudStorage/GoogleDrive-jtvivian@gmail.com/My Drive/projects/covid19-drDFM/covid19_drdfm - + - - - + + - - - - + + + + - - + + - + - + + + + + + + + + + + + - + - - - - + + + - - - - - - - + + + + + + + - - - + + + - + + - - - - - - - - - + + + + + + + + + + + - - - - + + + + - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + - + - - - - + + + + + + @@ -164,7 +150,7 @@ - + @@ -178,8 +164,35 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_processing.py b/tests/test_processing.py index 771076b..cbed7ce 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -3,6 +3,7 @@ import pandas as pd import pytest +from covid19_drdfm.constants import NAME_MAP from covid19_drdfm.processing import ( DATA_DIR, ROOT_DIR, @@ -34,8 +35,8 @@ def test_get_govt_fund_dist(): assert int(sum(govt_fund) + 0.00001) == 1, "Distribution must sum to 1" -def test_adjust_inflation(sample_data): - input_df = sample_data.copy() +def test_adjust_inflation(raw_data): + input_df = raw_data.copy() output_df = adjust_inflation(input_df) assert input_df.Demand_1.iloc[0] < output_df.Demand_1.iloc[0] @@ -45,9 +46,9 @@ def test_adjust_pandemic_response(sample_data): #! Note - this is testing functionality, but is used per-state not on whole df out = adjust_pandemic_response(input_df) df = get_df() - responses = [f"Pandemic_Response_{x}" for x in [13, 14, 15]] + responses = [NAME_MAP[f"Pandemic_Response_{x}"] for x in [13, 14, 15]] for r in responses: - assert df[r].sum() == out[r].sum() + assert int(df[r].sum()) == int(out[r].sum()) def test_fix_datetime(raw_data): @@ -58,7 +59,7 @@ def test_fix_datetime(raw_data): def test_run(): df = get_df() - expected_columns = ["State", "Supply_1", "Demand_1", "Pandemic_Response_13", "Time"] + expected_columns = ["State", "GDP", "Deaths1"] assert all(col in df.columns for col in expected_columns) From 62e154f9374e4872610565f79f7112af46aa8481 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Thu, 7 Dec 2023 14:07:23 -0800 Subject: [PATCH 19/19] Update docstring --- covid19_drdfm/dfm.py | 4 ++-- covid19_drdfm/processing.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/covid19_drdfm/dfm.py b/covid19_drdfm/dfm.py index d3aa3a5..9fc880e 100644 --- a/covid19_drdfm/dfm.py +++ b/covid19_drdfm/dfm.py @@ -74,8 +74,8 @@ def run_model(df: pd.DataFrame, state: str, outdir: Path): # -> sm.tsa.DynamicF state (str): Two-letter state code to process outdir (str): Output directory for model CSV files - Returns: - sm.tsa.DynamicFactor: Dynamic Factor Model + # Returns: + # sm.tsa.DynamicFactor: Dynamic Factor Model """ df = state_process(df, state) diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index 0c947d9..8f1a22c 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -148,7 +148,7 @@ def diff_vars(df: pd.DataFrame, cols: list[str], log: bool = False) -> pd.DataFr Args: df (pd.DataFrame): Input DataFrame - vars (List[str]): List of columns to difference + cols (List[str]): List of columns to difference log bool: Whether to take the log(difference) or not Returns: