vega / altair

Declarative statistical visualization library for Python
https://altair-viz.github.io/
BSD 3-Clause "New" or "Revised" License
9.37k stars 793 forks source link

Select data from Cross Plot and have images from the selected samples display corresponding images #2278

Closed Philliec459 closed 1 year ago

Philliec459 commented 4 years ago

Hello,

I am not sure if this is a capability yet in Altair, but the following image illustrates the function that I would like Altair if possible. I created this in Spotfire, but it would be nice to do in Altair.

ThinSection

Philliec459 commented 2 years ago

I love the interactive aspects of Altair. At this moment I do not know how to select samples from the cross plot as shown above and render the images associated with then, but tooltips does allow you to render the images if you hover over the sample point as shown below. This is great. Thank you for developing this excellent tool. Now to figure out how to show the tooltips for all the selected points on the cross plot????

Clastic_Thin_Sections

joelostblom commented 2 years ago

It is possible to show the images of selected points by using two layered charts:

import altair as alt
import pandas as pd

source = pd.DataFrame.from_records(
    [{'a': 1, 'b': 1, 'image': 'https://altair-viz.github.io/_static/altair-logo-light.png'},
     {'a': 2, 'b': 2, 'image': 'https://avatars.githubusercontent.com/u/11796929?s=200&v=4'}]
)

brush = alt.selection_interval()
points = alt.Chart(source).mark_circle(size=200).encode(
    x='a',
    y='b',
).add_selection(
    brush
)

imgs = alt.Chart(source).mark_image(width=100, height=100).encode(
    x='a',
    y='b',
    url='image'
).transform_filter(
    brush
)

points + imgs

ezgif com-gif-maker(14)

There is no automatic dodging as in your example from Spotfire, so they will likely show up on top of each other. An alternative is to use a faceted chart below the original chart:

brush = alt.selection_interval()
points = alt.Chart(source).mark_circle(size=200).encode(
    x='a',
    y='b',
).add_selection(
    brush
)

imgs = alt.Chart(source, width=200, height=200).mark_image().encode(
    url='image'
).facet(
    alt.Facet('image', title='', header=alt.Header(labelFontSize=0))
).transform_filter(
    brush
)

points & imgs

ezgif com-gif-maker(15)

You can play around starting from these two examples and see if you can get the results you want. See this issue for how to use local image via base64 encoding instead of a URL https://github.com/altair-viz/altair/issues/2318 and this issue for automatic resizing of the overall chart area https://github.com/altair-viz/altair/issues/2512

If you want more explicit control over the selected points, you could try the Dashboarding library Panel which support custom interactions with Altair selections.

Philliec459 commented 2 years ago

This is finally what I was looking to do. I would say this is complete. Thank you for your help.

Mode_of_Image_Kurtosis_with_TS

Brush for selection

brush = alt.selection(type='interval')

vega_pane = pn.pane.Vega(chart.add_selection( brush ))

imgs = alt.Chart(source).mark_image(width=50, height=50).encode( url='image' ).facet( alt.Facet('image',title='Select Thin Sections', header=alt.Header(labelFontSize=0)), columns=2 ).transform_window( row_number='row_number()' ).transform_filter( brush ).transform_window( rank='rank(row_number)' ).transform_filter( alt.datum.rank<15 )

------------------------------------------------

#

Text Data for Routine Core Analysis data

#

------------------------------------------------

Base chart for data tables

ranked_text = alt.Chart(source7).mark_text(align='right').encode( y=alt.Y('row_number:O',axis=None) ).transform_window( row_number='row_number()' ).transform_filter( brush ).transform_window( rank='rank(row_number)' ).transform_filter( alt.datum.rank<28 )

Data Tables

Porosity = ranked_text.encode(text='Porosity:N').properties(title=alt.TitleParams(text='Porosity', align='right')) Permeability = ranked_text.encode(text='Permeability:N').properties(title=alt.TitleParams(text='Permeability', align='right')) Sample = ranked_text.encode(text='Sample:N').properties(title=alt.TitleParams(text='Sample', align='right')) sqrt = ranked_text.encode(text='sqrt(k/phi):N').properties(title=alt.TitleParams(text ='sqrt(k/phi)', align='right'))

Image = ranked_text.encode(tooltip ='image').properties(title=alt.TitleParams(text ='image', align='right'))

