Repository: holoviz/hvplot

Issue:
Setting global default plot options
I would like to set default options globally for the plots generated by hvplots.

Normally in holoviews I would do this to set defaults for every plot:

    import holoviews as hv
    from holoviews import opts
    hv.extension('bokeh')

    # Set default options here
    opts.defaults(opts.Curve( height=400, width=900 ,show_grid=True))


Now that I'm using hvplots the import has become the following for xarray based plotting

    import xarray as xr
    import hvplot.xarray

If I have an xarray dataset and I want to plot one of the dimensions then I use this

    ds.myDims.hvplot()

However this gives a plot with a default width and no grid lines. To add this I need to specify the options with the plot.

    ds.myDims.hvplot(height=400, width=900,grid=True)

Ideally I'd like to set the height, width, grid etc, colours etc globally, like I can in holoviews, so I don't have to specify it for every plot.





Discussion:

Resolving patch:
diff --git a/hvplot/converter.py b/hvplot/converter.py
--- a/hvplot/converter.py
+++ b/hvplot/converter.py
@@ -118,7 +118,7 @@ class HoloViewsConverter(object):
     rot: number
         Rotates the axis ticks along the x-axis by the specified
         number of degrees.
-    shared_axes (default=False): boolean
+    shared_axes (default=True): boolean
         Whether to link axes between plots
     title (default=''): str
         Title for the plot
@@ -208,7 +208,7 @@ class HoloViewsConverter(object):
                      'yaxis', 'xformatter', 'yformatter', 'xlabel', 'ylabel',
                      'clabel', 'padding']
 
-    _style_options = ['color', 'alpha', 'colormap', 'fontsize', 'c']
+    _style_options = ['color', 'alpha', 'colormap', 'fontsize', 'c', 'cmap']
 
     _op_options = ['datashade', 'rasterize', 'x_sampling', 'y_sampling',
                    'aggregator']
@@ -252,26 +252,33 @@ class HoloViewsConverter(object):
                          "bottom_right", "right", "left", "top",
                          "bottom")
 
+    _default_plot_opts = {
+        'logx': False, 'logy': False, 'show_legend': True, 'legend_position': 'right',
+        'show_grid': False, 'responsive': False, 'shared_axes': True}
+
+    _default_cmaps = {
+        'linear': 'kbc_r',
+        'categorical': 'Category10',
+        'cyclic': 'colorwheel',
+        'diverging': 'coolwarm'
+    }
+
     def __init__(self, data, x, y, kind=None, by=None, use_index=True,
                  group_label='Variable', value_label='value',
                  backlog=1000, persist=False, use_dask=False,
                  crs=None, fields={}, groupby=None, dynamic=True,
-                 width=None, height=None, shared_axes=True,
-                 grid=False, legend=True, rot=None, title=None,
-                 xlim=None, ylim=None, clim=None, xticks=None, yticks=None,
-                 logx=False, logy=False, loglog=False, hover=True,
+                 grid=None, legend=None, rot=None, title=None,
+                 xlim=None, ylim=None, clim=None,
+                 logx=None, logy=None, loglog=None, hover=True,
                  subplots=False, label=None, invert=False,
                  stacked=False, colorbar=None, fontsize=None,
-                 colormap=None, datashade=False, rasterize=False,
+                 datashade=False, rasterize=False,
                  row=None, col=None, figsize=None, debug=False,
-                 xaxis=True, yaxis=True, framewise=True, aggregator=None,
-                 projection=None, global_extent=False, geo=False,
-                 precompute=False, flip_xaxis=False, flip_yaxis=False,
+                 framewise=True, aggregator=None,
+                 projection=None, global_extent=None, geo=False,
+                 precompute=False, flip_xaxis=None, flip_yaxis=None,
                  dynspread=False, hover_cols=[], x_sampling=None,
-                 y_sampling=None, project=False, xlabel=None, ylabel=None,
-                 clabel=None, xformatter=None, yformatter=None, tools=[],
-                 padding=None, responsive=False, min_width=None,
-                 min_height=None, max_height=None, max_width=None,
+                 y_sampling=None, project=False, tools=[],
                  attr_labels=True, coastline=False, tiles=False,
                  sort_date=True, **kwds):
 
