Skip to content

Commit 7aece4a

Browse files
enryHclaude
andauthored
🎨 highlight that smoothing slider is both for IQR outlier removal and data smoothing (#54)
* 🎨 highlight that smoothing slider is both for IQR outlier removal and data smoothing - could be separated - could be considered to have two sliders * Split rolling window into separate smoothing and IQR sliders; make smoothing optional - Replace single `rolling_window` slider with `rolling_window_smoothing` and `rolling_window_iqr` so each can be tuned independently - Add "Apply rolling median smoothing" checkbox; when unchecked the smoothing slider is disabled and `df_rolling` passes through filtered data directly - Update IQR kwargs, session state saves, debug panel, and data dashboard label to use the new keys Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01By3g9ScEi2dZh7pYEYCiKV * Fix black formatting: wrap long f-string lines Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01By3g9ScEi2dZh7pYEYCiKV * 🔧 update minimum for slider * Fall back to legacy rolling_window key for backward session compatibility When restoring a session saved before the slider split, both new sliders now seed from the old rolling_window value instead of hard-coding 21, preserving the user's previous preprocessing parameters. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01By3g9ScEi2dZh7pYEYCiKV --------- Co-authored-by: enryh <noreply@anthropic.com>
1 parent f3fa95a commit 7aece4a

2 files changed

Lines changed: 52 additions & 19 deletions

File tree

AutoGrowth/0_data_dashboard.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
masked = st.session_state.get("masked")
2323
start_time = st.session_state.get("start_time")
2424
processing_summary = st.session_state.get("upload_processing_summary_msg")
25-
rolling_window = st.session_state.get("rolling_window")
25+
rolling_window_smoothing = st.session_state.get("rolling_window_smoothing")
26+
apply_smoothing = st.session_state.get("apply_smoothing", True)
2627
st.session_state.setdefault("yaxis_scale", False)
2728
st.session_state.setdefault("USE_ELAPSED_TIME_FOR_PLOTS", True)
2829
use_same_yaxis_scale = bool(st.session_state.get("yaxis_scale", False))
@@ -283,12 +284,12 @@
283284
if df_rolling is not None:
284285
with st.container(border=True):
285286
st.header("Smoothed data view.")
286-
if rolling_window is not None:
287+
if apply_smoothing and rolling_window_smoothing is not None:
287288
st.subheader(
288-
f"Rolling median in window of {rolling_window}s using filtered OD data"
289+
f"Rolling median smoothing (window={rolling_window_smoothing}) applied to filtered OD data"
289290
)
290291
else:
291-
st.subheader("Filtered raw OD data (untrimmed and not calibrated)")
292+
st.subheader("Filtered raw OD data (no smoothing applied)")
292293
fig = px.scatter(
293294
df_wide_raw_od_data_filtered,
294295
x=df_wide_raw_od_data_filtered.index,

AutoGrowth/0_upload_data.py

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,14 @@ def apply_linear_adjustments(
413413
),
414414
)
415415
with filter_columns[1]:
416+
apply_smoothing = st.checkbox(
417+
"Apply rolling median smoothing",
418+
value=st.session_state.get("apply_smoothing", True),
419+
help=(
420+
"If checked, a rolling median is applied to smooth the OD data "
421+
"before growth rate estimation."
422+
),
423+
)
416424
quantile_max = st.slider(
417425
"Max quantile for maximum removal",
418426
0.9,
@@ -428,11 +436,21 @@ def apply_linear_adjustments(
428436
step=0.1,
429437
help="Used when outlier method is IQR. Multiplier of the IQR.",
430438
)
431-
rolling_window = st.slider(
432-
"Rolling window (of timepoints) for IQR outlier removal",
439+
_rw_fallback = st.session_state.get("rolling_window", 21)
440+
rolling_window_smoothing = st.slider(
441+
"Rolling window (timepoints) for data smoothing",
442+
5,
443+
141,
444+
st.session_state.get("rolling_window_smoothing", _rw_fallback),
445+
step=2,
446+
disabled=not apply_smoothing,
447+
help="Rolling median window size for OD data smoothing.",
448+
)
449+
rolling_window_iqr = st.slider(
450+
"Rolling window (timepoints) for IQR outlier removal",
433451
11,
434452
141,
435-
st.session_state.get("rolling_window", 21),
453+
st.session_state.get("rolling_window_iqr", _rw_fallback),
436454
step=2,
437455
help="Used when outlier method is IQR.",
438456
)
@@ -535,7 +553,9 @@ def apply_linear_adjustments(
535553
st.session_state["outlier_method"] = outlier_method
536554
st.session_state["quantile_max"] = quantile_max
537555
st.session_state["iqr_range_value"] = iqr_range_value
538-
st.session_state["rolling_window"] = rolling_window
556+
st.session_state["apply_smoothing"] = apply_smoothing
557+
st.session_state["rolling_window_smoothing"] = rolling_window_smoothing
558+
st.session_state["rolling_window_iqr"] = rolling_window_iqr
539559
st.session_state["ecod_factor"] = ecod_factor
540560
st.session_state["round_time"] = round_time
541561
st.session_state["aggregate_duplicated_rounded_timepoint"] = (
@@ -717,7 +737,7 @@ def apply_linear_adjustments(
717737
kwargs_iqr = {
718738
"method": "iqr",
719739
"factor": iqr_range_value,
720-
"window_size": rolling_window,
740+
"window_size": rolling_window_iqr,
721741
}
722742
kwargs = (
723743
kwargs_iqr
@@ -806,15 +826,22 @@ def apply_linear_adjustments(
806826
# ! should I visualize the values differently?
807827
df_wide_raw_od_data_filtered = df_wide_raw_od_data_filtered.ffill().bfill()
808828

809-
df_rolling = (
810-
df_wide_raw_od_data_filtered.rolling(
811-
rolling_window,
812-
min_periods=min_periods,
813-
center=True,
829+
if apply_smoothing:
830+
df_rolling = (
831+
df_wide_raw_od_data_filtered.rolling(
832+
rolling_window_smoothing,
833+
min_periods=min_periods,
834+
center=True,
835+
)
836+
.median()
837+
.sort_index()
814838
)
815-
.median()
816-
.sort_index()
817-
)
839+
msg += (
840+
f"- Applied rolling median smoothing (window={rolling_window_smoothing}).\n"
841+
)
842+
else:
843+
df_rolling = df_wide_raw_od_data_filtered.sort_index()
844+
msg += "- Smoothing disabled; using filtered data directly.\n"
818845

819846
# ? Should it not be possible to be run twice in a single session?
820847
if od_adjustment_upload is not None:
@@ -861,7 +888,8 @@ def apply_linear_adjustments(
861888

862889
st.session_state["df_rolling"] = df_rolling
863890

864-
st.session_state["rolling_window"] = int(rolling_window)
891+
st.session_state["rolling_window_smoothing"] = int(rolling_window_smoothing)
892+
st.session_state["rolling_window_iqr"] = int(rolling_window_iqr)
865893

866894
st.session_state["upload_processing_summary_msg"] = msg
867895
st.write("### Data processing summary:")
@@ -885,7 +913,11 @@ def apply_linear_adjustments(
885913
"filter_by_iqr_range": st.session_state.get("filter_by_iqr_range"),
886914
"quantile_max": st.session_state.get("quantile_max"),
887915
"iqr_range_value": st.session_state.get("iqr_range_value"),
888-
"rolling_window": st.session_state.get("rolling_window"),
916+
"apply_smoothing": st.session_state.get("apply_smoothing"),
917+
"rolling_window_smoothing": st.session_state.get(
918+
"rolling_window_smoothing"
919+
),
920+
"rolling_window_iqr": st.session_state.get("rolling_window_iqr"),
889921
"round_time": st.session_state.get("round_time"),
890922
"time_ranges": st.session_state.get("time_ranges"),
891923
"update_zero_timepoint": st.session_state.get("update_zero_timepoint"),

0 commit comments

Comments
 (0)