RI = ranked_text.encode(text='ROCK_INDEX:N').properties(title=alt.TitleParams(text='PRT', align='right')) lith = ranked_text.encode(text='Lith:N').properties(title=alt.TitleParams(text='Lith', align='right'))

text = alt.hconcat(Sample, Porosity, Permeability,lith) # Combine data tables

------------------------------------------------

#

Gray Image Data Cross Plot

#

------------------------------------------------

kurt = alt.Chart(source).mark_circle(size=300).encode( alt.X('Mode_image:Q', scale=alt.Scale(domain=(0, 1))),

y='Perm',

alt.Y('Kurtosis:Q',scale=alt.Scale( domain=(.0, 12))),

color=alt.condition( brush,'ROCK_INDEX:O', alt.value('lightgray'),
                    scale=alt.Scale(
                    domain=[ 1,         2,         3  ,     4         ,5,       6],
                    range =['cyan', '#1e90ff',  'blue'  , 'orange','brown' ,'black'  ])),

tooltip=['image','Sample','Porosity','Permeability','Lith'],  # Must be a list for the image to render

).properties( width=500, height=500, title='Mode of Gray Image vs. Kurtosis Colored by Lithology', ).add_selection(brush)

------------------------------------------------

Concatenate Cross Plot, Text and Pc curves

------------------------------------------------

Build visualization

imgs|kurt|text works

kurt|imgs|text

joelostblom commented 2 years ago

Great! Thanks for sharing your code! I will keep this open as a reminder to add something along these lines to the docs.

Philliec459 commented 2 years ago

Thank you for you suggestions and ideas. We were able to implement your last comments in Altair to create the following visualization. Thank you.

Best Regards,


E. Craig Phillips CEO and Chief Petrophysicist Crested Butte Petrophysical Consultants

459 Cisneros Lane Crested Butte, CO 81224 USA Office: +1 970-343-0730 Mobile: +1 970-343-0730 email: @.*** Website: www.cbpetro.com GitHub: https://github.com/Philliec459

On Aug 8, 2022, at 11:54 AM, Joel Ostblom @.***> wrote:

It is possible to show the images of selected points by using two layered charts:

import altair as alt import pandas as pd

source = pd.DataFrame.from_records( [{'a': 1, 'b': 1, 'image': 'https://altair-viz.github.io/_static/altair-logo-light.png'}, {'a': 2, 'b': 2, 'image': 'https://avatars.githubusercontent.com/u/11796929?s=200&v=4'}] )

brush = alt.selection_interval() points = alt.Chart(source).mark_circle(size=200).encode( x='a', y='b', ).add_selection( brush )

imgs = alt.Chart(source).mark_image(width=100, height=100).encode( x='a', y='b', url='image' ).transform_filter( brush )

points + imgs https://user-images.githubusercontent.com/4560057/183458561-e52a45af-6fae-425f-825e-9be4c2a83537.gif There is no automatic dodging as in your example from Spotfire, so they will likely show up on top of each other. An alternative is to use a faceted chart below the original chart:

brush = alt.selection_interval() points = alt.Chart(source).mark_circle(size=200).encode( x='a', y='b', ).add_selection( brush )

imgs = alt.Chart(source, width=200, height=200).mark_image().encode( url='image' ).facet( alt.Facet('image', title='', header=alt.Header(labelFontSize=0)) ).transform_filter( brush )

points & imgs https://user-images.githubusercontent.com/4560057/183458930-f3946780-e80c-48fb-b58c-a843f6da28ea.gif You can play around starting from these two examples and see if you can get the results you want. See this issue for how to use local image via base64 encoding instead of a URL #2318 https://github.com/altair-viz/altair/issues/2318 and this issue for automatic resizing of the overall chart area #2512 https://github.com/altair-viz/altair/issues/2512 If you want more explicit control over the selected points, you could try the Dashboarding library Panel https://pyviz-dev.github.io/panel/reference/panes/Vega.html which support custom interactions with Altair selections.

— Reply to this email directly, view it on GitHub https://github.com/altair-viz/altair/issues/2278#issuecomment-1208305563, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANSKYPFDFOGGZJPTHULAATLVYEUTRANCNFSM4Q3DQLTQ. You are receiving this because you authored the thread.

joelostblom commented 1 year ago

This is now part of the documentation after https://github.com/altair-viz/altair/pull/3219, so closing this.