From cca1da74f257a421b8b3084271cafa54f00a4c36 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Fri, 30 Sep 2022 11:38:06 +0200 Subject: [PATCH 1/8] Observed data with 2 items --- tests/testdata/SW/eur_Hm0_F3_wind.dfs0 | Bin 0 -> 2905 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/testdata/SW/eur_Hm0_F3_wind.dfs0 diff --git a/tests/testdata/SW/eur_Hm0_F3_wind.dfs0 b/tests/testdata/SW/eur_Hm0_F3_wind.dfs0 new file mode 100644 index 0000000000000000000000000000000000000000..ed6036c91564d264b104e72d6ae62ca4ac9ff163 GIT binary patch literal 2905 zcmb7^O>7%Q6vxMQf*Y4ZQ??XB>I`sLMCy*!ea7x}DFg zUgS%eQsybXRN(nSiD#GW66ez=i`o3N5WldR&6Ki3x5EQ0%JwqOo@2|5u^Zw`F@e3| z?9*LG(;o7Ki;n0@pd@2jW=-2mB!D z1rO^1DLMSzsfz0y)Ppk6K54#lE$n>kI*)YEdeqdA)$oy3Xm@&|b0e zNIWqymWYoX8E2sZ5yulp<8kp}zOR19g7VaV4gRgwC*i^_b)ERS>m=5_ExyNR{NPTA zu~46oCC~3>lv_NVbQ2WIa@^X`1}!c3A)AV)wh$|B+016*g^KSs)3=fp)X@m;ac zb&U0(EJuv{k{1mlWLv*2 zgTH0xm;7Qx+*_4k6P8?I>1f8#c33N{NY^3rHL36nu2-@2C1t{{6%iRWw^44Yo*6!UwTG1V#=xqP(=0rq%7P2d?^n{32(Soo*FKTiN*wDn=f}3yYxb)UO0hSN|aBR`Gj+ ziq{%JSIi%QqOPOU5N&ks6537ePoRsc4`w6`{Vggg-Y^7hZD<6oM-LD*UENR6k;-F0 z%jz`oUr5koYX~TEHeZhdMH#k=hkzn&WhX(T36Z?^87f>LeNI%0#H7;8Y0c2j!|DAH~f z&jLlaM5}eXGpDK;O`inqqN4NlS)eEb>Mx>5i!wAiNyva`JM}D3 Date: Fri, 30 Sep 2022 11:38:21 +0200 Subject: [PATCH 2/8] Drag / Drop Quick compare --- fmskill/app.py | 47 +++++++++++++++++++++++++++++++++++++++++++ fmskill/comparison.py | 21 ++++++++++++++++--- fmskill/plot.py | 25 +++++++++++++---------- 3 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 fmskill/app.py diff --git a/fmskill/app.py b/fmskill/app.py new file mode 100644 index 000000000..77984ca12 --- /dev/null +++ b/fmskill/app.py @@ -0,0 +1,47 @@ +import os +import tempfile +import streamlit as st +import mikeio +import fmskill + +""" +# FMskill Drag and Drop +""" + +tmp_folder = tempfile.mkdtemp() + +with st.sidebar: + + obs = st.file_uploader("Observation", type="dfs0") + + if obs: + fn_obs = os.path.join(tmp_folder, "obs.dfs0") + with open(fn_obs, "wb") as f: + f.write(obs.getvalue()) + + dfs = mikeio.open(fn_obs) + items = [item.name for item in dfs.items] + obs_item = st.selectbox(label="Item", options=items) + + mod = st.file_uploader("Model", type="dfs0") + + if mod: + fn_mod = os.path.join(tmp_folder, "mod.dfs0") + with open(fn_mod, "wb") as f: + f.write(mod.getvalue()) + dfs = mikeio.open(fn_mod) + items = [item.name for item in dfs.items] + mod_item = st.selectbox(label="Item", options=items) + +if mod and obs: + + c = fmskill.compare(fn_obs, fn_mod, obs_item=obs_item, mod_item=mod_item) + + df = c.skill().to_dataframe() + st.dataframe(df) + + fig_ts = c.plot_timeseries(backend="plotly", return_fig=True) + st.plotly_chart(fig_ts) + + fig_sc = c.scatter(backend="plotly", return_fig=True) + st.plotly_chart(fig_sc) diff --git a/fmskill/comparison.py b/fmskill/comparison.py index 959409ba1..bab6d187a 100644 --- a/fmskill/comparison.py +++ b/fmskill/comparison.py @@ -840,6 +840,7 @@ def scatter( binsize: float = None, nbins: int = None, skill_table: bool = False, + return_fig=False, **kwargs, ): """Scatter plot showing compared data: observation vs modelled @@ -953,7 +954,7 @@ def scatter( if title is None: title = f"{self.mod_names[mod_id]} vs {self.name}" - scatter( + fig = scatter( x=x, y=y, bins=bins, @@ -971,6 +972,7 @@ def scatter( ylabel=ylabel, binsize=binsize, nbins=nbins, + return_fig=return_fig, **kwargs, ) if skill_table: @@ -1005,6 +1007,9 @@ def scatter( family="monospace", ) + if return_fig: + return fig + def taylor( self, model: Union[str, int, List[str], List[int]] = None, @@ -1545,7 +1550,14 @@ def __init__(self, observation, modeldata): self.df.dropna(inplace=True) def plot_timeseries( - self, title=None, *, ylim=None, figsize=None, backend="matplotlib", **kwargs + self, + title=None, + *, + ylim=None, + figsize=None, + backend="matplotlib", + return_fig=False, + **kwargs, ): """Timeseries plot showing compared data: observation vs modelled @@ -1622,7 +1634,10 @@ def plot_timeseries( fig.update_layout(title=title, yaxis_title=self._obs_unit_text, **kwargs) fig.update_yaxes(range=ylim) - fig.show() + if return_fig: + return fig + else: + fig.show() else: raise ValueError(f"Plotting backend: {backend} not supported") diff --git a/fmskill/plot.py b/fmskill/plot.py index 6e0d32450..8effaafca 100644 --- a/fmskill/plot.py +++ b/fmskill/plot.py @@ -32,6 +32,7 @@ def scatter( ylabel: str = "", binsize: float = None, nbins: int = None, + return_fig=False, **kwargs, ): """Scatter plot showing compared data: observation vs modelled @@ -84,10 +85,9 @@ def scatter( y-label text on plot, by default None kwargs """ - if show_hist==None and show_density==None: - #Default: points density - show_density=True - + if show_hist == None and show_density == None: + # Default: points density + show_density = True if (binsize is not None) or (nbins is not None): warnings.warn( @@ -178,14 +178,14 @@ def scatter( # if not an int nor None, it must be a squence of floats xq = np.quantile(x, q=quantiles) yq = np.quantile(y, q=quantiles) - + if show_hist: - #if histogram is wanted (explicit non-default flag) then density is off + # if histogram is wanted (explicit non-default flag) then density is off if show_density == True: raise TypeError( "if `show_hist=True` then `show_density` must be either `False` or `None`" - ) - + ) + if show_density: if not ((type(bins) == float) or (type(bins) == int)): raise TypeError( @@ -195,7 +195,7 @@ def scatter( if show_hist == True: raise TypeError( "if `show_density=True` then `show_hist` must be either `False` or `None`" - ) + ) # calculate density data z = _scatter_density(x_sample, y_sample, binsize=binsize) idx = z.argsort() @@ -268,7 +268,7 @@ def scatter( plt.grid( which="both", axis="both", linestyle=":", linewidth="0.2", color="grey" ) - if (show_hist or (show_density and show_points)): + if show_hist or (show_density and show_points): cbar = plt.colorbar(fraction=0.046, pad=0.04) cbar.set_label("# points") plt.title(title) @@ -351,7 +351,10 @@ def scatter( fig = go.Figure(data=data, layout=layout) fig.update_xaxes(range=xlim) fig.update_yaxes(range=ylim) - fig.show() # Should this be here + if return_fig: + return fig + else: + fig.show() # Should this be here else: From a5fa0ecfcee41bd132e4183d18cf253459ce05e8 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Tue, 4 Oct 2022 06:53:11 +0200 Subject: [PATCH 3/8] New folder --- web/README.md | 13 +++++++++++++ {fmskill => web}/app.py | 0 2 files changed, 13 insertions(+) create mode 100644 web/README.md rename {fmskill => web}/app.py (100%) diff --git a/web/README.md b/web/README.md new file mode 100644 index 000000000..2c6230ed6 --- /dev/null +++ b/web/README.md @@ -0,0 +1,13 @@ +# FMSkill web application + +## Installation + +``` +$pip install streamlit +``` + +Usage: +``` +$streamlit run web/app.py +``` + diff --git a/fmskill/app.py b/web/app.py similarity index 100% rename from fmskill/app.py rename to web/app.py From 6748f3462840aba78565b14e9371bc79a3ae94e2 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Tue, 4 Oct 2022 07:00:11 +0200 Subject: [PATCH 4/8] Screenshot --- web/README.md | 2 ++ web/screenshot.png | Bin 0 -> 63073 bytes 2 files changed, 2 insertions(+) create mode 100644 web/screenshot.png diff --git a/web/README.md b/web/README.md index 2c6230ed6..982382b53 100644 --- a/web/README.md +++ b/web/README.md @@ -11,3 +11,5 @@ Usage: $streamlit run web/app.py ``` +![](screenshot.png) + diff --git a/web/screenshot.png b/web/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..171efcdfd91c7841cb16f227550265b27c45422b GIT binary patch literal 63073 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 XJrlmn4+RVK;GwGxxm|qAI_!S|H^aiU literal 0 HcmV?d00001 From 5dc0d4c065102ff341c53ac99bc6d2bdf47893d0 Mon Sep 17 00:00:00 2001 From: Hendrik Wrenger <29595985+Hendrik1987@users.noreply.github.com> Date: Sat, 19 Nov 2022 01:00:21 +0000 Subject: [PATCH 5/8] dependencies for web apps --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index a436dcce7..76181078f 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,9 @@ "plotly", "shapely", ], + "web": [ + "streamlit" + ], }, entry_points=""" [console_scripts] From 6cbcfde3c75253eb40bb0f75fd618d7d26c3c9f3 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Fri, 10 Jan 2025 15:06:29 +0100 Subject: [PATCH 6/8] Updated --- pyproject.toml | 2 ++ web/app.py | 27 +++++++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1f16fa2e5..84766255b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,6 +66,8 @@ test = [ "geopandas", ] +app = ["streamlit>=1.41.1"] + notebooks = ["nbformat", "nbconvert", "jupyter", "plotly", "shapely", "seaborn"] [project.urls] diff --git a/web/app.py b/web/app.py index 77984ca12..a92d4a602 100644 --- a/web/app.py +++ b/web/app.py @@ -2,16 +2,16 @@ import tempfile import streamlit as st import mikeio -import fmskill +import modelskill +import matplotlib """ -# FMskill Drag and Drop +# ModelSkill Drag and Drop """ tmp_folder = tempfile.mkdtemp() with st.sidebar: - obs = st.file_uploader("Observation", type="dfs0") if obs: @@ -33,15 +33,22 @@ items = [item.name for item in dfs.items] mod_item = st.selectbox(label="Item", options=items) -if mod and obs: + metrics = st.multiselect( + "Metrics", + ["bias", "rmse", "mae", "cc", "r2"], + default=["bias", "rmse", "mae", "cc", "r2"], + ) - c = fmskill.compare(fn_obs, fn_mod, obs_item=obs_item, mod_item=mod_item) +if mod and obs: + c = modelskill.match(fn_obs, fn_mod, obs_item=obs_item, mod_item=mod_item) - df = c.skill().to_dataframe() + df = c.skill(metrics=metrics).to_dataframe() st.dataframe(df) - fig_ts = c.plot_timeseries(backend="plotly", return_fig=True) - st.plotly_chart(fig_ts) + c.plot.timeseries() + fig = matplotlib.pyplot.gcf() + st.pyplot(fig) - fig_sc = c.scatter(backend="plotly", return_fig=True) - st.plotly_chart(fig_sc) + c.plot.scatter() + fig_sc = matplotlib.pyplot.gcf() + st.pyplot(fig_sc) From ce2f8b5e67e1d637e408f06cca39253ab8a49179 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Fri, 10 Jan 2025 15:37:01 +0100 Subject: [PATCH 7/8] Tabs --- web/app.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/web/app.py b/web/app.py index a92d4a602..f7a3887f1 100644 --- a/web/app.py +++ b/web/app.py @@ -19,7 +19,7 @@ with open(fn_obs, "wb") as f: f.write(obs.getvalue()) - dfs = mikeio.open(fn_obs) + dfs: mikeio.Dfs0 = mikeio.open(fn_obs) # type: ignore items = [item.name for item in dfs.items] obs_item = st.selectbox(label="Item", options=items) @@ -29,9 +29,9 @@ fn_mod = os.path.join(tmp_folder, "mod.dfs0") with open(fn_mod, "wb") as f: f.write(mod.getvalue()) - dfs = mikeio.open(fn_mod) - items = [item.name for item in dfs.items] - mod_item = st.selectbox(label="Item", options=items) + mdfs: mikeio.Dfs0 = mikeio.open(fn_mod) # type: ignore + mitems = [item.name for item in mdfs.items] + mod_item = st.selectbox(label="Item", options=mitems) metrics = st.multiselect( "Metrics", @@ -40,15 +40,22 @@ ) if mod and obs: - c = modelskill.match(fn_obs, fn_mod, obs_item=obs_item, mod_item=mod_item) + c: modelskill.Comparer = modelskill.match( + fn_obs, fn_mod, obs_item=obs_item, mod_item=mod_item + ) # type: ignore + + tabskill, tabts, tabscatter = st.tabs(["Skill", "Time series", "Scatter"]) - df = c.skill(metrics=metrics).to_dataframe() - st.dataframe(df) + with tabskill: + df = c.skill(metrics=metrics).to_dataframe() + st.dataframe(df) - c.plot.timeseries() - fig = matplotlib.pyplot.gcf() - st.pyplot(fig) + with tabts: + c.plot.timeseries() + fig = matplotlib.pyplot.gcf() + st.pyplot(fig) - c.plot.scatter() - fig_sc = matplotlib.pyplot.gcf() - st.pyplot(fig_sc) + with tabscatter: + c.plot.scatter() + fig_sc = matplotlib.pyplot.gcf() + st.pyplot(fig_sc) From bb495627bf9edb2fbbc938f15fccd695052d7d42 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 26 Feb 2025 13:29:55 +0100 Subject: [PATCH 8/8] Terse code --- web/app.py | 50 ++++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/web/app.py b/web/app.py index f7a3887f1..36f0242da 100644 --- a/web/app.py +++ b/web/app.py @@ -1,61 +1,55 @@ -import os import tempfile +from pathlib import Path import streamlit as st import mikeio import modelskill -import matplotlib +import matplotlib.pyplot as plt """ # ModelSkill Drag and Drop """ -tmp_folder = tempfile.mkdtemp() +tmp = Path(tempfile.mkdtemp()) with st.sidebar: obs = st.file_uploader("Observation", type="dfs0") if obs: - fn_obs = os.path.join(tmp_folder, "obs.dfs0") - with open(fn_obs, "wb") as f: - f.write(obs.getvalue()) + fn_obs = tmp / "obs.dfs0" + fn_obs.write_bytes(obs.getvalue()) - dfs: mikeio.Dfs0 = mikeio.open(fn_obs) # type: ignore - items = [item.name for item in dfs.items] - obs_item = st.selectbox(label="Item", options=items) + dfs = mikeio.open(fn_obs) + obs_item = st.selectbox("Item", options=[i.name for i in dfs.items]) mod = st.file_uploader("Model", type="dfs0") if mod: - fn_mod = os.path.join(tmp_folder, "mod.dfs0") - with open(fn_mod, "wb") as f: - f.write(mod.getvalue()) - mdfs: mikeio.Dfs0 = mikeio.open(fn_mod) # type: ignore - mitems = [item.name for item in mdfs.items] - mod_item = st.selectbox(label="Item", options=mitems) + fn_mod = tmp / "mod.dfs0" + fn_mod.write_bytes(mod.getvalue()) + mdfs = mikeio.open(fn_mod) + mod_item = st.selectbox("Item", options=[i.name for i in mdfs.items]) metrics = st.multiselect( "Metrics", - ["bias", "rmse", "mae", "cc", "r2"], - default=["bias", "rmse", "mae", "cc", "r2"], + ["bias", "rmse", "mae", "cc", "r2", "si", "kge", "mape", "urmse"], + default=["bias", "rmse", "r2"], ) if mod and obs: - c: modelskill.Comparer = modelskill.match( - fn_obs, fn_mod, obs_item=obs_item, mod_item=mod_item - ) # type: ignore + c = modelskill.match( + fn_obs, fn_mod, obs_item=obs_item, mod_item=mod_item, gtype="point" + ) - tabskill, tabts, tabscatter = st.tabs(["Skill", "Time series", "Scatter"]) + tskill, tts, tsc = st.tabs(["Skill", "Time series", "Scatter"]) - with tabskill: + with tskill: df = c.skill(metrics=metrics).to_dataframe() st.dataframe(df) - with tabts: + with tts: c.plot.timeseries() - fig = matplotlib.pyplot.gcf() - st.pyplot(fig) + st.pyplot(plt.gcf()) - with tabscatter: + with tsc: c.plot.scatter() - fig_sc = matplotlib.pyplot.gcf() - st.pyplot(fig_sc) + st.pyplot(plt.gcf())