{"action":"created","comment":{"url":"https://api.github.com/repos/biocore/scikit-bio/pulls/comments/38800393","id":38800393,"diff_hunk":"@@ -0,0 +1,390 @@\n+# ----------------------------------------------------------------------------\n+# Copyright (c) 2013--, scikit-bio development team.\n+#\n+# Distributed under the terms of the Modified BSD License.\n+#\n+# The full license is in the file COPYING.txt, distributed with this software.\n+# ----------------------------------------------------------------------------\n+\n+from __future__ import absolute_import, division, print_function\n+\n+import numpy as np\n+\n+from skbio.util._decorator import experimental\n+from ._unifrac import _validate\n+from skbio.diversity._fast_base import counts_and_index\n+from skbio.diversity._fast_base_cy import tip_distances\n+\n+\n+def unifrac(m, i, j):\n+    \"\"\"Calculates unifrac(i,j) from m.\n+\n+    Parameters\n+    ----------\n+    m : np.array\n+        A 1D vector that represents the lengths in the tree in postorder.\n+    i,j : np.array\n+        A slice of states from m in postorder.\n+\n+    Returns\n+    -------\n+    float\n+        Unweighted unifrac metric\n+\n+    Notes\n+    -----\n+    This is cogent.maths.unifrac.fast_tree.unifrac, but there are\n+    other metrics that can (should?) be ported like:\n+     - unnormalized_unifrac\n+     - G\n+     - unnormalized_G\n+    \"\"\"\n+    _or = np.logical_or(i, j),\n+    _and = np.logical_and(i, j)\n+    return 1 - ((m * _and).sum() / (m * _or).sum())\n+\n+\n+def w_unifrac(m, i, j, i_sum, j_sum):\n+    \"\"\"Calculates weighted unifrac(i, j) from m\n+\n+    Parameters\n+    ----------\n+    m : np.array\n+        A 1D vector that represents the lengths in the tree in postorder.\n+    i, j : np.array\n+        A slice of states from m in postorder.\n+    i_sum, j_sum: float\n+        Counts of the observations in each environment\n+\n+    Returns\n+    -------\n+    float\n+        The weighted unifrac score\n+    \"\"\"\n+    if i_sum:\n+        i_ = i / i_sum\n+    else:\n+        i_ = 0.0\n+\n+    if j_sum:\n+        j_ = j / j_sum\n+    else:\n+        j_ = 0.0\n+\n+    return (m * abs(i_ - j_)).sum()\n+\n+\n+def make_pdist(counts, obs_ids, tree, indexed=None, metric=unifrac,\n+               normalized=False, **kwargs):\n+    \"\"\"Construct a metric for use with skbio.diversity.beta.pw_distances\n+\n+    Parameters\n+    ----------\n+    counts : np.ndarray\n+        A matrix of environment counts where each row is an environment, and\n+        each column is an observation. The observations are expected to be in\n+        index order w.r.t. obs_ids, but do not need to be in tree order.\n+    obs_ids : np.ndarray\n+        A vector of observation IDs. These IDs must map to tips in the tree.\n+    tree : skbio.tree.TreeNode\n+        A tree that represents the relationships between the observations.\n+    indexed : dict, optiona;\n+        The result of skbio.diversity.beta.index_tree\n+    metric : function, {unweighted_unifrac_fast, weighted_unifrac_fast}\n+        The specific metric to use.\n+    normalized : bool\n+        Whether the weighted unifrac calculation should be normalized.\n+\n+    Notes\n+    -------\n+    example usage\n+\n+    metric, counts, length = make_unweighted_pdist(input_counts, tip_ids, tree)\n+    mat = pw_distances(metric, counts, ids=['%d' % i for i in range(10)])\n+    \"\"\"\n+    count_array, indexed = counts_and_index(counts, obs_ids, tree, indexed)\n+    length = indexed['length']\n+\n+    if metric is unifrac:\n+        def f(u_counts, v_counts):\n+            boundary = _boundary_case(u_counts.sum(), v_counts.sum())\n+            if boundary is not None:\n+                return boundary\n+            return unifrac(length, u_counts, v_counts)\n+\n+    elif metric is w_unifrac:\n+        # This block is duplicated in weighted_unifrac_fast -- possibly should\n+        # be decomposed.\n+        # There is a lot in common with both of these methods, but pulling the\n+        # normalized check out reduces branching logic.\n+        tip_idx = np.array([n.id for n in indexed['id_index'].values()\n+                            if n.is_tip()])\n+        if normalized:\n+            def f(u_counts, v_counts):\n+                u_sum = np.take(u_counts, tip_idx).sum()\n+                v_sum = np.take(v_counts, tip_idx).sum()\n+\n+                boundary = _boundary_case(u_sum, v_sum, normalized,\n+                                          unweighted=False)\n+                if boundary is not None:\n+                    return boundary\n+\n+                tip_ds = tip_distances(length, tree, tip_idx)\n+\n+                u = w_unifrac(length, u_counts, v_counts, u_sum, v_sum)\n+                u /= _branch_correct(tip_ds, u_counts, v_counts, u_sum, v_sum)\n+                return u\n+        else:\n+            def f(u_counts, v_counts):\n+                u_sum = np.take(u_counts, tip_idx).sum()\n+                v_sum = np.take(v_counts, tip_idx).sum()\n+\n+                boundary = _boundary_case(u_sum, v_sum, unweighted=False)\n+                if boundary is not None:\n+                    return boundary\n+\n+                u = w_unifrac(length, u_counts, v_counts, u_sum, v_sum)\n+                return u\n+    else:\n+        raise AttributeError(\"Unknown metric: %s\" % metric)\n+\n+    return f, count_array.T, length\n+\n+\n+@experimental(as_of=\"0.4.0\")\n+def unweighted_unifrac_fast(u_counts, v_counts, otu_ids, tree,\n+                            validate=True, indexed=None, **kwargs):\n+    \"\"\"TODO: add or just pull from educational's __doc__?\"\"\"\n+    if validate:\n+        _validate(u_counts, v_counts, otu_ids, tree)\n+\n+    # cast to numpy types\n+    u_counts = np.asarray(u_counts)\n+    v_counts = np.asarray(v_counts)\n+    otu_ids = np.asarray(otu_ids)\n+\n+    boundary = _boundary_case(u_counts.sum(), v_counts.sum())\n+    if boundary is not None:\n+        return boundary\n+\n+    # aggregate state information up the tree (stored in counts_array), and\n+    # retrieve the aggregated state information for each input count vector\n+    counts = np.vstack([u_counts, v_counts])\n+    count_array, indexed = counts_and_index(counts, otu_ids, tree, indexed)\n+    u_counts = count_array[:, 0]\n+    v_counts = count_array[:, 1]\n+\n+    length = indexed['length']\n+\n+    return unifrac(length, u_counts, v_counts)\n+\n+\n+@experimental(as_of=\"0.4.0\")\n+def weighted_unifrac_fast(u_counts, v_counts, otu_ids, tree, normalized=False,\n+                          validate=True, indexed=None, **kwargs):\n+    \"\"\"TODO: add or just pull from educational's __doc__?\"\"\"\n+    if validate:\n+        _validate(u_counts, v_counts, otu_ids, tree)\n+\n+    # convert to numpy types\n+    u_counts = np.asarray(u_counts)\n+    v_counts = np.asarray(v_counts)\n+    otu_ids = np.asarray(otu_ids)\n+\n+    u_sum = u_counts.sum()\n+    v_sum = v_counts.sum()\n+\n+    # check boundary conditions and shortcut if necessary\n+    boundary = _boundary_case(u_sum, v_sum, normalized, unweighted=False)\n+    if boundary is not None:\n+        return boundary\n+\n+    # aggregate state information up the tree (stored in counts_array), and\n+    # retrieve the aggregated state information for each input count vector\n+    counts = np.vstack([u_counts, v_counts])\n+    count_array, indexed = counts_and_index(counts, otu_ids, tree, indexed)\n+    u_counts = count_array[:, 0]\n+    v_counts = count_array[:, 1]\n+\n+    # fetch the lengths\n+    length = indexed['length']\n+\n+    u = w_unifrac(length, u_counts, v_counts, u_sum, v_sum)\n+    if normalized:\n+        # get the index positions for tips in counts_array, and determine the\n+        # tip distances to the root\n+        tip_indices = np.array([n.id for n in indexed['id_index'].values()\n+                                if n.is_tip()])\n+        tip_ds = tip_distances(length, tree, tip_indices)\n+        u /= _branch_correct(tip_ds, u_counts, v_counts, u_sum, v_sum)\n+\n+    return u\n+\n+\n+def _boundary_case(u_sum, v_sum, normalized=False, unweighted=True):\n+    \"\"\"Test for boundary conditions\n+\n+    Parameters\n+    ----------\n+    u_sum, v_sum: float\n+        The sum of the observations in both environments.\n+    normalized: bool\n+        Indicates if the method is normalized.\n+    unweighted: bool\n+        Indicates if the method is weighted.\n+\n+    Returns\n+    -------\n+    float or None\n+        Specifically, one of `[0.0, 1.0, None]`. `None` indicates that a\n+        boundary condition was not observed.\n+\n+    Notes\n+    -----\n+    The following boundary conditions are tested:\n+\n+        * if u_sum or v_sum are zero\n+        * if both u_sum and v_sum are zero\n+        * both of the above conditions with respect to normalized and weighted\n+    \"\"\"\n+    if u_sum and v_sum:\n+        return None\n+\n+    if u_sum + v_sum:\n+        # u or v counts are all zeros\n+        if unweighted or normalized:\n+            # u or v counts are all zeros\n+            return 1.0\n+        # NOTE: we cannot handle the unnormalized case here yet as it\n+        # requires operations on the tree vector\n+    else:\n+        # u and v are zero\n+        return 0.0\n+\n+    return None\n+\n+\n+def _branch_correct(tip_dists, i, j, i_sum, j_sum):\n+    \"\"\"Calculates weighted unifrac branch length correction.\n+\n+    Parameters\n+    ----------\n+    tip_dists : np.ndarray\n+        1D column vector of branch lengths in post order form. Only tips\n+        should be non-zero as this represents the distance from each tip to\n+        root.\n+    i, j : np.ndarray\n+        Aggregated environment counts. This vector is expected to be in index\n+        order with tip_dists\n+    i_sum, j_sum: float\n+        Counts of the observations in each environment\n+\n+    Returns\n+    -------\n+    np.ndarray\n+        The corrected branch lengths\n+    \"\"\"\n+    return (tip_dists.ravel() * ((i / i_sum) + (j / j_sum))).sum()\n+\n+\n+# TODO: define a block pdist method such that \"blocks\" of the resulting\n+# distance matrix can be computed independently. This would allow for an\n+# embarassingly parallel strategy for computing large distance matrices. This\n+# method needs to be paired with a DistanceMatrix assembly method, however,\n+# it may be beneficial to \"assemble\" directly into a memory mapped data\n+# structure.\n+\n+\n+def block_pdist(observation_data):\n+    \"\"\"Compute a distance matrix in parallel\n+","path":"skbio/diversity/beta/_fast_unifrac.py","position":300,"original_position":300,"commit_id":"602323b3db14019df536bbaed41d6d328a4c788a","original_commit_id":"602323b3db14019df536bbaed41d6d328a4c788a","user":{"login":"mortonjt","id":4184797,"avatar_url":"https://avatars.githubusercontent.com/u/4184797?v=3","gravatar_id":"","url":"https://api.github.com/users/mortonjt","html_url":"https://github.com/mortonjt","followers_url":"https://api.github.com/users/mortonjt/followers","following_url":"https://api.github.com/users/mortonjt/following{/other_user}","gists_url":"https://api.github.com/users/mortonjt/gists{/gist_id}","starred_url":"https://api.github.com/users/mortonjt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mortonjt/subscriptions","organizations_url":"https://api.github.com/users/mortonjt/orgs","repos_url":"https://api.github.com/users/mortonjt/repos","events_url":"https://api.github.com/users/mortonjt/events{/privacy}","received_events_url":"https://api.github.com/users/mortonjt/received_events","type":"User","site_admin":false},"body":"Do you think that this would be more appropriate to put in `skbio.stats.distance._base.py`?  There is a function called `DistanceMatrix.from_iterable` that does this serially.","created_at":"2015-09-04T22:44:36Z","updated_at":"2015-09-04T22:44:36Z","html_url":"https://github.com/biocore/scikit-bio/pull/1040#discussion_r38800393","pull_request_url":"https://api.github.com/repos/biocore/scikit-bio/pulls/1040","_links":{"self":{"href":"https://api.github.com/repos/biocore/scikit-bio/pulls/comments/38800393"},"html":{"href":"https://github.com/biocore/scikit-bio/pull/1040#discussion_r38800393"},"pull_request":{"href":"https://api.github.com/repos/biocore/scikit-bio/pulls/1040"}}},"pull_request":{"url":"https://api.github.com/repos/biocore/scikit-bio/pulls/1040","id":42083676,"html_url":"https://github.com/biocore/scikit-bio/pull/1040","diff_url":"https://github.com/biocore/scikit-bio/pull/1040.diff","patch_url":"https://github.com/biocore/scikit-bio/pull/1040.patch","issue_url":"https://api.github.com/repos/biocore/scikit-bio/issues/1040","number":1040,"state":"open","locked":false,"title":"Fast UniFrac","user":{"login":"wasade","id":474290,"avatar_url":"https://avatars.githubusercontent.com/u/474290?v=3","gravatar_id":"","url":"https://api.github.com/users/wasade","html_url":"https://github.com/wasade","followers_url":"https://api.github.com/users/wasade/followers","following_url":"https://api.github.com/users/wasade/following{/other_user}","gists_url":"https://api.github.com/users/wasade/gists{/gist_id}","starred_url":"https://api.github.com/users/wasade/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wasade/subscriptions","organizations_url":"https://api.github.com/users/wasade/orgs","repos_url":"https://api.github.com/users/wasade/repos","events_url":"https://api.github.com/users/wasade/events{/privacy}","received_events_url":"https://api.github.com/users/wasade/received_events","type":"User","site_admin":false},"body":"Laying the ground work for a Fast UniFrac implementation in scikit-bio. Right now, the code appears to work for unweighted UniFrac in a couple of one-off tests. Weighted still has some tinkering with. For the most part, all I've done is pulled out the minimal set of functionality from PyCogent based on observed function calls, and done some general maintenance to get the code to operate with scikit-bio's `TreeNode`. \r\n\r\nThere is a lot left to do (outlining below). A review is not necessary at this time. It is not assured that this PR will be merged once the code is in shape as that is contingent on benchmarking. The algorithm that backs Fast UniFrac should offer a reasonable performance benefit over UniFrac, the downside being that the code is more complex. If this gets merged it may make sense to adopt a strategy similar to the aligners where there is an educational implementation with an `EfficiencyWarning` and a more performing one that is recommended for general use.\r\n\r\n- [x] adapt weighted unifrac code\r\n- [x] adapt PD\r\n- [x] tie into the unifrac interface defined in #1027\r\n- [x] tie into the test code defined in #1027\r\n- [x] tie PD into test code defined in #1027 \r\n- [x] benchmark against educational implementation (see [here](https://github.com/wasade/yummy-octo-duck/blob/master/ipynb/UniFrac%20benchmarking.ipynb))\r\n- [ ] benchmark against QIIME\r\n\r\nIf the benchmarks pan out, then the next (minimal) steps necessary are:\r\n\r\n- [x] profile, as there may be hotspots given the time since the original implementation\r\n- [x] optimize unweighted unifrac\r\n- [x] optimize weighted unifrac\r\n- [ ] continue optimization of normalized weighted unifrac (currently ~10x slower than weighted)\r\n- [x] optimize faith's pd\r\n- [ ] implement a driver for Faith's PD that can compute multiple envs in parallel\r\n- [x] add stability decorators\r\n- [x] refresh docstrings to be in numpydoc\r\n- [ ] include examples in the docstrings\r\n- [x] optimize implementation to the API\r\n- [x] migrate PD method call from `skbio.diversity.beta` to `skbio.diversity.alpha`\r\n- [ ] implement a block distance matrix method (presently stubbed out in code)\r\n- [ ] squash commits\r\n- [ ] changelog mention\r\n","created_at":"2015-08-11T01:16:19Z","updated_at":"2015-09-04T22:44:36Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5c17bb7b34d152687bc2501965c2717c665f7110","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/biocore/scikit-bio/pulls/1040/commits","review_comments_url":"https://api.github.com/repos/biocore/scikit-bio/pulls/1040/comments","review_comment_url":"https://api.github.com/repos/biocore/scikit-bio/pulls/comments{/number}","comments_url":"https://api.github.com/repos/biocore/scikit-bio/issues/1040/comments","statuses_url":"https://api.github.com/repos/biocore/scikit-bio/statuses/602323b3db14019df536bbaed41d6d328a4c788a","head":{"label":"wasade:fast_unifrac","ref":"fast_unifrac","sha":"602323b3db14019df536bbaed41d6d328a4c788a","user":{"login":"wasade","id":474290,"avatar_url":"https://avatars.githubusercontent.com/u/474290?v=3","gravatar_id":"","url":"https://api.github.com/users/wasade","html_url":"https://github.com/wasade","followers_url":"https://api.github.com/users/wasade/followers","following_url":"https://api.github.com/users/wasade/following{/other_user}","gists_url":"https://api.github.com/users/wasade/gists{/gist_id}","starred_url":"https://api.github.com/users/wasade/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wasade/subscriptions","organizations_url":"https://api.github.com/users/wasade/orgs","repos_url":"https://api.github.com/users/wasade/repos","events_url":"https://api.github.com/users/wasade/events{/privacy}","received_events_url":"https://api.github.com/users/wasade/received_events","type":"User","site_admin":false},"repo":{"id":17808144,"name":"scikit-bio","full_name":"wasade/scikit-bio","owner":{"login":"wasade","id":474290,"avatar_url":"https://avatars.githubusercontent.com/u/474290?v=3","gravatar_id":"","url":"https://api.github.com/users/wasade","html_url":"https://github.com/wasade","followers_url":"https://api.github.com/users/wasade/followers","following_url":"https://api.github.com/users/wasade/following{/other_user}","gists_url":"https://api.github.com/users/wasade/gists{/gist_id}","starred_url":"https://api.github.com/users/wasade/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wasade/subscriptions","organizations_url":"https://api.github.com/users/wasade/orgs","repos_url":"https://api.github.com/users/wasade/repos","events_url":"https://api.github.com/users/wasade/events{/privacy}","received_events_url":"https://api.github.com/users/wasade/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/wasade/scikit-bio","description":"Core objects, functions and statistics for working with biological data in Python.","fork":true,"url":"https://api.github.com/repos/wasade/scikit-bio","forks_url":"https://api.github.com/repos/wasade/scikit-bio/forks","keys_url":"https://api.github.com/repos/wasade/scikit-bio/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wasade/scikit-bio/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wasade/scikit-bio/teams","hooks_url":"https://api.github.com/repos/wasade/scikit-bio/hooks","issue_events_url":"https://api.github.com/repos/wasade/scikit-bio/issues/events{/number}","events_url":"https://api.github.com/repos/wasade/scikit-bio/events","assignees_url":"https://api.github.com/repos/wasade/scikit-bio/assignees{/user}","branches_url":"https://api.github.com/repos/wasade/scikit-bio/branches{/branch}","tags_url":"https://api.github.com/repos/wasade/scikit-bio/tags","blobs_url":"https://api.github.com/repos/wasade/scikit-bio/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wasade/scikit-bio/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wasade/scikit-bio/git/refs{/sha}","trees_url":"https://api.github.com/repos/wasade/scikit-bio/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wasade/scikit-bio/statuses/{sha}","languages_url":"https://api.github.com/repos/wasade/scikit-bio/languages","stargazers_url":"https://api.github.com/repos/wasade/scikit-bio/stargazers","contributors_url":"https://api.github.com/repos/wasade/scikit-bio/contributors","subscribers_url":"https://api.github.com/repos/wasade/scikit-bio/subscribers","subscription_url":"https://api.github.com/repos/wasade/scikit-bio/subscription","commits_url":"https://api.github.com/repos/wasade/scikit-bio/commits{/sha}","git_commits_url":"https://api.github.com/repos/wasade/scikit-bio/git/commits{/sha}","comments_url":"https://api.github.com/repos/wasade/scikit-bio/comments{/number}","issue_comment_url":"https://api.github.com/repos/wasade/scikit-bio/issues/comments{/number}","contents_url":"https://api.github.com/repos/wasade/scikit-bio/contents/{+path}","compare_url":"https://api.github.com/repos/wasade/scikit-bio/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wasade/scikit-bio/merges","archive_url":"https://api.github.com/repos/wasade/scikit-bio/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wasade/scikit-bio/downloads","issues_url":"https://api.github.com/repos/wasade/scikit-bio/issues{/number}","pulls_url":"https://api.github.com/repos/wasade/scikit-bio/pulls{/number}","milestones_url":"https://api.github.com/repos/wasade/scikit-bio/milestones{/number}","notifications_url":"https://api.github.com/repos/wasade/scikit-bio/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wasade/scikit-bio/labels{/name}","releases_url":"https://api.github.com/repos/wasade/scikit-bio/releases{/id}","created_at":"2014-03-16T20:13:28Z","updated_at":"2014-06-25T14:53:19Z","pushed_at":"2015-08-31T20:12:06Z","git_url":"git://github.com/wasade/scikit-bio.git","ssh_url":"git@github.com:wasade/scikit-bio.git","clone_url":"https://github.com/wasade/scikit-bio.git","svn_url":"https://github.com/wasade/scikit-bio","homepage":"https://github.com/biocore/scikit-bio","size":20718,"stargazers_count":0,"watchers_count":0,"language":"Python","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":"biocore:master","ref":"master","sha":"41e3e49e0a7a2700fc36bf5dd48faa29edfd085f","user":{"login":"biocore","id":6241857,"avatar_url":"https://avatars.githubusercontent.com/u/6241857?v=3","gravatar_id":"","url":"https://api.github.com/users/biocore","html_url":"https://github.com/biocore","followers_url":"https://api.github.com/users/biocore/followers","following_url":"https://api.github.com/users/biocore/following{/other_user}","gists_url":"https://api.github.com/users/biocore/gists{/gist_id}","starred_url":"https://api.github.com/users/biocore/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/biocore/subscriptions","organizations_url":"https://api.github.com/users/biocore/orgs","repos_url":"https://api.github.com/users/biocore/repos","events_url":"https://api.github.com/users/biocore/events{/privacy}","received_events_url":"https://api.github.com/users/biocore/received_events","type":"Organization","site_admin":false},"repo":{"id":15168264,"name":"scikit-bio","full_name":"biocore/scikit-bio","owner":{"login":"biocore","id":6241857,"avatar_url":"https://avatars.githubusercontent.com/u/6241857?v=3","gravatar_id":"","url":"https://api.github.com/users/biocore","html_url":"https://github.com/biocore","followers_url":"https://api.github.com/users/biocore/followers","following_url":"https://api.github.com/users/biocore/following{/other_user}","gists_url":"https://api.github.com/users/biocore/gists{/gist_id}","starred_url":"https://api.github.com/users/biocore/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/biocore/subscriptions","organizations_url":"https://api.github.com/users/biocore/orgs","repos_url":"https://api.github.com/users/biocore/repos","events_url":"https://api.github.com/users/biocore/events{/privacy}","received_events_url":"https://api.github.com/users/biocore/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/biocore/scikit-bio","description":"scikit-bio is an open-source, BSD-licensed, Python package providing data structures, algorithms, and educational resources for bioinformatics.","fork":false,"url":"https://api.github.com/repos/biocore/scikit-bio","forks_url":"https://api.github.com/repos/biocore/scikit-bio/forks","keys_url":"https://api.github.com/repos/biocore/scikit-bio/keys{/key_id}","collaborators_url":"https://api.github.com/repos/biocore/scikit-bio/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/biocore/scikit-bio/teams","hooks_url":"https://api.github.com/repos/biocore/scikit-bio/hooks","issue_events_url":"https://api.github.com/repos/biocore/scikit-bio/issues/events{/number}","events_url":"https://api.github.com/repos/biocore/scikit-bio/events","assignees_url":"https://api.github.com/repos/biocore/scikit-bio/assignees{/user}","branches_url":"https://api.github.com/repos/biocore/scikit-bio/branches{/branch}","tags_url":"https://api.github.com/repos/biocore/scikit-bio/tags","blobs_url":"https://api.github.com/repos/biocore/scikit-bio/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/biocore/scikit-bio/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/biocore/scikit-bio/git/refs{/sha}","trees_url":"https://api.github.com/repos/biocore/scikit-bio/git/trees{/sha}","statuses_url":"https://api.github.com/repos/biocore/scikit-bio/statuses/{sha}","languages_url":"https://api.github.com/repos/biocore/scikit-bio/languages","stargazers_url":"https://api.github.com/repos/biocore/scikit-bio/stargazers","contributors_url":"https://api.github.com/repos/biocore/scikit-bio/contributors","subscribers_url":"https://api.github.com/repos/biocore/scikit-bio/subscribers","subscription_url":"https://api.github.com/repos/biocore/scikit-bio/subscription","commits_url":"https://api.github.com/repos/biocore/scikit-bio/commits{/sha}","git_commits_url":"https://api.github.com/repos/biocore/scikit-bio/git/commits{/sha}","comments_url":"https://api.github.com/repos/biocore/scikit-bio/comments{/number}","issue_comment_url":"https://api.github.com/repos/biocore/scikit-bio/issues/comments{/number}","contents_url":"https://api.github.com/repos/biocore/scikit-bio/contents/{+path}","compare_url":"https://api.github.com/repos/biocore/scikit-bio/compare/{base}...{head}","merges_url":"https://api.github.com/repos/biocore/scikit-bio/merges","archive_url":"https://api.github.com/repos/biocore/scikit-bio/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/biocore/scikit-bio/downloads","issues_url":"https://api.github.com/repos/biocore/scikit-bio/issues{/number}","pulls_url":"https://api.github.com/repos/biocore/scikit-bio/pulls{/number}","milestones_url":"https://api.github.com/repos/biocore/scikit-bio/milestones{/number}","notifications_url":"https://api.github.com/repos/biocore/scikit-bio/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/biocore/scikit-bio/labels{/name}","releases_url":"https://api.github.com/repos/biocore/scikit-bio/releases{/id}","created_at":"2013-12-13T16:24:41Z","updated_at":"2015-09-03T12:48:25Z","pushed_at":"2015-09-04T22:08:50Z","git_url":"git://github.com/biocore/scikit-bio.git","ssh_url":"git@github.com:biocore/scikit-bio.git","clone_url":"https://github.com/biocore/scikit-bio.git","svn_url":"https://github.com/biocore/scikit-bio","homepage":"http://scikit-bio.org","size":41868,"stargazers_count":155,"watchers_count":155,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":86,"mirror_url":null,"open_issues_count":162,"forks":86,"open_issues":162,"watchers":155,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/biocore/scikit-bio/pulls/1040"},"html":{"href":"https://github.com/biocore/scikit-bio/pull/1040"},"issue":{"href":"https://api.github.com/repos/biocore/scikit-bio/issues/1040"},"comments":{"href":"https://api.github.com/repos/biocore/scikit-bio/issues/1040/comments"},"review_comments":{"href":"https://api.github.com/repos/biocore/scikit-bio/pulls/1040/comments"},"review_comment":{"href":"https://api.github.com/repos/biocore/scikit-bio/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/biocore/scikit-bio/pulls/1040/commits"},"statuses":{"href":"https://api.github.com/repos/biocore/scikit-bio/statuses/602323b3db14019df536bbaed41d6d328a4c788a"}}}}