holoviz / holoviews

With Holoviews, your data visualizes itself.
https://holoviews.org
BSD 3-Clause "New" or "Revised" License
2.7k stars 404 forks source link

Trying to replicate Measles example with options and magics #2946

Closed Gordon90s closed 4 years ago

Gordon90s commented 6 years ago

I'm trying to reproduce and slightly alter the HoloViews measle examples from here and here with many difficulties.

I would like to have 1) the hover function on the heatmap and 2) the interactive link between heatmap and curve plotting. I would also like to do this in particular with .options and not with magics.

Here is the code to get to the pandas data frame.

# presented in [Great Data Visualization Tools for Python](https://www.youtube.com/watch?v=TPl9bMg8j8U)
# (except OpenGL, JavaScript, pandas and graphviz)
# Data fetched and saved on 16/8/2018

import io
import requests
import holoviews as hv
import pandas as pd
import numpy as np
hv.extension('bokeh')

# Read data from file
url="https://raw.githubusercontent.com/Gordon90s/holoviews_stream/master/df_python_library_visualization.csv"
s=requests.get(url).content
df_big=pd.read_csv(io.StringIO(s.decode('utf-8')))

# Sort data frame with respect to 'total commits' in descending order
df_big = df_big.sort_values(by=['total commits', 'repo'], ascending=False)

# Remove columns that are not of interests for the visualization (was too lazy to correctly import the data set...)
df_big = df_big.drop(columns=['total commits', 'Unnamed: 0']).reset_index(drop=True)

# Rename 'repo' into 'library'
#df_big = df_big.rename(columns={df_big.columns[0]:'library'})

# Format to HoloViews data set
#hv_dataset = hv.Dataset(df_big, kdims=['week','library'], vdims=['commits']) 
hv_dataset = hv.Dataset(df_big, kdims=['week','repo'], vdims=['commits'])  

# Create heatmap
hv_heatmap = hv.HeatMap(hv_dataset, label = 'Repository Activity')

# Declare Tap stream with heatmap as source and initial values
posxy = hv.streams.Tap(source=hv_heatmap, x=26, y='holoviews')

# Define function to obtain curve of commmits per week based on library selected by tap on location
def tap_heatmap(x, y):
#    return hv.Curve(hv_dataset.select(library=y), kdims='week',    
    return hv.Curve(hv_dataset.select(repo=y), kdims='week',
                   label='library: %s' % y)

# Create dynamic map 
hv_dmap = hv.DynamicMap(tap_heatmap, streams = [posxy])

Using magics, I get both:

%opts HeatMap [width=900 height=500 logz=True fontsize={'xticks': '8pt', 'title': '12pt'} tools=['hover'] toolbar='above' xrotation=90] 
%opts HeatMap (cmap='Inferno') 
%opts Curve [width=900 height=250 yaxis='left' xrotation=90 toolbar=None] (line_color='black') {+framewise}
%opts Curve [fontsize={'xticks': '8pt', 'title': '12pt'}]

example = (hv_heatmap + hv_dmap).cols(1)
example

Now using options, I get 1) but not 2):

options = {'HeatMap': dict(width = 900, height = 500, tools=['hover'], logz = True, invert_yaxis = False, labelled = [], 
                           toolbar = 'above', xaxis = None, fontsize = {'xticks': '8pt', 'title': '12pt'},
                           cmap = 'Inferno', colorbar = True),
          'Curve': dict(width=900, height=250, line_color = 'black', xticks = 52, xrotation = 90, show_title = True,
                       fontsize = {'xticks': '8pt', 'title': '12pt'})}

example = (hv_heatmap + hv_dmap).cols(1).options(options)
example

What is the issue here?

Furthermore, I have not been able to translate {+framewise} to .options. How can I do that (if possible)? I would also like to remove the toolbar, but that seems not to be compatible with tools=['hover']?

jbednar commented 6 years ago

I made a version of that figure using .options; see http://pyviz.org/tutorial/01_Workflow_Introduction.html . Does that already achieve what you are after?

You can use .options(framewise=True).

Good question about the toolbar; I don't know if it's possible to have hover while also having no toolbar. I would have thought that showing the toolbar wouldn't matter, but if it does, that's presumably a Bokeh issue to raise on their site by finding one of their examples that uses hover and turning off the toolbar to see if hover still works.

jbednar commented 6 years ago

