From 5a4620ad6d7322f1221613020ae5411c1901fce7 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Mon, 18 Dec 2023 17:50:29 -0800 Subject: [PATCH 1/7] Fix processing issues after source file update --- covid19_drdfm/dfm.py | 2 +- covid19_drdfm/processing.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/covid19_drdfm/dfm.py b/covid19_drdfm/dfm.py index 9fc880e..4fd2ebb 100644 --- a/covid19_drdfm/dfm.py +++ b/covid19_drdfm/dfm.py @@ -37,7 +37,7 @@ def state_process(df: pd.DataFrame, state: str) -> pd.DataFrame: Returns: pd.DataFrame: Processed DataFrame, ready for model """ - df = df[df.State == state] + df = df[df.State == state].fillna(0) #! The trunctation will be removed when data is updated in OCT - A.C. df = df[:-12] const_cols = [x for x in df.columns if is_constant(df[x])] diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index 8f1a22c..230f5f8 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -34,7 +34,6 @@ def get_df() -> pd.DataFrame: dfs = [pd.read_csv(x) for x in paths] return ( reduce(lambda x, y: pd.merge(x, y, on=["State", "Year", "Period"], how="left"), dfs) - .fillna(0) .drop(columns=["Monetary_1_x", "Monetary_11_x"]) .rename(columns={"Monetary_1_y": "Monetary_1", "Monetary_11_y": "Monetary_11"}) .drop( @@ -46,9 +45,8 @@ def get_df() -> pd.DataFrame: .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 + .pipe(normalize) ) @@ -90,7 +88,7 @@ def adjust_inflation(df: pd.DataFrame) -> pd.DataFrame: .assign(Demand_3=lambda x: x.Demand_3.div(x.Monetary_3 / 100)) .assign(Demand_4=lambda x: x.Demand_4.div(x.Monetary_3 / 100)) .assign(Demand_5=lambda x: x.Demand_5.div(x.Monetary_3 / 100)) - .assign(Supply_1=lambda x: x.Supply_1.div(x.Monetary_3 / 100)) + .assign(GDP=lambda x: x.GDP.div(x.Monetary_3 / 100)) .assign(Supply_6=lambda x: x.Supply_6.div(x.Monetary_3 / 100)) ) From f72b9bfb976a8ab38da1ba2a4d8b1f57b01e85c3 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Mon, 18 Dec 2023 17:50:43 -0800 Subject: [PATCH 2/7] Update coverage --- .coverage | Bin 69632 -> 69632 bytes coverage.xml | 58 +++++++++++++++++++++++++-------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.coverage b/.coverage index 6463e1a2ed556aaa96134685b740a247bd255976..797f7bc7e2bfc34b9e1183b0476013e0b42e3af2 100644 GIT binary patch delta 3369 zcmbu=e^6A{6$kKl-mksfhx^br);gmpvI0fJ4-Ekp2q`h?u0dm+Vfk%|ThWEE%dbGt zz_agKlPWR6orG4aZesWuYk{R=la{6?nl>~>?M$bWF-@8#q}rJ{?Zix{GtKrc_uV(` zz#p9{e|^5^oZY?e?c2lth`c`{Kf6h+7oKtmDSi8=nOm(48ON^)rxk1b7o7oTako)3z~Pfw1xb;+S>!c-E^ivmW1vF zGpqffV0*_S!H#x1vQb*(Gg6jq6c;J4TH${*Kwpy&lF+r_ZNs-8PSTwdg8rs{R9~al zIp>@c^g2#Bm)oe5TDeErsz>xU^uLIEl}pO!>IU^>`1%LA#=DN~ zjt9ld;sx=XI3}JHk2>6rOvf6>atBtwQ2(yZsh8CY>N#~xeOEoI9#n(sUh$y1Q%zM@ zsy@}JZdUJA&Z?sFN97gergBYrQ4A^#;!e@0Jg*#5dX+Z)y8fAdUcaJL>t~cw`JYOL z{Hd}`{!mfnx8xZ^o|JdUL-H~Ch}m-e@VBb-$@sxPNaa$i zJ0)VLaD&RM^$Kco8V?KD3+g%8kxLrAL(Xyeqvf&N8kp-{Y z&VUQH)8JROQ{gr4lsWjNK>v95Rl-}g|EEJ4i=OKLKGYI=XGBp{glMb|X6!U6MBx{9 znh++z__-a&g$a1oPG^O2c*Ra;v891L9{|I{Xh0`&Z<}Klb5P@@cip1LB zr*=9;dsB8g5%WWIiD4Zy<`+)Dq}@DD)5L$IQ!vg`tW7u$KjBF@BAkY^b~+sEg&*@2 z>lF^e8J+}FI07&8Z^!6^vAB)+^V4gUf^yCL4-Th67KuRy}7Xoj`32U5q`)W3BV7yYke@lUE_o2xvPEf z9Cv+3IO*dxM$I7f^U`1qJj-1@1kZ2}Rl^bPs$qDV+gA>KwrkW@`l#~-{YS0!2#~FdLZ21X)tYj;k(?^Uf9p=@q)o!JPW|pB8SLdQnT2ldq7rzV+g$`*-1%Z1uJA>_xTPjZPhsIX2Tau_CoWIa5(VwHc2j#_yQW>#&TB7gC$)a< zfYzloYIWK+?S3s!%g|P7Nt%Q|$2ahGd=+29ui|k$h>ziC@B!S7gV>KN@fKW)^YD6{ zjF)2#)BoN#kb%f{CBFKV;VMMF#aQJ-WE*4U2qF(Nj#MJD)vD5gEsPbTh?FsoRv_|_ zRiy))8GZC+UW;_VV{r_TZ&K2wp^VYt2U*!&h{ywst{g-*SO{# z5V_A{Dk3F}DRY?IOKB}{Op2{QcQ+&c@_!E6|mV$wodF^JB7s27?|hzOXULv08LrW0Gx$ zQt%xKuhMExh8e>anpehmW+tf%) zUuC60BO)spBLPId!dOcmfaQ!ely@*zQ+}DTzGL9MM7Wc#V?>fzX|M(n-5MN1#K|~R zjmQ$ls$oPD8GYr5XclV`!4~N|Bv_;`Ll)`Jz`^&ld%@)SLy z#8_63h{))jMg%N+5fK=ty~s2eJ(RtS#k0ulVJs>}<`ayCw7QRR#)HhgjHN~yGP{{e z=)~iUMJ34WVssZFvy(C3jm*aw3+QZzRV_efJ7ZoxGTRtu^N`ufn3ICc7RGEBGDD16 zltBfS#-E~0>Sk8XEgd-IgpACLb;#VqO6jz_n=vgNnGKAow7QFNt!v;zC%go?15fJE F{ck6DiRu6V delta 4288 zcmcK6dr(x@9S89HyRSWa?!qN$leEs5L|G9uvI_OVLkN*-vqZ2YsS5}c3?f8cyNE&5 z%icpCCDlr|N^2*`BOoT$>aIj`vT`uGz=`MT>1LEothi6kMKu-)}V zECjnej|b}f0Z(H`Lq|hRv*(e9T7Qkd)3a(z%ho3zZEx|{)Yo~|w6xT3sVnq1bkr?s zY-gPh);HBOY)RkP(&Sm&dH>9sr`6xmShuk~5Na-&9lBbqRs=;w7EEWn_Sj4}+*lU~ zG&I+zw{`|Wrkgq9wOMFg2|Y6@j%Su!-X+HI<7bD?me8+60D4MF+)n9cNs{imE$F*+ zNE8x#61rTs>Em|8RklM8c0t^>y;|ic4@$D>jN2Ape%O?tD4UMBZ5CQroe<9xt9Hlp z)9KrPwwit)f@~^rd-)?btI>C*Vwe*?D<{Fn5#{1l5RoG^eW|x^-q)%Dwt?`Vsx2Sfw0Q zuBfxrXF^xrTWZ{Ll!_gW)nZs26Ay@cMAMP&NO#P2-0i^XZS|k(wEB+vmimhNk~*ON zP<>6^t~RURQESCU)vG3|qME1NQZv*PWxsMmc|#diE-R;%Ua?du*8in{pzKoo$|j{; zKc~N|59+6shx8vQcgpX}|CaxxERZkA9~p939+MBqtK~hiDR;=Fa-&=&XUmJ^yQI&h zkEFj!Z%Zeo!;&HGmNrUR(&N$^=>aK862%Y1cf>cvGvhv((o}E)h)V8C_${|P8Gd7X z3A|x@F`T!(5R7wnNQKvJ&xh9%=!c4;s0b6$3V78{Cxi(&Yo~~C0)EY}@{Pj(HfZGDOw{OgcCdoFVJK}{woean5Sr~@B$p?N!Tll z!ZADTj_!b;^Az17?1rN}2|dDI_?dmz!Cp9GFLet+csaHN^lLSQZg@#k4ff;&4LHnC z$OytXcdr45_!S*O4~+4JzyOSLHwEBD?zScv;r6$|Fn60DhPYeXV30f1do9;H@FX1I zg(@HXguBuQKjz-xgMRMX4%p9KIRg8*M=C=}f1hhq4MQI<4OhWl?ut?P0k>~G?6F-1 zyKPs(_ib;0=WSO&FZcQ|e2?2Z3D0pCdEr@VW9D59``{VAP+SX7a~DlOkL@BL+!IA$ za(jy)$elk0-Q0QkV81Ubr(hSaPkLb|cVV#sJ9sFVf-dg70@%)-n+KiT*}3o(cg_@S z&`!eTeHaxX807H*Flnz_?Gp=;CW z8EI~4;HA_w*vviO4fWh9ZrH>D@TToD_+5e^fL9z5 z^w0E9^y_*=|Kz4F^*X2bIp9RiT-QA=%_V3zwHw+M?UHt0JEgs>4QPE@kG4%~)GD=g z+A3|CmZr_tk~9h5!vDZm@Fjc>pTtM-5Z;fU$K7}OAz^{#l?seT3m?8DvPOz zJY;b`CIyt%j}ayhT7mu+VUlkJ`cs5S9t}pUkIsBVR?rMxX#gUL5&i0vPn^q6E?OLWj< zl3|A-OqQ?^oAQ_}wpQqn$HZd=I^!`(x7z54$7GSULMObFJP-@?MuWYWEVLHre0P!s zN^GRh%y&f6=n6f6AbliK8I2$!^BH>$CrOD>ROo0&WF9LA1`u&GHU$t#W^8LhWG z4Usuk)sM*ijIFeK-wgX}T(F{W;7LTj&X%g^!|*l6O3Hf}H&EWqSlfZfT~@;gB41@3 zsqFuY3+|)i9FZhe8m>Y_XRH`SB$3g#9+3o#w83Su5)sYf21Kw$`bsz%*M|{7jNVB^ z9E?R?L{!Ewy+dW@I6*0{MMSnbCJ>P<79k=sPEZ1)mr`KNpF*a=n3s>tU5qPfbtmJb z7nwU43yYE2#aKWawln4#1<34V&do#SQ;gZU$lS)5Lz_FSY7R2DGG=8Xvz>7&3z-4N zOgA$9j2W58Y-3!Kfy`FM#k8S?ad{y!n-#bt_5^uoxrvq2J^j6jupm8+mNv6eY8o=@ c8RygLCdL%XI>vb^{qHBji?F=^Ssgn619JP|Q~&?~ diff --git a/coverage.xml b/coverage.xml index 386fc1c..661a4b3 100644 --- a/coverage.xml +++ b/coverage.xml @@ -1,9 +1,9 @@ - + - /Users/jvivian/Library/CloudStorage/GoogleDrive-jtvivian@gmail.com/My Drive/projects/covid19-drDFM/covid19_drdfm + /home/jvivian/covid19-drDFM/covid19_drdfm @@ -134,48 +134,48 @@ + + - - - - - - - - - + + + + + + + + + - + - + - - - - - - - - - - + + + + + + + + + - - - - + + + + + - - From c28f68585cab9e9fdcc62f99d99b35339f4e4c54 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Tue, 19 Dec 2023 14:48:54 -0800 Subject: [PATCH 3/7] Update constants.py --- covid19_drdfm/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/covid19_drdfm/constants.py b/covid19_drdfm/constants.py index 8f81179..d25f57a 100644 --- a/covid19_drdfm/constants.py +++ b/covid19_drdfm/constants.py @@ -33,7 +33,7 @@ "Demand_5": "Cons5", "Demand_6": "Employment1", "Demand_7": "Employment2", - "Supply_1": "GDP", + "GDP": "GDP", "Supply_2": "UI", "Supply_3": "PartR", "Supply_4": "UR", From 51dbc9c88bafb26e1e4a6c349be3c9b5f326c022 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Tue, 19 Dec 2023 14:49:11 -0800 Subject: [PATCH 4/7] Add second normalization step --- covid19_drdfm/dfm.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/covid19_drdfm/dfm.py b/covid19_drdfm/dfm.py index 4fd2ebb..e4cc73a 100644 --- a/covid19_drdfm/dfm.py +++ b/covid19_drdfm/dfm.py @@ -13,6 +13,7 @@ from statsmodels.tsa.stattools import adfuller from covid19_drdfm.constants import FACTORS +from covid19_drdfm.processing import normalize @dataclass @@ -37,12 +38,15 @@ def state_process(df: pd.DataFrame, state: str) -> pd.DataFrame: Returns: pd.DataFrame: Processed DataFrame, ready for model """ - df = df[df.State == state].fillna(0) + df = df[df.State == state] #! The trunctation will be removed when data is updated in OCT - A.C. df = df[:-12] + #! Test double-norm + df = normalize(df).fillna(0) + #! TEST REMOVE 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 = df.drop(columns=const_cols).set_index("Time", drop=True) return df From 17d65786915d5c5b3ebe6ff0acf4aeaff695f10c Mon Sep 17 00:00:00 2001 From: John Vivian Date: Tue, 19 Dec 2023 14:49:26 -0800 Subject: [PATCH 5/7] Reorder processing and fix meta_col bug --- covid19_drdfm/processing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/covid19_drdfm/processing.py b/covid19_drdfm/processing.py index 230f5f8..4491225 100644 --- a/covid19_drdfm/processing.py +++ b/covid19_drdfm/processing.py @@ -45,8 +45,8 @@ def get_df() -> pd.DataFrame: .pipe(adjust_pandemic_response) .pipe(diff_vars, cols=DIFF_COLS) .pipe(diff_vars, cols=LOG_DIFF_COLS, log=True) - .drop(index=0) # Drop first row with NaNs from diff .pipe(normalize) + .drop(index=0) # Drop first row with NaNs from diff ) @@ -169,11 +169,12 @@ def normalize(df: pd.DataFrame) -> pd.DataFrame: Returns: pd.DataFrame: Normalized and stationary DataFrame """ - meta_cols = df[["State", "Time"]] + meta_cols = df[["State", "Time"]].copy().reset_index(drop=True) # 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"] + new["Time"] = meta_cols["Time"] return new From c735d1bad15c98935bfbe7efa08026bb4a9fa07e Mon Sep 17 00:00:00 2001 From: John Vivian Date: Tue, 19 Dec 2023 14:49:37 -0800 Subject: [PATCH 6/7] Fix streamlit dashboard --- covid19_drdfm/streamlit/runner.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/covid19_drdfm/streamlit/runner.py b/covid19_drdfm/streamlit/runner.py index b424f04..fa729a2 100644 --- a/covid19_drdfm/streamlit/runner.py +++ b/covid19_drdfm/streamlit/runner.py @@ -10,15 +10,15 @@ from rich import print as pprint from sklearn.preprocessing import MinMaxScaler +from covid19_drdfm.constants import FACTORS from covid19_drdfm.dfm import state_process -from covid19_drdfm.processing import get_df, get_factors -from covid19_drdfm.processing import NAME_MAP +from covid19_drdfm.processing import NAME_MAP, get_df, normalize st.set_page_config(layout="wide") pio.templates.default = "plotly_white" DEFAULTS = { - "Uncat": ["Monetary_5", "Monetary_9", "Monetary_10", "Supply_1", "Supply_7"], + "Uncat": ["Monetary_5", "Monetary_9", "Monetary_10", "GDP", "Supply_7"], "Consumption": ["Demand_3", "Demand_4", "Demand_5"], "Response": [ "Pandemic_Response_1", @@ -32,8 +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) +DEFAULTS = {x: [NAME_MAP[z] for z in y] for x, y in DEFAULTS.items()} +# st.write(DEFAULTS) def center_title(text): @@ -55,10 +55,9 @@ def run_parameterized_model( """ # Factors and input data - factors = get_factors() factor_multiplicities = {"Global": global_multiplier} df = state_process(df, state) - columns = list(columns) + ["State", "Time"] + columns = list(columns) # + ["State", "Time"] columns = [x for x in columns if x in df.columns] new = df[columns] variables = list(factors.keys()) @@ -73,9 +72,9 @@ def run_parameterized_model( # Run Model if (out / "model.csv").exists(): return - model = sm.tsa.DynamicFactorMQ(new, factors=factors, factor_multiplicities=factor_multiplicities) + model = sm.tsa.DynamicFactorMQ(new, factors=FACTORS, factor_multiplicities=factor_multiplicities) try: - results = model.fit(disp=10, maxiter=5_000) + results = model.fit(disp=10, maxiter=10_000) except Exception as e: with open(outdir / "failed.txt", "a") as f: f.write(f"{state}\t{e}\n") @@ -86,7 +85,7 @@ def run_parameterized_model( f.write(results.summary().as_csv()) filtered = results.factors["filtered"] filtered["State"] = state - filtered.to_csv(out / "filtered-factors.csv") + filtered.to_csv(out / "filtered-factors.csv", index=None) return model @@ -97,7 +96,7 @@ def get_data(): df = get_df() sub = pd.Series([x for x in df.columns if x not in ["State", "Time"]], name="Variables").to_frame() -factors = get_factors() +factors = FACTORS.copy() factor_vars = list(factors.keys()) _ = [factors.pop(x) for x in factor_vars if x not in df.columns] sub["Group"] = [factors[x][1] for x in sub.Variables if x in df.columns] From 0591e689792ca7553943ffc78b684414e4a594b3 Mon Sep 17 00:00:00 2001 From: John Vivian Date: Tue, 19 Dec 2023 14:49:47 -0800 Subject: [PATCH 7/7] Update tests and coverage --- .coverage | Bin 69632 -> 69632 bytes coverage.xml | 101 ++++++++++++++++++++++++---------------------- tests/test_dfm.py | 7 ++-- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/.coverage b/.coverage index 797f7bc7e2bfc34b9e1183b0476013e0b42e3af2..b0f5e6da129a2daff5c096c08080b448d28f0a30 100644 GIT binary patch literal 69632 zcmeI536vGZ+4rk%Z`Ipv_F?A&!#W!SBA0d8WfWu)1{elsy}-b1%)+7q-frWH8^kSf zLB%y{T!^Si)Ff(D5)({Jj2etF!6hz9jJ_t(cfO~(`f0$#_vktAcYHN<56t|kx~gyW z|5pFIpRR|?DU0XVw`Wponp+#I+f(C67eXnSo=OoyO!(Us{^H93P_Y2N=~4V?Xp+J; zZ@OL?ar6s`cdEP4D{!uMhua68M)Q38TBF*mg$?p06_5%@1*8K1*$QMAJ4Q~QKGfgV zUR~3WX>YBr&9sW2?yMzqW-OhPS~_Fq{5dI6ml|oM;8jqNnvrU4-kfU5w5Hb7H)K-v zO?CCP)$R37Yg6s(GT6GKEmOxYbWjJEI^M7_?9ZvPz7Be|XV$_lT3YKHt6R6GHe|Nu zhYg~4YtL+H7ZtFRO#Rv>-Xb+J>OLyfnpu-+%{0|!+QcYE*4K@)Mg==8Bd2?JdQK)h zrs~#O_+uY)G*EHmP)gUpj%u4*>-gwvTC1CC*JaxBQ`PYi);7a9x3r6+ZEbEWYHv<8 z)Hj8P+0<6wUf_JihR)%bn>re6K=Sn+jV=7cfga+G+nYbF=cn~;Z;lT3Gk-S!=CeF< zyvKZ;C^`Q`MX!#g`i&i#_`}an9qE}L9!67heu^Ii%z$CGo-?sKB`_;9TiP}@!2MQb zbw_)1SgwQ*zp@y9IA?WbM$Vu?^qh_1MB(yO*3`GR#b6#YY2xZnnL2#?r_URHmK-}t z_*vA@kZN8No-(|5N_;pln~y$cicpN|R(_(Tnxlp=(l2zj@xj6vQZ)_DHOJ1iaK^M{ z-~<6Lz-DVYnriu}(#DUWws}*gwR&x)Fxo0?;ftO~Fm;MeJ!j6KPn$aN6sRnY(aQGG zjhue{sBebTDEhRbU&b+02tUbxW23mKF;m^v(TYa$Kba)|Z&Is;H{I7P*LGYR*r@$vJ1fPBQE!FMoQvW)3UJU~uq3Jo3 zdw$x$`4B3L`A&R<>(rzgMm92u|J!I3g*53NRp&EfQ3)ZErq50_Pb*=n1FwW$&7 zs@u2=wT(#CG&eV7s++{M0eY0S!F$%XrNS}%>s6`b$UfS(@_nq`G$I@mUR=1%FNKp1 z&o6++#_FcJ=z7I3!(y-C{ru$Q?;}hEI;KXzN!bRc=qL7xJB_w-$HM&=wtyyluZ`7p z;&kU9%29jVye{)EyAxNV+12ft_WH(5iZ=-d6Hctn)orQT)(mWfYw4&F%KtPC_@`A_ z%zyGInekh>>>z67=kP1izqyka_ zslZoH0fi>0!Q=mg_X_deg&*=I6_5%@1*8H}0jYpgKq?>=kP1izqyka_slb<00Vkmh z@G!-r*rQ6noG1xkLgBc=VhfHc;k{10*S+_@oChdJEftUoNCl(_2k{ed&0ZatMCRV{|E*7k_t!#qyka_sen{K zDj*e*3P=T{0#X5~z!$8*I9(x)MeCXyGezq+)o-e=Zh|i)*;HRwJh7mzb@to^SgNdR zty|Mr)K=RFUkWfuQ^@I`y;DnTGkl3p8+-u|^qr_GWb0?|3*TMQ)?VENUxw1Mb)53K z#tP%*of4m`Q*A>%bV^f&Z2s(ntgNhWs&B8Xgr0o;Ur*jcydQhJydmD|1 zU;TVz74g6F`8tXCKao~8o1a~kqvC)0{eNHdgqH6w6_5%@1*8H}0jYpgKq?>=kP1iz zqykcb|E~%tkTl@(^?&MpMBtBnNd=?=QUR%eR6r^q6_5%@1*8H}0jYpgKq~MRR6tQ& zldu1iE?>celZPY~kP1izqyka_sen{KDj*e*3P=T{0#X5~K&${?|CjN9EQbWCfK)&# zAQg}bNCl(L@=oG0%lpuK*R#C&Uf})F zd(r#3_qg|v_ib;lca?XMx5aDpYP{v%4DWa^&r5kdlby-GCErLMO8z4Gr2VXua`rke zx|QyJ_tm7DoSS?&xi5KJ^4jF?{YH( zE>^y#oUUwC)+(#j!D=tnR*CYy@}}~#TCOfsXR7C_r>j3zf2i(LUseC1ovyWL8LeDf zsLj?UYK7WRt+)Os{df9v`qTO&`abZJ*R^r#qwwxCmG{^p zmS*`Z#dgXfUdbrN%sCiZRB>HF_J4{-%CN ze^!4&e@MSezd^rTKVRPt<65Ja>nH0o_2c!?`aor+_7CkHtxWs1c0hYXyH{(}`tcJ- zNt31j+b56(&$CQ;o|QxM5J%9_h$HDlLa@-ArV)qJ5s1C$aAhS?vDlBEh&Y4*43QP0m8JNPuV(ACZwPN3L^s>L3oK zHa6%=&KElKH|avo!+MXLi>-%H1F;*`u|apT^GL^T#2(~AY~G)IjI}xBa6Avjkbh!zZ}KN>oFaeMZXt}%Pux0$T!T26T#c9_-$Wcl z{((4+AHQMw0q2xX}6dsk^5qvNbbRrjwg2`9!EXIaWsiImfVS0 zOzuD&L%xMrL~chcB)1_JkXsS+$t{R^~-m(sB@&!fdLpFqdNJeL;5Jcky> zJewB8Jd5U|;sxZ}hy%!7=9A>$mr$%vA2J?WSMm+SF61gik6ekEBv&B1HH)5r3EZwn5cku$9;Ncb&Ogz&2$tW2rKcI#W8tkci19{FUQ zcAE9tC+pNLxEJ^@v7`MZHh};=J}ClTYdhQc>`YL&9o`kRI-C>0yUDJNabXwP-4Yir zCA*sA!X@O=rnqo1xu`KNTtqHuII3{*hPZGc*||P0>?9Y}A62+;U0k?;>|7fc&LbDB zi3{hF^D=Sa9CB_Qmy^N~Z0oEIPc=AzfSgl<)u-*QM%;Q@72=kym57_SoQk+<^J>J7 zO%;f{J60h!ZYxJzzkelS{rVM%we`ypYii37t811aR#h)Wtf)E#vAkkQj5jZiv3wEY z&E*RbSC*fQxMJl3#IhCh5to&ngt&iMcss}CDQzsp+PMejAnLZtHT1g4wfoana$G$QZaeV0%#Nx4&5yupVcYOTa zipEUD+MSXz@zqpU#DT-RAPyMlA@+Sk+%dt}4dgn5BxpSVg&DrQ=oR!W3h_}Z(qfE-4WjCxC1Bk(Iv#zl&wZ6vQw6)uORyS7fN&=z?Qz%!Pwdv|DQ?_4dVKCdzLC+eReF8_mit9rG1i8@6+ zOWmS2sPoh+b*VZ=9je+8#lNP!sQgs9N4Zhis+^;&R>mm{l^M!##Y%jXcqj3@#Ix|+ z;{M#B^vURkOUoTYe;jiWeIn-3^zoQS(8ppPPJe_v^f3J)JY0awVL~JOI#<&l#HIVl zKKf`}x|iHXABjtFoy9#&noOTU{(?t!54o2<9FOsCau5A}T)K*-H1vrR!kZ{c-7<&RY5%EOpkBYv?{Kk#9oj zzN1Rl(R;Df*-E}i@4*teh}=l;j!PGGZl~YIQs;JZ0lf=L#3vWgJ5l%@JLw(B+jr1! zAqU&&?a14<)7xSW=&i^*x6xbp!Bdz5?Zl@yA$SuwE2IS@z zdOdPeGu?yS*hH^GZfK;}B5!D**C4OoK(9uwUr)b@yr-Ui1G%D%UWL4>j9!UcUPiA# zu4<>3BUkLDmm%-1pkGH`brao%bCCCy(X)}~SJAH_m+q%$#av1QD?jL!LB+ZbeQ{qFazB9-y0% z(-Y|?Rc6Z1 zS`*g~q1DKThR`bH)DT*UJUB&9jrnD|I_AN&0{P{^bQN-HFfB(uoT4j{`;Mn8kaPOd z<;cBqXc=;kUUV69w;psUa@TJ36yz>l=@MkG3tfzy^yngFCrK9~+YUV$*|KSPTMo{l zSybGXn+dIxo)ooE(`#sH%(Zl0%%{*3VlJa|W3HjIVqQXLBCk!;66EwoIxXgEIyL6S zbPDp)G@a}mBz6=IVs*NTPKbFCJwE37bbQ3x?Pc`o=u)7P-JRsWBEwb5nGi1iIWkIh zBNX4n{!MA}26AH)c^$c-k-UbyAx-{@ygp6-h+Lm0e?VTBrUQ}Jq{%DDnKbzwa$TCd zj9i;0hmdR1AfK8hzd>G|CeI^Rq{*+5SEb3XkjvBLIpmdT z@=N3uY4Qu?XbBTc@GJUvYwLM};@2a%_x$pgq!(_}yLlr*^?d2*V32YFJO>_eWICifzz z8)(;dDpUcbWGstBtDB7x4 zww$7^YGt?61CakuPfT1-VEzBkko$kg`=$4^_o#QjcZYX_cO|U-pXHtAwRmg13U7%w z&zt5Q=Z%I8fL@;CC6XT|-%0)<`C{_fFlYVkbvME`YEKe?iY=9}r zvB{Cifyo|8D@oiB+_&7{!|MMt?qlvl?%nRq?$z!t_gr_o+YUJZRc@I(-<|2E-9k6l z?dx`Nb;ttvtMj^Z$oZx7wDYKQzjKFkgL5Th1DxfY=Cn9#oC;@&GtZgkjB`furxHg$ zlQ`yy#3sAZuCrG`PC%(W-JW0<+QaPrc6S?YRjmKA-nL$aClf!j9=E=0-DBMX&n9+T z=UF?f4r>EEn^bXW%D`nfcXP+zj>#5qj{BivH3N~ zCD>@LHCLObn5AZkIo`}O2b(#j15ZvqV1Hu2W6!at*&}QpyNzATcC&NYHr5(Gn^?@| zvdL@=8^-!FkEzDrjdzS!VLkpC<1ynw<1XVy<4WT~!#6g=^Nebv%s9!IZj3iZ8-t7< zhN=Iz{vJG=_${o|wCVMbF|b5GL7%FR)ko+9^sc(0eXP9) zIRl5ZUusWj4{P^nw`tdDmucr~JG2gMgH{bI_a|vHvK4}Umc?MR$Vore5kymysA78&nteY>{sqk zu2(KsE>O-?I+XQFl~Sh6hbI@uD|t#v>8aQXO?;5}Q{t7xuM{2+0E;#)8&`OEN6 z)cC4^+K>6AK(IWJUc>x(5!N#Qga}Vz{@e)541W$F^crpWvjz2K{;X(o4fAJ4xWw>h zi0z{cf4ZPP%r6m}*QS|2Eo!-u`BNjTX8x227c+mdz@=%!p9BbR3N%sBNW)JH>dpKK zqG7s<`Nv1Ni236qoX`B@B0P!t<03qf`C}t2Wqxsl^O!#-!V{QZ6yaRv7e+XT`2`Wq zW`2HzvkX5E5Js3|_@f1lVE(9Rb0zafM!3-MM~Lmi4S%?xUd$gBZO$-1H^NhyKQzLV znLi}LWrjZ(5Z-CL;im)*GyFk}H?@IJR{x+aBQxBikmn!yC1V$_;5FyHQm3 zwX!Whi9{G48QEr0-N(o_0fCA|Mz&E9jHE$Oo{`-k+VwWG>p3NOJjJs0oT=KBX4!QD z8=F{mt-yvxBfEy9sphb3MpUkcrgZ}A0c!=O*RpI)gv(jBTHqRJP$e(}SShd$@Kk}d zfU5=809FXB23#eu3b0&YCE!Ydrvk1JxEgS|zzV=JfvW(Q2`mR(DsUy>DFRmjE)lpK zaIwHLz(oR=f#M4RLC``Yd$OQ`EW1E#J_TCN7q|rQB!P&bwT zu`U3NjCDRBH&*C&5+HY0z!L!@Yb^zgtaTnBw^nFy0wDKRz`1~txy}KM%yl+kWUjLS zBXgYz7@6w~KyI$Ehv|UaT>(n~xxE5T1C0E2Dq!TVQvf4>oeUWH>m)$**GViJ`Rhce zMSq=WWJ7;7!M?KGUP0UfBg@?tXn>Io&DB)L8rjfWO|{s_a%+VN28rjfPO?9Y|4L#LVhZxz=QcZQRkqsTyRN-9bh6=~g)yQ%`1?pmCxt#)e zMwYuNP}0b9GX-*uY~-bmk&V36HnQAHVXJ9mxs?K0MwUA%P!A&;8mX!FGqRD7_A;`O zkM=aO+(%)|(2n~kPYJrw!)~mhXTP!xrYKVBg;J$M#_xPK~2>#xPL;8VQ}|^M!Lbx6KZsWTPM_L z26s-V(F|^!P@@{$H=#x~xNQO`2KP*Wgb`Y%2{oZ%notwjWv9vAk|!DL3M}rH3CPEV zd;?4PB~(M22e(UJ57`D5H%!iuY+!NAJC)5_n6OS|(`F`|Q|Z{mgmEgnJDBiIrEwb*wyCV&&xC6#_3N21 zO{KP;3C~n&YMHQ1rMiX*$5g7SnJ`SHqKXN>RLU!uuuG-|e%8MUT=(CVWzvdw>a>ROZZO!X=fGIZT+O zGOL6MkAxHgCM;5!S;B-vDl=v>VUWu78BF-2QZk(hdsL>CFyW5MfoV*bqcZjtCcIG@ zU&@3vD#c@&a7JZJF%!lJc>_%NqEd8_30qW(ikNUkE z!V#5MMloTC%AoNq^h2ujKg_rvf@+8RGwz2#eFw4752@0lFAM#UD!txjp&#;00T%ip zRl4_NqePFlyR*;_p{@rD{g5hMy0g#^sp55Ep&wGk^;qbKRB>Du`XNW#b5^aLlejlcBpi+&Fa_1%-^eAuKl(yui2{0_7Jl z?uI~l`HZ_E&1Sm=f{@kR~{-H;}72C&c#fqJvh4QZlB4h!9oCLj$U zbVHix+Kq*7$ddqA=!QH8z~F8Ow@|Jj%rMcZCp4Y`&q&_4@3wBTzF}Q#odvlEO;)Y70^<9b)&wgb5>0wpwnZTO;7yQ4zN7+D z0jYpgKq?>=kP3X+6kvgflwggC1tL^}B_3jPB5cxfhq6G# zO^~6;0ueZAxkW4xk&~7?ngt?s(sDL*a~>`6y+vb0S};f0a6pK zfX7h00GWwaz{4nBfD}M0;E@zBKn9={@L-A;AOX+{cs#`mkpE`|JfwmGPyaIm9#thM z&;GLl9#`>Jko;!_Jhb8k$o;bd9$oPQr2bg}53s^!owZiLV=O4}#6K(GVHV#4d4E>G zBP~(5&I)+2#hXFapB3uW7Km_6 zTMs)E@t6kjejoxeZBIQ5L`0@lz$6kOnYId0#AI4Ipa{yes&*ELs7$Na%K{OWX?rVJ zAmTD@)lDoAfthyGDi(;yOoQhFfe6hscpMOj*i2h_3kyVWrj;#Yfr!quGT502&$Kew zgNV7B5>*f=I;x%3LFNw zMqutR=4T>&iTQN`UxEg;0*3(BM0kk#)e#P1ewDyOfRzFvlJ`#)I5@@p)e*kT{E7$% zGk=x9mjTNKrT|w8JPh4d2<$ta`O5|7z>;K{z+O4bkDR&(tg}T<4dJ~XIW@%he&p0$ ux-vg)T{lVpD6)bOyxkDS`F4WB!;Nj1yxL!&me&i@C>>DgWY literal 69632 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 - + /home/jvivian/covid19-drDFM/covid19_drdfm - + @@ -42,7 +42,7 @@ - + @@ -52,67 +52,69 @@ - - + + - - - - - - - + + + + + + - + + - - - - - - + + + + + - + + - - - + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - + + + + - - - - - + + + + - - - + + + + + + @@ -176,6 +178,7 @@ + diff --git a/tests/test_dfm.py b/tests/test_dfm.py index 9f6a6c9..90a9e21 100644 --- a/tests/test_dfm.py +++ b/tests/test_dfm.py @@ -9,7 +9,8 @@ # TODO: output should go in a directory instead of dumping shit everywhere def test_run_model(): df = get_df() - run_model(df, "NY", Path("./testdir")) - assert Path("./testdir/NY/model.csv").exists() - assert Path("./testdir/NY/results.csv").exists() + state = "SD" + run_model(df, state, Path("./testdir")) + assert Path("./testdir/SD/model.csv").exists() + assert Path("./testdir/SD/results.csv").exists() shutil.rmtree("./testdir")