holoviz / holoviews

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

Difficulties creating a holomap of composed rasterized images #3741

Closed Jacob-Barhak closed 5 years ago

Jacob-Barhak commented 5 years ago

The code below should generate a holomap of holoviews rasterized images bunched together using + operators.

import numpy
import matplotlib
import bokeh
from matplotlib.cm import viridis, plasma, gist_ncar, gist_stern, PuBu, Blues
import holoviews
from holoviews.operation.datashader import rasterize
holoviews.extension('bokeh')
#AggregationMethods = [ 'mean','max','mode','std']
AggregationMethods = [ 'mean','max']
Hows = [ 'linear', 'bilinear' ]
# uncomment the next line to see a different error message
#Hows = [ 'nearest', 'linear' ]

OutputImageFilePattern = 'TestImage_%s_%i_%s_%s' 
ImageIntensityScale = 100000
PlotHeight = 100
PlotSizes = [500]
for library in ['numpy', 'matplotlib', 'holoviews']:
    print ( library + ' ' + str(eval(library + '.__version__')))

def PlotCluster(MatrixClusters, MatrixDistances, OutputFile, How, AggregationMethod, PlotSize, PlotHeight = PlotHeight):
    "plot the cluster"
    DistBarData = holoviews.Image(MatrixDistances)
    ShadedDistBar = rasterize (DistBarData, aggregator = AggregationMethod, interpolation = How, dynamic  = True).opts(cmap=PuBu, xaxis=None, yaxis=None, height=PlotHeight, width=PlotSize, tools=['hover']) 
    ClusterBarData = holoviews.Image(MatrixClusters)
    ShadedClusterBar = rasterize (ClusterBarData, aggregator = AggregationMethod, interpolation = How, dynamic  = True).opts(cmap=PuBu, xaxis=None, yaxis=None, height=PlotHeight, width=PlotSize, tools=['hover']) 
    Merged = (ShadedDistBar + ShadedClusterBar).cols(1)
    return Merged

HoloMapDict = {}
numpy.random.seed(1)
UnitDistanceVec = numpy.random.rand(25000)
UnitClusterVec = (numpy.random.rand(25000)*100).astype(int)  
print (UnitDistanceVec[:10])    
print (UnitClusterVec[:10])
BarSize = (PlotHeight,1)
UnitClusterVecRearranged = numpy.array(UnitClusterVec)
MatrixClusters = numpy.tile( UnitClusterVecRearranged, BarSize).astype(int)  
UnitDistanceVecsRarranged = numpy.array(UnitDistanceVec)
UnitDistanceVecAsInt = (numpy.array(UnitDistanceVecsRarranged)/max(UnitClusterVec)*ImageIntensityScale).astype(int)
MatrixDistances = numpy.tile( UnitDistanceVecAsInt, BarSize)

for AggregationMethod in AggregationMethods:
    for How in Hows:
        for PlotSize in PlotSizes:
            OutputFile = OutputImageFilePattern%('',PlotSize,How,AggregationMethod)
            Merged = PlotCluster(MatrixClusters,MatrixDistances, OutputFile, How, AggregationMethod, PlotSize)
            HoloMapDict[(AggregationMethod, How )] = Merged
Combined = holoviews.HoloMap(HoloMapDict, kdims=['Aggregation', 'interpolation'])
holoviews.save(Combined, 'combined', 'html')

The code above does not work as expected and generates the following printout:

numpy 1.16.3
matplotlib 3.0.3
holoviews 1.12.1
[4.17022005e-01 7.20324493e-01 1.14374817e-04 3.02332573e-01
 1.46755891e-01 9.23385948e-02 1.86260211e-01 3.45560727e-01
 3.96767474e-01 5.38816734e-01]
