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

Interval selection issue #2185

Closed
arberas96 opened this issue Jun 1, 2020 · 5 comments
Closed

Interval selection issue #2185

arberas96 opened this issue Jun 1, 2020 · 5 comments

Comments

@arberas96
Copy link

Related to the issue #1049 , I'm having problems to insert a brush into a nx_altair visualization. I want to select a node with the brush and all the edges that are connected with that node to be colored.

Instead of that, what I obtain is a similar result as it is presented at the top of this issue: the edges are colored only if the selected node is taken by altair as the 'source node'.

Foto_1

I've tried solving it by using layering as @amitkaps has suggested in #1049 , but I'm not able to solve it properly. I get a different result which is that the edges only get colored if they are completely seleccted by the brush.

Foto_2

The code has been taken from nx_altair and I've modified it only to show my main problem. The code below corresponds to the sencond explained case (the layered case).

import random
import networkx as nx

# Generate a random graph
H = nx.fast_gnp_random_graph(n=7, p=0.5)

# Compute positions for viz.
pos = nx.kamada_kawai_layout(H,weight='distance')

# Add attributes to each node.
for n in H.nodes():
  H.nodes[n]['name'] = n
    

# Add attributes to edge.    
for e in H.edges():
    H.edges[(e[0],e[1])]['M'] = random.uniform(0, 1)
import altair as alt
from nx_altair.core import to_pandas_edges

df_edges = to_pandas_edges(H, pos)

brush = alt.selection_interval(empty='none')

edge_base = alt.Chart(df_edges).properties(
    width=300,
    height=300).mark_line(size=5).encode(
        alt.X('x'),
        alt.Y('y'),
        detail='edge')

edge_color = edge_base.encode(
        color=alt.condition(brush, alt.Color('M',
            scale=alt.Scale(scheme='yelloworangered'),type='quantitative'),alt.value('lightgray'))
    ).add_selection(brush).transform_filter(brush)

edge_grey=edge_base.encode(
    color=alt.value('lightgray'))

viz=[]
viz.append(edge_grey)
viz.append(edge_color)
total=alt.layer(*viz)
@jakevdp
Copy link
Collaborator

jakevdp commented Jun 1, 2020

I don't know of any way to do this within the Vega-Lite grammar.

@amitkaps
Copy link

amitkaps commented Jun 2, 2020

Since I suggested "layering" as a way to do this (and you tagged me on this), let me elaborate and share two examples on how to approach selections.

  • Undirected Graph with Brush Interval Selection to highlight all linked edges (your use case)
  • Directed Graph with Nearest Single Selection to highlight source (outbound) and target (inbound) edges

https://observablehq.com/@amitkaps/interactive-graphs-in-vega-lite

These examples use two data sources - nodes dataframe & links dataframe - which is the typical case for network datasets. I am only using Vega-Lite Grammar for data transformation (one sided lookups, flatten) and Selections. You will need to adapt this to Altair syntax and can more efficiently do the data transformation in Pandas.

Also, these are quick hacks and can probably be further optimised in terms of number of layers.

Hope this helps.

@arberas96
Copy link
Author

First of all thank you for the fast answer. In particular thank you @amitkaps for your help and those both incredible examples. Sorry for not answering sooner, I've been making some tries using your ideas in altair.

I'm not able to implement the first example that you have created and I sense where is the problem but I don't know how to solve it.

I'm trying to use de following dataframe:

foto_data

And the first idea that you have sent in altair I've done like this:

base=alt.Chart(graph)

highlightNode=alt.selection(type='interval', empty='all')

a=base.mark_point().encode(
    alt.X('posX',type='quantitative',scale=alt.Scale(domain=[0,1])),
    alt.Y('posY',type='quantitative',scale=alt.Scale(domain=[0,1])),
    opacity=alt.value(0.7)).add_selection(highlightNode)

b=base.mark_line(stroke='grey',strokeOpacity=0.5).encode(
    alt.X('posX', type='quantitative'),
    alt.Y('posY', type='quantitative'),
    order=alt.Order('edge',type='nominal'))

c=base.transform_lookup('edge',from_=alt.LookupData('highlightNode',key='edge'), as_='highlightEdges').mark_line(stroke='blue').encode(
    alt.X('posX', type='quantitative'),
    alt.Y('posY', type='quantitative'),
    order=alt.Order('edge',type='nominal')).transform_filter('datum.highlightEdges')
alt.layer(c,a,b)

But what I get is that in the c graph the column 'highlightEdges' is empty or not created.
¿Could be that 'higlihgtNodes' in LookupData is taken as url?

lookup = alt.LookupData(data='highlightNode', key='edge')
dct = lookup.to_dict()
dct['data']
{'url':'highlightNode'} #This is the output of dct['data']

Thanks again for your help, you have helped me a lot.

@amitkaps
Copy link

amitkaps commented Jun 3, 2020

You need to use “LookupSelection” instead of “LookupData” and then “filter” the data for the selection (it is missing)

For further debugging, please try these:

  1. Convert your altair-viz to vgspec to see the json specification in a notebook to check and compare.
  2. Alternatively, open the vis in vega-editor which will allow you to see the spec as well as all the transformed data.

@arberas96
Copy link
Author

Done!!

Thank you, You have helped me a lot!!

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

No branches or pull requests

3 participants