@@ -337,75 +344,64 @@ def __init__(self, data, x, y, kind=None, by=None, use_index=True,
 
         # Process options
         self.stacked = stacked
-        style_opts, plot_opts, kwds = self._process_style(colormap, kwds)
+        self._style_opts, kwds = self._process_style(kwds)
+
+        plot_opts = {**self._default_plot_opts,
+                     **self._process_plot(self._style_opts.get('color'))}
+        if xlim is not None:
+            plot_opts['xlim'] = tuple(xlim)
+        if ylim is not None:
+            plot_opts['ylim'] = tuple(ylim)
+
         self.invert = invert
-        plot_opts['logx'] = logx or loglog
-        plot_opts['logy'] = logy or loglog
-        plot_opts['show_grid'] = grid
-        plot_opts['shared_axes'] = shared_axes
-        plot_opts['show_legend'] = bool(legend)
+        if loglog is not None:
+            logx = logx or loglog
+            logy = logy or loglog
+        if logx is not None:
+            plot_opts['logx'] = logx
+        if logy is not None:
+            plot_opts['logy'] = logy
+
+        if grid is not None:
+            plot_opts['show_grid'] = grid
+
+        if legend is not None:
+            plot_opts['show_legend'] = bool(legend)
 
         if legend in self._legend_positions:
             plot_opts['legend_position'] = legend
-        elif legend in (True, False, None):
-            plot_opts['legend_position'] = 'right'
-        else:
+        elif legend not in (True, False, None):
             raise ValueError('The legend option should be a boolean or '
                              'a valid legend position (i.e. one of %s).'
                              % list(self._legend_positions))
 
-        if xticks:
-            plot_opts['xticks'] = xticks
-        if yticks:
-            plot_opts['yticks'] = yticks
-        if not xaxis:
-            plot_opts['xaxis'] = None
-        elif xaxis != True:
-            plot_opts['xaxis'] = xaxis
-        if not yaxis:
-            plot_opts['yaxis'] = None
-        elif yaxis != True:
-            plot_opts['yaxis'] = yaxis
-        if xlabel is not None:
-            plot_opts['xlabel'] = xlabel
-        if ylabel is not None:
-            plot_opts['ylabel'] = ylabel
-        if clabel is not None:
-            plot_opts['clabel'] = clabel
-        if xlim is not None:
-            plot_opts['xlim'] = tuple(xlim)
-        if ylim is not None:
-            plot_opts['ylim'] = tuple(ylim)
-        if padding is not None:
-            plot_opts['padding'] = padding
-        if xformatter is not None:
-            plot_opts['xformatter'] = xformatter
-        if yformatter is not None:
-            plot_opts['yformatter'] = yformatter
+        plotwds = ['xticks', 'yticks', 'xlabel', 'ylabel', 'clabel',
+                   'padding', 'xformatter', 'yformatter',
+                   'height', 'width',
+                   'min_width', 'min_height', 'max_width', 'max_height',
+                   'fontsize', 'responsive', 'shared_axes']
+        for plotwd in plotwds:
+            if plotwd in kwds:
+                plot_opts[plotwd] = kwds.pop(plotwd)
+
+        for axis_name in ['xaxis', 'yaxis']:
+            if axis_name in kwds:
+                axis = kwds.pop(axis_name)
+                if not axis:
+                    plot_opts[axis_name] = None
+                elif axis != True:
+                    plot_opts[axis_name] = axis
+                elif axis_name in plot_opts:
+                    plot_opts.pop(axis_name, None)
+
         if flip_xaxis:
             plot_opts['invert_xaxis'] = True
         if flip_yaxis:
             plot_opts['invert_yaxis'] = True
-        if responsive:
-            if width:
-                plot_opts['width'] = width
-            if height:
-                plot_opts['height'] = height
-        else:
-            plot_opts['width'] = width or 700
-            plot_opts['height'] = height or 300
-        if min_width is not None:
-            plot_opts['min_width'] = min_width
-        if min_height is not None:
-            plot_opts['min_height'] = min_height
-        if max_width is not None:
-            plot_opts['max_width'] = max_width
-        if max_height is not None:
-            plot_opts['max_height'] = max_height
-        if responsive:
-            plot_opts['responsive'] = responsive
-        if fontsize:
-            plot_opts['fontsize'] = fontsize
+        if not plot_opts.get('responsive', True):
+            plot_opts['width'] = plot_opts.get('width', 700)
+            plot_opts['height'] = plot_opts.get('height', 300)
+
         if isinstance(colorbar, bool):
             plot_opts['colorbar'] = colorbar
         elif self.kind in self._colorbar_types:
@@ -418,7 +414,7 @@ def __init__(self, data, x, y, kind=None, by=None, use_index=True,
             axis = 'yrotation' if invert else 'xrotation'
             plot_opts[axis] = rot
 
-        tools = list(tools)
+        tools = list(tools) or list(plot_opts.get('tools', []))
         if hover and not any(t for t in tools if isinstance(t, HoverTool)
                              or t == 'hover'):
             tools.append('hover')
@@ -430,21 +426,12 @@ def __init__(self, data, x, y, kind=None, by=None, use_index=True,
             plot_opts['projection'] = process_crs(projection)
         if title is not None:
             plot_opts['title_format'] = title
+
         self._plot_opts = plot_opts
-        self._overlay_opts = {k: v for k, v in plot_opts.items()
+        self._overlay_opts = {k: v for k, v in self._plot_opts.items()
                               if k in OverlayPlot.params()}
-        options = Store.options(backend='bokeh')
-        el_type = self._kind_mapping[self.kind].__name__
-        if el_type in options:
-            style = options[el_type].groups['style']
-            cycled_opts = [k for k, v in style.kwargs.items() if isinstance(v, Cycle)]
-            for opt in cycled_opts:
-                color = style_opts.get('color', None)
-                if color is None:
-                    color = process_cmap(colormap or 'Category10', categorical=True)
-                style_opts[opt] = Cycle(values=color) if isinstance(color, list) else color
-        self._style_opts = style_opts
-        self._norm_opts = {'framewise': framewise, 'axiswise': not shared_axes}
+
+        self._norm_opts = {'framewise': framewise, 'axiswise': not plot_opts.get('shared_axes')}
         self.kwds = kwds
 
         # Process dimensions and labels
@@ -668,34 +655,51 @@ def _process_data(self, kind, data, x, y, by, groupby, row, col,
                                    'because {e}; suppress this warning '
                                    'with attr_labels=False.'.format(e=e))
 
-    def _process_style(self, colormap, kwds):
-        plot_options = {}
+    def _process_plot(self, color):
+        kind = self.kind
+        options = Store.options(backend='bokeh')
+        elname = self._kind_mapping[kind].__name__
+        plot_opts = options[elname].groups['plot'].options if elname in options else {}
+
+        if kind.startswith('bar'):
+            plot_opts['stacked'] = self.stacked
+
+        # Color
+        if color is not None and 'colorbar' not in plot_opts:
+            if 'c' in self._kind_options.get(kind, []) and (color in self.variables):
+                if self.data[color].dtype.kind not in 'OSU':
+                    plot_opts['colorbar'] = True
+
+        return plot_opts
+
+    def _process_style(self, kwds):
         kind = self.kind
         eltype = self._kind_mapping[kind]
-        if eltype in Store.registry['bokeh']:
-            valid_opts = Store.registry['bokeh'][eltype].style_opts
+        registry =  Store.registry['bokeh']
+
+        if eltype in registry:
+            valid_opts = registry[eltype].style_opts
         else:
             valid_opts = []
 
         for opt in valid_opts:
-            if opt not in kwds or not isinstance(kwds[opt], list) or opt == 'cmap':
+            if opt not in kwds or not isinstance(kwds[opt], list) or opt in ['cmap', 'colormap']:
                 continue
             kwds[opt] = Cycle(kwds[opt])
 
-        style_opts = {kw: kwds[kw] for kw in list(kwds) if kw in valid_opts}
-
         # Process style options
-        if 'cmap' in kwds and colormap:
+        options = Store.options(backend='bokeh')
+        elname = eltype.__name__
+        style = options[elname].groups['style'].kwargs if elname in options else {}
+        style_opts = {k: v for k, v in style.items() if not isinstance(v, Cycle) and k != 'cmap'}
+        style_opts.update(**{k: v for k, v in kwds.items() if k in valid_opts})
+
+        # Color
+        if 'cmap' in kwds and 'colormap' in kwds:
             raise TypeError("Only specify one of `cmap` and `colormap`.")
-        elif 'cmap' in kwds:
-            cmap = kwds.pop('cmap')
-        else:
-            cmap = colormap
 
-        if kind.startswith('bar'):
-            plot_options['stacked'] = self.stacked
+        cmap = kwds.pop('cmap', kwds.pop('colormap', None))
 
-        # Color
         if 'color' in kwds or 'c' in kwds:
             color = kwds.pop('color', kwds.pop('c', None))
             if isinstance(color, (np.ndarray, pd.Series)):
@@ -707,9 +711,20 @@ def _process_style(self, colormap, kwds):
                 style_opts['color'] = color
                 if 'c' in self._kind_options.get(kind, []) and (color in self.variables):
                     if self.data[color].dtype.kind in 'OSU':
-                        cmap = cmap or 'Category10'
+                        cmap = cmap or self._default_cmaps['categorical']
                     else:
-                        plot_options['colorbar'] = True
+                        cmap = cmap or self._default_cmaps['linear']
+
+        if cmap in self._default_cmaps:
+            cmap = self._default_cmaps[cmap]
+
+        if cmap is not None:
+            style_opts['cmap'] = cmap
+
+        color = style_opts.get('color', process_cmap(cmap or self._default_cmaps['categorical'], categorical=True))
+        for k, v in style.items():
+            if isinstance(v, Cycle):
+                style_opts[k] = Cycle(values=color) if isinstance(color, list) else color
 
         # Size
         if 'size' in kwds or 's' in kwds:
@@ -728,15 +743,7 @@ def _process_style(self, colormap, kwds):
         if 'marker' in kwds and 'marker' in self._kind_options[self.kind]:
             style_opts['marker'] = kwds.pop('marker')
 
-        # Alpha
-        if 'marker' in kwds:
-            style_opts['alpha'] = kwds.pop('alpha')
-
-        if cmap:
-            style_opts['cmap'] = cmap
-
-        return style_opts, plot_options, kwds
-
+        return style_opts, kwds
 
     def _validate_kwds(self, kwds):
         kind_opts = self._kind_options.get(self.kind, [])
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -123,6 +123,7 @@ def package_assets(example_path):
 
 install_requires = [
     'bokeh >=1.0.0',
+    'colorcet',
     'holoviews >=1.11.0',
     'pandas'
 ]


Test patch:
diff --git a/hvplot/tests/testdefaults.py b/hvplot/tests/testdefaults.py
deleted file mode 100644
--- a/hvplot/tests/testdefaults.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import pandas as pd
-from hvplot import hvPlot, patch
-from holoviews import Store, Scatter
-from holoviews.element.comparison import ComparisonTestCase
-
-
-class TestDefaults(ComparisonTestCase):
-
-    def setUp(self):
-        patch('pandas')
-        self.df = pd.DataFrame([[1, 2], [3, 4], [5, 6]], columns=['x', 'y'])
-
-    def test_define_default_options(self):
-        hvplot = hvPlot(self.df, width=42, height=42)
-        curve = hvplot(y='y')
-        opts = Store.lookup_options('bokeh', curve, 'plot')
-        self.assertEqual(opts.options.get('width'), 42)
-        self.assertEqual(opts.options.get('height'), 42)
-
-    def test_define_custom_method(self):
-        hvplot = hvPlot(self.df, {'custom_scatter': {'width': 42, 'height': 42}})
-        custom_scatter = hvplot.custom_scatter(y='y')
-        scatter = hvplot.scatter(y='y')
-        custom_opts = Store.lookup_options('bokeh', custom_scatter, 'plot')
-        opts = Store.lookup_options('bokeh', scatter, 'plot')
-        self.assertEqual(custom_opts.options.get('width'), 42)
-        self.assertEqual(custom_opts.options.get('height'), 42)
-        self.assertNotEqual(opts.options.get('width'), 42)
-        self.assertNotEqual(opts.options.get('height'), 42)
-
-    def test_define_customize_method(self):
-        hvplot = hvPlot(self.df, {'scatter': {'width': 42, 'height': 42}})
-        custom_scatter = hvplot.scatter(y='y')
-        curve = hvplot.line(y='y')
-        custom_opts = Store.lookup_options('bokeh', custom_scatter, 'plot')
-        opts = Store.lookup_options('bokeh', curve, 'plot')
-        self.assertEqual(custom_opts.options.get('width'), 42)
-        self.assertEqual(custom_opts.options.get('height'), 42)
-        self.assertNotEqual(opts.options.get('width'), 42)
-        self.assertNotEqual(opts.options.get('height'), 42)
-
-    def test_attempt_to_override_kind_on_method(self):
-        hvplot = hvPlot(self.df, {'scatter': {'kind': 'line'}})
-        self.assertIsInstance(hvplot.scatter(y='y'), Scatter)
diff --git a/hvplot/tests/testoptions.py b/hvplot/tests/testoptions.py
--- a/hvplot/tests/testoptions.py
+++ b/hvplot/tests/testoptions.py
@@ -1,10 +1,12 @@
-from unittest import SkipTest
+from unittest import SkipTest, expectedFailure
 
 from parameterized import parameterized
 
 from holoviews import Store
+from holoviews.core.options import Options, OptionTree
 from holoviews.element.comparison import ComparisonTestCase
 from hvplot import patch
+import holoviews as hv
 
 
 class TestOptions(ComparisonTestCase):
@@ -14,10 +16,20 @@ def setUp(self):
             import pandas as pd
         except:
             raise SkipTest('Pandas not available')
+        self.backend = 'bokeh'
+        hv.extension(self.backend)
+        Store.current_backend = self.backend
+        self.store_copy = OptionTree(sorted(Store.options().items()),
+                                     groups=Options._option_groups)
         patch('pandas')
         self.df = pd.DataFrame([[1, 2, 'A', 0.1], [3, 4, 'B', 0.2], [5, 6, 'C', 0.3]],
                                columns=['x', 'y', 'category', 'number'])
 
+    def tearDown(self):
+        Store.options(val=self.store_copy)
+        Store._custom_options = {k:{} for k in Store._custom_options.keys()}
+        super(TestOptions, self).tearDown()
+
     def test_scatter_legend_position(self):
         plot = self.df.hvplot.scatter('x', 'y', c='category', legend='left')
         opts = Store.lookup_options('bokeh', plot, 'plot')
@@ -88,3 +100,130 @@ def test_alpha_dim_overlay(self, kind):
         self.assertEqual(opts.kwargs['alpha'], 'number')
         self.assertIn('number', plot.last.vdims)
 
+    def test_hvplot_defaults(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='category')
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['show_legend'], True)
+        self.assertEqual(opts.kwargs['legend_position'], 'right')
+        self.assertEqual(opts.kwargs['show_grid'], False)
+        self.assertEqual(opts.kwargs['responsive'], False)
+        self.assertEqual(opts.kwargs['shared_axes'], True)
+        self.assertEqual(opts.kwargs['height'], 300)
+        self.assertEqual(opts.kwargs['width'], 700)
+        self.assertEqual(opts.kwargs['logx'], False)
+        self.assertEqual(opts.kwargs['logy'], False)
+        self.assertEqual(opts.kwargs.get('logz'), None)
+
+    def test_holoviews_defined_default_opts(self):
+        hv.opts.defaults(hv.opts.Scatter( height=400, width=900 ,show_grid=True))
+        plot = self.df.hvplot.scatter('x', 'y', c='category')
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['legend_position'], 'right')
+        self.assertEqual(opts.kwargs['show_grid'], True)
+        self.assertEqual(opts.kwargs['height'], 400)
+        self.assertEqual(opts.kwargs['width'], 900)
+
+    def test_holoviews_defined_default_opts_overwritten_in_call(self):
+        hv.opts.defaults(hv.opts.Scatter(height=400, width=900, show_grid=True))
+        plot = self.df.hvplot.scatter('x', 'y', c='category', width=300, legend='left')
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['legend_position'], 'left')
+        self.assertEqual(opts.kwargs['show_grid'], True)
+        self.assertEqual(opts.kwargs['height'], 400)
+        self.assertEqual(opts.kwargs['width'], 300)
+
+    def test_holoviews_defined_default_opts_are_not_mutable(self):
+        hv.opts.defaults(hv.opts.Scatter(tools=['tap']))
+        plot = self.df.hvplot.scatter('x', 'y', c='category')
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['tools'], ['tap', 'hover'])
+        default_opts = Store.options(backend='bokeh')['Scatter'].groups['plot'].options
+        self.assertEqual(default_opts['tools'], ['tap'])
+
+    def test_axis_set_to_visible_by_default(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='category')
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        assert 'xaxis' not in opts.kwargs
+        assert 'yaxis' not in opts.kwargs
+
+    def test_axis_set_to_none(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='category', xaxis=None, yaxis=None)
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['xaxis'], None)
+        self.assertEqual(opts.kwargs['yaxis'], None)
+
+    def test_axis_set_to_false(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='category', xaxis=False, yaxis=False)
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['xaxis'], None)
+        self.assertEqual(opts.kwargs['yaxis'], None)
+
+    def test_axis_set_to_none_in_holoviews_opts_default(self):
+        hv.opts.defaults(hv.opts.Scatter(xaxis=None, yaxis=None))
+        plot = self.df.hvplot.scatter('x', 'y', c='category')
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['xaxis'], None)
+        self.assertEqual(opts.kwargs['yaxis'], None)
+
+    @expectedFailure
+    def test_axis_set_to_none_in_holoviews_opts_default_overwrite_in_call(self):
+        hv.opts.defaults(hv.opts.Scatter(xaxis=None, yaxis=None))
+        plot = self.df.hvplot.scatter('x', 'y', c='category', xaxis=True, yaxis=True)
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        assert 'xaxis' not in opts.kwargs
+        assert 'yaxis' not in opts.kwargs
+
+    def test_loglog_opts(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='category', loglog=True)
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['logx'], True)
+        self.assertEqual(opts.kwargs['logy'], True)
+        self.assertEqual(opts.kwargs.get('logz'), None)
+
+    def test_logy_opts(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='category', logy=True)
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['logx'], False)
+        self.assertEqual(opts.kwargs['logy'], True)
+        self.assertEqual(opts.kwargs.get('logz'), None)
+
+    def test_holoviews_defined_default_opts_logx(self):
+        hv.opts.defaults(hv.opts.Scatter(logx=True))
+        plot = self.df.hvplot.scatter('x', 'y', c='category')
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['logx'], True)
+        self.assertEqual(opts.kwargs['logy'], False)
+        self.assertEqual(opts.kwargs.get('logz'), None)
+
+    def test_holoviews_defined_default_opts_logx_overwritten_in_call(self):
+        hv.opts.defaults(hv.opts.Scatter(logx=True))
+        plot = self.df.hvplot.scatter('x', 'y', c='category', logx=False)
+        opts = Store.lookup_options('bokeh', plot, 'plot')
+        self.assertEqual(opts.kwargs['logx'], False)
+        self.assertEqual(opts.kwargs['logy'], False)
+        self.assertEqual(opts.kwargs.get('logz'), None)
+
+    def test_hvplot_default_cat_cmap_opts(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='category')
+        opts = Store.lookup_options('bokeh', plot, 'style')
+        self.assertEqual(opts.kwargs['cmap'], 'Category10')
+
+    def test_hvplot_default_num_cmap_opts(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='number')
+        opts = Store.lookup_options('bokeh', plot, 'style')
+        self.assertEqual(opts.kwargs['cmap'], 'kbc_r')
+
+    def test_cmap_opts_by_type(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='number', cmap='diverging')
+        opts = Store.lookup_options('bokeh', plot, 'style')
+        self.assertEqual(opts.kwargs['cmap'], 'coolwarm')
+
+    def test_cmap_opts_in_call(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='number', cmap='fire')
+        opts = Store.lookup_options('bokeh', plot, 'style')
+        self.assertEqual(opts.kwargs['cmap'], 'fire')
+
+    def test_colormap_opts_in_call(self):
+        plot = self.df.hvplot.scatter('x', 'y', c='number', colormap='fire')
+        opts = Store.lookup_options('bokeh', plot, 'style')
+        self.assertEqual(opts.kwargs['cmap'], 'fire')