From 56ce56697559d2cbb587e010e0812a98ec0f86b7 Mon Sep 17 00:00:00 2001 From: afmagee42 Date: Fri, 20 Dec 2024 06:56:36 -0800 Subject: [PATCH 1/4] fix #49 --- ringvax/summary.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ringvax/summary.py b/ringvax/summary.py index e5eb2cd..80f1bda 100644 --- a/ringvax/summary.py +++ b/ringvax/summary.py @@ -94,7 +94,9 @@ def summarize_detections(df: pl.DataFrame) -> pl.DataFrame: return pl.DataFrame( { "prob_detect": 1.0 - count_nodetect / n_infections, - "prob_active": count_active / n_active_eligible, + "prob_active": count_active / n_active_eligible + if n_active_eligible > 0 + else None, "prob_passive": count_passive / n_infections, "prob_detect_before_infectious": df.filter(pl.col("detected")) .filter(pl.col("t_detected") < pl.col("t_infectious")) From 32693f0caa82203de8161d94494b35081806d12c Mon Sep 17 00:00:00 2001 From: afmagee42 Date: Fri, 20 Dec 2024 09:22:10 -0800 Subject: [PATCH 2/4] more than needed to make 0/0 problem go away --- ringvax/app.py | 26 ++++++++++++++++---------- ringvax/summary.py | 29 ++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/ringvax/app.py b/ringvax/app.py index 8b39e7e..ed33239 100644 --- a/ringvax/app.py +++ b/ringvax/app.py @@ -17,6 +17,20 @@ ) +def render_percents(df: pl.DataFrame) -> pl.DataFrame: + return df.with_columns( + pl.when(pl.col(col).is_nan()) + .then(pl.lit("Not a number")) + .otherwise( + pl.col(col).map_elements( + lambda x: f"{round(x):.0f}%", return_dtype=pl.String + ) + ) + .alias(col) + for col in df.columns + ) + + def make_graph(sim: Simulation) -> graphviz.Digraph: """Make a transmission graph""" graph = graphviz.Digraph() @@ -268,19 +282,11 @@ def app(): ) st.write( - "The following table provides summaries of marginal probabilities regarding detection. Aside from the marginal probability of active detection, these are the observed probabilities that any individual is detected in this manner. The marginal probability of active detection excludes index cases, which are not eligible for active detection." + "The following table provides summaries of marginal probabilities regarding detection. Aside from the marginal probability of active detection, these are the observed probabilities that any individual is detected in this manner. The marginal probability of active detection excludes index cases, which are not eligible for active detection, including the index case." ) detection = summarize_detections(sim_df) st.dataframe( - detection.select( - (pl.col(col) * 100).round(0).cast(pl.Int64) - for col in detection.columns - ) - .with_columns( - pl.concat_str([pl.col(col), pl.lit("%")], separator="") - for col in detection.columns - ) - .rename( + render_percents(detection).rename( { "prob_detect": "Any detection", "prob_active": "Active detection", diff --git a/ringvax/summary.py b/ringvax/summary.py index 80f1bda..46a8529 100644 --- a/ringvax/summary.py +++ b/ringvax/summary.py @@ -26,6 +26,17 @@ assert set(infection_schema.keys()) == Simulation.PROPERTIES +def frac(num, denom) -> float: + if denom == 0.0: + if num == 0.0: + return float("nan") + elif num > 0.0: + return float("inf") + else: + return float("-inf") + return num / denom + + def get_all_person_properties( sims: Sequence[Simulation], exclude_termination_if: list[str] = ["max_infections"] ) -> pl.DataFrame: @@ -93,15 +104,15 @@ def summarize_detections(df: pl.DataFrame) -> pl.DataFrame: return pl.DataFrame( { - "prob_detect": 1.0 - count_nodetect / n_infections, - "prob_active": count_active / n_active_eligible - if n_active_eligible > 0 - else None, - "prob_passive": count_passive / n_infections, - "prob_detect_before_infectious": df.filter(pl.col("detected")) - .filter(pl.col("t_detected") < pl.col("t_infectious")) - .shape[0] - / n_infections, + "prob_detect": 1.0 - frac(count_nodetect, n_infections), + "prob_active": frac(count_active, n_active_eligible), + "prob_passive": frac(count_passive, n_infections), + "prob_detect_before_infectious": frac( + df.filter(pl.col("detected")) + .filter(pl.col("t_detected") < pl.col("t_infectious")) + .shape[0], + n_infections, + ), } ) From 436f2838c0eae2bbda44d6ae65cbbb0633b92256 Mon Sep 17 00:00:00 2001 From: afmagee42 Date: Mon, 23 Dec 2024 16:58:29 -0800 Subject: [PATCH 3/4] fix typo per #52 --- ringvax/app.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ringvax/app.py b/ringvax/app.py index ed33239..0babf30 100644 --- a/ringvax/app.py +++ b/ringvax/app.py @@ -282,7 +282,13 @@ def app(): ) st.write( - "The following table provides summaries of marginal probabilities regarding detection. Aside from the marginal probability of active detection, these are the observed probabilities that any individual is detected in this manner. The marginal probability of active detection excludes index cases, which are not eligible for active detection, including the index case." + ( + "The following table provides summaries of marginal probabilities regarding detection. " + "Aside from the marginal probability of active detection, these are the observed " + "probabilities that any individual is detected in this manner, including the index case. " + "The marginal probability of active detection excludes index cases, which are not " + "eligible for active detection." + ) ) detection = summarize_detections(sim_df) st.dataframe( From 12f43ee881fd6f08c41df2d5b51567708f7b951e Mon Sep 17 00:00:00 2001 From: afmagee42 Date: Mon, 23 Dec 2024 17:13:39 -0800 Subject: [PATCH 4/4] don't reinvent wheel --- ringvax/summary.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/ringvax/summary.py b/ringvax/summary.py index 46a8529..8c2c766 100644 --- a/ringvax/summary.py +++ b/ringvax/summary.py @@ -26,17 +26,6 @@ assert set(infection_schema.keys()) == Simulation.PROPERTIES -def frac(num, denom) -> float: - if denom == 0.0: - if num == 0.0: - return float("nan") - elif num > 0.0: - return float("inf") - else: - return float("-inf") - return num / denom - - def get_all_person_properties( sims: Sequence[Simulation], exclude_termination_if: list[str] = ["max_infections"] ) -> pl.DataFrame: @@ -76,6 +65,7 @@ def _prepare_for_df(infection: dict) -> dict: } +@np.errstate(invalid="ignore") def summarize_detections(df: pl.DataFrame) -> pl.DataFrame: """ Get marginal detection probabilities from simulations. @@ -104,10 +94,10 @@ def summarize_detections(df: pl.DataFrame) -> pl.DataFrame: return pl.DataFrame( { - "prob_detect": 1.0 - frac(count_nodetect, n_infections), - "prob_active": frac(count_active, n_active_eligible), - "prob_passive": frac(count_passive, n_infections), - "prob_detect_before_infectious": frac( + "prob_detect": 1.0 - np.divide(count_nodetect, n_infections), + "prob_active": np.divide(count_active, n_active_eligible), + "prob_passive": np.divide(count_passive, n_infections), + "prob_detect_before_infectious": np.divide( df.filter(pl.col("detected")) .filter(pl.col("t_detected") < pl.col("t_infectious")) .shape[0],