Repository: bokeh/bokeh

Issue:
Step chart should use Line, not Segment
ref: #1773 



Discussion:

Resolving patch:
diff --git a/bokeh/charts/builder/step_builder.py b/bokeh/charts/builder/step_builder.py
--- a/bokeh/charts/builder/step_builder.py
+++ b/bokeh/charts/builder/step_builder.py
@@ -19,10 +19,10 @@
 import numpy as np
 from six import string_types
 
-from ..utils import chunk, cycle_colors
+from ..utils import cycle_colors
 from .._builder import create_and_build, Builder
-from ...models import ColumnDataSource, DataRange1d, GlyphRenderer, Range1d
-from ...models.glyphs import Segment
+from ...models import ColumnDataSource, DataRange1d, GlyphRenderer
+from ...models.glyphs import Line
 from ...properties import Any
 
 #-----------------------------------------------------------------------------
@@ -65,59 +65,47 @@ def get_data(self):
         used by the segment glyph inside the ``draw`` method.
         """
         self._data = dict()
-
-        # list to save all the attributes we are going to create
-        self._attr = []
         self._groups = []
-        xs = self._values_index
-        self.set_and_get("x", "", np.array(xs)[:-1])
-        self.set_and_get("x2", "", np.array(xs)[1:])
-        for col in self._values.keys():
+
+        orig_xs = self._values_index
+        xs = np.empty(2*len(orig_xs)-1, dtype=np.int)
+        xs[::2] = orig_xs[:]
+        xs[1::2] = orig_xs[1:]
+        self._data['x'] = xs
+
+        for i, col in enumerate(self._values.keys()):
             if isinstance(self.index, string_types) and col == self.index:
                 continue
 
             # save every new group we find
             self._groups.append(col)
-            values = [self._values[col][x] for x in xs]
-            self.set_and_get("y1_", col, values[:-1])
-            self.set_and_get("y2_", col, values[1:])
+
+            orig_ys = np.array([self._values[col][x] for x in orig_xs])
+            ys = np.empty(2*len(orig_ys)-1)
+            ys[::2] = orig_ys[:]
+            ys[1::2] = orig_ys[:-1]
+            self._data['y_%s' % col] = ys
 
     def get_source(self):
         """ Push the Step data into the ColumnDataSource and calculate
         the proper ranges.
         """
         sc = self._source = ColumnDataSource(self._data)
-        self.x_range = DataRange1d(sources=[sc.columns("x"), sc.columns("x2")])
-        y_names = self._attr[1:]
-        endy = max(max(self._data[i]) for i in y_names)
-        starty = min(min(self._data[i]) for i in y_names)
-        self.y_range = Range1d(
-            start=starty - 0.1 * (endy - starty),
-            end=endy + 0.1 * (endy - starty)
-        )
+        self.x_range = DataRange1d(sources=[sc.columns("x")])
+
+        y_sources = [sc.columns("y_%s" % col) for col in self._groups]
+        self.y_range = DataRange1d(sources=y_sources)
 
     def draw(self):
         """Use the line glyphs to connect the xy points in the Step.
 
         Takes reference points from the data loaded at the ColumnDataSource.
         """
-        tuples = list(chunk(self._attr[2:], 2))
-        colors = cycle_colors(tuples, self.palette)
+        colors = cycle_colors(self._groups, self.palette)
 
-        # duplet: y1, y2 values of each series
-        for i, duplet in enumerate(tuples):
+        for i, name in enumerate(self._groups):
             # draw the step horizontal segment
-            glyph = Segment(
-                x0="x2", y0=duplet[0], x1="x2", y1=duplet[1],
-                line_color=colors[i]
-            )
-            yield GlyphRenderer(data_source=self._source, glyph=glyph)
-
-            # draw the step vertical segment
-            glyph = Segment(
-                x0="x", y0=duplet[0], x1="x2", y1=duplet[0],
-                line_color=colors[i]
-            )
+            glyph = Line(x="x", y="y_%s" % name, line_color=colors[i], line_width=2)
             renderer = GlyphRenderer(data_source=self._source, glyph=glyph)
             self._legends.append((self._groups[i], [renderer]))
             yield renderer
diff --git a/examples/charts/steps.py b/examples/charts/steps.py
--- a/examples/charts/steps.py
+++ b/examples/charts/steps.py
@@ -15,11 +15,14 @@
 #xyvalues = pd.DataFrame(xyvalues)
 #xyvalues = list(xyvalues.values())
 #xyvalues = np.array(list(xyvalues.values()))
+
 output_file("steps.html", title="line.py example")
+
 chart = Step(xyvalues, title="Steps", ylabel='measures', filename="steps.html", legend='top_left')
+
 # step.xlabel('time').legend("top_left").show()
 # chart = Chart(title="Steps", ylabel='measures', filename="steps.html", legend=True)
 # chart.add_builder(Step(xyvalues))
 # chart.add_builder(Line(xyvalues))
-# chart.show()
+
 show(chart)


Test patch:
diff --git a/bokeh/charts/builder/tests/test_step_builder.py b/bokeh/charts/builder/tests/test_step_builder.py
--- a/bokeh/charts/builder/tests/test_step_builder.py
+++ b/bokeh/charts/builder/tests/test_step_builder.py
@@ -36,39 +36,27 @@ def test_supported_input(self):
         xyvalues['jython'] = [22, 43, 10, 25, 26]
         xyvaluesdf = pd.DataFrame(xyvalues)
 
-        y1_python = [2, 3, 7, 5]
-        y2_python = [3, 7, 5, 26]
-        y1_jython = [22, 43, 10, 25]
-        y2_jython = [43, 10, 25, 26]
-        y1_pypy = [12, 33, 47, 15]
-        y2_pypy = [33, 47, 15, 126]
-        x2 = [1, 2, 3, 4]
-        x = [0, 1, 2, 3]
+        y_python = [ 2., 2., 3.,  3., 7., 7.,  5., 5., 26.]
+        y_jython = [ 22., 22.,43., 43., 10., 10., 25., 25., 26.]
+        y_pypy = [  12., 12., 33., 33., 47., 47., 15., 15., 126.]
+        x = [0, 1, 1, 2, 2, 3, 3, 4, 4]
 
         for i, _xy in enumerate([xyvalues, xyvaluesdf]):
             hm = create_chart(Step, _xy)
             builder = hm._builders[0]
             self.assertEqual(sorted(builder._groups), sorted(list(xyvalues.keys())))
             assert_array_equal(builder._data['x'], x)
-            assert_array_equal(builder._data['x2'], x2)
-
-            assert_array_equal(builder._data['y1_python'], y1_python)
-            assert_array_equal(builder._data['y2_python'], y2_python)
-            assert_array_equal(builder._data['y1_jython'], y1_jython)
-            assert_array_equal(builder._data['y2_jython'], y2_jython)
-            assert_array_equal(builder._data['y1_pypy'], y1_pypy)
-            assert_array_equal(builder._data['y2_pypy'], y2_pypy)
 
+            assert_array_equal(builder._data['y_python'], y_python)
+            assert_array_equal(builder._data['y_jython'], y_jython)
+            assert_array_equal(builder._data['y_pypy'], y_pypy)
 
         lvalues = [[2, 3, 7, 5, 26], [12, 33, 47, 15, 126], [22, 43, 10, 25, 26]]
         for _xy in [lvalues, np.array(lvalues)]:
             hm = create_chart(Step, _xy)
             builder = hm._builders[0]
             self.assertEqual(builder._groups, ['0', '1', '2'])
-            assert_array_equal(builder._data['y1_0'], y1_python)
-            assert_array_equal(builder._data['y2_0'], y2_python)
-            assert_array_equal(builder._data['y1_1'], y1_pypy)
-            assert_array_equal(builder._data['y2_1'], y2_pypy)
-            assert_array_equal(builder._data['y1_2'], y1_jython)
-            assert_array_equal(builder._data['y2_2'], y2_jython)
+            assert_array_equal(builder._data['y_0'], y_python)
+            assert_array_equal(builder._data['y_1'], y_pypy)
+            assert_array_equal(builder._data['y_2'], y_jython)