Maintain and update a single chart

Summary

Trying to plot my brain waves in streamlit but got an issue where it creates many plot appending on the page:

Steps to reproduce

Code snippet:
code: brainwaves.py · GitHub

Expected behavior:

just a single chart where traces are added

The basic way to do it is to create an empty placeholder container, and always reuse that container. Here’s a very simplified version of your app:

@blackary thanks that’s perfect!

It solved my problem but i’m facing the issue again when using two “with” with columns:

ezgif.com-video-to-gif

import json
import asyncio

import numpy as np
import streamlit as st
import plotly.graph_objects as go
import pandas as pd
from sklearn.decomposition import IncrementalPCA
from websockets import connect

waves = {
  "delta": [0.1, 4],
  "theta": [4, 7.5],
  "alpha": [7.5, 12.5],
  "beta": [12.5, 30],
  "gamma": [30, 100]  
}

figs = {}
pcas = {}
data = {
    "delta": [],
    "theta": [],
    "alpha": [],
    "beta": [],
    "gamma": []
}

empty = {
    "delta": st.empty(),
    "theta": st.empty(),
    "alpha": st.empty(),
    "beta": st.empty(),
    "gamma": st.empty()
}


for name, band in waves.items():
    figs[name] = go.Figure()
    pcas[name] = IncrementalPCA(n_components=3)
    
batch_size = 10

async def print_messages():
  global data
  
  async with connect("ws://localhost:8080") as ws:
    while True:
      msg = await ws.recv()
      # Extract theta
      new_data = json.loads(msg)['data']
      for name in waves:
        data[name].append(new_data[name])
      
      if len(data["alpha"]) >= batch_size:
        for name in waves:
          
          # Extract theta
          X = np.array(data[name])
            
          # Reshape
          X = X.reshape(len(data[name]), -1)  
            
          # Update PCA
          pcas[name].partial_fit(X)
          data[name] = []
          
          # Create dataframe
          df = pd.DataFrame(pcas[name].transform(X), 
                            columns=['PC1', 'PC2', 'PC3'])

          # Update figure
          figs[name].add_trace(go.Scatter3d(
              x=df['PC1'], y=df['PC2'], z=df['PC3'],
              mode='markers')
          )
          
        
        col1, col2 = st.columns(2)

        with empty["delta"].container(), col1:
            st.header("Delta")
            st.plotly_chart(figs["delta"], use_container_width=True)

        with empty["theta"].container(), col2:
            st.header("Theta")
            st.plotly_chart(figs["theta"], use_container_width=True)

        col3, col4 = st.columns(2)

        with empty["alpha"].container(), col3:
            st.header("Alpha")
            st.plotly_chart(figs["alpha"], use_container_width=True)
            
        with empty["beta"].container(), col4:
            st.header("Beta")
            st.plotly_chart(figs["beta"], use_container_width=True)
            
        col5, _ = st.columns(2)

        with empty["gamma"].container(), col5:
            st.header("Gamma")
            st.plotly_chart(figs["gamma"], use_container_width=True)

asyncio.run(print_messages())

1 Like

I think you can solve that issue by declaring the columns once, before the loop

Here’s another much-simplified example, where I pre-created all of the empty objects already inside of pre-created columns:

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.