holoviz / geoviews

Simple, concise geographical visualization in Python
http://geoviews.org
BSD 3-Clause "New" or "Revised" License
597 stars 77 forks source link

Geoviews does not support NaT with time zone aware datetimes #617

Open IPerezGonzalez opened 1 year ago

IPerezGonzalez commented 1 year ago

I have a dataset that includes a datetime variable that is time aware, and has NaT values. I can create a gv.Dataset and also a gv.Points geometry with it, but it fails on display -> which makes me think it can't actually manage it.

After looking at it updside down once and again, I've realized that this is because my dataset has time zone aware datetimes. With the example I give below, it is clear for me that a dataset with NaT works fine when datetimes are time zone naive, but not if they are time zone aware.

I am running Geoviews version 1.9.6

(Unfortunately I cannot get GitHub uploading the image when the time zone naive gv.Points feature is displayed, but it does display correctly.)

import datetime
import numpy as np
import pandas as pd
import geoviews as gv
gv.extension('bokeh')
import geoviews.tile_sources as gvts
from bokeh.models.tools import HoverTool

print('Geoviews version', gv.__version__)

lats = [50,50.2,51]
lons = [0.1,2,0.6]
dates = [datetime.datetime(2023,1,1,1),np.nan,datetime.datetime(2023,1,2,1)]
values = [2,3,-1]
source = gvts.CartoEco

data_df = pd.DataFrame({'lat':lats,'lon':lons,'naive_date':dates,'value':values})
data_df['aware_date'] = data_df['naive_date'].dt.tz_localize(tz='UTC')

cdims =  ['lon', 'lat']
vdims_naive = ['naive_date','value'] 
vdims_aware = ['aware_date','value'] 
dataset_naive = gv.Dataset(data_df,cdims,vdims_naive)
dataset_aware = gv.Dataset(data_df,cdims,vdims_aware)
naive = source*dataset_naive.to(gv.Points,cdims,vdims_naive)
aware = source*dataset_aware.to(gv.Points,cdims,vdims_aware)

Geoviews version 1.9.6

naive

(map with 3 points displays here...)

aware
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~/.conda/envs/notebooks/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj, include, exclude)
    968 
    969             if method is not None:
--> 970                 return method(include=include, exclude=exclude)
    971             return None
    972         else:

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/dimension.py in _repr_mimebundle_(self, include, exclude)
   1288         combined and returned.
   1289         """
-> 1290         return Store.render(self)
   1291 
   1292 

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/options.py in render(cls, obj)
   1423         data, metadata = {}, {}
   1424         for hook in hooks:
-> 1425             ret = hook(obj)
   1426             if ret is None:
   1427                 continue

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in pprint_display(obj)
    277     if not ip.display_formatter.formatters['text/plain'].pprint:
    278         return None
--> 279     return display(obj, raw_output=True)
    280 
    281 

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in display(obj, raw_output, **kwargs)
    245     elif isinstance(obj, (CompositeOverlay, ViewableElement)):
    246         with option_state(obj):
--> 247             output = element_display(obj)
    248     elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
    249         with option_state(obj):

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in wrapped(element)
    139         try:
    140             max_frames = OutputSettings.options['max_frames']
--> 141             mimebundle = fn(element, max_frames=max_frames)
    142             if mimebundle is None:
    143                 return {}, {}

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in element_display(element, max_frames)
    185         return None
    186 
--> 187     return render(element)
    188 
    189 

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in render(obj, **kwargs)
     66         renderer = renderer.instance(fig='png')
     67 
---> 68     return renderer.components(obj, **kwargs)
     69 
     70 

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/plotting/renderer.py in components(self, obj, fmt, comm, **kwargs)
    392 
    393         if embed or config.comms == 'default':
--> 394             return self._render_panel(plot, embed, comm)
    395         return self._render_ipywidget(plot)
    396 

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/plotting/renderer.py in _render_panel(self, plot, embed, comm)
    399         doc = Document()
    400         with config.set(embed=embed):
--> 401             model = plot.layout._render_model(doc, comm)
    402         if embed:
    403             return render_model(model, comm)

~/.conda/envs/notebooks/lib/python3.7/site-packages/panel/viewable.py in _render_model(self, doc, comm)
    506         if comm is None:
    507             comm = state._comm_manager.get_server_comm()
--> 508         model = self.get_root(doc, comm)
    509 
    510         if config.embed:

~/.conda/envs/notebooks/lib/python3.7/site-packages/panel/viewable.py in get_root(self, doc, comm, preprocess)
    557         """
    558         doc = init_doc(doc)
--> 559         root = self._get_model(doc, comm=comm)
    560         if preprocess:
    561             self._preprocess(root)

~/.conda/envs/notebooks/lib/python3.7/site-packages/panel/layout/base.py in _get_model(self, doc, root, parent, comm)
    144         if root is None:
    145             root = model
