5

I have a Streamlit dashboard which lets me interactively explore a t-SNE embedding using an Altair plot. I am trying to figure out how to access the metadata of the selected datum so that I can visualize the corresponding image. In other words, given:

selector = alt.selection_single()
chart = (
    alt.Chart(df)
    .mark_circle()
    .encode(x="tSNE_dim1", y="tSNE_dim2", color="predicted class", tooltip=["image url", "predicted class"])
    .add_selection(selector)
)

...is there something akin to

selected_metadata = selector.tooltip
update_dashboard_img(img=selected_metadata["image url"], caption=selected_metadata["predicted class"])

I am aware about image marks but the images are on S3 and there are too many of them to fit in the plot.

crypdick
  • 16,152
  • 7
  • 51
  • 74

3 Answers3

5

Unfortunately, Altair does not provide any mechanism to pass information from the javascript rendering back to the Python kernel, so there is no way to do what you want to do in general.

(Edit: it appears streamlit has a solution to this that works if you are using their platform, but I am not aware of any generally applicable solution for this).

jakevdp
  • 77,104
  • 11
  • 125
  • 160
3

I hate to disagree with the creator of Altair, but I was able to achieve this using streamlit-vega-lite package. This works by wrapping the call to the chart creation function with altair_component():

    from streamlit_vega_lite import altair_component
    ...

    event_dict = altair_component(altair_chart=create_tsne_chart(tsne_df))
    # note: works with selector = alt.selection_interval(), not selection_single()
    dim1_bounds, dim2_bounds = event_dict.get("dim1"), event_dict.get("dim2")
    if dim1_bounds:
        (dim1_min, dim1_max), (dim2_min, dim2_max) = dim1_bounds, dim2_bounds
        selected_images = tsne_df[
            (tsne_df.dim1 >= dim1_min)
            & (tsne_df.dim1 <= dim1_max)
            & (tsne_df.dim2 >= dim2_min)
            & (tsne_df.dim2 <= dim2_max)
        ]
        st.write("Selected Images")
        st.write(selected_images)

        if len(selected_images) > 0:
            for _index, row in selected_images.iterrows():
                img = get_img(row["image url"])
                st.image(img, caption=f"{row['image url']} {row['predicted class']}", use_column_width=True)

The event_dict only contains information about the selector bounds. So, you have to use those values to reselect the data that was selected in the interactive chart.

Note that this package is a POC and has various limitations. Please upvote the Streamlit feature request created by the author of streamlit_vega_lite.

crypdick
  • 16,152
  • 7
  • 51
  • 74
  • 4
    Thanks for the update! It appears, however, that this only works on Streamlit's platform. So I still don't think there is any general solution to the problem, unfortunately. – jakevdp Nov 03 '20 at 00:45
1

Listening to Vega events will also be possible with Panel when the 0.13 version is released. For more details, see https://github.com/holoviz/panel/pull/2592.

image

joelostblom
  • 43,590
  • 17
  • 150
  • 159