Skip to content
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 116 additions & 7 deletions src/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pyopenms as poms
from src.common.common import show_fig, display_large_dataframe
from typing import Union
from plotly.subplots import make_subplots


def get_df(file: Union[str, Path]) -> pd.DataFrame:
Expand Down Expand Up @@ -166,10 +167,11 @@ def plot_ms_spectrum(df, title, bin_peaks, num_x_bins):
)
return fig


@st.fragment
def view_peak_map():
df = st.session_state.view_ms1

# Apply Box Selection Filtering
if "view_peak_map_selection" in st.session_state:
box = st.session_state.view_peak_map_selection.selection.box
if box:
Expand All @@ -178,29 +180,103 @@ def view_peak_map():
df = df[df["mz"] > box[0]["y"][1]]
df = df[df["mz"] < box[0]["y"][0]]
df = df[df["RT"] < box[0]["x"][1]]

# ✅ Main Peak Map
peak_map = df.plot(
kind="peakmap",
x="RT",
y="mz",
z="inty",
title=st.session_state.view_selected_file,
xlabel="Retention Time (s)",
ylabel="m/z",
grid=False,
show_plot=False,
bin_peaks=True,
backend="ms_plotly",
aggregate_duplicates=True,
)
peak_map.update_layout(template="simple_white", dragmode="select")

# ✅ TIC Plot with Selection Mode (Inverted y-axis)
df_tic = df.groupby("RT").sum().reset_index()

tic_fig = go.Figure()
tic_fig.add_trace(
go.Scatter(
x=df_tic["RT"],
y=df_tic["inty"],
mode="lines",
line=dict(color="#f24c5c", width=2),
name="TIC",
)
)

tic_fig.update_layout(
height=200,
margin=dict(l=0, r=0, t=0, b=0),
plot_bgcolor="rgb(255,255,255)",
xaxis=dict(
title="Retention Time (s)",
rangeslider=dict(visible=False),
showgrid=False
),
yaxis=dict(
title="TIC",
autorange="reversed" # ✅ Invert the y-axis
),
dragmode="select", # ✅ Horizontal selection mode
selectdirection = "h"
)

# ✅ Combined Figure
combined_fig = make_subplots(
rows=2,
cols=1,
shared_xaxes=True,
row_heights=[0.7, 0.3], # Peak map gets 70%, TIC gets 30%
vertical_spacing=0.05
)

for trace in peak_map.data:
combined_fig.add_trace(trace, row=1, col=1)

for trace in tic_fig.data:
combined_fig.add_trace(trace, row=2, col=1)

combined_fig.update_layout(
template="simple_white",
dragmode="zoom", # Enable zooming and panning
xaxis=dict(title="Retention Time (s)", showgrid=False),
yaxis=dict(title="m/z"), # Y-axis for peak map
yaxis2=dict(
title="TIC",
autorange="reversed" # ✅ Inverted y-axis for TIC
),
height=850,
margin=dict(t=100, b=100),
title=dict(
text=st.session_state.view_selected_file,
x=0.5,
y=0.99,
xanchor="center",
yanchor="top",
font=dict(size=18, family="Arial, sans-serif")
)
)
Comment on lines +245 to +261
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Combined figure layout could be improved.

While the layout configuration is generally good, there's an issue with the minallowed and maxallowed properties:

-        xaxis=dict(title="Retention Time (s)", showgrid=False, minallowed="0", maxallowed="1000"),
+        xaxis=dict(title="Retention Time (s)", showgrid=False, range=[0, 1000]),

