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

Better access to logical operand predicates #695

Closed
jakevdp opened this issue Apr 2, 2018 · 11 comments · Fixed by #3668
Closed

Better access to logical operand predicates #695

jakevdp opened this issue Apr 2, 2018 · 11 comments · Fixed by #3668
Assignees

Comments

@jakevdp
Copy link
Collaborator

jakevdp commented Apr 2, 2018

Vega-Lite has a number of logical operands; e.g.

  • alt.LogicalAndPredicate
  • alt.LogicalOrPredicate
  • alt.LogicalNotPredicate

These come up, for example, in constructing filter transforms. It would be useful to make these available to Altair users via Python's logical operators, &, |, and ~, respectively.

There are two challenges:

  1. How to implement them effectively... maybe create some sort of PredicateMixin for these classes? If we were constructing the wrapper classes by hand, it would make sense for alt.Predicate to be the base class of each of these logical predicates... perhaps we should change the code generation framework so that this kind of class hierarchy is (optionally) inferred in generated classes? Sounds hard to do well in a general way, though.

  2. The interaction with selection operands (e.g. alt.SelectionOr, alt.SelectionAnd, alt.SelectionNot, and alt.NamedSelection) makes this a bit tricky, because NamedSelection objects serve two purposes in Altair (they act both as a alt.SelectionDef and a alt.SelectionPredicate depending on the context).

@jakevdp jakevdp self-assigned this Apr 2, 2018
@kanitw
Copy link
Member

kanitw commented Apr 7, 2018

I think using &, |, ~ is definitely way more natural.

We have to do {"and/or": [...]} in Vega-Lite because we're limited to be just a JSON format.

@pagpires
Copy link
Contributor

A question regarding the combination of multiple operands: can we do that in altair now?
I met an error when I wanted to combine 'or' and 'not':

pop = vega_datasets.data.population()
alt.Chart(pop).mark_line().encode(
    x='age:O',
    y='sum(people):Q',
    color='year:O'
).properties(
    width=600, height=200
).transform_filter(
    {'or': [alt.FieldRangePredicate(field='year', range=[1850, 1880]), 
            alt.FieldRangePredicate(field='year', range=[1960, 2000])],
     # if I remove the following line and only keep 'or', it will work
    'not': alt.FieldRangePredicate(field='year', range=[1870, 1880])
    }    
)

@jakevdp
Copy link
Collaborator Author

jakevdp commented Apr 30, 2018

Yes, you can do them in Altair. But no, you can't have or and not as keys within the same object... I'm not quite sure how to interpret your intent here.

Is this what you were intending to do?

{'and': [
        {'or': [
            alt.FieldRangePredicate(field='year', range=[1850, 1880]), 
            alt.FieldRangePredicate(field='year', range=[1960, 2000])
        ]},
        {'not': alt.FieldRangePredicate(field='year', range=[1870, 1880])}
    ]
} 

@pagpires
Copy link
Contributor

Oh, I see... Yes this is exactly what I'm asking. Thanks!

@arrrobase
Copy link

Do logical operands work within "alt.condition"? I have a bar chart with a selection_single, and a scatter plot with a selection_interval, and I'm trying to set opacity of the scatter to be conditional on both selections. I've tried the following and other variations but keep getting schema errors.

The API says alt.condition should be able to take an operand, but I'm not sure about the syntax, which I adapted from the examples from transform_filter.

cond= {'and': [
    single_bar,
    interval_scatter
]}

opacity=alt.condition(cond, alt.value(0.5), alt.value(0.1))

Here's a minimal example of what I'm trying to do. It works if I set the opacity condition to just a single selector.

single_bar = alt.selection_single(encodings=['y'])
interval_scatter = alt.selection_interval(encodings=['x', 'y'])

cond = {'and': [
    single_bar,
    interval_scatter
]}

scatter= alt.Chart(df).mark_circle().encode(
    x='scan_quality:O',
    y='SSI:Q',,
    opacity=alt.condition(cond, alt.value(0.5), alt.value(0.1)),
).properties(
    selection=interval_scatter
)

bar = alt.Chart(df).mark_bar().encode(
    color='qc:N',
    x='count(qc)',
    y='scan_size:O',
).transform_filter(
    {'and': [alt.FieldRangePredicate(field='scan_quality', range=[1, 11]),
             interval_scatter.ref()]
    }
).properties(
    selection=single_bar
)

scatter & bar

@jakevdp
Copy link
Collaborator Author

jakevdp commented May 17, 2018

For selections, logical operands like & and | work (this issue is about transforms, not selections). So instead of {'and': [single_bar, interval_scatter]}, you should use single_bar & interval_scatter.

@arrrobase
Copy link

That did it, thanks!

@gh-owestesson
Copy link

gh-owestesson commented Aug 8, 2018

Sorry in advance if this is addressed by the above, but is it possible to combine a logical operand with a selection? I'd like to display all points not selected, like this:

import random
import numpy as np
data = pd.DataFrame( {col: np.random.randn(50) for col in 'xyz'})

selector = alt.selection_interval()
chart = alt.Chart( data )
xy = chart.mark_point().encode(
     x = 'x', 
     y = 'y').add_selection( selector )

xz = chart.mark_point().encode(
    x = 'x',
    y = 'z').transform_filter( ~selector)
xy | xz

@jakevdp
Copy link
Collaborator Author

jakevdp commented Aug 8, 2018

Hi @breadbaron – I think this should work, but there is a bug in the way ~selection is being serialized.

Here's a workaround until this is fixed:

import numpy as np
data = pd.DataFrame( {col: np.random.randn(50) for col in 'xyz'})

selector = alt.selection_interval()
chart = alt.Chart( data )
xy = chart.mark_point().encode(
     x = 'x', 
     y = 'y'
).add_selection( selector )

xz = chart.mark_point().encode(
    x = 'x',
    y = 'z'
).transform_filter( {'not': selector.ref()})
xy | xz

@gh-owestesson
Copy link

@jakevdp thank you!!

@jakevdp
Copy link
Collaborator Author

jakevdp commented Aug 9, 2018

@breadbaron fixed in #1075, which will make it into the 2.2 release 😄

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

Successfully merging a pull request may close this issue.

5 participants