Updating a table based on plot selection by altair

Viewed 87

For the past week, I have been trying to create an interactive altair dashboard. My goal is to create a plot connected to a table, where the table gets updated based on the selection of the plot. For example, my data is:

data = pd.DataFrame([[1,'This is good', 'pos'],
                    [1,'This is bad', 'neg'],
                    [2,'This is ok', 'neu'],
                    [2,'This is good', 'pos'],
                    [3, 'This is fine', 'neu'],
                    [3, 'Bad bad', 'neg'],
                    [3, 'Amazing', 'pos'],], columns=['date', 'text', 'sent'])

For plotting the data, I want x='date' and for the y axis, I want to plot the number of 'pos', 'neu', 'neg' in each date. So something like this:

enter image description here

I then want to create a table that gets updated based on the selection on the plot. For example, if date=2 and sent=neu is selected, I want to show in the table its corresponding text (that is 'This is fine').

First I tried to follow this post. I created a second dataframe data2 that has the data for 'date' and the number of 'pos','neg','neu' for each date.

data2 = pd.DataFrame([[1,'pos',1],
                  [1,'neg',1],
                  [1,'neu',0],
                  [2,'pos',1],
                  [2,'neg',0],
                  [2,'neu',1],
                  [3,'pos',0],
                  [3,'neg',1],
                  [3,'neu',0]], columns=['date', 'sent', 'count'])

But then I couldn't use the selector of data2 to filter the table made from the original data.

I also tried to directly make the plot from data by

base = alt.Chart(data).mark_line(point=True).encode(
x='date:Q',
y=alt.Y('count(sent):Q')
)

but this only counts the number of times that each date is showing up in the data set, not the frequencies of 'pos','neg','neu'.

So I wanted to ask how I can either filter the table from data by using a selector on data2, or directly plot the chart from data?

Thanks for the help!

1 Answers

You can build upon the example you linked and do something like this:

import altair as alt
from vega_datasets import data

source = data.cars()

# Brush for selection
brush = alt.selection(type='interval')

# Scatter Plot
points = alt.Chart(source).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
    color=alt.condition(brush, alt.Color('Origin:N', legend=None), alt.value('grey'))
).add_selection(brush)

# Base chart for data tables
ranked_text = alt.Chart(source).mark_text(align='right').encode(
    y=alt.Y('row_number:O',axis=None)
).transform_filter(
    brush
).transform_window(
    row_number='row_number()'
).transform_filter(
    'datum.row_number < 15'
).transform_aggregate(
    count='count()',
    groupby=['Origin']
)

# Data Tables
count = ranked_text.encode(
    text='count:N',
    color='Origin',
    yOffset='Origin'
).properties(
    title=alt.TitleParams(text='Horsepower', align='right')
)

# Build chart
alt.hconcat(
    points,
    count
).resolve_legend(
    color="independent"
).configure_view(
    strokeWidth=0
)

enter image description here

Note that this relies on the current development version of Altair from GitHub, which introduces the yOffset encoding. Without it, the colors are plotted on top of each other, and I couldn't get it to work nicely with faceting. If you don't need to offset, you can remove the groupby in the aggregate transform and just plot a single count.

Related