The minallowed and maxallowed properties define input constraints but don't limit the visible range. The range property would better ensure the visualization starts with the desired range. Also, the values are passed as strings instead of numbers, which is inconsistent with Plotly conventions.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
combined_fig.update_layout(
template="simple_white",
dragmode="zoom",
xaxis=dict(title="Retention Time (s)", showgrid=False, minallowed="0", maxallowed="1000"),
yaxis=dict(title="m/z", fixedrange=True),
yaxis2=dict(title="TIC", autorange="reversed", fixedrange=True),
height=850,
margin=dict(t=100, b=100),
title=dict(
text=st.session_state.view_selected_file,
x=0.5,
y=0.99,
xanchor="center",
yanchor="top",
font=dict(size=18, family="Arial, sans-serif")
)
)
combined_fig.update_layout(
template="simple_white",
dragmode="zoom",
- xaxis=dict(title="Retention Time (s)", showgrid=False, minallowed="0", maxallowed="1000"),
+ xaxis=dict(title="Retention Time (s)", showgrid=False, range=[0, 1000]),
yaxis=dict(title="m/z", fixedrange=True),
yaxis2=dict(title="TIC", autorange="reversed", fixedrange=True),
height=850,
margin=dict(t=100, b=100),
title=dict(
text=st.session_state.view_selected_file,
x=0.5,
y=0.99,
xanchor="center",
yanchor="top",
font=dict(size=18, family="Arial, sans-serif")
)
)


# ✅ Display the Plot
c1, c2 = st.columns(2)

with c1:
st.info(
"💡 Zoom in via rectangular selection for more details and 3D plot. Double click plot to zoom back out."
"💡 Select ranges on the TIC plot below for more details. "
"Double click to reset the selection."
)
show_fig(
peak_map,
combined_fig,
f"peak_map_{st.session_state.view_selected_file}",
selection_session_state_key="view_peak_map_selection",
)

with c2:
if df.shape[0] < 2500:
peak_map_3D = df.plot(
Expand All @@ -211,6 +287,8 @@ def view_peak_map():
y="mz",
z="inty",
zlabel="Intensity",
xlabel="Retention Time (s)",
ylabel="m/z",
title="",
show_plot=False,
grid=False,
Expand All @@ -220,9 +298,17 @@ def view_peak_map():
width=900,
aggregate_duplicates=True,
)
st.plotly_chart(peak_map_3D, use_container_width=True)

peak_map_3D.update_layout(
scene=dict(
xaxis=dict(title="Retention Time (s)"),
yaxis=dict(title="m/z"),
zaxis=dict(title="Intensity"),
dragmode="orbit"
)
)
Comment on lines +297 to +304
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

3D peak map layout alignment with 2D plot.

The 3D peak map layout is correctly configured to maintain consistency with the 2D visualization, however it has the same issue with minallowed and maxallowed properties:

-                    xaxis=dict(title="Retention Time (s)", minallowed="0", maxallowed="1000"),
+                    xaxis=dict(title="Retention Time (s)", range=[0, 1000]),

Setting dragmode="orbit" is a good choice for 3D visualization as it provides more intuitive navigation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
peak_map_3D.update_layout(
scene=dict(
xaxis=dict(title="Retention Time (s)", minallowed="0", maxallowed="1000"),
yaxis=dict(title="m/z", fixedrange=True),
zaxis=dict(title="Intensity"),
dragmode="orbit"
)
)
peak_map_3D.update_layout(
scene=dict(
xaxis=dict(title="Retention Time (s)", range=[0, 1000]),
yaxis=dict(title="m/z", fixedrange=True),
zaxis=dict(title="Intensity"),
dragmode="orbit"
)
)


st.plotly_chart(peak_map_3D, use_container_width=True)
@st.fragment
def view_spectrum():
cols = st.columns([0.34, 0.66])
Expand Down Expand Up @@ -316,4 +402,27 @@ def view_bpc_tic():
key="view_eic_ppm",
)
fig = plot_bpc_tic()
show_fig(fig, f"BPC-TIC-{st.session_state.view_selected_file}")

fig.update_layout(
xaxis=dict(
rangeslider=dict(visible=True), # Range slider for x-axis zoom
rangeselector=dict( # Zoom buttons
buttons=list([
dict(count=1, label="1s", step="second", stepmode="backward"),
dict(count=10, label="10s", step="second", stepmode="backward"),
dict(count=1, label="1m", step="minute", stepmode="backward"),
dict(step="all")
])
)
),
margin=dict(l=0, r=0, t=0, b=0), # Remove margins for full-width display
height=700, # Increase height for better visualization
hovermode="x unified", # Unified hover tooltip
modebar=dict(
orientation='h', # Horizontal toolbar
bgcolor='rgba(255,255,255,0.7)', # Toolbar background color
)
)

# Display full-width chromatogram
st.plotly_chart(fig, use_container_width=True)