{"action":"created","comment":{"url":"https://api.github.com/repos/pydata/xarray/pulls/comments/168577188","pull_request_review_id":96969011,"id":168577188,"diff_hunk":"@@ -654,6 +692,204 @@ def _outer_to_numpy_indexer(key, shape):\n         return _outer_to_vectorized_indexer(key, shape)\n \n \n+class IndexingSupport(object):  # could inherit from enum.Enum on Python 3\n+    # for backends that support only basic indexer\n+    BASIC = 'BASIC'\n+    # for backends that support basic / outer indexer\n+    OUTER = 'OUTER'\n+    # for backends that support outer indexer including at most 1 vector.\n+    OUTER_1VECTOR = 'OUTER_1VECTOR'\n+    # for backends that support full vectorized indexer.\n+    VECTORIZED = 'VECTORIZED'\n+\n+\n+def decompose_indexer(indexer, shape, indexing_support):\n+    if isinstance(indexer, VectorizedIndexer):\n+        return _decompose_vectorized_indexer(indexer, shape, indexing_support)\n+    if isinstance(indexer, OuterIndexer):\n+        return _decompose_outer_indexer(indexer, shape, indexing_support)\n+    if isinstance(indexer, BasicIndexer):\n+        return indexer, ()\n+    raise TypeError('unexpected key type: {}'.format(indexer))\n+\n+\n+def _decompose_vectorized_indexer(indexer, shape, indexing_support):\n+    \"\"\"\n+    Decompose vectorized indexer to the successive two indexers, where the\n+    first indexer will be used to index backend arrays, while the second one\n+    is used to index loaded on-memory np.ndarray.\n+\n+    Parameters\n+    ----------\n+    indexer: VectorizedIndexer\n+    indexing_support: one of IndexerSupport entries\n+\n+    Returns\n+    -------\n+    backend_indexer: OuterIndexer or BasicIndexer\n+    np_indexers: a sequence of vectorizedIndexer\n+\n+    Note\n+    ----\n+    This function is used to realize the vectorized indexing for the backend\n+    arrays that only support basic or outer indexing.\n+\n+    As an example, let us consider to index a few elements from a backend array\n+    with a vectorized indexer ([0, 3, 1], [2, 3, 2]).\n+    Even if the backend array only supports outer indexing, it is more\n+    efficient to load a subslice of the array than loading the entire array,\n+\n+    >>> backend_indexer = OuterIndexer([0, 1, 3], [2, 3])\n+    >>> array = array[backend_indexer]  # load subslice of the array\n+    >>> np_indexer = VectorizedIndexer([0, 2, 1], [0, 1, 0])\n+    >>> array[np_indexer]  # vectorized indexing for on-memory np.ndarray.\n+    \"\"\"\n+    assert isinstance(indexer, VectorizedIndexer)\n+\n+    if indexing_support is IndexingSupport.VECTORIZED:\n+        return indexer, ()\n+\n+    backend_indexer = []\n+    np_indexer = []\n+    # convert negative indices\n+    indexer = [np.where(k < 0, k + s, k) if isinstance(k, np.ndarray) else k\n+               for k, s in zip(indexer.tuple, shape)]\n+\n+    for k in indexer:\n+        if isinstance(k, slice):\n+            # If it is a slice, then we will slice it as-is (k) in the backend,\n+            # and then use all of it (slice(None)) for the in-memory portion.\n+            backend_indexer.append(k)\n+            np_indexer.append(slice(None))\n+        else:  # np.ndarray\n+            oind, vind = np.unique(k, return_inverse=True)\n+            backend_indexer.append(oind)\n+            np_indexer.append(vind.reshape(*k.shape))\n+\n+    backend_indexer = OuterIndexer(tuple(backend_indexer))\n+    np_indexer = VectorizedIndexer(tuple(np_indexer))\n+\n+    if indexing_support is IndexingSupport.OUTER:\n+        return backend_indexer, (np_indexer, )\n+\n+    shape = tuple(s if isinstance(k, slice) else len(k) for k, s in\n+                  zip(backend_indexer.tuple, shape))\n+    backend_indexer, np_indexers = _decompose_outer_indexer(\n+        backend_indexer, shape, indexing_support)\n+    return backend_indexer, (np_indexers[0], np_indexer)\n+\n+\n+def _decompose_outer_indexer(indexer, shape, indexing_support):\n+    \"\"\"\n+    Decompose outer indexer to the successive two indexers, where the\n+    first indexer will be used to index backend arrays, while the second one\n+    is used to index the loaded on-memory np.ndarray.\n+\n+    Parameters\n+    ----------\n+    indexer: VectorizedIndexer\n+    indexing_support: One of the entries of IndexingSupport\n+\n+    Returns\n+    -------\n+    backend_indexer: OuterIndexer or BasicIndexer\n+    np_indexers: a sequence of OuterIndexer\n+\n+    Note\n+    ----\n+    This function is used to realize the vectorized indexing for the backend\n+    arrays that only support basic or outer indexing.\n+\n+    As an example, let us consider to index a few elements from a backend array\n+    with a orthogonal indexer ([0, 3, 1], [2, 3, 2]).\n+    Even if the backend array only supports basic indexing, it is more\n+    efficient to load a subslice of the array than loading the entire array,\n+\n+    >>> backend_indexer = BasicIndexer(slice(0, 3), slice(2, 3))\n+    >>> array = array[backend_indexer]  # load subslice of the array\n+    >>> np_indexer = OuterIndexer([0, 2, 1], [0, 1, 0])\n+    >>> array[np_indexer]  # outer indexing for on-memory np.ndarray.\n+    \"\"\"\n+    if indexing_support in [IndexingSupport.OUTER, IndexingSupport.VECTORIZED]:\n+        return indexer, ()\n+    assert isinstance(indexer, OuterIndexer)\n+\n+    backend_indexer = []\n+    np_indexer = []\n+    # posify\n+    indexer = [np.where(k < 0, k + s, k) if isinstance(k, np.ndarray) else k\n+               for k, s in zip(indexer.tuple, shape)]\n+\n+    if indexing_support is IndexingSupport.OUTER_1VECTOR:\n+        # some backends such as h5py supports only 1 vector in indexers\n+        # We choose the most efficient axis\n+        gains = [(np.max(k) - np.min(k) + 1.0) / len(np.unique(k))\n+                 if isinstance(k, np.ndarray) else 0 for k in indexer]\n+        array_index = np.argmax(np.array(gains)) if len(gains) > 0 else None\n+\n+        for i, k in enumerate(indexer):\n+            if isinstance(k, np.ndarray) and i != array_index:\n+                backend_indexer.append(slice(np.min(k), np.max(k) + 1))\n+                np_indexer.append(k - np.min(k))\n+            elif isinstance(k, np.ndarray):\n+                pkey, ekey = np.unique(k, return_inverse=True)\n+                backend_indexer.append(pkey)\n+                np_indexer.append(ekey)\n+            else:\n+                backend_indexer.append(k)\n+                np_indexer.append(slice(None))\n+\n+        return (OuterIndexer(tuple(backend_indexer)),\n+                (OuterIndexer(tuple(np_indexer)), ))\n+\n+    if indexing_support == IndexingSupport.OUTER:\n+        for k in indexer:\n+            if (isinstance(k, integer_types + (slice, )) or\n+                    (np.diff(k) >= 0).all()):\n+                backend_indexer.append(k)\n+                np_indexer.append(slice(None))\n+            else:  # not sorted np.ndarray indexer\n+                oind, vind = np.unique(k, return_inverse=True)\n+                backend_indexer.append(oind)\n+                np_indexer.append(vind.reshape(*k.shape))\n+\n+        return (OuterIndexer(tuple(backend_indexer)),\n+                (OuterIndexer(tuple(np_indexer)), ))\n+\n+    # basic\n+    for i, k in enumerate(indexer):\n+        if isinstance(k, np.ndarray):\n+            backend_indexer.append(slice(np.min(k), np.max(k) + 1))\n+            np_indexer.append(k - np.min(k))\n+        else:\n+            backend_indexer.append(k)\n+            np_indexer.append(slice(None))\n+\n+    return (BasicIndexer(tuple(backend_indexer)),\n+            (OuterIndexer(tuple(np_indexer)), ))\n+\n+\n+def _arrayize_vectorized_indexer(indexer, shape):\n+    \"\"\" Return an identical vindex but slices are replaced by arrays \"\"\"\n+    slices = [v for v in indexer.tuple if isinstance(v, slice)]\n+    if len(slices) == 0:\n+        return indexer\n+\n+    arrays = [v for v in indexer.tuple if isinstance(v, np.ndarray)]\n+    n_dim = arrays[0].ndim if len(arrays) > 0 else 0\n+    i_dim = 0\n+    new_key = []\n+    for v, size in zip(indexer.tuple, shape):\n+        if isinstance(v, np.ndarray):\n+            new_key.append(np.reshape(v, v.shape + (1, ) * len(slices)))\n+        else:  # slice\n+            shape = ((1,) * (n_dim + i_dim) + (-1,) +\n+                     (1,) * (len(slices) - i_dim - 1))\n+            new_key.append(np.arange(*v.indices(size)).reshape(shape))","path":"xarray/core/indexing.py","position":356,"original_position":356,"commit_id":"936954a318c5af48e4391e9ac38d5880eb663a3e","original_commit_id":"936954a318c5af48e4391e9ac38d5880eb663a3e","user":{"login":"WeatherGod","id":291576,"avatar_url":"https://avatars0.githubusercontent.com/u/291576?v=4","gravatar_id":"","url":"https://api.github.com/users/WeatherGod","html_url":"https://github.com/WeatherGod","followers_url":"https://api.github.com/users/WeatherGod/followers","following_url":"https://api.github.com/users/WeatherGod/following{/other_user}","gists_url":"https://api.github.com/users/WeatherGod/gists{/gist_id}","starred_url":"https://api.github.com/users/WeatherGod/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WeatherGod/subscriptions","organizations_url":"https://api.github.com/users/WeatherGod/orgs","repos_url":"https://api.github.com/users/WeatherGod/repos","events_url":"https://api.github.com/users/WeatherGod/events{/privacy}","received_events_url":"https://api.github.com/users/WeatherGod/received_events","type":"User","site_admin":false},"body":"According to the tests, `v` is sometimes an integer at this point, and thus has an attribute error for `v.indices`.\r\nFrom the test `TestLazyArray.test_vectorized_lazily_indexed_array`:\r\n\r\n```\r\nindexer = BasicIndexer((slice(1, 3, 2), 0)), shape = (1, -1)\r\n    def _arrayize_vectorized_indexer(indexer, shape):\r\n        \"\"\" Return an identical vindex but slices are replaced by arrays \"\"\"\r\n        slices = [v for v in indexer.tuple if isinstance(v, slice)]\r\n        if len(slices) == 0:\r\n            return indexer\r\n    \r\n        arrays = [v for v in indexer.tuple if isinstance(v, np.ndarray)]\r\n        n_dim = arrays[0].ndim if len(arrays) > 0 else 0\r\n        i_dim = 0\r\n        new_key = []\r\n        for v, size in zip(indexer.tuple, shape):\r\n            if isinstance(v, np.ndarray):\r\n                new_key.append(np.reshape(v, v.shape + (1, ) * len(slices)))\r\n            else:  # slice\r\n                shape = ((1,) * (n_dim + i_dim) + (-1,) +\r\n                         (1,) * (len(slices) - i_dim - 1))\r\n>               new_key.append(np.arange(*v.indices(size)).reshape(shape))\r\nE               AttributeError: 'int' object has no attribute 'indices'\r\n```","created_at":"2018-02-15T19:08:01Z","updated_at":"2018-02-15T19:09:53Z","html_url":"https://github.com/pydata/xarray/pull/1899#discussion_r168577188","pull_request_url":"https://api.github.com/repos/pydata/xarray/pulls/1899","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/pydata/xarray/pulls/comments/168577188"},"html":{"href":"https://github.com/pydata/xarray/pull/1899#discussion_r168577188"},"pull_request":{"href":"https://api.github.com/repos/pydata/xarray/pulls/1899"}}},"pull_request":{"url":"https://api.github.com/repos/pydata/xarray/pulls/1899","id":168214895,"html_url":"https://github.com/pydata/xarray/pull/1899","diff_url":"https://github.com/pydata/xarray/pull/1899.diff","patch_url":"https://github.com/pydata/xarray/pull/1899.patch","issue_url":"https://api.github.com/repos/pydata/xarray/issues/1899","number":1899,"state":"open","locked":false,"title":"Vectorized lazy indexing","user":{"login":"fujiisoup","id":6815844,"avatar_url":"https://avatars2.githubusercontent.com/u/6815844?v=4","gravatar_id":"","url":"https://api.github.com/users/fujiisoup","html_url":"https://github.com/fujiisoup","followers_url":"https://api.github.com/users/fujiisoup/followers","following_url":"https://api.github.com/users/fujiisoup/following{/other_user}","gists_url":"https://api.github.com/users/fujiisoup/gists{/gist_id}","starred_url":"https://api.github.com/users/fujiisoup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fujiisoup/subscriptions","organizations_url":"https://api.github.com/users/fujiisoup/orgs","repos_url":"https://api.github.com/users/fujiisoup/repos","events_url":"https://api.github.com/users/fujiisoup/events{/privacy}","received_events_url":"https://api.github.com/users/fujiisoup/received_events","type":"User","site_admin":false},"body":" - [x] Closes #1897\r\n - [x] Tests added (for all bug fixes or enhancements)\r\n - [x] Tests passed (for all non-documentation changes)\r\n - [x] Fully documented, including `whats-new.rst` for all changes and `api.rst` for new API (remove if this change should not be visible to users, e.g., if it is an internal clean-up, or if this is part of a larger project that will be documented later)\r\n\r\nI tried to support lazy vectorised indexing inspired by #1897.\r\nMore tests would be necessary but I want to decide whether it is worth to continue.\r\n\r\nMy current implementation is\r\n+ For outer/basic indexers, we combine successive indexers (as we are doing now).\r\n+ For vectorised indexers, we just store them as is and index sequentially when the evaluation.\r\n\r\nThe implementation was simpler than I thought, but it has a clear limitation.\r\nIt requires to load array before the vectorised indexing (I mean, the evaluation time).\r\nIf we make a vectorised indexing for a large array, the performance significantly drops and it is not noticeable until the evaluation time.\r\n\r\nI appreciate any suggestions.","created_at":"2018-02-09T11:22:01Z","updated_at":"2018-02-15T19:09:53Z","closed_at":null,"merged_at":null,"merge_commit_sha":"319c7f6c3e0c905015ae8d4f86fcb503763c32a6","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/pydata/xarray/pulls/1899/commits","review_comments_url":"https://api.github.com/repos/pydata/xarray/pulls/1899/comments","review_comment_url":"https://api.github.com/repos/pydata/xarray/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pydata/xarray/issues/1899/comments","statuses_url":"https://api.github.com/repos/pydata/xarray/statuses/936954a318c5af48e4391e9ac38d5880eb663a3e","head":{"label":"fujiisoup:vectorized_lazy_indexing","ref":"vectorized_lazy_indexing","sha":"936954a318c5af48e4391e9ac38d5880eb663a3e","user":{"login":"fujiisoup","id":6815844,"avatar_url":"https://avatars2.githubusercontent.com/u/6815844?v=4","gravatar_id":"","url":"https://api.github.com/users/fujiisoup","html_url":"https://github.com/fujiisoup","followers_url":"https://api.github.com/users/fujiisoup/followers","following_url":"https://api.github.com/users/fujiisoup/following{/other_user}","gists_url":"https://api.github.com/users/fujiisoup/gists{/gist_id}","starred_url":"https://api.github.com/users/fujiisoup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fujiisoup/subscriptions","organizations_url":"https://api.github.com/users/fujiisoup/orgs","repos_url":"https://api.github.com/users/fujiisoup/repos","events_url":"https://api.github.com/users/fujiisoup/events{/privacy}","received_events_url":"https://api.github.com/users/fujiisoup/received_events","type":"User","site_admin":false},"repo":{"id":82444894,"name":"xarray","full_name":"fujiisoup/xarray","owner":{"login":"fujiisoup","id":6815844,"avatar_url":"https://avatars2.githubusercontent.com/u/6815844?v=4","gravatar_id":"","url":"https://api.github.com/users/fujiisoup","html_url":"https://github.com/fujiisoup","followers_url":"https://api.github.com/users/fujiisoup/followers","following_url":"https://api.github.com/users/fujiisoup/following{/other_user}","gists_url":"https://api.github.com/users/fujiisoup/gists{/gist_id}","starred_url":"https://api.github.com/users/fujiisoup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fujiisoup/subscriptions","organizations_url":"https://api.github.com/users/fujiisoup/orgs","repos_url":"https://api.github.com/users/fujiisoup/repos","events_url":"https://api.github.com/users/fujiisoup/events{/privacy}","received_events_url":"https://api.github.com/users/fujiisoup/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/fujiisoup/xarray","description":"N-D labeled arrays and datasets in Python","fork":true,"url":"https://api.github.com/repos/fujiisoup/xarray","forks_url":"https://api.github.com/repos/fujiisoup/xarray/forks","keys_url":"https://api.github.com/repos/fujiisoup/xarray/keys{/key_id}","collaborators_url":"https://api.github.com/repos/fujiisoup/xarray/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/fujiisoup/xarray/teams","hooks_url":"https://api.github.com/repos/fujiisoup/xarray/hooks","issue_events_url":"https://api.github.com/repos/fujiisoup/xarray/issues/events{/number}","events_url":"https://api.github.com/repos/fujiisoup/xarray/events","assignees_url":"https://api.github.com/repos/fujiisoup/xarray/assignees{/user}","branches_url":"https://api.github.com/repos/fujiisoup/xarray/branches{/branch}","tags_url":"https://api.github.com/repos/fujiisoup/xarray/tags","blobs_url":"https://api.github.com/repos/fujiisoup/xarray/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/fujiisoup/xarray/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/fujiisoup/xarray/git/refs{/sha}","trees_url":"https://api.github.com/repos/fujiisoup/xarray/git/trees{/sha}","statuses_url":"https://api.github.com/repos/fujiisoup/xarray/statuses/{sha}","languages_url":"https://api.github.com/repos/fujiisoup/xarray/languages","stargazers_url":"https://api.github.com/repos/fujiisoup/xarray/stargazers","contributors_url":"https://api.github.com/repos/fujiisoup/xarray/contributors","subscribers_url":"https://api.github.com/repos/fujiisoup/xarray/subscribers","subscription_url":"https://api.github.com/repos/fujiisoup/xarray/subscription","commits_url":"https://api.github.com/repos/fujiisoup/xarray/commits{/sha}","git_commits_url":"https://api.github.com/repos/fujiisoup/xarray/git/commits{/sha}","comments_url":"https://api.github.com/repos/fujiisoup/xarray/comments{/number}","issue_comment_url":"https://api.github.com/repos/fujiisoup/xarray/issues/comments{/number}","contents_url":"https://api.github.com/repos/fujiisoup/xarray/contents/{+path}","compare_url":"https://api.github.com/repos/fujiisoup/xarray/compare/{base}...{head}","merges_url":"https://api.github.com/repos/fujiisoup/xarray/merges","archive_url":"https://api.github.com/repos/fujiisoup/xarray/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/fujiisoup/xarray/downloads","issues_url":"https://api.github.com/repos/fujiisoup/xarray/issues{/number}","pulls_url":"https://api.github.com/repos/fujiisoup/xarray/pulls{/number}","milestones_url":"https://api.github.com/repos/fujiisoup/xarray/milestones{/number}","notifications_url":"https://api.github.com/repos/fujiisoup/xarray/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/fujiisoup/xarray/labels{/name}","releases_url":"https://api.github.com/repos/fujiisoup/xarray/releases{/id}","deployments_url":"https://api.github.com/repos/fujiisoup/xarray/deployments","created_at":"2017-02-19T08:15:17Z","updated_at":"2017-02-19T08:15:19Z","pushed_at":"2018-02-15T13:55:39Z","git_url":"git://github.com/fujiisoup/xarray.git","ssh_url":"git@github.com:fujiisoup/xarray.git","clone_url":"https://github.com/fujiisoup/xarray.git","svn_url":"https://github.com/fujiisoup/xarray","homepage":"http://xarray.pydata.org","size":11689,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"pydata:master","ref":"master","sha":"1d3239982db9778e89a48fe55b01d0a525673a7a","user":{"login":"pydata","id":1284191,"avatar_url":"https://avatars0.githubusercontent.com/u/1284191?v=4","gravatar_id":"","url":"https://api.github.com/users/pydata","html_url":"https://github.com/pydata","followers_url":"https://api.github.com/users/pydata/followers","following_url":"https://api.github.com/users/pydata/following{/other_user}","gists_url":"https://api.github.com/users/pydata/gists{/gist_id}","starred_url":"https://api.github.com/users/pydata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pydata/subscriptions","organizations_url":"https://api.github.com/users/pydata/orgs","repos_url":"https://api.github.com/users/pydata/repos","events_url":"https://api.github.com/users/pydata/events{/privacy}","received_events_url":"https://api.github.com/users/pydata/received_events","type":"Organization","site_admin":false},"repo":{"id":13221727,"name":"xarray","full_name":"pydata/xarray","owner":{"login":"pydata","id":1284191,"avatar_url":"https://avatars0.githubusercontent.com/u/1284191?v=4","gravatar_id":"","url":"https://api.github.com/users/pydata","html_url":"https://github.com/pydata","followers_url":"https://api.github.com/users/pydata/followers","following_url":"https://api.github.com/users/pydata/following{/other_user}","gists_url":"https://api.github.com/users/pydata/gists{/gist_id}","starred_url":"https://api.github.com/users/pydata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pydata/subscriptions","organizations_url":"https://api.github.com/users/pydata/orgs","repos_url":"https://api.github.com/users/pydata/repos","events_url":"https://api.github.com/users/pydata/events{/privacy}","received_events_url":"https://api.github.com/users/pydata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pydata/xarray","description":"N-D labeled arrays and datasets in Python","fork":false,"url":"https://api.github.com/repos/pydata/xarray","forks_url":"https://api.github.com/repos/pydata/xarray/forks","keys_url":"https://api.github.com/repos/pydata/xarray/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pydata/xarray/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pydata/xarray/teams","hooks_url":"https://api.github.com/repos/pydata/xarray/hooks","issue_events_url":"https://api.github.com/repos/pydata/xarray/issues/events{/number}","events_url":"https://api.github.com/repos/pydata/xarray/events","assignees_url":"https://api.github.com/repos/pydata/xarray/assignees{/user}","branches_url":"https://api.github.com/repos/pydata/xarray/branches{/branch}","tags_url":"https://api.github.com/repos/pydata/xarray/tags","blobs_url":"https://api.github.com/repos/pydata/xarray/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pydata/xarray/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pydata/xarray/git/refs{/sha}","trees_url":"https://api.github.com/repos/pydata/xarray/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pydata/xarray/statuses/{sha}","languages_url":"https://api.github.com/repos/pydata/xarray/languages","stargazers_url":"https://api.github.com/repos/pydata/xarray/stargazers","contributors_url":"https://api.github.com/repos/pydata/xarray/contributors","subscribers_url":"https://api.github.com/repos/pydata/xarray/subscribers","subscription_url":"https://api.github.com/repos/pydata/xarray/subscription","commits_url":"https://api.github.com/repos/pydata/xarray/commits{/sha}","git_commits_url":"https://api.github.com/repos/pydata/xarray/git/commits{/sha}","comments_url":"https://api.github.com/repos/pydata/xarray/comments{/number}","issue_comment_url":"https://api.github.com/repos/pydata/xarray/issues/comments{/number}","contents_url":"https://api.github.com/repos/pydata/xarray/contents/{+path}","compare_url":"https://api.github.com/repos/pydata/xarray/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pydata/xarray/merges","archive_url":"https://api.github.com/repos/pydata/xarray/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pydata/xarray/downloads","issues_url":"https://api.github.com/repos/pydata/xarray/issues{/number}","pulls_url":"https://api.github.com/repos/pydata/xarray/pulls{/number}","milestones_url":"https://api.github.com/repos/pydata/xarray/milestones{/number}","notifications_url":"https://api.github.com/repos/pydata/xarray/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pydata/xarray/labels{/name}","releases_url":"https://api.github.com/repos/pydata/xarray/releases{/id}","deployments_url":"https://api.github.com/repos/pydata/xarray/deployments","created_at":"2013-09-30T17:21:10Z","updated_at":"2018-02-15T02:24:00Z","pushed_at":"2018-02-15T16:53:37Z","git_url":"git://github.com/pydata/xarray.git","ssh_url":"git@github.com:pydata/xarray.git","clone_url":"https://github.com/pydata/xarray.git","svn_url":"https://github.com/pydata/xarray","homepage":"http://xarray.pydata.org","size":11039,"stargazers_count":737,"watchers_count":737,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":237,"mirror_url":null,"archived":false,"open_issues_count":419,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":237,"open_issues":419,"watchers":737,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pydata/xarray/pulls/1899"},"html":{"href":"https://github.com/pydata/xarray/pull/1899"},"issue":{"href":"https://api.github.com/repos/pydata/xarray/issues/1899"},"comments":{"href":"https://api.github.com/repos/pydata/xarray/issues/1899/comments"},"review_comments":{"href":"https://api.github.com/repos/pydata/xarray/pulls/1899/comments"},"review_comment":{"href":"https://api.github.com/repos/pydata/xarray/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pydata/xarray/pulls/1899/commits"},"statuses":{"href":"https://api.github.com/repos/pydata/xarray/statuses/936954a318c5af48e4391e9ac38d5880eb663a3e"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"WeatherGod"}}