--> 146         objects = self._get_objects(model, [], doc, root, comm)
    147         props = dict(self._init_params(), objects=objects)
    148         model.update(**self._process_param_change(props))

~/.conda/envs/notebooks/lib/python3.7/site-packages/panel/layout/base.py in _get_objects(self, model, old_objects, doc, root, comm)
    129             else:
    130                 try:
--> 131                     child = pane._get_model(doc, root, model, comm)
    132                 except RerenderError:
    133                     return self._get_objects(model, current_objects[:i], doc, root, comm)

~/.conda/envs/notebooks/lib/python3.7/site-packages/panel/pane/holoviews.py in _get_model(self, doc, root, parent, comm)
    263             plot = self.object
    264         else:
--> 265             plot = self._render(doc, comm, root)
    266 
    267         plot.pane = self

~/.conda/envs/notebooks/lib/python3.7/site-packages/panel/pane/holoviews.py in _render(self, doc, comm, root)
    340                 kwargs['comm'] = comm
    341 
--> 342         return renderer.get_plot(self.object, **kwargs)
    343 
    344     def _cleanup(self, root: Model | None = None) -> None:

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/plotting/bokeh/renderer.py in get_plot(self_or_cls, obj, doc, renderer, **kwargs)
     68         combining the bokeh model with another plot.
     69         """
---> 70         plot = super().get_plot(obj, doc, renderer, **kwargs)
     71         if plot.document is None:
     72             plot.document = Document() if self_or_cls.notebook_context else curdoc()

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/plotting/renderer.py in get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
    234             init_key = tuple(v if d is None else d for v, d in
    235                              zip(plot.keys[0], defaults))
--> 236             plot.update(init_key)
    237         else:
    238             plot = obj

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/plotting/plot.py in update(self, key)
    934     def update(self, key):
    935         if len(self) == 1 and ((key == 0) or (key == self.keys[0])) and not self.drawn:
--> 936             return self.initialize_plot()
    937         item = self.__getitem__(key)
    938         self.traverse(lambda x: setattr(x, '_updated', True))

~/.conda/envs/notebooks/lib/python3.7/site-packages/geoviews/plotting/bokeh/plot.py in initialize_plot(self, ranges, plot, plots, source)
    105     def initialize_plot(self, ranges=None, plot=None, plots=None, source=None):
    106         opts = {} if isinstance(self, HvOverlayPlot) else {'source': source}
--> 107         fig = super(GeoPlot, self).initialize_plot(ranges, plot, plots, **opts)
    108         if self.geographic and self.show_bounds and not self.overlaid:
    109             from . import GeoShapePlot

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/plotting/bokeh/element.py in initialize_plot(self, ranges, plot, plots)
   2332             if self.tabs:
   2333                 subplot.overlaid = False
-> 2334             child = subplot.initialize_plot(ranges, plot, plots)
   2335             if isinstance(element, CompositeOverlay):
   2336                 # Ensure that all subplots are in the same state

~/.conda/envs/notebooks/lib/python3.7/site-packages/geoviews/plotting/bokeh/plot.py in initialize_plot(self, ranges, plot, plots, source)
    105     def initialize_plot(self, ranges=None, plot=None, plots=None, source=None):
    106         opts = {} if isinstance(self, HvOverlayPlot) else {'source': source}
--> 107         fig = super(GeoPlot, self).initialize_plot(ranges, plot, plots, **opts)
    108         if self.geographic and self.show_bounds and not self.overlaid:
    109             from . import GeoShapePlot

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/plotting/bokeh/element.py in initialize_plot(self, ranges, plot, plots, source)
   1380         self.handles['plot'] = plot
   1381 
-> 1382         self._init_glyphs(plot, element, ranges, source)
   1383         if not self.overlaid:
   1384             self._update_plot(key, plot, style_element)

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/plotting/bokeh/element.py in _init_glyphs(self, plot, element, ranges, source)
   1324         else:
   1325             style = self.style[self.cyclic_index]
-> 1326             data, mapping, style = self.get_data(element, ranges, style)
   1327             current_id = element._plot_id
   1328 

~/.conda/envs/notebooks/lib/python3.7/site-packages/geoviews/plotting/bokeh/plot.py in get_data(self, element, ranges, style)
    164     def get_data(self, element, ranges, style):
    165         if self._project_operation and self.geographic:
--> 166             element = self._project_operation(element, projection=self.projection)
    167         return super(GeoPlot, self).get_data(element, ranges, style)
    168 

~/.conda/envs/notebooks/lib/python3.7/site-packages/param/parameterized.py in __new__(class_, *args, **params)
   3656         inst = class_.instance()
   3657         inst.param._set_name(class_.__name__)
-> 3658         return inst.__call__(*args,**params)
   3659 
   3660     def __call__(self,*args,**kw):

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/operation.py in __call__(self, element, **kwargs)
    218         kwargs['link_dataset'] = self._propagate_dataset
    219         kwargs['link_inputs'] = self.p.link_inputs
--> 220         return element.apply(self, **kwargs)
    221 
    222 

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/accessors.py in pipelined_call(*args, **kwargs)
     41 
     42             try:
---> 43                 result = __call__(*args, **kwargs)
     44 
     45                 if not in_method:

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/accessors.py in __call__(self, apply_function, streams, link_inputs, link_dataset, dynamic, per_element, **kwargs)
    197             if hasattr(apply_function, 'dynamic'):
    198                 inner_kwargs['dynamic'] = False
--> 199             new_obj = apply_function(self._obj, **inner_kwargs)
    200             if (link_dataset and isinstance(self._obj, Dataset) and
    201                 isinstance(new_obj, Dataset) and new_obj._dataset is None):

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/operation.py in __call__(self, element, **kwargs)
    212             elif ((self._per_element and isinstance(element, Element)) or
    213                   (not self._per_element and isinstance(element, ViewableElement))):
--> 214                 return self._apply(element)
    215         elif 'streams' not in kwargs:
    216             kwargs['streams'] = self.p.streams

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/operation.py in _apply(self, element, key)
    139             if not in_method:
    140                 element._in_method = True
--> 141         ret = self._process(element, key)
    142         if hasattr(element, '_in_method') and not in_method:
    143             element._in_method = in_method

~/.conda/envs/notebooks/lib/python3.7/site-packages/geoviews/operation/projection.py in _process(self, element, key)
     38 
     39     def _process(self, element, key=None):
---> 40         return element.map(self._process_element, self.supported_types)
     41 
     42 

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/data/__init__.py in pipelined_fn(*args, **kwargs)
    197 
    198             try:
--> 199                 result = method_fn(*args, **kwargs)
    200                 if PipelineMeta.disable:
    201                     return result

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/data/__init__.py in map(self, *args, **kwargs)
   1205     @wraps(LabelledData.map)
   1206     def map(self, *args, **kwargs):
-> 1207         return super().map(*args, **kwargs)
   1208 
   1209     @wraps(LabelledData.relabel)

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/dimension.py in map(self, map_fn, specs, clone)
    698             return deep_mapped
    699         else:
--> 700             return map_fn(self) if applies else self
    701 
    702 

~/.conda/envs/notebooks/lib/python3.7/site-packages/geoviews/operation/projection.py in _process_element(self, element)
    167         mask = np.isfinite(coordinates[:, 0])
    168         dims = [d for d in element.dimensions() if d not in (xdim, ydim)]
--> 169         new_data = {k: v[mask] for k, v in element.columns(dims).items()}
    170         new_data[xdim.name] = coordinates[mask, 0]
    171         new_data[ydim.name] = coordinates[mask, 1]

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/data/__init__.py in pipelined_fn(*args, **kwargs)
    197 
    198             try:
--> 199                 result = method_fn(*args, **kwargs)
    200                 if PipelineMeta.disable:
    201                     return result

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/data/__init__.py in columns(self, dimensions)
   1154         else:
   1155             dimensions = [self.get_dimension(d, strict=True) for d in dimensions]
-> 1156         return OrderedDict([(d.name, self.dimension_values(d)) for d in dimensions])
   1157 
   1158 

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/data/__init__.py in <listcomp>(.0)
   1154         else:
   1155             dimensions = [self.get_dimension(d, strict=True) for d in dimensions]
-> 1156         return OrderedDict([(d.name, self.dimension_values(d)) for d in dimensions])
   1157 
   1158 

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/data/__init__.py in pipelined_fn(*args, **kwargs)
    197 
    198             try:
--> 199                 result = method_fn(*args, **kwargs)
    200                 if PipelineMeta.disable:
    201                     return result

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/data/__init__.py in dimension_values(self, dimension, expanded, flat)
   1091         """
   1092         dim = self.get_dimension(dimension, strict=True)
-> 1093         values = self.interface.values(self, dim, expanded, flat)
   1094         if dim.nodata is not None:
   1095             # Ensure nodata applies to boolean data in py2

~/.conda/envs/notebooks/lib/python3.7/site-packages/holoviews/core/data/pandas.py in values(cls, dataset, dim, expanded, flat, compute, keep_index)
    353         if data.dtype.kind == 'M' and getattr(data.dtype, 'tz', None):
    354             dts = [dt.replace(tzinfo=None) for dt in data.dt.to_pydatetime()]
--> 355             data = np.array(dts, dtype=data.dtype.base)
    356         if not expanded:
    357             return pd.unique(data)

ValueError: cannot convert float NaN to integer

:Overlay
   .WMTS.I   :WMTS   [Longitude,Latitude]
   .Points.I :Points   [lon,lat]   (aware_date,value)