{"action":"created","comment":{"url":"https://api.github.com/repos/bokeh/bokeh/pulls/comments/23227071","id":23227071,"diff_hunk":"@@ -0,0 +1,230 @@\n+\"\"\"This is the Bokeh charts interface. It gives you a high level API to build\n+complex plot is a simple way.\n+\n+This is the Horizon class which lets you build your Horizon charts just\n+passing the arguments to the Chart class and calling the proper functions.\n+\"\"\"\n+\n+from six import string_types\n+from collections import OrderedDict\n+import math\n+\n+from ._chartobject import ChartObject\n+from ..models import ColumnDataSource, Range1d, DataRange1d, FactorRange, HoverTool, CategoricalAxis\n+\n+#-----------------------------------------------------------------------------\n+# Classes and functions\n+#-----------------------------------------------------------------------------\n+\n+\n+class Horizon(ChartObject):\n+    \"\"\"This is the Horizon class and it is in charge of plotting\n+    Horizon charts in an easy and intuitive way.\n+\n+    Essentially, we provide a way to ingest the data, separate the data into\n+    a number of folds which stack on top of each others.\n+    We additionally make calculations for the ranges.\n+    And finally add the needed lines taking the references from the source.\n+\n+    Examples:\n+        import datetime\n+        from collections import OrderedDict\n+        from bokeh.charts import Horizon\n+\n+        now = datetime.datetime.now()\n+        delta = datetime.timedelta(minutes=1)\n+        dts = [now + delta*i for i in range(5)]\n+        dtss = ['%s'%dt for dt in dts]\n+        xyvalues = OrderedDict({'Date': dts})\n+        y_python = xyvalues['python'] = [2, 3, 7, 5, 26]\n+        y_pypy = xyvalues['pypy'] = [12, 33, 47, 15, 126]\n+        y_jython = xyvalues['jython'] = [22, 43, 10, 25, 26]\n+\n+        ts = Horizon(xyvalues, index='Date', title=\"horizon\",\n+                        ylabel='Stock Prices', filename=\"stocks_ts.html\")\n+        ts.legend(\"top_left\").show()\n+\n+    \"\"\"\n+    def __init__(self, values, index=None, title=None, xlabel=None, ylabel=None,\n+                 legend=False, xscale=\"datetime\", yscale=\"linear\", width=800,\n+                 height=600, tools=True, filename=False, server=False, nb_folds=3,\n+                 notebook=False, facet=False, xgrid=False, ygrid=False,\n+                 pos_color='#006400', neg_color='#6495ed'):\n+        \"\"\"\n+        Args:\n+            values (iterable): iterable 2d representing the data series\n+                values matrix.\n+            index (str|1d iterable, optional): can be used to specify a\n+                common custom index for all data series as follows:\n+                    - As a 1d iterable of any sort (of datetime values)\n+                        that will be used as series common index\n+                    - As a string that corresponds to the key of the\n+                        mapping to be used as index (and not as data\n+                        series) if area.values is a mapping (like a dict,\n+                        an OrderedDict or a pandas DataFrame). The values\n+                        must be datetime values.\n+            title (str, optional): the title of your chart. Defaults\n+                to None.\n+            xlabel (str, optional): the x-axis label of your chart.\n+                Defaults to None.\n+            ylabel (str, optional): the y-axis label of your chart.\n+                Defaults to None.\n+            legend (str, optional): the legend of your chart. The legend\n+                content is inferred from incoming input.It can be\n+                ``top_left``, ``top_right``, ``bottom_left``,\n+                ``bottom_right``. ``top_right`` is set if you set it\n+                 as True. Defaults to None.\n+            xscale (str, optional): the x-axis type scale of your chart.\n+                It can be ``linear``, ``datetime`` or ``categorical``.\n+                Defaults to ``datetime``.\n+            yscale (str, optional): the y-axis type scale of your chart.\n+                It can be ``linear``, ``datetime`` or ``categorical``.\n+                Defaults to ``linear``.\n+            width (int, optional): the width of your chart in pixels.\n+                Defaults to 800.\n+            height (int, optional): the height of you chart in pixels.\n+                Defaults to 600.\n+            tools (bool, optional): to enable or disable the tools in\n+                your chart. Defaults to True\n+            filename (str or bool, optional): the name of the file where\n+                your chart. will be written. If you pass True to this\n+                argument, it will use ``untitled`` as a filename.\n+                Defaults to False.\n+            server (str or bool, optional): the name of your chart in\n+                the server. If you pass True to this argument, it will\n+                use ``untitled`` as the name in the server.\n+                Defaults to False.\n+            notebook (bool, optional): whether to output to IPython notebook\n+                (default: False)\n+            facet (bool, optional): generate multiple areas on multiple\n+                separate charts for each series if True. Defaults to\n+                False\n+            xgrid (bool, optional): whether to display x grid lines\n+                (default: True)\n+            ygrid (bool, optional): whether to display y grid lines\n+                (default: True)\n+            pos_color (hex color string): the color of the positive folds\n+                (default: #006400)\n+            neg_color (hex color string): the color of the negative folds\n+                (default: #6495ed)\n+\n+        Attributes:\n+            source (obj): datasource object for your plot,\n+                initialized as a dummy None.\n+            xdr (obj): x-associated datarange object for you plot,\n+                initialized as a dummy None.\n+            ydr (obj): y-associated datarange object for you plot,\n+                initialized as a dummy None.\n+            groups (list): to be filled with the incoming groups of data.\n+                Useful for legend construction.\n+            data (dict): to be filled with the incoming data and be passed\n+                to the ColumnDataSource in each chart inherited class.\n+                Needed for _set_And_get method.\n+            attr (list): to be filled with the new attributes created after\n+                loading the data dict.\n+                Needed for _set_And_get method.\n+        \"\"\"\n+        self.values = values\n+        self.source = None\n+        self.xdr = None\n+        self.ydr = None\n+        self.groups = []\n+        self.series = []\n+        self.fold_height = {}\n+        self.max_y = 0\n+        self.data = dict()\n+        self.attr = []\n+        self.index = index\n+        self.nb_folds = nb_folds\n+        self.pos_color = pos_color\n+        self.neg_color = neg_color\n+\n+        super(Horizon, self).__init__(\n+            title, xlabel, ylabel, legend, xscale, yscale, width, height,\n+            tools, filename, server, notebook, facet, xgrid, ygrid\n+        )\n+\n+    def get_data(self):\n+        \"\"\"Take the x/y data from the horizon values.\n+\n+        It calculates the chart properties accordingly. Then build a dict\n+        containing references to all the points to be used by\n+        the multiple area glyphes inside the ``draw`` method.\n+\n+        \"\"\"\n+\n+        def fold_coordinates(y, fold_no, fold_height, y_origin=0):\n+            \"\"\" Function that calculate the coordinates for a value given a fold\n+            \"\"\"\n+            height = fold_no * fold_height\n+            quotient, remainder = divmod(abs(y), float(height))\n+            v = fold_height\n+            if math.floor(quotient) == 0:\n+                v = (remainder - height + fold_height) if remainder >= height - fold_height else 0\n+            return (v + y_origin, fold_height + y_origin) if y > 0 else (y_origin, fold_height - v + y_origin)\n+\n+        for col in self.values.keys():\n+            if isinstance(self.index, string_types) and col == self.index:\n+                continue\n+ \n+            self.series.append(col)\n+            self.set_and_get(\"x_\", col, self.values_index)\n+            self.max_y = max(max(self.values[col]), self.max_y)\n+\n+        self.fold_height = self.max_y / self.nb_folds","path":"bokeh/charts/horizon.py","position":174,"original_position":174,"commit_id":"8c7ecae4a797cf70907a0ee4e9b6efbf5be6a550","original_commit_id":"8c7ecae4a797cf70907a0ee4e9b6efbf5be6a550","user":{"login":"fpliger","id":1084690,"avatar_url":"https://avatars.githubusercontent.com/u/1084690?v=3","gravatar_id":"","url":"https://api.github.com/users/fpliger","html_url":"https://github.com/fpliger","followers_url":"https://api.github.com/users/fpliger/followers","following_url":"https://api.github.com/users/fpliger/following{/other_user}","gists_url":"https://api.github.com/users/fpliger/gists{/gist_id}","starred_url":"https://api.github.com/users/fpliger/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fpliger/subscriptions","organizations_url":"https://api.github.com/users/fpliger/orgs","repos_url":"https://api.github.com/users/fpliger/repos","events_url":"https://api.github.com/users/fpliger/events{/privacy}","received_events_url":"https://api.github.com/users/fpliger/received_events","type":"User","site_admin":false},"body":"This is prone to different behaviours in Py2 and Py3. Please use `from __future__ import division`","created_at":"2015-01-20T15:01:36Z","updated_at":"2015-01-20T15:01:36Z","html_url":"https://github.com/bokeh/bokeh/pull/1725#discussion_r23227071","pull_request_url":"https://api.github.com/repos/bokeh/bokeh/pulls/1725","_links":{"self":{"href":"https://api.github.com/repos/bokeh/bokeh/pulls/comments/23227071"},"html":{"href":"https://github.com/bokeh/bokeh/pull/1725#discussion_r23227071"},"pull_request":{"href":"https://api.github.com/repos/bokeh/bokeh/pulls/1725"}}},"pull_request":{"url":"https://api.github.com/repos/bokeh/bokeh/pulls/1725","id":27639278,"html_url":"https://github.com/bokeh/bokeh/pull/1725","diff_url":"https://github.com/bokeh/bokeh/pull/1725.diff","patch_url":"https://github.com/bokeh/bokeh/pull/1725.patch","issue_url":"https://api.github.com/repos/bokeh/bokeh/issues/1725","number":1725,"state":"open","locked":false,"title":"Adding horizon plot to the high-level charts API","user":{"login":"matdrapeau","id":439887,"avatar_url":"https://avatars.githubusercontent.com/u/439887?v=3","gravatar_id":"","url":"https://api.github.com/users/matdrapeau","html_url":"https://github.com/matdrapeau","followers_url":"https://api.github.com/users/matdrapeau/followers","following_url":"https://api.github.com/users/matdrapeau/following{/other_user}","gists_url":"https://api.github.com/users/matdrapeau/gists{/gist_id}","starred_url":"https://api.github.com/users/matdrapeau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matdrapeau/subscriptions","organizations_url":"https://api.github.com/users/matdrapeau/orgs","repos_url":"https://api.github.com/users/matdrapeau/repos","events_url":"https://api.github.com/users/matdrapeau/events{/privacy}","received_events_url":"https://api.github.com/users/matdrapeau/received_events","type":"User","site_admin":false},"body":"This is an initial pull in order to get some reviews and feedbacks.\r\n\r\nI would need help in order to add an y axis which would represents the series names. Also, being able to discard the original y axis which doesn't represent the right numbers since the folds are stacking the heights.\r\n\r\nAbout the hover tooltips, I don't know how to configure them to display the: series names / datetime / and y value of the specific series being mover over.\r\n\r\nThank you for taking the time to look at this patch...","created_at":"2015-01-19T22:09:42Z","updated_at":"2015-01-20T15:01:36Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/bokeh/bokeh/pulls/1725/commits","review_comments_url":"https://api.github.com/repos/bokeh/bokeh/pulls/1725/comments","review_comment_url":"https://api.github.com/repos/bokeh/bokeh/pulls/comments/{number}","comments_url":"https://api.github.com/repos/bokeh/bokeh/issues/1725/comments","statuses_url":"https://api.github.com/repos/bokeh/bokeh/statuses/8c7ecae4a797cf70907a0ee4e9b6efbf5be6a550","head":{"label":"matdrapeau:master","ref":"master","sha":"8c7ecae4a797cf70907a0ee4e9b6efbf5be6a550","user":{"login":"matdrapeau","id":439887,"avatar_url":"https://avatars.githubusercontent.com/u/439887?v=3","gravatar_id":"","url":"https://api.github.com/users/matdrapeau","html_url":"https://github.com/matdrapeau","followers_url":"https://api.github.com/users/matdrapeau/followers","following_url":"https://api.github.com/users/matdrapeau/following{/other_user}","gists_url":"https://api.github.com/users/matdrapeau/gists{/gist_id}","starred_url":"https://api.github.com/users/matdrapeau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matdrapeau/subscriptions","organizations_url":"https://api.github.com/users/matdrapeau/orgs","repos_url":"https://api.github.com/users/matdrapeau/repos","events_url":"https://api.github.com/users/matdrapeau/events{/privacy}","received_events_url":"https://api.github.com/users/matdrapeau/received_events","type":"User","site_admin":false},"repo":{"id":28887753,"name":"bokeh","full_name":"matdrapeau/bokeh","owner":{"login":"matdrapeau","id":439887,"avatar_url":"https://avatars.githubusercontent.com/u/439887?v=3","gravatar_id":"","url":"https://api.github.com/users/matdrapeau","html_url":"https://github.com/matdrapeau","followers_url":"https://api.github.com/users/matdrapeau/followers","following_url":"https://api.github.com/users/matdrapeau/following{/other_user}","gists_url":"https://api.github.com/users/matdrapeau/gists{/gist_id}","starred_url":"https://api.github.com/users/matdrapeau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matdrapeau/subscriptions","organizations_url":"https://api.github.com/users/matdrapeau/orgs","repos_url":"https://api.github.com/users/matdrapeau/repos","events_url":"https://api.github.com/users/matdrapeau/events{/privacy}","received_events_url":"https://api.github.com/users/matdrapeau/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/matdrapeau/bokeh","description":"Interactive Web Plotting for Python","fork":true,"url":"https://api.github.com/repos/matdrapeau/bokeh","forks_url":"https://api.github.com/repos/matdrapeau/bokeh/forks","keys_url":"https://api.github.com/repos/matdrapeau/bokeh/keys{/key_id}","collaborators_url":"https://api.github.com/repos/matdrapeau/bokeh/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/matdrapeau/bokeh/teams","hooks_url":"https://api.github.com/repos/matdrapeau/bokeh/hooks","issue_events_url":"https://api.github.com/repos/matdrapeau/bokeh/issues/events{/number}","events_url":"https://api.github.com/repos/matdrapeau/bokeh/events","assignees_url":"https://api.github.com/repos/matdrapeau/bokeh/assignees{/user}","branches_url":"https://api.github.com/repos/matdrapeau/bokeh/branches{/branch}","tags_url":"https://api.github.com/repos/matdrapeau/bokeh/tags","blobs_url":"https://api.github.com/repos/matdrapeau/bokeh/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/matdrapeau/bokeh/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/matdrapeau/bokeh/git/refs{/sha}","trees_url":"https://api.github.com/repos/matdrapeau/bokeh/git/trees{/sha}","statuses_url":"https://api.github.com/repos/matdrapeau/bokeh/statuses/{sha}","languages_url":"https://api.github.com/repos/matdrapeau/bokeh/languages","stargazers_url":"https://api.github.com/repos/matdrapeau/bokeh/stargazers","contributors_url":"https://api.github.com/repos/matdrapeau/bokeh/contributors","subscribers_url":"https://api.github.com/repos/matdrapeau/bokeh/subscribers","subscription_url":"https://api.github.com/repos/matdrapeau/bokeh/subscription","commits_url":"https://api.github.com/repos/matdrapeau/bokeh/commits{/sha}","git_commits_url":"https://api.github.com/repos/matdrapeau/bokeh/git/commits{/sha}","comments_url":"https://api.github.com/repos/matdrapeau/bokeh/comments{/number}","issue_comment_url":"https://api.github.com/repos/matdrapeau/bokeh/issues/comments/{number}","contents_url":"https://api.github.com/repos/matdrapeau/bokeh/contents/{+path}","compare_url":"https://api.github.com/repos/matdrapeau/bokeh/compare/{base}...{head}","merges_url":"https://api.github.com/repos/matdrapeau/bokeh/merges","archive_url":"https://api.github.com/repos/matdrapeau/bokeh/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/matdrapeau/bokeh/downloads","issues_url":"https://api.github.com/repos/matdrapeau/bokeh/issues{/number}","pulls_url":"https://api.github.com/repos/matdrapeau/bokeh/pulls{/number}","milestones_url":"https://api.github.com/repos/matdrapeau/bokeh/milestones{/number}","notifications_url":"https://api.github.com/repos/matdrapeau/bokeh/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/matdrapeau/bokeh/labels{/name}","releases_url":"https://api.github.com/repos/matdrapeau/bokeh/releases{/id}","created_at":"2015-01-06T23:01:47Z","updated_at":"2015-01-20T05:33:56Z","pushed_at":"2015-01-20T05:33:55Z","git_url":"git://github.com/matdrapeau/bokeh.git","ssh_url":"git@github.com:matdrapeau/bokeh.git","clone_url":"https://github.com/matdrapeau/bokeh.git","svn_url":"https://github.com/matdrapeau/bokeh","homepage":"","size":110103,"stargazers_count":0,"watchers_count":0,"language":"CoffeeScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"bokeh:master","ref":"master","sha":"17303ecabf3f5465a977b277c3a91fff9de5a172","user":{"login":"bokeh","id":8440965,"avatar_url":"https://avatars.githubusercontent.com/u/8440965?v=3","gravatar_id":"","url":"https://api.github.com/users/bokeh","html_url":"https://github.com/bokeh","followers_url":"https://api.github.com/users/bokeh/followers","following_url":"https://api.github.com/users/bokeh/following{/other_user}","gists_url":"https://api.github.com/users/bokeh/gists{/gist_id}","starred_url":"https://api.github.com/users/bokeh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bokeh/subscriptions","organizations_url":"https://api.github.com/users/bokeh/orgs","repos_url":"https://api.github.com/users/bokeh/repos","events_url":"https://api.github.com/users/bokeh/events{/privacy}","received_events_url":"https://api.github.com/users/bokeh/received_events","type":"Organization","site_admin":false},"repo":{"id":3834332,"name":"bokeh","full_name":"bokeh/bokeh","owner":{"login":"bokeh","id":8440965,"avatar_url":"https://avatars.githubusercontent.com/u/8440965?v=3","gravatar_id":"","url":"https://api.github.com/users/bokeh","html_url":"https://github.com/bokeh","followers_url":"https://api.github.com/users/bokeh/followers","following_url":"https://api.github.com/users/bokeh/following{/other_user}","gists_url":"https://api.github.com/users/bokeh/gists{/gist_id}","starred_url":"https://api.github.com/users/bokeh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bokeh/subscriptions","organizations_url":"https://api.github.com/users/bokeh/orgs","repos_url":"https://api.github.com/users/bokeh/repos","events_url":"https://api.github.com/users/bokeh/events{/privacy}","received_events_url":"https://api.github.com/users/bokeh/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/bokeh/bokeh","description":"Interactive Web Plotting for Python","fork":false,"url":"https://api.github.com/repos/bokeh/bokeh","forks_url":"https://api.github.com/repos/bokeh/bokeh/forks","keys_url":"https://api.github.com/repos/bokeh/bokeh/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bokeh/bokeh/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bokeh/bokeh/teams","hooks_url":"https://api.github.com/repos/bokeh/bokeh/hooks","issue_events_url":"https://api.github.com/repos/bokeh/bokeh/issues/events{/number}","events_url":"https://api.github.com/repos/bokeh/bokeh/events","assignees_url":"https://api.github.com/repos/bokeh/bokeh/assignees{/user}","branches_url":"https://api.github.com/repos/bokeh/bokeh/branches{/branch}","tags_url":"https://api.github.com/repos/bokeh/bokeh/tags","blobs_url":"https://api.github.com/repos/bokeh/bokeh/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bokeh/bokeh/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bokeh/bokeh/git/refs{/sha}","trees_url":"https://api.github.com/repos/bokeh/bokeh/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bokeh/bokeh/statuses/{sha}","languages_url":"https://api.github.com/repos/bokeh/bokeh/languages","stargazers_url":"https://api.github.com/repos/bokeh/bokeh/stargazers","contributors_url":"https://api.github.com/repos/bokeh/bokeh/contributors","subscribers_url":"https://api.github.com/repos/bokeh/bokeh/subscribers","subscription_url":"https://api.github.com/repos/bokeh/bokeh/subscription","commits_url":"https://api.github.com/repos/bokeh/bokeh/commits{/sha}","git_commits_url":"https://api.github.com/repos/bokeh/bokeh/git/commits{/sha}","comments_url":"https://api.github.com/repos/bokeh/bokeh/comments{/number}","issue_comment_url":"https://api.github.com/repos/bokeh/bokeh/issues/comments/{number}","contents_url":"https://api.github.com/repos/bokeh/bokeh/contents/{+path}","compare_url":"https://api.github.com/repos/bokeh/bokeh/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bokeh/bokeh/merges","archive_url":"https://api.github.com/repos/bokeh/bokeh/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bokeh/bokeh/downloads","issues_url":"https://api.github.com/repos/bokeh/bokeh/issues{/number}","pulls_url":"https://api.github.com/repos/bokeh/bokeh/pulls{/number}","milestones_url":"https://api.github.com/repos/bokeh/bokeh/milestones{/number}","notifications_url":"https://api.github.com/repos/bokeh/bokeh/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bokeh/bokeh/labels{/name}","releases_url":"https://api.github.com/repos/bokeh/bokeh/releases{/id}","created_at":"2012-03-26T15:40:01Z","updated_at":"2015-01-19T23:45:05Z","pushed_at":"2015-01-20T14:54:49Z","git_url":"git://github.com/bokeh/bokeh.git","ssh_url":"git@github.com:bokeh/bokeh.git","clone_url":"https://github.com/bokeh/bokeh.git","svn_url":"https://github.com/bokeh/bokeh","homepage":"","size":137005,"stargazers_count":2352,"watchers_count":2352,"language":"CoffeeScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":344,"mirror_url":null,"open_issues_count":336,"forks":344,"open_issues":336,"watchers":2352,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/bokeh/bokeh/pulls/1725"},"html":{"href":"https://github.com/bokeh/bokeh/pull/1725"},"issue":{"href":"https://api.github.com/repos/bokeh/bokeh/issues/1725"},"comments":{"href":"https://api.github.com/repos/bokeh/bokeh/issues/1725/comments"},"review_comments":{"href":"https://api.github.com/repos/bokeh/bokeh/pulls/1725/comments"},"review_comment":{"href":"https://api.github.com/repos/bokeh/bokeh/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/bokeh/bokeh/pulls/1725/commits"},"statuses":{"href":"https://api.github.com/repos/bokeh/bokeh/statuses/8c7ecae4a797cf70907a0ee4e9b6efbf5be6a550"}}}}