Oops; I forgot that I had changed 01_Workflow to use hvPlot rather than HoloViews directly. I think I started with the same version you did (HoloViews+%%opts), then I converted it to use HoloViews+.opts(), then to use HoloViews+.options(), and then finally to use hvPlot+.options() as is currently on the website. Unfortunately, I don't seem to have committed the intermediate version with HoloViews+.options(). You may find the HoloViews.opts() version useful; you can take the HoloViews bits from there and the .options from the current website...

Gordon90s commented 6 years ago

I made a version of that figure using .options; see http://pyviz.org/tutorial/01_Workflow_Introduction.html . Does that already achieve what you are after?

Unfortunately no, as there is no strem between the heatmap and the plot below (what I am after).

I tried the following your .opts example, but neither 1) nor 2) works :/. Though 1) works when I remove the colormap.

hmap_opts = dict(width=900, height=500, tools=['hover'], logz=True, invert_yaxis=False, labelled=[], 
                           toolbar='above', xaxis=None, fontsize={'xticks': '8pt', 'title': '12pt'},
                           colorbar=True)
curve_opts = dict(width=900, height=250, yaxis='left', xrotation=90, 
                  fontsize = {'xticks': '8pt', 'title': '12pt'}, toolbar=None)
curve_opts_style = dict(line_color='black')
dmap_opts = curve_opts

opts = {'HeatMap': {'plot': hmap_opts},
        'HeatMap': {'style': hmap_opts_style},
        'Curve':   {'plot': curve_opts},
        'Curve':   {'style': curve_opts_style},
        'DynamicMap': {'plot': dmap_opts}}

example = (hv_heatmap + hv_dmap).opts(opts).cols(1)
example

It seems `hmap_opts_style = dict(cmap='Inferno')' is what breaks the plot here? Don't really know where I should place it otherwize? (Tried a few combinations without success.)

Not sure where I should place .options(framewise=True). Behind hv_dmap?

I don't really mind the toolbar, it is just something I noticed. (Couldn't find anything over at Bokeh on a quick search.)

jbednar commented 6 years ago

Unfortunately no, as there is no stream between the heatmap and the plot below (what I am after).

Ah, I see. I only clicked on your second link, which is what my code replicates (linked axes between the heatmap and a summary curve), and you want what's in your first link, i.e. tapping to pull up a curve for that particular heatmap cell. I haven't tried converting that one to .options(), but it seems like doing so would be a good way to make progress here, because then we can help guide you to a PR that replaces the old one with a .options() version, which is something we want anyway and helps ensure that we can run and debug it ourselves.

I think you'd place the framewise options after whatever you want to be framewise-normalized, namely hv.Curve(), but I haven't actually tested framewise with .options().

I'd translate all this to .options before going further, as in my example, as we are trying desperately to forget .opts ever existed. :-)

Not sure what the issue with Inferno is; hard to follow that bit through this example.

Gordon90s commented 6 years ago

Ok, .options seems indeed to be easier to set up! I have tried the .options version and it works well, except for the streaming link. (Though the stream works with the same code + magics.)

As written above, this is my step up:

options = {'HeatMap': dict(width = 900, height = 500, tools=['hover'], logz = True, invert_yaxis = False, labelled = [], 
                           toolbar = 'above', xaxis = None, fontsize = {'xticks': '8pt', 'title': '12pt'},
                           cmap = 'Inferno', colorbar = True),
          'Curve': dict(width=900, height=250, line_color = 'black', xticks = 52, xrotation = 90, show_title = True,
                       fontsize = {'xticks': '8pt', 'title': '12pt'})}

example = (hv_heatmap + hv_dmap).cols(1).options(options)
example

Here the colormap also works well.

I also have the two versions on my GitHub: https://github.com/Gordon90s/holoviews_stream

Gordon90s commented 6 years ago

I have been given the solution. One has to add .options when creating the HeatMap and DynamicMap objects, then it works.

# Create heatmap
hv_heatmap = hv.HeatMap(hv_dataset).options(options)

and

# Create dynamic map
hv_dmap = hv.DynamicMap(tap_heatmap, kdims=[],
streams=[posxy]).options(options)

So there must be a bug somehwere!

I added the working version to my GitHub: https://github.com/Gordon90s/holoviews_stream

Now I'm happy with the result! Thanks for the amazing tool btw =).

philippjfr commented 4 years ago

So the website example along with every other example was updated to use .opts now so I don't think there are any issues left here.

github-actions[bot] commented 2 weeks ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.