Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linking the x-axis of multiple charts when zooming #1481

Open
dbk123 opened this issue May 7, 2019 · 12 comments
Open

Linking the x-axis of multiple charts when zooming #1481

dbk123 opened this issue May 7, 2019 · 12 comments

Comments

@dbk123
Copy link

dbk123 commented May 7, 2019

Is there any way to link the x-axis of multiple charts so that all they zoom together and maintain the same x-axis range when one plot is zoomed?

Thanks!

@jakevdp
Copy link
Collaborator

jakevdp commented May 7, 2019

Yes, but you'll need to learn a bit about binding selections

When you do a chart like this:

import altair as alt
from vega_datasets import data

cars = data.cars()

alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon',
    color='Origin'
).interactive()

The interactive() method is a shorthand for this:

alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon'
    color='Origin'
).add_selection(
    alt.selection_interval(bind='scales')
)

Each time you call interactive(), it will create a new (and independent) selection with a binding to scales. If you would like two panels in a chart to share the same selection binding, then you can specify it manually and attach the same selection object to each chart, for example:

resize = alt.selection_interval(bind='scales')

chart1 = alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon',
    color='Origin'
).add_selection(
    resize
)

chart2 = alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Acceleration',
    color='Origin'
).add_selection(
    resize
)

alt.concat(chart1, chart2)

Because the selection is attached to both charts, changing it will change the scales of both charts.

@dbk123
Copy link
Author

dbk123 commented May 7, 2019

Jake,

Thank you for the prompt and detailed feedback!!!!

-Dave

@dbk123 dbk123 closed this as completed May 7, 2019
@jakevdp
Copy link
Collaborator

jakevdp commented May 7, 2019

I'm going to reopen this as a reminder to myself to add this info to the docs.

@jakevdp jakevdp reopened this May 7, 2019
@dbk123
Copy link
Author

dbk123 commented May 7, 2019

Got it - sorry. (That will be very helpful thing to include!)

@YashaPushak
Copy link

Is it possible to have this work with layered charts as well?

Essentially I have:

backgroundChartOne = alt.Chart(df)...
foregroundChartOne = alt.Chart(df)...
backgroundChartTwo = alt.Chart(df)...
foregroundChartTwo = alt.Chart(df)...

chartOne = backgroundChartOne + foregroundChartOne
chartTwo = backgroundChartTwo + foregroundChartTwo

chartOne | chartTwo

I would like to have an interactive x-axis that is linked between chartOne and chartTwo. However, I haven't been able to get this to work yet, I keep getting a Duplicate signal name: "selector067_tuple" error.

I have tried, for example:

backgroundChartOne = alt.Chart(df)...
foregroundChartOne = alt.Chart(df)...
backgroundChartTwo = alt.Chart(df)...
foregroundChartTwo = alt.Chart(df)...

resize = alt.selection_interval(bind='scales')

backgroundChartOne = backgroundChartOne.add_selection(resize)
foregroundChartOne = foregroundChartOne.add_selection(resize)
backgroundChartTwo = backgroundChartTwo.add_selection(resize)
foregroundChartTwo = foregroundChartTwo.add_selection(resize)

chartOne = backgroundChartOne + foregroundChartOne
chartTwo = backgroundChartTwo + foregroundChartTwo

chartOne | chartTwo

and

backgroundChartOne = alt.Chart(df)...
foregroundChartOne = alt.Chart(df)...
backgroundChartTwo = alt.Chart(df)...
foregroundChartTwo = alt.Chart(df)...

resize = alt.selection_interval(bind='scales')

chartOne = backgroundChartOne + foregroundChartOne
chartTwo = backgroundChartTwo + foregroundChartTwo

chartOne = chartOne.add_selection(resize)
chartTwo = chartTwo.add_selection(resize)

chartOne | chartTwo

and neither of them seem to work.

@jakevdp
Copy link
Collaborator

jakevdp commented Oct 22, 2019

You cannot bind the same selection object to two charts in a layer (this leads to duplicate signal names). Try binding the selection to only one of the subcharts in each layer.

@jakevdp
Copy link
Collaborator

jakevdp commented Oct 22, 2019

For example, like this:

import altair as alt
import pandas as pd

df = pd.DataFrame({
    'x': range(5),
    'y': [1, 3, 2, 4, 0]
})

zoom = alt.selection_interval(bind='scales')

layer1 = alt.Chart(df).mark_line().encode(x='x', y='y').add_selection(zoom)
layer2 = alt.Chart(df).mark_point().encode(x='x', y='y')

chartOne = layer1 + layer2

@YashaPushak
Copy link

Perfect, thank you so much!

@mycarta
Copy link

mycarta commented Dec 11, 2020

Yes, but you'll need to learn a bit about binding selections

When you do a chart like this:

import altair as alt
from vega_datasets import data

cars = data.cars()

alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon',
    color='Origin'
).interactive()

The interactive() method is a shorthand for this:

alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon'
    color='Origin'
).add_selection(
    alt.selection_interval(bind='scales')
)

Each time you call interactive(), it will create a new (and independent) selection with a binding to scales. If you would like two panels in a chart to share the same selection binding, then you can specify it manually and attach the same selection object to each chart, for example:

resize = alt.selection_interval(bind='scales')

chart1 = alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon',
    color='Origin'
).add_selection(
    resize
)

chart2 = alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Acceleration',
    color='Origin'
).add_selection(
    resize
)

alt.concat(chart1, chart2)

Because the selection is attached to both charts, changing it will change the scales of both charts.

@jakevdp - is it possible to link both x and y axes so they are both synchronized on zooming?

@jakevdp
Copy link
Collaborator

jakevdp commented Dec 11, 2020

Yes, the last code snippet you quoted should do this.

@kennydataml
Copy link

kennydataml commented Jan 31, 2021

zoom = alt.selection_interval(bind='scales')
        selector = alt.selection_single(
            empty='all',
            fields=['ticker']
        )

base = alt.Chart(df).transform_filter(
    # slider_selection
    (alt.datum.date2 >= select_range_start.date) & (
        alt.datum.date2 <= select_range_end.date)
).add_selection(
    selector,
    select_range_start,
    select_range_end
)

bars = base.mark_bar().encode(
    # https://stackoverflow.com/questions/52877697/order-bar-chart-in-altair
    x=alt.X('ticker',
            sort=alt.EncodingSortField(
                field="ticker", op="count", order='descending'),
            axis=alt.Axis(title='Stock Tickers')
            ),
    y=alt.Y("count(id)",
            axis=alt.Axis(title='Number of Mentions'),
            # scale=alt.Scale(zero=False)
            ),
    color=alt.condition(selector, 'id:O', alt.value(
        'lightgray'), legend=None),
).properties(
    width=1400,
    height=600
).add_selection(
    zoom
)

The x axis is just stock tickers. they y axis is count.
I want to zoom in the bars that are really small. ie - the bars would get taller as I zoom-in on just the y-axis
Is this even supported? seems like zoom only works if both x and y axis are not string.

EDIT: used a slider, selection_single, transform_aggregate, and transform_filter to achieve what I wanted

@jakevdp
Copy link
Collaborator

jakevdp commented Feb 1, 2021

Vega-lite currently does not support selections on aggregated fields; see vega/vega-lite#5308. You're using a count aggregate, which makes the zoom selection fail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants