From d019b19438d5ca8f1dcd7264784d5457c93f423f Mon Sep 17 00:00:00 2001 From: Tim Cera Date: Sat, 14 Dec 2024 22:09:21 -0500 Subject: [PATCH] Added "pandas_offset_by_version" to address deprecated offset codes. * Some pandas offset codes changed in pandas 2.2. If <2.2 offset codes are with >2.2 throws deprecation warnings and if >2.2 codes used with <2.2 throws value errors. Using the new "pandas_offset_by_version" function will used the >2.2 codes and change to the older codes if run with pandas <2.2. * Fixed to_numeric(..., errors="ignore", ...) deprecation warnings. * Fixed resample(..., kind="timestamp", ...) deprecation warnings. * Removed unused FILES block entry for WDM2 and fixed comments ("***" has to be in columns less than 80) in tests/ZRW_WestIndian/HSPF.uci and removed the unused tests/ZRW_WestIndian/HSPF.WDM. * Added missing WDM file in tests/ipwater. --- src/hsp2/hsp2/utilities.py | 54 +++++++++++++++++++++++++++++-- src/hsp2/hsp2io/io.py | 35 ++++++++++++++------ src/hsp2/hsp2tools/readUCI.py | 6 +++- tests/ZRW_WestIndian/HSPF.WDM | Bin 40960 -> 0 bytes tests/ZRW_WestIndian/HSPF.uci | 5 ++- tests/ipwater/data/HSPF_Test.wdm | Bin 0 -> 40960 bytes 6 files changed, 84 insertions(+), 16 deletions(-) delete mode 100644 tests/ZRW_WestIndian/HSPF.WDM create mode 100644 tests/ipwater/data/HSPF_Test.wdm diff --git a/src/hsp2/hsp2/utilities.py b/src/hsp2/hsp2/utilities.py index cde6cad3..0496798e 100644 --- a/src/hsp2/hsp2/utilities.py +++ b/src/hsp2/hsp2/utilities.py @@ -224,7 +224,7 @@ def transform(ts, name, how, siminfo): if freq == tsfreq: pass - elif tsfreq == None: # Sparse time base, frequency not defined + elif tsfreq is None: # Sparse time base, frequency not defined ts = ts.reindex(siminfo["tbase"]).ffill().bfill() elif how == "SAME": ts = ts.resample(freq).ffill() # tsfreq >= freq assumed, or bad user choice @@ -618,7 +618,7 @@ def get_gener_timeseries( if link.SVOLNO in gener_instances: gener = gener_instances[link.SVOLNO] series = zeros(len(gener.ts_output)) + gener.ts_output - if type(link.MFACTOR) == float and link.MFACTOR != 1: + if isinstance(link.MFACTOR, float) and link.MFACTOR != 1: series *= link.MFACTOR key = f"{link.TMEMN}{link.TMEMSB1} {link.TMEMSB2}".rstrip() @@ -700,3 +700,53 @@ def clean_name(TMEMN, TMEMSB): tname = TMEMN + "1" return tname + + +def pandas_offset_by_version(new_offset: str) -> str: + """ + Convert the time offset code to match the version of pandas. + + Parameters + ---------- + offset + The offset to convert. + + Returns + ------- + offset_by_version + The offset converted to the correct version of pandas. + """ + new_to_old_freq = {} + major, minor = pd.__version__.split(".")[:2] + if (int(major) + int(minor) / 10) < 2.2: + new_to_old_freq = { + "Y": "A", + "ME": "M", + "BME": "BM", + "SME": "SM", + "CBME": "CBM", + "QE": "Q", + "BQE": "BQ", + "BYE": "BY", + "h": "H", + "bh": "BH", + "cbh": "CBH", + "min": "T", + "s": "S", + "ms": "L", + "us": "U", + "ns": "N", + "YE-JAN": "A-JAN", + "YE-FEB": "A-FEB", + "YE-MAR": "A-MAR", + "YE-APR": "A-APR", + "YE-MAY": "A-MAY", + "YE-JUN": "A-JUN", + "YE-JUL": "A-JUL", + "YE-AUG": "A-AUG", + "YE-SEP": "A-SEP", + "YE-OCT": "A-OCT", + "YE-NOV": "A-NOV", + "YE-DEC": "A-DEC", + } + return new_to_old_freq.get(new_offset, new_offset) diff --git a/src/hsp2/hsp2io/io.py b/src/hsp2/hsp2io/io.py index 089069e9..5290a3d6 100644 --- a/src/hsp2/hsp2io/io.py +++ b/src/hsp2/hsp2io/io.py @@ -1,9 +1,9 @@ from typing import List, Union import pandas as pd -from pandas.core.frame import DataFrame from hsp2.hsp2.uci import UCI +from hsp2.hsp2.utilities import pandas_offset_by_version from hsp2.hsp2io.protocols import ( Category, SupportsReadTS, @@ -86,11 +86,14 @@ def write_ts( if drop_columns: data_frame = data_frame.drop(columns=drop_columns) + if not isinstance(data_frame.index, pd.core.indexes.datetimes.DatetimeIndex): + data_frame = data_frame.to_timestamp() + if outstep == 3: # change time step of output to daily - sumdf1 = data_frame.resample("D", kind="timestamp", origin="start").sum() - lastdf2 = data_frame.resample("D", kind="timestamp", origin="start").last() - meandf3 = data_frame.resample("D", kind="timestamp", origin="start").mean() + sumdf1 = data_frame.resample("D", origin="start").sum() + lastdf2 = data_frame.resample("D", origin="start").last() + meandf3 = data_frame.resample("D", origin="start").mean() data_frame = pd.merge( lastdf2.add_suffix("_last"), sumdf1.add_suffix("_sum"), @@ -105,9 +108,15 @@ def write_ts( ) elif outstep == 4: # change to monthly - sumdf1 = data_frame.resample("M", kind="timestamp", origin="start").sum() - lastdf2 = data_frame.resample("M", kind="timestamp", origin="start").last() - meandf3 = data_frame.resample("M", kind="timestamp", origin="start").mean() + sumdf1 = data_frame.resample( + pandas_offset_by_version("ME"), origin="start" + ).sum() + lastdf2 = data_frame.resample( + pandas_offset_by_version("ME"), origin="start" + ).last() + meandf3 = data_frame.resample( + pandas_offset_by_version("ME"), origin="start" + ).mean() data_frame = pd.merge( lastdf2.add_suffix("_last"), sumdf1.add_suffix("_sum"), @@ -122,9 +131,15 @@ def write_ts( ) elif outstep == 5: # change to annual - sumdf1 = data_frame.resample("Y", kind="timestamp", origin="start").sum() - lastdf2 = data_frame.resample("Y", kind="timestamp", origin="start").last() - meandf3 = data_frame.resample("Y", kind="timestamp", origin="start").mean() + sumdf1 = data_frame.resample( + pandas_offset_by_version("YE"), origin="start" + ).sum() + lastdf2 = data_frame.resample( + pandas_offset_by_version("YE"), origin="start" + ).last() + meandf3 = data_frame.resample( + pandas_offset_by_version("YE"), origin="start" + ).mean() data_frame = pd.merge( lastdf2.add_suffix("_last"), sumdf1.add_suffix("_sum"), diff --git a/src/hsp2/hsp2tools/readUCI.py b/src/hsp2/hsp2tools/readUCI.py index 05c9c00e..8d8be3cb 100644 --- a/src/hsp2/hsp2tools/readUCI.py +++ b/src/hsp2/hsp2tools/readUCI.py @@ -168,7 +168,11 @@ def fix_df(df, op, save, ddfaults, valid): pass cols = [c.replace("(", "").replace(")", "") for c in df.columns] df.columns = cols - df = df.apply(pd.to_numeric, errors="ignore") # todo: 'ignore' is deprecated. + for col in df.columns: + try: + df[col] = pd.to_numeric(df[col]) + except ValueError: + pass return df diff --git a/tests/ZRW_WestIndian/HSPF.WDM b/tests/ZRW_WestIndian/HSPF.WDM deleted file mode 100644 index d20499bd0335bbca77f386ed942c8056a08326b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40960 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 *s}j z=6ClfufJUSh2AqMjqu;KNhdrSpSaC`JR`3jGiRZ$c9rwp@8n$X2#>}mZu8$Y&8ZiC zw?Nqw$H`e9yKqx<=ZgdcbPFi}gF--4<9OYp-2>P6I|B}mZu8UY zJWwyTo~xUdcqn}H1!n!A@rm2~?li~MTcgM6wBMZ-zRe5QWPPIXiQD|Js87{ zJLQ?AMs;hWKUlay+CSI-JK@pz#BIJ-*##>0#xVUxo-@KPu4m47G(K^gub6MR8tH4R z@7~)ie1|oMqkqZG||uX<9A|yXY%-c!lUskx^bJIKfbr>+PS^{?&c2R zefdrckH#l%^Hr8KSHJFTq9=c5j_oIhxM;x?b^Y{RQX)57ry5Y>x4(+6Sw(l ze_yWhc4R^Qdfs^9pYCn;7c@Tc(7)dk=0pt6u0MWjhOnDcf9bXz=AiNIe_yEe3)%FP zq8|u1<%O-nq48~Q%eRj1|J!ikKCSVIaAUF)I7{VOHFSR z6t3uH!=dqQ?)>aH6`r=1aAl{AlJ?N}Huu4MQ`Pt(RfPLxOssHde4G1o;|!H@Kn>w0 zOdBm68sFwlG@Yul6yaXX?+mGB`bFd0T!mee)Gu!uZv05o9va`~z6@Kep1RBL=lxEu zI}4;=G``LCIq|V-l%kVxEqg2y4vlYfy$+95lQx+;4=+7VI5fV^MOA-WRa{a*+AHz= zFyYYnHWyQ-p9-tQy_MhjaEX~O8sFxoWgMs)bTr2;(0GEhhsL+LSGsgm+5OyW`kh`W zMhJ(-x49B|TdB-jxd-$+-wrV2L*v`rrugbA@5YT=pQpQ^{#492(!|hJSWH zu(B|Fq~9rcp^0#4e4Bf(|AoN4W7nm<8uOnP4vlYfE$bW(ocl6Cxb53wghS)o+=jQl z3VacDOt@d#RuT@4Z*zyDwgxU=IV#+@=M9I(x4HZgrvsf^anJ2{T5L4)K;zq7%CiZ9 z4-4^I6!*W)TB@D&i^jLPo9|r+Wciuzf&5PNSW{;-zRgwN`%55ocsk+sR(M0&L*v_A zn$-IP>019SvG zxzUBb3DnGC`rR|RnQ&-)n@hL<$G{7Jr;+xq>@fYJ@ony!J{QmxG76WzcwcD`jc;>D zw_goRxx?=++I;X) zx4AO;_6G|8ct_gn>wi%=G``KU&S-p_ zD^O}@;MIh4(r<;|gVG)v-{!8(*%r7m;<#|FqN9XE