Streamlit-echarts

Thanks Fanilo. I think it could be related to the issue you linked to above, not sure though. It often happens on the first load of the page and then starts working after rerunning the script once or twice.

1 Like

Thanks Fanilo for this wonderful component. Do you have any plan to expand this component for dynamic plot in echarts (Examples - Apache ECharts) and Radar Charts (Examples - Apache ECharts).

Thanks,
Krishna

Hello @Krishna_Kant, thanks for using my component, really appreciate :slight_smile:

I have actually looked a bit into the animation/dynamic stuff months ago, and did not understand why my component did not render them correctly. Thanks for reminding me.

Are you able to put a JSON options of a dynamic plot in an issue in Github here so I remember to have a look at it somewhere this month?

Thanks a lot :slight_smile:
Fanilo

Thanks. I have create an issue on github.
Looking forward to have dynamic plot with echarts in streamlit.
Thanks once again for streamlit-echarts components.

1 Like

Hi @andfanilo ,

I am trying to use st_echarts component with placeholder. The idea is to animate existing echart plot.

placeholder.write(st_echarts(option, height=“500px”))

In most cases i am getting DuplicateWidgetID error (due to not having unique key). When i add a key argument, instead of replacing old canvas, it creates additional figures.

st_echarts(option, height=“500px”, key=str(i))

Here is a snippet, Let me know if there is any workaround.

for i in range(10):

placeholder.empty()

placeholder.write(st_echarts(option, height="500px"))

time.sleep(5)

Once again Thank you for echarts component.

Best,
Krishna

Hello @Krishna_Kant,


2 small fixes to your app before diving into the real problem:

  • The key for your chart should stay the same for it to stay and be updated in the app. For example:
data = [random.randint(0, 100) for _ in range(7)]
option = {
    "xAxis": {
        "type": "category",
        "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
    },
    "yAxis": {"type": "value"},
    "series": [
        {
            "data": data,
            "type": "bar",
            "showBackground": True,
            "backgroundStyle": {"color": "rgba(180, 180, 180, 0.2)"},
        }
    ],
}
st_echarts(option, height="500px", key="chart") # <-- preserve me with updated options on next Streamlit run
st.button("Regenerate data")
  • For the placeholder to work correctly with st_echarts, you need to use the context manager version instead of st.write(st_echarts):
st.title("Hello world!")
placeholder = st.empty()
st.button("Regenerate data")
st.caption("By Fanilo")

data = [random.randint(0, 100) for _ in range(7)]
option = {
    "xAxis": {
        "type": "category",
        "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
    },
    "yAxis": {"type": "value"},
    "series": [
        {
            "data": data,
            "type": "bar",
            "showBackground": True,
            "backgroundStyle": {"color": "rgba(180, 180, 180, 0.2)"},
        }
    ],
}

with placeholder: # <-- holds the st_echarts
    st_echarts(option, height="500px", key="chart")

Now after those little fixes…my logic was,

for i in range(10):
    st_echarts(..., key="chart")

won’t work because you are creating 10 echarts with the same key instead of reusing the same echart instance. So you need to find a way to loop your Streamlit app while keeping the echarts creation as a one time event in your script, and keeping your iteration number in some state variable.

Using a combination of session_state and experimental_rerun:

if "iteration" not in st.session_state:
    st.session_state["iteration"] = 0

data = [random.randint(0, 100) for _ in range(7)]
option = {
    "xAxis": {
        "type": "category",
        "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
    },
    "yAxis": {"type": "value"},
    "series": [
        {
            "data": data,
            "type": "bar",
            "showBackground": True,
            "backgroundStyle": {"color": "rgba(180, 180, 180, 0.2)"},
        }
    ],
}

while st.session_state["iteration"] < 10:
    st.markdown(f"Iteration number {st.session_state['iteration']}")

    st_echarts(option, height="500px", key="chart")  # <-- the same echarts, but with new data, is reused between reruns
    time.sleep(1)
    st.session_state["iteration"] += 1

    if st.session_state["iteration"] < 10:
        st.experimental_rerun()

looks like it works, let’s add the placeholder:

st.title("Hello world!")
placeholder = st.empty()
st.caption("By Fanilo")

if "iteration" not in st.session_state:
    st.session_state["iteration"] = 0

data = [random.randint(0, 100) for _ in range(7)]
option = {
    "xAxis": {
        "type": "category",
        "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
    },
    "yAxis": {"type": "value"},
    "series": [
        {
            "data": data,
            "type": "bar",
            "showBackground": True,
            "backgroundStyle": {"color": "rgba(180, 180, 180, 0.2)"},
        }
    ],
}

while st.session_state["iteration"] < 10:
    st.markdown(f"Iteration number {st.session_state['iteration']}")

    with placeholder:
        st_echarts(option, height="500px", key="chart")
    time.sleep(1)
    st.session_state["iteration"] += 1

    if st.session_state["iteration"] < 10:
        st.experimental_rerun()