[91 86 65 77 59 47 48 45  8 87]
WARNING:param.Warning: Nesting Layouts within a HoloMap makes it difficult to access your data or control how it appears; we recommend calling .collate() on the HoloMap in order to follow the recommended nesting structure shown in the Composing Data user guide (https://goo.gl/2YS8LJ)
Traceback (most recent call last):
  File "Test2.py", line 50, in <module>
    holoviews.save(Combined, 'combined', 'html')
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\util\__init__.py", line 749, in save
    return renderer_obj.save(obj, filename, fmt=fmt)
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\plotting\renderer.py", line 549, in save
    plot = self_or_cls.get_plot(obj)
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\plotting\bokeh\renderer.py", line 134, in get_plot
    plot = super(BokehRenderer, self_or_cls).get_plot(obj, renderer, **kwargs)
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\plotting\renderer.py", line 190, in get_plot
    obj = collate(obj)
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\plotting\util.py", line 75, in collate
    return obj.collate()
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\core\spaces.py", line 349, in collate
    drop_constant=drop_constant)()
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\core\element.py", line 468, in __call__
    dict(constant_keys))
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\core\element.py", line 519, in _add_dimensions
    new_item[k] = self._add_dimensions(v, dims[::-1], constant_keys)
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\core\element.py", line 508, in _add_dimensions
    new_item = new_item.add_dimension(dim, 0, val)
  File "C:\Users\Work\Anaconda2\envs\py3\lib\site-packages\holoviews\core\spaces.py", line 1804, in add_dimension
    raise NotImplementedError('Cannot add dimensions to a DynamicMap, '
NotImplementedError: Cannot add dimensions to a DynamicMap, cast to a HoloMap first.

I tired using collate on the holomap object created after creating the holomap object and adding data directly to the object, yet I am getting the same error.

I need a solution to have combo boxes that will alow me to switch between different aggregation and interpolation method - if holoviews does not implement it, can I do it with panel? My alternative is using tab layout in panel - however, it would be nice having it with the holoviews combo boxes if possible. In any case, I want it to be a static html page without the need to run a server. Hopefully iy is possible.

jlstevens commented 5 years ago

The rendering code path is hitting collate which can be avoided if the hierarchy you build conforms to nesting structure described here. It should be possible to reorganize things so you don't hit collate but still offer the same functionality.

Jacob-Barhak commented 5 years ago

Thanks for the comment, yet the layout you pointed to shows raster images and holomap in different paths. How can I create a few combo boxes that allow me to switch between rasterized images? I assumed my solution was to use holoviews holomap. Can I do it with panel instead and still be able to generate a static html page that does not need a server to allow changing a combo box to switch between the rasterized images?
I know I should be able to do it with panel switching between html pages generated for each rasterized image using a tab layoout object. Yet since its multidimensional data, I want to use the combo boxes. If panel should work, I will switch to it since holoviews is moving towards integrating with it, yet I want to make sure I can do it before investing the effort.

jbednar commented 5 years ago

Apart from adding collate, I think all you need is dynamic=False, which isn't a problem if you're exporting to HTML anyway. The underlying problem is that rasterize creates a DynamicMap if dynamic=True, and you can't simply collate and reorganize a dynamic process like that. Instead, this code should be rewritten to create one big DynamicMap with non-dynamic components, but that's a complete reorganization, and this should work for now since you don't need the result to be dynamic anyway...

import numpy
import matplotlib
import bokeh
from matplotlib.cm import viridis, plasma, gist_ncar, gist_stern, PuBu, Blues
import holoviews
from holoviews.operation.datashader import rasterize
holoviews.extension('bokeh')
#AggregationMethods = [ 'mean','max','mode','std']
AggregationMethods = [ 'mean','max']
Hows = [ 'linear', 'bilinear' ]
# uncomment the next line to see a different error message
#Hows = [ 'nearest', 'linear' ]

OutputImageFilePattern = 'TestImage_%s_%i_%s_%s' 
ImageIntensityScale = 100000
PlotHeight = 100
PlotSizes = [500]
for library in ['numpy', 'matplotlib', 'holoviews']:
    print ( library + ' ' + str(eval(library + '.__version__')))

def PlotCluster(MatrixClusters, MatrixDistances, OutputFile, How, AggregationMethod, PlotSize, PlotHeight = PlotHeight):
    "plot the cluster"
    DistBarData = holoviews.Image(MatrixDistances)
    ShadedDistBar = rasterize (DistBarData, aggregator = AggregationMethod, interpolation = How, dynamic  = False).opts(cmap=PuBu, xaxis=None, yaxis=None, height=PlotHeight, width=PlotSize, tools=['hover'], axiswise=True, framewise=True) 
    ClusterBarData = holoviews.Image(MatrixClusters)
    ShadedClusterBar = rasterize (ClusterBarData, aggregator = AggregationMethod, interpolation = How, dynamic  = False).opts(cmap=PuBu, xaxis=None, yaxis=None, height=PlotHeight, width=PlotSize, tools=['hover'], axiswise=True, framewise=True) 
    Merged = (ShadedDistBar + ShadedClusterBar).cols(1)
    return Merged

HoloMapDict = {}
numpy.random.seed(1)
UnitDistanceVec = numpy.random.rand(25000)
UnitClusterVec = (numpy.random.rand(25000)*100).astype(int)  
print (UnitDistanceVec[:10])    
print (UnitClusterVec[:10])
BarSize = (PlotHeight,1)
UnitClusterVecRearranged = numpy.array(UnitClusterVec)
MatrixClusters = numpy.tile( UnitClusterVecRearranged, BarSize).astype(int)  
UnitDistanceVecsRarranged = numpy.array(UnitDistanceVec)
UnitDistanceVecAsInt = (numpy.array(UnitDistanceVecsRarranged)/max(UnitClusterVec)*ImageIntensityScale).astype(int)
MatrixDistances = numpy.tile( UnitDistanceVecAsInt, BarSize)

for AggregationMethod in AggregationMethods:
    for How in Hows:
        for PlotSize in PlotSizes:
            OutputFile = OutputImageFilePattern%('',PlotSize,How,AggregationMethod)
            Merged = PlotCluster(MatrixClusters,MatrixDistances, OutputFile, How, AggregationMethod, PlotSize)
            HoloMapDict[(AggregationMethod, How )] = Merged

Combined = holoviews.HoloMap(HoloMapDict, kdims=['Aggregation', 'interpolation']).collate()
holoviews.save(Combined, 'combined', 'html')
jbednar commented 5 years ago

You can also use Panel for this, which would be very straightforward.

Jacob-Barhak commented 5 years ago

Thanks James, Eliminating Dynamic does fix the code so it will work, yet for some reason the saved holomap does not work as intended - with drop down boxes . The html file generated on my machine just shows the image for one holomap bin without the drop down menus one usually sees in a holomap - is this related to the fact that those are rasterized images? Or is this related to something else - I really want to have those drop down boxes if easy.

jbednar commented 5 years ago

The widgets are there in Jupyter: image @jlstevens, is there anything special that needs to be done for the widgets to be in the saved html file?

Jacob-Barhak commented 5 years ago

Sorry James, This is very odd, the html generated also has other artifacts like not following the correct color or size. Here is an image of what I see: image

code versions I am using are: numpy 1.16.3 matplotlib 3.0.3 holoviews 1.12.1 Also: python 3.7.3 bokeh 1.1.0

If this is another problem, I'll be happy to open another issue. I tried panel tabs and it works nice, yet if possible, I would like this beautiful drop down menu functionality holoviews can offer.

jbednar commented 5 years ago

Oh, the loss of options is just due to a debugging bit I had in the above code; now fixed (removed "Merged = holoviews.Image(MatrixDistances) + holoviews.Image(MatrixClusters)"). And Panel can give drop-down menus as well; should be fine to use either. I'd guess there's some JS missing from the HTML export, which is why I'm hoping @jlstevens or @philippjfr can investigate.

Jacob-Barhak commented 5 years ago

If this helps locate the issue, note that using panel save function of the holoviews object works well and shows the drop down menu. So it seems I have a solution. Thanks for the rapid responses.

Jacob-Barhak commented 5 years ago

One correction, when using panel to show the holoviews object, the drop down menus are shown, yet they are populated with the Cartesian product of all dimensions and selecting a value from those drop down boxes does not change the image. It seems that there is something not working even when this holoviews object is viewed when panel is generating the html.

sameerCoder commented 5 years ago

Hi , Is there any other way in which i keep dynamic =True and still plot using Holomap and get the dynamic data available in each plot.

philippjfr commented 5 years ago

Is there any other way in which i keep dynamic =True and still plot using Holomap and get the dynamic data available in each plot.

No, a plot is either dynamic or isn't.

sameerCoder commented 5 years ago

Hi, My Holomap is working fine when i am making dynamic=False i.e :-

def plotthis(datetime):
    return rasterize(gv.TriMesh((tri_sub,gv.Points(verts)) ),**dynamic=False**).options(**opts)

allplot={k:plotthis(k)for k in perdelta(strt, strt + timedelta(days=1), timedelta(hours=9))}
hmap = hv.HoloMap(allplot, kdims='date')
hmap

Then Holomap is be working fine but i lost the dynamic property of plot i.e while zoomin no update of data. so i am thinking to plot using Holomap in which dyanmic data also work . :)

