{"action":"created","comment":{"url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/comments/70475466","id":70475466,"diff_hunk":"@@ -0,0 +1,389 @@\n+# -*- coding: utf-8 -*-\n+# Copyright 2016 The HyperSpy developers\n+#\n+# This file is part of  HyperSpy.\n+#\n+#  HyperSpy is free software: you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation, either version 3 of the License, or\n+# (at your option) any later version.\n+#\n+#  HyperSpy is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with  HyperSpy.  If not, see <http://www.gnu.org/licenses/>.\n+\n+import logging\n+\n+import numpy as np\n+import scipy.linalg\n+\n+from hyperspy.misc.machine_learning.import_sklearn import (\n+    fast_svd, sklearn_installed)\n+\n+_logger = logging.getLogger(__name__)\n+\n+def _thresh(X, lambda1):\n+    res = np.abs(X) - lambda1\n+    return np.sign(X) * ((res > 0) * res)\n+\n+def rpca(X, rank, fast=False, lambda1=None,\n+         power=None, tol=None, maxiter=None):\n+    \"\"\"\n+    This function performs Robust PCA with missing or corrupted data,\n+    using the GoDec algorithm\n+\n+    Parameters\n+    ----------\n+    X : numpy array\n+        is the [nfeatures x nsamples] matrix of observations.\n+    rank : int\n+        The model dimensionality.\n+    lambda1 : None | float\n+        Regularization parameter.\n+        If None, set to 1 / sqrt(nsamples)\n+    power : None | integer\n+        The number of power iterations used in the initialization\n+        If None, set to 0 for speed\n+    tol : None | float\n+        Convergence tolerance\n+        If None, set to 1e-3\n+    maxiter : None | integer\n+        Maximum number of iterations\n+        If None, set to 1e3\n+\n+    Returns\n+    -------\n+    Xhat : numpy array\n+        is the [nfeatures x nsamples] low-rank matrix\n+    Ehat : numpy array\n+        is the [nfeatures x nsamples] sparse error matrix\n+    Ghat : numpy array\n+        is the [nfeatures x nsamples] Gaussian noise matrix\n+    U, S, V : numpy arrays\n+        are the results of an SVD on Xhat\n+\n+    Notes\n+    -----\n+    Algorithm based on the following research paper:\n+       Tianyi Zhou and Dacheng Tao, \"GoDec: Randomized Low-rank & Sparse Matrix\n+       Decomposition in Noisy Case\", ICML-11, (2011), pp. 33-40.\n+\n+    \"\"\"\n+    if fast is True and sklearn_installed is True:\n+        def svd(X):\n+            return fast_svd(X, rank)\n+    else:\n+        def svd(X):\n+            return scipy.linalg.svd(X, full_matrices=False)\n+\n+    # Get shape\n+    m, n = X.shape\n+\n+    # Operate on transposed matrix for speed\n+    transpose = False\n+    if m < n:\n+        transpose = True\n+        X = X.T\n+\n+    # Get shape\n+    m, n = X.shape\n+\n+    # Check options if None\n+    if lambda1 is None:\n+        _logger.warning(\"Threshold 'lambda1' is set to \"\n+                        \"default: 1 / sqrt(nsamples)\")\n+        lambda1 = 1.0 / np.sqrt(n)\n+    if power is None:\n+        _logger.warning(\"Number of power iterations not specified. \"\n+                        \"Defaulting to 0\")\n+        power = 0\n+    if tol is None:\n+        _logger.warning(\"Convergence tolerance not specifed. \"\n+                        \"Defaulting to 1e-3\")\n+        tol = 1e-3\n+    if maxiter is None:\n+        _logger.warning(\"Maximum iterations not specified. \"\n+                        \"Defaulting to 1e3\")\n+        maxiter = 1e3\n+\n+    # Get min & max of data matrix for scaling\n+    X_max = np.max(X)\n+    X_min = np.min(X)\n+    X = (X - X_min) / X_max\n+\n+    # Initialize L and E\n+    L = X\n+    E = np.zeros(L.shape)\n+\n+    itr = 0\n+    while True:\n+        itr += 1\n+\n+        # Initialization with bilateral random projections\n+        Y2 = np.random.randn(n, rank)\n+        for i in range(power + 1):\n+            Y2 = np.dot(L.T, np.dot(L, Y2));\n+        Q, tmp = scipy.linalg.qr(Y2, mode='economic')\n+\n+        # Estimate the new low-rank and sparse matrices\n+        Lnew = np.dot(np.dot(L, Q), Q.T)\n+        A = L - Lnew + E\n+        L = Lnew\n+        E = _thresh(A, lambda1)\n+        A -= E\n+        L += A\n+\n+        # Check convergence\n+        eps = np.linalg.norm(A)\n+        if (eps < tol):\n+            _logger.info(\"Converged to %f in %d iterations\" % (eps, itr))\n+            break\n+        elif (itr >= maxiter):\n+            _logger.warning(\"Maximum iterations reached\")\n+            break\n+\n+    # Get the remaining Gaussian noise matrix\n+    G = X - L - E\n+\n+    # Transpose back\n+    if transpose:\n+        L = L.T\n+        E = E.T\n+        G = G.T\n+\n+    # Rescale\n+    Xhat = (L * X_max) + X_min\n+    Ehat = (E * X_max) + X_min\n+    Ghat = (G * X_max) + X_min\n+\n+    # Do final SVD\n+    U, S, Vh = svd(Xhat)\n+    V = Vh.T\n+\n+    # Chop small singular values which\n+    # likely arise from numerical noise\n+    # in the SVD.\n+    S[rank:] = 0.\n+\n+    return Xhat, Ehat, Ghat, U, S, V\n+\n+\n+def _solveproj(z, X, I, lambda2):\n+    m, n = X.shape\n+    s = np.zeros(m)\n+    x = np.zeros(n)\n+    maxiter = 1e9\n+    itr = 0\n+\n+    ddt = np.dot(scipy.linalg.inv(np.dot(X.T, X) + I), X.T)\n+\n+    while True:\n+        itr += 1\n+        xtmp = x\n+        x = np.dot(ddt, (z - s))\n+        stmp = s\n+        s = np.maximum(z - np.dot(X, x) - lambda2, 0.0)\n+        stopx = np.sqrt(np.dot(x - xtmp, (x - xtmp).conj()))\n+        stops = np.sqrt(np.dot(s - stmp, (s - stmp).conj()))\n+        stop = max(stopx, stops) / m\n+        if stop < 1e-6 or itr > maxiter:\n+            break\n+\n+    return x, s\n+\n+def _updatecol(X, A, B, I):\n+    tmp, n = X.shape\n+    L = X\n+    A = A + I\n+\n+    for i in range(n):\n+        b = B[:, i]\n+        x = X[:, i]\n+        a = A[:, i]\n+        temp = (b - np.dot(X, a)) / A[i, i] + x\n+        L[:, i] = temp / max(np.sqrt(np.dot(temp, temp.conj())), 1)\n+\n+    return L\n+\n+def orpca(X, rank, fast=False,\n+          lambda1=None,\n+          lambda2=None,\n+          method=None,\n+          learning_rate=None,\n+          init=None,\n+          training_samples=None):\n+    \"\"\"\n+    This function performs Online Robust PCA\n+    with missing or corrupted data.\n+\n+    Parameters\n+    ----------\n+    X : numpy array\n+        is the [nfeatures x nsamples] matrix of observations.\n+    rank : int\n+        The model dimensionality.\n+    lambda1 : None | float\n+        Nuclear norm regularization parameter.\n+        If None, set to 1 / sqrt(nsamples)\n+    lambda2 : None | float\n+        Sparse error regularization parameter.\n+        If None, set to 1 / sqrt(nsamples)\n+    method : None | 'CF' | 'BCD' | 'SGD'\n+        'CF'  - Closed-form solver\n+        'BCD' - Block-coordinate descent\n+        'SGD' - Stochastic gradient descent\n+        If None, set to 'CF'\n+    learning_rate : None | float\n+        Learning rate for the stochastic gradient\n+        descent algorithm\n+        If None, set to 1\n+    init : None | 'qr' | 'rand'\n+        'qr'   - QR-based initialization\n+        'rand' - Random initialization\n+        If None, set to 'qr'\n+    training_samples : integer\n+        Specifies the number of training samples to use in\n+        the 'qr' initialization\n+        If None, set to 10\n+\n+    Returns\n+    -------\n+    Xhat : numpy array\n+        is the [nfeatures x nsamples] low-rank matrix\n+    Ehat : numpy array\n+        is the [nfeatures x nsamples] sparse error matrix\n+    U, S, V : numpy arrays\n+        are the results of an SVD on Xhat\n+\n+    Notes\n+    -----\n+    The ORPCA code is based on a transcription of MATLAB code obtained from\n+    the following research paper:\n+       Jiashi Feng, Huan Xu and Shuicheng Yuan, \"Online Robust PCA via\n+       Stochastic Optimization\", Advances in Neural Information Processing\n+       Systems 26, (2013), pp. 404-412.\n+\n+    It has been updated to include a new initialization method based\n+    on a QR decomposition of the first n \"training\" samples of the data.\n+    A stochastic gradient descent solver is also implemented.\n+","path":"hyperspy/learn/rpca.py","position":273,"original_position":273,"commit_id":"a60ad43d75184b87057d8e23111bb32cc972f5a8","original_commit_id":"a60ad43d75184b87057d8e23111bb32cc972f5a8","user":{"login":"francisco-dlp","id":989719,"avatar_url":"https://avatars.githubusercontent.com/u/989719?v=3","gravatar_id":"","url":"https://api.github.com/users/francisco-dlp","html_url":"https://github.com/francisco-dlp","followers_url":"https://api.github.com/users/francisco-dlp/followers","following_url":"https://api.github.com/users/francisco-dlp/following{/other_user}","gists_url":"https://api.github.com/users/francisco-dlp/gists{/gist_id}","starred_url":"https://api.github.com/users/francisco-dlp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/francisco-dlp/subscriptions","organizations_url":"https://api.github.com/users/francisco-dlp/orgs","repos_url":"https://api.github.com/users/francisco-dlp/repos","events_url":"https://api.github.com/users/francisco-dlp/events{/privacy}","received_events_url":"https://api.github.com/users/francisco-dlp/received_events","type":"User","site_admin":false},"body":"If this is a translation from MATLAB code, could you provide a link to the original code?","created_at":"2016-07-12T16:47:58Z","updated_at":"2016-07-12T16:47:58Z","html_url":"https://github.com/hyperspy/hyperspy/pull/1128#discussion_r70475466","pull_request_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/1128","_links":{"self":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/comments/70475466"},"html":{"href":"https://github.com/hyperspy/hyperspy/pull/1128#discussion_r70475466"},"pull_request":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/1128"}}},"pull_request":{"url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/1128","id":77098790,"html_url":"https://github.com/hyperspy/hyperspy/pull/1128","diff_url":"https://github.com/hyperspy/hyperspy/pull/1128.diff","patch_url":"https://github.com/hyperspy/hyperspy/pull/1128.patch","issue_url":"https://api.github.com/repos/hyperspy/hyperspy/issues/1128","number":1128,"state":"open","locked":false,"title":"Robust PCA & Online RPCA algorithms","user":{"login":"tjof2","id":6626247,"avatar_url":"https://avatars.githubusercontent.com/u/6626247?v=3","gravatar_id":"","url":"https://api.github.com/users/tjof2","html_url":"https://github.com/tjof2","followers_url":"https://api.github.com/users/tjof2/followers","following_url":"https://api.github.com/users/tjof2/following{/other_user}","gists_url":"https://api.github.com/users/tjof2/gists{/gist_id}","starred_url":"https://api.github.com/users/tjof2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tjof2/subscriptions","organizations_url":"https://api.github.com/users/tjof2/orgs","repos_url":"https://api.github.com/users/tjof2/repos","events_url":"https://api.github.com/users/tjof2/events{/privacy}","received_events_url":"https://api.github.com/users/tjof2/received_events","type":"User","site_admin":false},"body":"This PR takes over from #1071, which I broke.\r\n\r\n----\r\n\r\nRobust PCA seems to be making its way into scikit-learn, slowly... (see #972), but with one of the slow, iterative, memory-intensive SVT algorithms. However, I've been using this example of \"online\" robust PCA for a while now for outlier detection and working with missing data, which is much less memory-intensive and actually isn't too slow.\r\n\r\nSince OR-PCA actually returns a low-rank component **L** and a sparse error component **S** (where the observations **X** = **L** + **S**), to fit this into the current HyperSpy decomposition framework, I carry out a subsequent SVD on **L** to return factors and loadings.\r\n\r\nAlthough I've not currently implemented it in a way that allows for it, you can actually run OR-PCA in a truly online manner, i.e. update the estimated factors and loadings with each new sample that arrives, without having to re-run over the whole dataset. However, I think this quickly becomes complicated in the current HyperSpy machine learning framework, so consider this implementation more aimed at outlier detection.\r\n\r\nYou can see an example here: https://github.com/tjof2/hyperspy/blob/my-demos/examples/Online%20Robust%20PCA.ipynb\r\n\r\n- [x] Documentation\r\n- [x] Write simple tests\r\n- [x] Option to feed in an initial mask for the sparse component **S**. \r\n- [x] Add batch method (GoDec) for comparison with online version\r\n- [x] Add new QR initialization method (discussion with @thomasaarholt)\r\n- [x] Add stochastic gradient descent (SGD) method","created_at":"2016-07-12T10:49:01Z","updated_at":"2016-07-12T16:47:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5d847c7581b2b96fe79a2186785aedf42358d77f","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/1128/commits","review_comments_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/1128/comments","review_comment_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/hyperspy/hyperspy/issues/1128/comments","statuses_url":"https://api.github.com/repos/hyperspy/hyperspy/statuses/a60ad43d75184b87057d8e23111bb32cc972f5a8","head":{"label":"tjof2:enh-robust-pca","ref":"enh-robust-pca","sha":"a60ad43d75184b87057d8e23111bb32cc972f5a8","user":{"login":"tjof2","id":6626247,"avatar_url":"https://avatars.githubusercontent.com/u/6626247?v=3","gravatar_id":"","url":"https://api.github.com/users/tjof2","html_url":"https://github.com/tjof2","followers_url":"https://api.github.com/users/tjof2/followers","following_url":"https://api.github.com/users/tjof2/following{/other_user}","gists_url":"https://api.github.com/users/tjof2/gists{/gist_id}","starred_url":"https://api.github.com/users/tjof2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tjof2/subscriptions","organizations_url":"https://api.github.com/users/tjof2/orgs","repos_url":"https://api.github.com/users/tjof2/repos","events_url":"https://api.github.com/users/tjof2/events{/privacy}","received_events_url":"https://api.github.com/users/tjof2/received_events","type":"User","site_admin":false},"repo":{"id":61048580,"name":"hyperspy","full_name":"tjof2/hyperspy","owner":{"login":"tjof2","id":6626247,"avatar_url":"https://avatars.githubusercontent.com/u/6626247?v=3","gravatar_id":"","url":"https://api.github.com/users/tjof2","html_url":"https://github.com/tjof2","followers_url":"https://api.github.com/users/tjof2/followers","following_url":"https://api.github.com/users/tjof2/following{/other_user}","gists_url":"https://api.github.com/users/tjof2/gists{/gist_id}","starred_url":"https://api.github.com/users/tjof2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tjof2/subscriptions","organizations_url":"https://api.github.com/users/tjof2/orgs","repos_url":"https://api.github.com/users/tjof2/repos","events_url":"https://api.github.com/users/tjof2/events{/privacy}","received_events_url":"https://api.github.com/users/tjof2/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/tjof2/hyperspy","description":"Multidimensional data analysis","fork":true,"url":"https://api.github.com/repos/tjof2/hyperspy","forks_url":"https://api.github.com/repos/tjof2/hyperspy/forks","keys_url":"https://api.github.com/repos/tjof2/hyperspy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tjof2/hyperspy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tjof2/hyperspy/teams","hooks_url":"https://api.github.com/repos/tjof2/hyperspy/hooks","issue_events_url":"https://api.github.com/repos/tjof2/hyperspy/issues/events{/number}","events_url":"https://api.github.com/repos/tjof2/hyperspy/events","assignees_url":"https://api.github.com/repos/tjof2/hyperspy/assignees{/user}","branches_url":"https://api.github.com/repos/tjof2/hyperspy/branches{/branch}","tags_url":"https://api.github.com/repos/tjof2/hyperspy/tags","blobs_url":"https://api.github.com/repos/tjof2/hyperspy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tjof2/hyperspy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tjof2/hyperspy/git/refs{/sha}","trees_url":"https://api.github.com/repos/tjof2/hyperspy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tjof2/hyperspy/statuses/{sha}","languages_url":"https://api.github.com/repos/tjof2/hyperspy/languages","stargazers_url":"https://api.github.com/repos/tjof2/hyperspy/stargazers","contributors_url":"https://api.github.com/repos/tjof2/hyperspy/contributors","subscribers_url":"https://api.github.com/repos/tjof2/hyperspy/subscribers","subscription_url":"https://api.github.com/repos/tjof2/hyperspy/subscription","commits_url":"https://api.github.com/repos/tjof2/hyperspy/commits{/sha}","git_commits_url":"https://api.github.com/repos/tjof2/hyperspy/git/commits{/sha}","comments_url":"https://api.github.com/repos/tjof2/hyperspy/comments{/number}","issue_comment_url":"https://api.github.com/repos/tjof2/hyperspy/issues/comments{/number}","contents_url":"https://api.github.com/repos/tjof2/hyperspy/contents/{+path}","compare_url":"https://api.github.com/repos/tjof2/hyperspy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tjof2/hyperspy/merges","archive_url":"https://api.github.com/repos/tjof2/hyperspy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tjof2/hyperspy/downloads","issues_url":"https://api.github.com/repos/tjof2/hyperspy/issues{/number}","pulls_url":"https://api.github.com/repos/tjof2/hyperspy/pulls{/number}","milestones_url":"https://api.github.com/repos/tjof2/hyperspy/milestones{/number}","notifications_url":"https://api.github.com/repos/tjof2/hyperspy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tjof2/hyperspy/labels{/name}","releases_url":"https://api.github.com/repos/tjof2/hyperspy/releases{/id}","deployments_url":"https://api.github.com/repos/tjof2/hyperspy/deployments","created_at":"2016-06-13T15:42:00Z","updated_at":"2016-06-13T15:42:04Z","pushed_at":"2016-07-12T11:07:22Z","git_url":"git://github.com/tjof2/hyperspy.git","ssh_url":"git@github.com:tjof2/hyperspy.git","clone_url":"https://github.com/tjof2/hyperspy.git","svn_url":"https://github.com/tjof2/hyperspy","homepage":"http://www.hyperspy.org","size":78061,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"hyperspy:master","ref":"master","sha":"3b48a7e7626839b3d61a85f46824c7a48174bfb6","user":{"login":"hyperspy","id":989756,"avatar_url":"https://avatars.githubusercontent.com/u/989756?v=3","gravatar_id":"","url":"https://api.github.com/users/hyperspy","html_url":"https://github.com/hyperspy","followers_url":"https://api.github.com/users/hyperspy/followers","following_url":"https://api.github.com/users/hyperspy/following{/other_user}","gists_url":"https://api.github.com/users/hyperspy/gists{/gist_id}","starred_url":"https://api.github.com/users/hyperspy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hyperspy/subscriptions","organizations_url":"https://api.github.com/users/hyperspy/orgs","repos_url":"https://api.github.com/users/hyperspy/repos","events_url":"https://api.github.com/users/hyperspy/events{/privacy}","received_events_url":"https://api.github.com/users/hyperspy/received_events","type":"Organization","site_admin":false},"repo":{"id":2233998,"name":"hyperspy","full_name":"hyperspy/hyperspy","owner":{"login":"hyperspy","id":989756,"avatar_url":"https://avatars.githubusercontent.com/u/989756?v=3","gravatar_id":"","url":"https://api.github.com/users/hyperspy","html_url":"https://github.com/hyperspy","followers_url":"https://api.github.com/users/hyperspy/followers","following_url":"https://api.github.com/users/hyperspy/following{/other_user}","gists_url":"https://api.github.com/users/hyperspy/gists{/gist_id}","starred_url":"https://api.github.com/users/hyperspy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hyperspy/subscriptions","organizations_url":"https://api.github.com/users/hyperspy/orgs","repos_url":"https://api.github.com/users/hyperspy/repos","events_url":"https://api.github.com/users/hyperspy/events{/privacy}","received_events_url":"https://api.github.com/users/hyperspy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/hyperspy/hyperspy","description":"Multidimensional data analysis","fork":false,"url":"https://api.github.com/repos/hyperspy/hyperspy","forks_url":"https://api.github.com/repos/hyperspy/hyperspy/forks","keys_url":"https://api.github.com/repos/hyperspy/hyperspy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hyperspy/hyperspy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hyperspy/hyperspy/teams","hooks_url":"https://api.github.com/repos/hyperspy/hyperspy/hooks","issue_events_url":"https://api.github.com/repos/hyperspy/hyperspy/issues/events{/number}","events_url":"https://api.github.com/repos/hyperspy/hyperspy/events","assignees_url":"https://api.github.com/repos/hyperspy/hyperspy/assignees{/user}","branches_url":"https://api.github.com/repos/hyperspy/hyperspy/branches{/branch}","tags_url":"https://api.github.com/repos/hyperspy/hyperspy/tags","blobs_url":"https://api.github.com/repos/hyperspy/hyperspy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hyperspy/hyperspy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hyperspy/hyperspy/git/refs{/sha}","trees_url":"https://api.github.com/repos/hyperspy/hyperspy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hyperspy/hyperspy/statuses/{sha}","languages_url":"https://api.github.com/repos/hyperspy/hyperspy/languages","stargazers_url":"https://api.github.com/repos/hyperspy/hyperspy/stargazers","contributors_url":"https://api.github.com/repos/hyperspy/hyperspy/contributors","subscribers_url":"https://api.github.com/repos/hyperspy/hyperspy/subscribers","subscription_url":"https://api.github.com/repos/hyperspy/hyperspy/subscription","commits_url":"https://api.github.com/repos/hyperspy/hyperspy/commits{/sha}","git_commits_url":"https://api.github.com/repos/hyperspy/hyperspy/git/commits{/sha}","comments_url":"https://api.github.com/repos/hyperspy/hyperspy/comments{/number}","issue_comment_url":"https://api.github.com/repos/hyperspy/hyperspy/issues/comments{/number}","contents_url":"https://api.github.com/repos/hyperspy/hyperspy/contents/{+path}","compare_url":"https://api.github.com/repos/hyperspy/hyperspy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hyperspy/hyperspy/merges","archive_url":"https://api.github.com/repos/hyperspy/hyperspy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hyperspy/hyperspy/downloads","issues_url":"https://api.github.com/repos/hyperspy/hyperspy/issues{/number}","pulls_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls{/number}","milestones_url":"https://api.github.com/repos/hyperspy/hyperspy/milestones{/number}","notifications_url":"https://api.github.com/repos/hyperspy/hyperspy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hyperspy/hyperspy/labels{/name}","releases_url":"https://api.github.com/repos/hyperspy/hyperspy/releases{/id}","deployments_url":"https://api.github.com/repos/hyperspy/hyperspy/deployments","created_at":"2011-08-19T13:14:36Z","updated_at":"2016-07-12T09:38:00Z","pushed_at":"2016-07-12T16:42:32Z","git_url":"git://github.com/hyperspy/hyperspy.git","ssh_url":"git@github.com:hyperspy/hyperspy.git","clone_url":"https://github.com/hyperspy/hyperspy.git","svn_url":"https://github.com/hyperspy/hyperspy","homepage":"http://www.hyperspy.org","size":104276,"stargazers_count":60,"watchers_count":60,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":49,"mirror_url":null,"open_issues_count":165,"forks":49,"open_issues":165,"watchers":60,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/1128"},"html":{"href":"https://github.com/hyperspy/hyperspy/pull/1128"},"issue":{"href":"https://api.github.com/repos/hyperspy/hyperspy/issues/1128"},"comments":{"href":"https://api.github.com/repos/hyperspy/hyperspy/issues/1128/comments"},"review_comments":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/1128/comments"},"review_comment":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/1128/commits"},"statuses":{"href":"https://api.github.com/repos/hyperspy/hyperspy/statuses/a60ad43d75184b87057d8e23111bb32cc972f5a8"}}}}
{"actor":{"display_login":"francisco-dlp"}}