Repository: bokeh/bokeh

Issue:
Donut chart is broken when called with DataFrame inputs
```
Traceback (most recent call last):
  File "donut.py", line 36, in <module>
    donut.legend(True).width(800).height(800).show()
  File "/Users/fpliger/dev/repos/bokeh/bokeh/charts/_chartobject.py", line 405, in show
    self._prepare_show()
  File "/Users/fpliger/dev/repos/bokeh/bokeh/charts/_chartobject.py", line 434, in _prepare_show
    self.get_data()
  File "/Users/fpliger/dev/repos/bokeh/bokeh/charts/donut.py", line 230, in get_data
    self.groups = df.columns = self.cat
  File "/Users/fpliger/anaconda/envs/pybokeh27/lib/python2.7/site-packages/pandas/core/generic.py", line 1942, in __setattr__
    object.__setattr__(self, name, value)
  File "pandas/src/properties.pyx", line 65, in pandas.lib.AxisProperty.__set__ (pandas/lib.c:41020)
  File "/Users/fpliger/anaconda/envs/pybokeh27/lib/python2.7/site-packages/pandas/core/generic.py", line 400, in _set_axis
    self._data.set_axis(axis, labels)
  File "/Users/fpliger/anaconda/envs/pybokeh27/lib/python2.7/site-packages/pandas/core/internals.py", line 2257, in set_axis
    'new values have %d elements' % (old_len, new_len))
ValueError: Length mismatch: Expected axis has 1 elements, new values have 3 elements
```



Discussion:

Resolving patch:
diff --git a/bokeh/charts/_chartobject.py b/bokeh/charts/_chartobject.py
--- a/bokeh/charts/_chartobject.py
+++ b/bokeh/charts/_chartobject.py
@@ -761,7 +761,9 @@ def values(self):
             return self._values
 
         else:
-            return list(self._values)
+            # assuming it's a dataframe, in that case it returns transposed
+            # values compared to it's dict equivalent..
+            return list(values.T)
 
     def items(self):
         return [(key, self[key]) for key in self]
diff --git a/bokeh/charts/donut.py b/bokeh/charts/donut.py
--- a/bokeh/charts/donut.py
+++ b/bokeh/charts/donut.py
@@ -16,6 +16,7 @@
 #-----------------------------------------------------------------------------
 # Imports
 #-----------------------------------------------------------------------------
+from __future__ import division
 from math import pi, cos, sin
 import pandas as pd
 