philippjfr commented 5 years ago

There is no way a HoloMap will ever support dynamic updates, that's the whole point of a HoloMap. If you want to have the dynamic features apply rasterize to the HoloMap, i.e.:

def plotthis(datetime):
    return gv.TriMesh((tri_sub,gv.Points(verts)))

allplot={k:plotthis(k)for k in perdelta(strt, strt + timedelta(days=1), timedelta(hours=9))}
hmap = hv.HoloMap(allplot, kdims='date')
rasterize(hmap).options(**opts)
sameerCoder commented 5 years ago

@philippjfr Thanks alot for replying so fast. After using the suggested code:- `
return gv.TriMesh((tri_sub,gv.Points(verts))).options(**opts)

allplot={k:plotthis(k)for k in perdelta(strt, strt + timedelta(days=1), timedelta(hours=9))}

hmap = hv.HoloMap(allplot, kdims='date') tt=rasterize(hmap) tt I am getting keyError KeyError: (numpy.datetime64('2019-07-03T00:00:00.000000000'),)

:DynamicMap [date] `

sameerCoder commented 5 years ago

Hi , Or i should do using Panel instead of Holomap for getting dynamic data ?

Jacob-Barhak commented 5 years ago

This specific problem was solved by James a long time ago - I should have been closed. So I am closing it so it wont bother the developers. There was another issue there with saving the html with the widgets which is related to another issue I started (https://github.com/pyviz/holoviews/issues/3849) that deals with saving.

To close this problem I am reiterating the code that will work.

import numpy
import matplotlib
import bokeh
from matplotlib.cm import viridis, plasma, gist_ncar, gist_stern, PuBu, Blues
import holoviews
from holoviews.operation.datashader import rasterize
holoviews.extension('bokeh')
#AggregationMethods = [ 'mean','max','mode','std']
AggregationMethods = [ 'mean','max']
Hows = [ 'linear', 'bilinear' ]
# uncomment the next line to see a different error message
#Hows = [ 'nearest', 'linear' ]

OutputImageFilePattern = 'TestImage_%s_%i_%s_%s' 
ImageIntensityScale = 100000
PlotHeight = 100
PlotSizes = [500]
for library in ['numpy', 'matplotlib', 'holoviews']:
    print ( library + ' ' + str(eval(library + '.__version__')))

def PlotCluster(MatrixClusters, MatrixDistances, OutputFile, How, AggregationMethod, PlotSize, PlotHeight = PlotHeight):
    "plot the cluster"
    DistBarData = holoviews.Image(MatrixDistances)
    ShadedDistBar = rasterize (DistBarData, aggregator = AggregationMethod, interpolation = How, dynamic  = False).opts(cmap=PuBu, xaxis=None, yaxis=None, height=PlotHeight, width=PlotSize, tools=['hover'], axiswise=True, framewise=True) 
    ClusterBarData = holoviews.Image(MatrixClusters)
    ShadedClusterBar = rasterize (ClusterBarData, aggregator = AggregationMethod, interpolation = How, dynamic  = False).opts(cmap=PuBu, xaxis=None, yaxis=None, height=PlotHeight, width=PlotSize, tools=['hover'], axiswise=True, framewise=True) 
    Merged = (ShadedDistBar + ShadedClusterBar).cols(1)
    return Merged

HoloMapDict = {}
numpy.random.seed(1)
UnitDistanceVec = numpy.random.rand(25000)
UnitClusterVec = (numpy.random.rand(25000)*100).astype(int)  
print (UnitDistanceVec[:10])    
print (UnitClusterVec[:10])
BarSize = (PlotHeight,1)
UnitClusterVecRearranged = numpy.array(UnitClusterVec)
MatrixClusters = numpy.tile( UnitClusterVecRearranged, BarSize).astype(int)  
UnitDistanceVecsRarranged = numpy.array(UnitDistanceVec)
UnitDistanceVecAsInt = (numpy.array(UnitDistanceVecsRarranged)/max(UnitClusterVec)*ImageIntensityScale).astype(int)
MatrixDistances = numpy.tile( UnitDistanceVecAsInt, BarSize)

for AggregationMethod in AggregationMethods:
    for How in Hows:
        for PlotSize in PlotSizes:
            OutputFile = OutputImageFilePattern%('',PlotSize,How,AggregationMethod)
            Merged = PlotCluster(MatrixClusters,MatrixDistances, OutputFile, How, AggregationMethod, PlotSize)
            HoloMapDict[(AggregationMethod, How )] = Merged

Combined = holoviews.HoloMap(HoloMapDict, kdims=['Aggregation', 'interpolation']).collate()
holoviews.save(Combined, 'combined.html')

Thanks again.

github-actions[bot] commented 1 month 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.