[Python] How to Link Zoom (X-Axis) of Two Separate Plotly Plots in Streamlit?

Discussão em 'Python' iniciado por Stack, Abril 12, 2025.

  1. Stack

    Stack Membro Participativo

    I want to visualize audio data in Streamlit with two separate Plotly plots: one for the Time Domain waveform and one for the MFCC (Mel-frequency cepstral coefficients). I want to link their X-axes so that zooming or panning one plot updates the other, keeping them synchronized.

    I’ve tried using st.session_state to store the X-axis range and update both plots via on_change, but it’s unreliable—sometimes the plots don’t sync, or Streamlit rerenders incorrectly.

    Here’s a minimal example using Librosa’s example audio (trumpet) to avoid file uploads. It creates two Plotly charts, but zooming one doesn’t affect the other. How can I link their X-axes for zoom/pan?

    import streamlit as st
    import librosa
    import numpy as np
    import plotly.graph_objects as go

    st.title("Audio Plots")

    # Load Librosa example audio
    audio, sr = librosa.load(librosa.ex('trumpet'))
    duration = len(audio) / sr
    time_axis = np.linspace(0, duration, len(audio))

    # Compute MFCCs
    mfccs = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=20)
    mfcc_time = librosa.frames_to_time(np.arange(mfccs.shape[1]), sr=sr)

    # Time Domain plot
    fig1 = go.Figure()
    fig1.add_trace(go.Scatter(x=time_axis, y=audio, line=dict(color="#4169E1"), name="Time Domain"))
    fig1.update_layout(xaxis_title="Time (s)", yaxis_title="Amplitude", title="Time Domain", height=300)
    st.plotly_chart(fig1, use_container_width=True)

    # MFCC plot
    fig2 = go.Figure()
    fig2.add_trace(go.Heatmap(z=mfccs, x=mfcc_time, y=np.arange(1, 21), colorscale="Viridis", name="MFCC"))
    fig2.update_layout(xaxis_title="Time (s)", yaxis_title="Coefficient", title="MFCC", height=300)
    st.plotly_chart(fig2, use_container_width=True)

    st.write(f"Duration: {duration:.2f}s, Samples: {len(audio)}, MFCC Frames: {mfccs.shape[1]}")


    What I’ve Tried:

    • Using make_subplots(shared_xaxes=True), but I want separate plots for layout flexibility.
    • Storing X-axis range in st.session_state with on_change:

    if "x_range" not in st.session_state:
    st.session_state.x_range = None
    def update_range(change):
    if change and "xaxis.range[0]" in change:
    st.session_state.x_range = [change["xaxis.range[0]"], change["xaxis.range[1]"]]
    st.plotly_chart(fig1, key="time", on_change=update_range)


    This sometimes works but often fails due to Streamlit’s rerendering.

    Question: How can I reliably sync the X-axes of these two Plotly plots in Streamlit so zooming one updates the other? Is there a better way to handle Plotly’s relayoutData-like events in Streamlit?

    Testing the Example:

    1. Install: pip install streamlit plotly librosa numpy
    2. Save: As test_streamlit.py
    3. Run: streamlit run test_streamlit.py

    Thanks a million!

    Continue reading...

Compartilhe esta Página