@@ -135,14 +136,17 @@ def get_data(self):
         points to be used by the Wedge glyph inside the ``draw`` method.
 
         """
-        self.df = df = pd.DataFrame(self.values.values())
-        self.groups = df.columns = self.cat
-        df.index = self.values.keys()
-        aggregated = df.sum()
+        dd = dict(zip(self.values.keys(), self.values.values()))
+        self.df = df = pd.DataFrame(dd)
+        self.groups = df.index = self.cat
+        df.columns = self.values.keys()
+
+        # Get the sum per category
+        aggregated = df.T.sum()
+        # Get the total (sum of all categories)
         self.total_units = total = aggregated.sum()
         radians = lambda x: 2*pi*(x/total)
         angles = aggregated.map(radians).cumsum()
-
         end_angles = angles.tolist()
         start_angles = [0] + end_angles[:-1]
         colors = self._set_colors(self.cat)
@@ -193,8 +197,7 @@ def draw_external_ring(self, colors=None):
         first = True
         for i, (cat, start_angle, end_angle) in enumerate(zip(
                 self.cat, self.data['start'], self.data['end'])):
-
-            details = self.df[cat]
+            details = self.df.ix[i]
             radians = lambda x: 2*pi*(x/self.total_units)
 
             angles = details.map(radians).cumsum() + start_angle
@@ -207,6 +210,7 @@ def draw_external_ring(self, colors=None):
             x, y = polar_to_cartesian(1.25, start, end)
 
             source = ColumnDataSource(dict(start=start, end=end, fill=fill))
+
             self.chart.make_annular(
                 source, x=0, y=0, inner_radius=1, outer_radius=1.5,
                 start_angle="start", end_angle="end", line_color="white",
diff --git a/examples/charts/donut.py b/examples/charts/donut.py
--- a/examples/charts/donut.py
+++ b/examples/charts/donut.py
@@ -1,4 +1,5 @@
 import pandas as pd
+import numpy as np
 from collections import OrderedDict
 
 from bokeh.sampledata.olympics2014 import data
@@ -19,10 +20,10 @@
 # later, we build a dict containing the grouped data
 medals = OrderedDict(bronze=bronze, silver=silver, gold=gold)
 
-# any of the following commented are valid Bar inputs
-#medals = pd.DataFrame(medals).T.values
-#medals = list(medals.values())
-#medas = pd.DataFrame(medals)
+# any of the following commented are valid Donut inputs
+# medals = list(medals.values())
+# medals = np.array(list(medals.values()))
+# medals = pd.DataFrame(medals)
 
 donut = Donut(medals, countries, filename="donut.html")
 donut.title("Medals Donut").xlabel("countries").ylabel("medals")


Test patch:
diff --git a/bokeh/tests/test_charts.py b/bokeh/tests/test_charts.py
--- a/bokeh/tests/test_charts.py
+++ b/bokeh/tests/test_charts.py
@@ -850,35 +850,43 @@ def test_supported_input(self):
         xyvalues['python'] = [2., 5., 3.]
         xyvalues['pypy'] = [4., 1., 4.]
         xyvalues['jython'] = [6., 4., 3.]
-        cat = ["sets", "dicts", "odicts"]
-        start = [0, 2.3561944901923448, 4.3196898986859651]
-        end = [2.3561944901923448, 4.3196898986859651, 6.2831853071795862]
-        colors = ['#f22c40', '#5ab738', '#407ee7']
 
-        # TODO: Chart is not working with DataFrames anymore.
-        #       Fix it and add test case for , pd.DataFrame(xyvalues)
-        for i, _xy in enumerate([xyvalues]):
-            _chart = create_chart(Donut, _xy, cat=cat)
+        xyvalues_int = OrderedDict()
+        for k, values in xyvalues.items():
+            xyvalues_int[k] = [int(val) for val in values]
 
-            self.assertEqual(_chart.groups, cat)
-            assert_array_equal(_chart.data['start'], start)
-            assert_array_equal(_chart.data['end'], end)
-            assert_array_equal(_chart.data['colors'], colors)
+        for xyvalues in [xyvalues, xyvalues_int]:
+            cat = ["sets", "dicts", "odicts"]
+            start = [0, 2.3561944901923448, 4.3196898986859651]
+            end = [2.3561944901923448, 4.3196898986859651, 6.2831853071795862]
+            colors = ['#f22c40', '#5ab738', '#407ee7']
 
-            # TODO: Test for external ring source values is missing as it needs
-            #       some refactoring to expose those values calculation
+            # TODO: Chart is not working with DataFrames anymore.
+            #       Fix it and add test case for , pd.DataFrame(xyvalues)
+            for i, _xy in enumerate([xyvalues]):
+                _chart = create_chart(Donut, _xy, cat=cat)
 
-        lvalues = [[2., 5., 3.], [4., 1., 4.], [6., 4., 3.]]
-        for i, _xy in enumerate([lvalues, np.array(lvalues)]):
-            _chart = create_chart(Donut, _xy, cat=cat)
+                self.assertEqual(_chart.groups, cat)
+                assert_array_equal(_chart.data['start'], start)
+                assert_array_equal(_chart.data['end'], end)
+                assert_array_equal(_chart.data['colors'], colors)
 
-            self.assertEqual(_chart.groups, cat)
-            assert_array_equal(_chart.data['start'], start)
-            assert_array_equal(_chart.data['end'], end)
-            assert_array_equal(_chart.data['colors'], colors)
+                # TODO: Test for external ring source values is missing as it needs
+                #       some refactoring to expose those values calculation
 
-            # TODO: Test for external ring source values is missing as it needs
-            #       some refactoring to expose those values calculation
+        lvalues = [[2., 5., 3.], [4., 1., 4.], [6., 4., 3.]]
+        lvalues_int = [[2, 5, 3], [4, 1, 4], [6, 4, 3]]
+        for lvalues in [lvalues, lvalues_int]:
+            for i, _xy in enumerate([lvalues, np.array(lvalues)]):
+                _chart = create_chart(Donut, _xy, cat=cat)
+
+                self.assertEqual(_chart.groups, cat)
+                assert_array_equal(_chart.data['start'], start)
+                assert_array_equal(_chart.data['end'], end)
+                assert_array_equal(_chart.data['colors'], colors)
+
+                # TODO: Test for external ring source values is missing as it needs
+                #       some refactoring to expose those values calculation
 
 
 class TestDataAdapter(unittest.TestCase):