Repository: bokeh/bokeh

Issue:
Color cycles for Bokeh charts
I noticed that whenever I try to use the bokeh.charts.BoxPlot function I hit an error with plots with more than 6 boxplots.

`import numpy as np
from bokeh.charts import BoxPlot
test = {}
for i in range(0,7):
    test[str(i)] = np.random.normal(0,1,100)
plot = BoxPlot(test)
show(plot)
`

This is the resulting stacktrace:

`
 Traceback (most recent call last):
  File "temp.py", line 6, in <module>
    plot = BoxPlot(test)
  File "/usr/local/lib/python2.7/site-packages/bokeh/charts/builder/boxplot_builder.py", line 84, in BoxPlot
    xscale=xscale, yscale=yscale, xgrid=xgrid, ygrid=ygrid, **kw
  File "/usr/local/lib/python2.7/site-packages/bokeh/charts/_builder.py", line 42, in create_and_build
    chart.add_builder(builder)
  File "/usr/local/lib/python2.7/site-packages/bokeh/charts/_chart.py", line 118, in add_builder
    builder.create(self)
  File "/usr/local/lib/python2.7/site-packages/bokeh/charts/_builder.py", line 169, in create
    chart.add_renderers(self, renderers)
  File "/usr/local/lib/python2.7/site-packages/bokeh/charts/_chart.py", line 113, in add_renderers
    self.renderers += renderers
  File "/usr/local/lib/python2.7/site-packages/bokeh/charts/builder/boxplot_builder.py", line 283, in _yield_renderers
    line_color="black", fill_color=self.palette[i])
IndexError: list index out of range
`

It appears that whatever predefined color list for the BoxPlot function has run out of colors for new boxplots.

Is there a way to either define a new color list (ideally, a cycle that will simply repeat colors if it runs out) or disable coloring entirely?



Discussion:
This is definitely a bug but you can bypass it by specifying a custom palette all the colors you want. If you specify a palette long enough you'll avoid stepping this bug. Here's a quick workaround to make your code work while this isn't fixed:

``` Python
import numpy as np
from bokeh.charts import BoxPlot
from bokeh.charts.utils import cycle_colors
test = {}
for i in range(0,10):
    test[str(i)] = np.random.normal(0,1,100)
palette = cycle_colors(test)
plot = BoxPlot(test, palette=palette)
show(plot)
```


Resolving patch:
diff --git a/bokeh/charts/builder/boxplot_builder.py b/bokeh/charts/builder/boxplot_builder.py
--- a/bokeh/charts/builder/boxplot_builder.py
+++ b/bokeh/charts/builder/boxplot_builder.py
@@ -22,7 +22,7 @@
 import numpy as np
 import pandas as pd
 
-from ..utils import make_scatter
+from ..utils import make_scatter, cycle_colors
 from .._builder import Builder, create_and_build
 from ...models import ColumnDataSource, FactorRange, GlyphRenderer, Range1d
 from ...models.glyphs import Rect, Segment
@@ -156,6 +156,7 @@ def _process_data(self):
         lower_center_boxes = []
         lower_height_boxes = []
         out_x, out_y, out_color = ([], [], [])
+        colors = cycle_colors(self._groups, self.palette)
 
         for i, (level, values) in enumerate(self._values.items()):
             # Compute quantiles, center points, heights, IQR, etc.
@@ -187,7 +188,7 @@ def _process_data(self):
                 o = values[out]
                 out_x.append(level)
                 out_y.append(o)
-                out_color.append(self.palette[i])
+                out_color.append(colors[i])
 
         # Store
         self.set_and_get(self._data_scatter, self._attr_scatter, "out_x", out_x)
@@ -205,7 +206,7 @@ def _process_data(self):
         self.set_and_get(self._data_rect, self._attr_rect, "upper_height_boxes", upper_height_boxes)
         self.set_and_get(self._data_rect, self._attr_rect, "lower_center_boxes", lower_center_boxes)
         self.set_and_get(self._data_rect, self._attr_rect, "lower_height_boxes", lower_height_boxes)
-        self.set_and_get(self._data_rect, self._attr_rect, "colors", self.palette)
+        self.set_and_get(self._data_rect, self._attr_rect, "colors", colors)
 
     def _set_sources(self):
         "Push the BoxPlot data into the ColumnDataSource and calculate the proper ranges."


Test patch:
diff --git a/bokeh/charts/builder/tests/test_boxplot_builder.py b/bokeh/charts/builder/tests/test_boxplot_builder.py
--- a/bokeh/charts/builder/tests/test_boxplot_builder.py
+++ b/bokeh/charts/builder/tests/test_boxplot_builder.py
@@ -45,7 +45,7 @@ def test_supported_input(self):
         xyvaluesdf = pd.DataFrame(xyvalues)
         xyvaluesbl = blaze.Data(xyvaluesdf)
         exptected_datarect = {
-            'colors': ['#f22c40', '#5ab738', '#407ee7', '#df5320', '#00ad9c', '#c33ff3'],
+            'colors': ['#f22c40', '#5ab738', '#407ee7'],
             'groups': ['bronze', 'silver', 'gold'],
             'iqr_centers': [2.5, 2.5, 2.5],
             'iqr_lengths': [3.0, 3.0, 4.5],