test

Hope this helps you get started on those more advanced Streamlit concepts!
Fanilo

Merci!

I will try this workaround. I will also have a look into advance Streamlit concepts.
Now, echarts can be used to update real-time plots :smile: This is cool.

Dynamic plot still remain an issue (sorry to remind you again).

Merci beaucoup,
K.

Actually now I’m not sure what you mean about dynamic plots :o I was thinking about something unrelated

My technique when converting examples from the ECharts page to Python dict is to add console.log(option) at the end of the echarts example, open the web console in your browser devtools and then copy/analyze the option there


If you understand my sample app in my previous answer, with some work you can replicate the line dynamic plot:

import datetime
import random
import time
import streamlit as st
from streamlit_echarts import JsCode
from streamlit_echarts import st_echarts

st.set_page_config(layout="wide")
st.title("Animated ECharts - Dynamic Data + Time Axis")

origin_day = datetime.date(1997, 9, 3)


def _generate_datum(origin_day, day):
    date = origin_day + datetime.timedelta(days=day)
    return {
        "name": date.strftime("%Y/%m/%d"),
        "value": [date.strftime("%Y/%m/%d"), random.randint(1800, 2000)],
    }


if "data" not in st.session_state:
    st.session_state["data"] = [
        _generate_datum(origin_day, day) for day in range(1, 365)
    ]

option = {
    "title": {"text": "Something"},
    "tooltip": {
        "trigger": "axis",
        "formatter": JsCode(
            "function(params){params=params[0];var date=new Date(params.name);return date.getDate()+'/'+(date.getMonth()+1)+'/'+date.getFullYear()+' : '+params.value[1]}"
        ).js_code,
        "axisPointer": {"animation": False},
    },
    "xAxis": {"type": "time", "splitLine": {"show": False}},
    "yAxis": {
        "type": "value",
        "boundaryGap": [0, "100%"],
        "splitLine": {"show": False},
    },
    "series": [
        {
            "name": "模拟数据",
            "type": "line",
            "showSymbol": False,
            "hoverAnimation": False,
            "data": st.session_state["data"],
        }
    ],
}

st_echarts(option, height="600px", key="chart")

latest_day = datetime.datetime.strptime(
    st.session_state["data"][-1]["name"], "%Y/%m/%d"
)
new_data = st.session_state["data"][5:] + [
    _generate_datum(latest_day, day) for day in range(1, 6)
]

time.sleep(1)
st.session_state["data"] = new_data
st.experimental_rerun()

test


Your second example is a radar plot, most of the JSON you can translate into Python code:

import streamlit as st
from streamlit_echarts import st_echarts

st.set_page_config(layout="wide")
st.title("Radar plot")

START_YEAR = 2001
END_YEAR = 2029


def _generate_datum(year):
    i = year - 2000
    return {
        "name": "浏览器(数据纯属虚构) ",
        "type": "radar",
        "symbol": "none",
        "lineStyle": {"width": 1},
        "emphasis": {"areaStyle": {"color": "rgba(0,250,0,0.3)"}},
        "data": [
            {
                "value": [
                    (40 - i) * 10,
                    (38 - i) * 4 + 60,
                    i * 5 + 10,
                    i * 9,
                    i * i / 2,
                ],
                "name": str(year),
            }
        ],
    }


option = {
    "title": {"text": "浏览器占比变化", "subtext": "纯属虚构", "top": 10, "left": 10},
    "tooltip": {"trigger": "item"},
    "legend": {
        "type": "scroll",
        "bottom": 10,
        "data": [str(y) for y in range(START_YEAR, END_YEAR)],
    },
    "visualMap": {
        "top": "middle",
        "right": 10,
        "color": ["red", "yellow"],
        "calculable": True,
    },
    "radar": {
        "indicator": [
            {"text": "IE8-", "max": 400},
            {"text": "IE9+", "max": 400},
            {"text": "Safari", "max": 400},
            {"text": "Firefox", "max": 400},
            {"text": "Chrome", "max": 400},
        ]
    },
    "series": [_generate_datum(year) for year in range(START_YEAR, END_YEAR)],
}

st_echarts(option, height="800px")

test2


With those under the belt, you can now replicate about 85-90% of the echarts gallery I’d say :slight_smile: !

Thanks once again. I agree, it is working nicely. I was not exploring st.session_state previously.

@andfanilo - FYI, I’ve finally got a project underway to start using streamlit-echarts! :crossed_fingers:t4:

2 Likes

You’ll love it @asehmi! :raised_hands:

1 Like

Really, really nice work Fanilo! :star_struck:

Hi!
Thanks for this amazing component! It’s by far my favorite!!
I’m still discovering what could be done with it, and I’ve seen that we can add interactivity with events.
Unfortunately, I was not able to do it myself.
Do you have an example of how to use events?
For example, on a bar chart, when clicking on a bar, a specific action is triggered with the data related to this bar?