{"action":"created","comment":{"url":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments/151161805","pull_request_review_id":76824675,"id":151161805,"diff_hunk":"@@ -0,0 +1,566 @@\n+\"\"\"\n+=====================\n+Statistical inference\n+=====================\n+\n+Here we will briefly cover multiple concepts of inferential statistics in an\n+introductory manner, and demonstrate how to use some MNE statistical functions.\n+\n+.. contents:: Topics\n+   :local:\n+   :depth: 3\n+\n+\"\"\"\n+\n+# Authors: Eric Larson <larson.eric.d@gmail.com>\n+# License: BSD (3-clause)\n+\n+from functools import partial\n+\n+import numpy as np\n+from scipy import stats\n+import matplotlib.pyplot as plt\n+from mpl_toolkits.mplot3d import Axes3D  # noqa, analysis:ignore\n+\n+import mne\n+from mne.stats import (ttest_1samp_no_p, bonferroni_correction, fdr_correction,\n+                       permutation_t_test, permutation_cluster_1samp_test)\n+\n+print(__doc__)\n+\n+###############################################################################\n+# Hypothesis testing\n+# ------------------\n+# Null hypothesis\n+# ^^^^^^^^^^^^^^^\n+# From `Wikipedia <https://en.wikipedia.org/wiki/Null_hypothesis>`_:\n+#\n+#     In inferential statistics, a general statement or default position that\n+#     there is no relationship between two measured phenomena, or no\n+#     association among groups.\n+#\n+# We typically want to reject a **null hypothesis** with\n+# some probability level (e.g., p < 0.05).\n+# To think about what this means, let's follow the illustrative example from\n+# [1]_ and construct a toy dataset consisting of a 40 x 40 square with a\n+# \"signal\" present in the center (at pixel [20, 20]) with white noise\n+# added and a 5-pixel-SD normal smoothing kernel applied.\n+width = 40\n+n_subjects = 10\n+signal_mean = 100\n+signal_sd = 100\n+noise_sd = 0.01\n+gaussian_sd = 5\n+sigma = 1e-3  # sigma for the \"hat\" method\n+threshold = -stats.distributions.t.ppf(0.05, n_subjects - 1)\n+n_permutations = 'all'  # run an exact test\n+n_src = width * width\n+\n+# For each \"subject\", make a smoothed noisy signal with a centered peak\n+rng = np.random.RandomState(42)\n+X = noise_sd * rng.randn(n_subjects, width, width)\n+# Add a signal at the dead center\n+X[:, width // 2, width // 2] = signal_mean + rng.randn(n_subjects) * signal_sd\n+# Spatially smooth with a 2D Gaussian kernel\n+size = width // 2 - 1\n+gaussian = np.exp(-(np.arange(-size, size + 1) ** 2 / float(gaussian_sd ** 2)))\n+for si in range(X.shape[0]):\n+    for ri in range(X.shape[1]):\n+        X[si, ri, :] = np.convolve(X[si, ri, :], gaussian, 'same')\n+    for ci in range(X.shape[2]):\n+        X[si, :, ci] = np.convolve(X[si, :, ci], gaussian, 'same')\n+\n+###############################################################################\n+# In this case, a null hypothesis we could test for each voxel is:\n+#\n+#     There is no difference between the mean value and zero\n+#     (:math:`H_0: \\mu = 0`).\n+#\n+# The alternative hypothesis, then, is that the voxel has a non-zero mean.\n+# This is a *two-tailed test* because the mean could be less than\n+# or greater than zero (whereas a *one-tailed test* would test only one of\n+# these possibilities, i.e. :math:`H_0: \\mu \\geq 0` or\n+# :math:`H_0: \\mu \\leq 0`).\n+#\n+# Parametric tests\n+# ^^^^^^^^^^^^^^^^\n+# Let's start with a **1-sample t-test**, which is a standard test\n+# for differences in paired sample means. This test is **parametric**,\n+# as it assumes that the underlying sample distribution is Gaussian, and is\n+# only valid in this case. (This happens to be satisfied by our toy dataset,\n+# but is not always satisfied for neuroimaging data.)\n+#\n+# In the context of our toy dataset, which has many voxels, applying the\n+# 1-sample t-test is called a *mass-univariate* approach as it treats\n+# each voxel independently.\n+\n+titles = ['t-statistic']\n+out = stats.ttest_1samp(X, 0, axis=0)\n+ts = [out[0]]\n+ps = [out[1]]\n+mccs = [False]  # these are not multiple-comparisons corrected\n+\n+\n+# let's make a plotting function\n+def plot_t_p(t, p, title, mcc, axes=None):\n+    if axes is None:\n+        fig = plt.figure(figsize=(6, 3))\n+        axes = [fig.add_subplot(121, projection='3d'), fig.add_subplot(122)]\n+        show = True\n+    else:\n+        fig = axes[0].figure\n+        show = False\n+    p_lims = [0.05, 0.001]\n+    t_lims = -stats.distributions.t.ppf(p_lims, n_subjects - 1)\n+    p_lims = [-np.log10(0.05), -np.log10(0.001)]\n+    # t plot\n+    x, y = np.mgrid[0:width, 0:width]\n+    surf = axes[0].plot_surface(x, y, np.reshape(t, (width, width)),\n+                                rstride=1, cstride=1, linewidth=0,\n+                                vmin=t_lims[0], vmax=t_lims[1], cmap='viridis')\n+    axes[0].set(xticks=[], yticks=[], zticks=[],\n+                xlim=[0, width - 1], ylim=[0, width - 1])\n+    axes[0].view_init(30, 15)\n+    cbar = plt.colorbar(ax=axes[0], shrink=0.75, orientation='horizontal',\n+                        fraction=0.1, pad=0.025, mappable=surf)\n+    cbar.set_ticks(t_lims)\n+    cbar.set_ticklabels(['%0.1f' % t_lim for t_lim in t_lims])\n+    cbar.set_label('t-value')\n+    cbar.ax.get_xaxis().set_label_coords(0.5, -0.3)\n+    if not show:\n+        axes[0].set(title=title)\n+        if mcc:\n+            axes[0].title.set_weight('bold')\n+    # p plot\n+    use_p = -np.log10(np.reshape(np.maximum(p, 1e-5), (width, width)))\n+    img = axes[1].imshow(use_p, cmap='inferno', vmin=p_lims[0], vmax=p_lims[1],\n+                         interpolation='nearest')\n+    axes[1].set(xticks=[], yticks=[])\n+    cbar = plt.colorbar(ax=axes[1], shrink=0.75, orientation='horizontal',\n+                        fraction=0.1, pad=0.025, mappable=img)\n+    cbar.set_ticks(p_lims)\n+    cbar.set_ticklabels(['%0.1f' % p_lim for p_lim in p_lims])\n+    cbar.set_label('$-\\log_{10}(p)$')\n+    cbar.ax.get_xaxis().set_label_coords(0.5, -0.3)\n+    if show:\n+        text = fig.suptitle(title)\n+        if mcc:\n+            text.set_weight('bold')\n+        plt.subplots_adjust(0, 0.05, 1, 0.9, wspace=0, hspace=0)\n+        mne.viz.utils.plt_show()\n+\n+\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# \"hat\" variance adjustment\n+# ~~~~~~~~~~~~~~~~~~~~~~~~~\n+# The \"hat\" technique regularizes the variance values used in the t-test\n+# calculation [1]_ to compensate for implausibly small variances.\n+ts.append(ttest_1samp_no_p(X, sigma=sigma))\n+ps.append(stats.distributions.t.sf(np.abs(ts[-1]), len(X) - 1) * 2)\n+titles.append('$\\mathrm{t_{hat}}$')\n+mccs.append(False)\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# Non-parametric tests\n+# ^^^^^^^^^^^^^^^^^^^^\n+# Instead of assuming an underlying Gaussian distribution, we could instead\n+# use a **non-parametric resampling** method. Under the null hypothesis,\n+# we have the principle of **exchangeability**, which means that, if the null\n+# is true, we should be able to exchange conditions and not change the\n+# distribution of the test statistic.\n+#\n+# In the case of a 2-tailed paired t-test against 0 (or between two conditions\n+# where you have already subtracted them), exchangeability means that we can\n+# flip the signs of our data. Therefore, we can\n+# construct the **null distribution**\n+# values by taking random subsets of samples (subjects), flipping the sign\n+# of their data, and recording the resulting statistic absolute value. The\n+# absolute value of the statistic evaluated on the veridical data can then be\n+# compared to this distribution, and the p-value is simply the proportion of\n+# null distribution values that were smaller.\n+#\n+# .. note:: In the case where ``n_permutations`` is large enough (or \"all\") so\n+#           that the complete set of unique resampling exchanges can be done\n+#           (which is :math:`2^{N_{samp}}-1=1023` for the one-tailed paired\n+#           test here, not counting the veridical distribution),\n+#           instead of randomly exchanging conditions the null is formed\n+#           from using all possible exchanges. This is known as a permutation\n+#           test (or exact test) form of a non-parametric resampling test.\n+\n+# Here we have to do a bit of gymnastics to get our function to do\n+# a permutation test without correcting for multiple comparisons:\n+\n+# Let's flatten the array for simplicity\n+X.shape = (n_subjects, n_src)\n+titles.append('Permutation')\n+ts.append(np.zeros(width * width))\n+ps.append(np.zeros(width * width))\n+mccs.append(False)\n+for ii in range(n_src):\n+    ts[-1][ii], ps[-1][ii] = \\\n+        permutation_t_test(X[:, [ii]], verbose=True if ii == 0 else False)[:2]\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# Multiple comparisons\n+# --------------------\n+# So far, we have done no correction for multiple comparisons. This is\n+# potentially problematic for these data because there are\n+# :math:`40 \\times 40 = 1600` tests being performed. If we just use\n+# a threshold ``p < 0.05`` for all of our tests, we would expect many\n+# voxels to be declared significant even if there were no true effect.\n+# In other words, we would make many **type I errors** (adapted from\n+# `Wikipedia <https://en.wikipedia.org/wiki/Type_I_and_type_II_errors>`_):\n+#\n+# .. rst-class:: skinnytable\n+#\n+#   +----------+--------+------------------+------------------+\n+#   |                   |          Null hypothesis            |\n+#   |                   +------------------+------------------+\n+#   |                   |       True       |       False      |\n+#   +==========+========+==================+==================+\n+#   |          |        | Type I error     | Correct          |\n+#   |          | Yes    |   False positive |   True positive  |\n+#   + Reject   +--------+------------------+------------------+\n+#   |          |        | Correct          | Type II error    |\n+#   |          | No     |   True Negative  |   False negative |\n+#   +----------+--------+------------------+------------------+\n+#\n+# To combat this problem, multiple methods exist. Typically these\n+# provide control over either the:\n+#\n+# 1. `Familywise error rate (FWER) <fwer>`_\n+#      The probability of making one or more type I error:\n+#      :math:`p(N_{\\mathrm{Type\\ I}} >= 1)`.\n+# 2. `False discovery rate (FDR) <fdr>`_\n+#      The expected proportion of rejected null hypotheses that are false:\n+#      :math:`N_{\\mathrm{Type\\ I}} / N_{\\mathrm{reject}}`.\n+#\n+# We cover some techniques that control FWER and FDR below.\n+#\n+# Bonferroni correction\n+# ^^^^^^^^^^^^^^^^^^^^^\n+# Perhaps the simplest way to deal with multiple comparisons, `Bonferroni\n+# correction<https://en.wikipedia.org/wiki/Bonferroni_correction>`_\n+# conservatively multiplies the p-values by the number of comparisons to\n+# control the FWER.\n+\n+titles.append('Bonferroni')\n+ts.append(ts[-1])\n+ps.append(bonferroni_correction(ps[0])[1])\n+mccs.append(True)\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# False discovery rate (FDR) correction\n+# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n+# Typically FDR is performed with the Benjamini/Hochberg procedure, which\n+# is less restrictive than Bonferroni correction for large numbers of\n+# comparisons (fewer type II errors) but provides less strict control of\n+# errors (more type I errors).\n+\n+titles.append('FDR')\n+ts.append(ts[-1])\n+ps.append(fdr_correction(ps[0])[1])\n+mccs.append(True)\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# Non-parametric resampling test with a maximum statistic\n+# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n+# **Non-parametric resampling tests** can also be used to correct for multiple\n+# comparisons. In its simplest form, we again do permutations using\n+# exchangeability under the null hypothesis, but this time we take the\n+# *maximum statistic across all tests* in each permutation to form the\n+# null distribution. The p-value for each vertex from the veridical data\n+# is then given by the proportion of null distrubtion values\n+# that were smaller.\n+#\n+# This method has two important features:\n+#\n+# 1. It controls FWER.\n+# 2. It is non-parametric. Even though our initial test statistic\n+#    (here a 1-sample t-test) for clustering is parametric, the null\n+#    distribution for the null hypothesis rejection (cluster size\n+#    distribution is indistinguishable from zero) is obtained by\n+#    permutations. This means that it makes no assumptions of Gaussianity\n+#    (which do hold for this example but do not in general).\n+\n+titles.append('$\\mathbf{Perm_{max}}$')\n+out = permutation_t_test(X)[:2]\n+ts.append(out[0])\n+ps.append(out[1])\n+mccs.append(True)\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# Clustering\n+# ^^^^^^^^^^\n+# Each of the aforementioned multiple comparisons corrections have the\n+# disadvantage of not fully incorporating the correlation structure of the\n+# data. However, by defining the connectivity/adjacency/neighbor structure\n+# in our data, we can use **clustering** to compensate.\n+#\n+# To use this, we need to rethink our null hypothesis. Instead\n+# of thinking about a null hypothesis about means per voxel, we consider a\n+# null hypothesis about sizes of clusters in our data, which could be stated\n+# like:\n+#\n+#     The distribution of spatial cluster sizes observed in two experimental\n+#     conditions are drawn from the same probability distribution.\n+#\n+# Here we only have a single condition and we contrast to zero, which can\n+# be thought of as:\n+#\n+#     The distribution of spatial cluster sizes is independent of the sign\n+#     of the data.\n+#\n+# In this case, we again do permutations with a maximum statistic, but, under\n+# each permutation, we:\n+#\n+# 1. Threshold the computed statistic with some **initial**\n+#    ``threshold`` value.\n+# 2. Cluster points that exceed this threshold (with the same sign)\n+#    based on adjacency.\n+# 3. Record the *size* of each cluster (measured, e.g., by a simple vertex\n+#    count or the sum of voxel t-values within the cluster).\n+#\n+# After doing these permutations, the cluster sizes in our veridical data\n+# are compared to this null distribution. The p-value associated with each\n+# cluster is again given by the proportion of smaller null distribution\n+# values. This can then be subjected to a standard p-value threshold\n+# (e.g., ``p < 0.05``) to reject the null hypothesis (i.e., find an effect\n+# of interest).\n+#\n+# This reframing to consider *cluster sizes* rather than *individual means*\n+# maintains the advantages of the standard non-parametric permutation\n+# test -- namely controlling FWER and making no assumptions of parametric\n+# data distribution.\n+# Cricitally, though, it also accounts for the correlation structure in the\n+# data -- which in this toy case is spatial but\n+# in general can be multidimensional (e.g., spatio-temporal) -- because the\n+# null distribution will be derived from data in a way that preserves these\n+# correlations.\n+#\n+# However, there is a drawback. If a cluster significantly deviates from\n+# the null, no further inference on the cluster (e.g., peak location) can be\n+# made, as the entire cluster as a whole is used to reject the null.\n+\n+# We need to define our connectivity/neighbor/adjacency matrix, which we know\n+# is just a grid. Using ``connectivity=None`` is highly optimized for this\n+# case.\n+\n+# Reshape to what is equivalent to (n_samples, n_space, n_time)\n+titles.append('Clustering')\n+X.shape = (n_subjects, width, width)\n+t_clust, clusters, p_values, H0 = permutation_cluster_1samp_test(\n+    X, n_jobs=1, threshold=threshold, connectivity=None, tail=1,\n+    n_permutations=n_permutations)\n+# Put the cluster data in a viewable format\n+p_clust = np.ones((width, width))\n+for cl, p in zip(clusters, p_values):\n+    p_clust[cl] = p\n+ts.append(t_clust)\n+ps.append(p_clust)\n+mccs.append(True)\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# \"hat\" variance adjustment\n+# ~~~~~~~~~~~~~~~~~~~~~~~~~\n+# This method can also be used in this context to correct for small\n+# variances [1]_:\n+titles.append(r'$\\mathbf{C_{hat}}$')\n+stat_fun_hat = partial(ttest_1samp_no_p, sigma=sigma)\n+t_hat, clusters, p_values, H0 = permutation_cluster_1samp_test(\n+    X, n_jobs=1, threshold=threshold, connectivity=None, tail=1,\n+    n_permutations=n_permutations, stat_fun=stat_fun_hat, buffer_size=None)\n+p_hat = np.ones((width, width))\n+for cl, p in zip(clusters, p_values):\n+    p_hat[cl] = p\n+ts.append(t_hat)\n+ps.append(p_hat)\n+mccs.append(True)\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# .. _tfce_example:\n+#\n+# Threshold-free cluster enhancement (TFCE)\n+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+# TFCE eliminates the free parameter initial ``threshold`` value that\n+# determines which points are included in clustering by approximating\n+# a continuous integration across possible threshold values with a standard\n+# `Riemann sum <https://en.wikipedia.org/wiki/Riemann_sum>`_ [2]_.\n+# This requires giving a starting threshold ``'start'`` and a step\n+# size ``'step'``, which in MNE is supplied as a dict.\n+# The smaller the ``'step'`` and closer to 0 the ``'start'`` value,\n+# the better the approximation, but the longer it takes).\n+#\n+# A significant advantage of TFCE is that, rather than modifying the\n+# statistical test, it modifies the data itself (while still controlling\n+# for multiple comparisons). The statistical test is then done at the level\n+# of voxels rather than clusters. This allows for evaluation of each point\n+# independently for significance rather than only as cluster groups.\n+\n+titles.append(r'$\\mathbf{C_{TFCE}}$')\n+threshold_tfce = dict(start=0, step=0.2)\n+t_tfce, _, p_tfce, H0 = permutation_cluster_1samp_test(\n+    X, n_jobs=1, threshold=threshold_tfce, connectivity=None, tail=1,\n+    n_permutations=n_permutations)\n+ts.append(t_tfce)\n+ps.append(p_tfce)\n+mccs.append(True)\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# We can also combine TFCE and the \"hat\" correction:\n+titles.append(r'$\\mathbf{C_{hat,TFCE}}$')\n+t_tfce_hat, _, p_tfce_hat, H0 = permutation_cluster_1samp_test(\n+    X, n_jobs=1, threshold=threshold_tfce, connectivity=None, tail=1,\n+    n_permutations=n_permutations, stat_fun=stat_fun_hat, buffer_size=None)\n+ts.append(t_tfce_hat)\n+ps.append(p_tfce_hat)\n+mccs.append(True)\n+plot_t_p(ts[-1], ps[-1], titles[-1], mccs[-1])\n+\n+###############################################################################\n+# Visualize and compare methods\n+# -----------------------------\n+# Let's take a look at these statistics. The top row shows each test statistic,\n+# and the bottom shows p-values for various statistical tests, with the ones\n+# with proper control over FWER or FDR with bold titles.\n+\n+fig = plt.figure(facecolor='w', figsize=(14, 3))\n+assert len(ts) == len(titles) == len(ps)\n+for ii in range(len(ts)):\n+    ax = [fig.add_subplot(2, 10, ii + 1, projection='3d'),\n+          fig.add_subplot(2, 10, 11 + ii)]\n+    plot_t_p(ts[ii], ps[ii], titles[ii], mccs[ii], ax)\n+fig.tight_layout(pad=0, w_pad=0.05, h_pad=0.1)\n+plt.show()\n+\n+###############################################################################\n+# The first three columns show the parametric and non-parametric statistics\n+# that are not corrected for multiple comparisons:\n+#\n+# - **t-test** has jagged edges.\n+# - **\"hat\" variance correction** of the t-test has reduced peaky edges,\n+#   correcting for sharpness in the statistic driven by low-variance voxels.\n+# - **non-parametric resampling test** is very similar to the t-test, as\n+#   the data are parametric.\n+#\n+# The next three columns show multiple comparison corrections of the\n+# mass univariate tests (parametric and non-parametric). These\n+# too conservatively correct for multiple comparisons because neighboring\n+# voxels in our data are correlated:\n+#\n+# - **Bonferroni correction** eliminates any significant activity.\n+# - **FDR correction** is less conservative than Bonferroni.\n+# - **Permutation test with a maximum statistic** also eliminates any\n+#   significant activity.\n+#\n+# The final four columns show the non-parametric, cluster-based permutation\n+# tests with a maximum statistic:\n+#\n+# - **Standard clustering** identifies the correct region. However, the whole\n+#   area must be declared significant, so no peak analysis can be done.\n+#   Also, the peak is broad.\n+# - **Clustering with \"hat\"** tightens the estimate of significant activity.\n+# - **Clustering with TFCE** allows analyzing each significant point\n+#   independently, but still has a broadened estimate.\n+# - **Clustering with TFCE and \"hat\"** tightens the area declared\n+#   significant (again FWER corrected).\n+#\n+# Statistical functions in MNE\n+# ----------------------------\n+# MNE provides several convenience parametric testing functions that can be\n+# used in conjunction with the non-parametric clustering methods. However,\n+# the set of functions we provide is not meant to be exhaustive.\n+#\n+# If the univariate statistical contrast of interest to you is not listed\n+# here (e.g., interaction term in an unbalanced ANOVA), consider checking\n+# out the :mod:`statsmodels` package. It offers many functions for computing\n+# statistical contrasts, e.g., :func:`statsmodels.stats.anova.anova_lm`.\n+# To use these functions in clustering:","path":"tutorials/plot_background_statistics.py","position":488,"original_position":488,"commit_id":"5c38ad3b74dad10630ae6bcf2f61fe8683182548","original_commit_id":"5c38ad3b74dad10630ae6bcf2f61fe8683182548","user":{"login":"larsoner","id":2365790,"avatar_url":"https://avatars1.githubusercontent.com/u/2365790?v=4","gravatar_id":"","url":"https://api.github.com/users/larsoner","html_url":"https://github.com/larsoner","followers_url":"https://api.github.com/users/larsoner/followers","following_url":"https://api.github.com/users/larsoner/following{/other_user}","gists_url":"https://api.github.com/users/larsoner/gists{/gist_id}","starred_url":"https://api.github.com/users/larsoner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/larsoner/subscriptions","organizations_url":"https://api.github.com/users/larsoner/orgs","repos_url":"https://api.github.com/users/larsoner/repos","events_url":"https://api.github.com/users/larsoner/events{/privacy}","received_events_url":"https://api.github.com/users/larsoner/received_events","type":"User","site_admin":false},"body":"I'm hoping @cbrnr will add one later, and we can link to it. I think including it here might be a bit much for this intro doc. We might want a separate one: \"which statistical test should I use for my data?\" (even in a simple univariate case)","created_at":"2017-11-15T15:37:17Z","updated_at":"2017-11-15T15:37:17Z","html_url":"https://github.com/mne-tools/mne-python/pull/4743#discussion_r151161805","pull_request_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/4743","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments/151161805"},"html":{"href":"https://github.com/mne-tools/mne-python/pull/4743#discussion_r151161805"},"pull_request":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/4743"}},"in_reply_to_id":150982301},"pull_request":{"url":"https://api.github.com/repos/mne-tools/mne-python/pulls/4743","id":151446032,"html_url":"https://github.com/mne-tools/mne-python/pull/4743","diff_url":"https://github.com/mne-tools/mne-python/pull/4743.diff","patch_url":"https://github.com/mne-tools/mne-python/pull/4743.patch","issue_url":"https://api.github.com/repos/mne-tools/mne-python/issues/4743","number":4743,"state":"open","locked":false,"title":"MRG: Better docstring for clustering","user":{"login":"larsoner","id":2365790,"avatar_url":"https://avatars1.githubusercontent.com/u/2365790?v=4","gravatar_id":"","url":"https://api.github.com/users/larsoner","html_url":"https://github.com/larsoner","followers_url":"https://api.github.com/users/larsoner/followers","following_url":"https://api.github.com/users/larsoner/following{/other_user}","gists_url":"https://api.github.com/users/larsoner/gists{/gist_id}","starred_url":"https://api.github.com/users/larsoner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/larsoner/subscriptions","organizations_url":"https://api.github.com/users/larsoner/orgs","repos_url":"https://api.github.com/users/larsoner/repos","events_url":"https://api.github.com/users/larsoner/events{/privacy}","received_events_url":"https://api.github.com/users/larsoner/received_events","type":"User","site_admin":false},"body":"@cbrnr @agramfort can you look and see if you're happy with this wording? If so I'll update the other clustering functions, too.\r\n\r\n- [x] modernize the cluster stats method doc a bit\r\n- [x] update all clustering functions with new threshold docs\r\n- [x] update all `stat_fun` functions with `None` and warning about `threshold=None`\r\n\r\nCloses #4734.","created_at":"2017-11-08T17:27:38Z","updated_at":"2017-11-15T15:37:17Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3788bf9d8ee2d148b4574ce5530f3bc6b26d3fd5","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/4743/commits","review_comments_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/4743/comments","review_comment_url":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mne-tools/mne-python/issues/4743/comments","statuses_url":"https://api.github.com/repos/mne-tools/mne-python/statuses/5c38ad3b74dad10630ae6bcf2f61fe8683182548","head":{"label":"larsoner:cluster-doc","ref":"cluster-doc","sha":"5c38ad3b74dad10630ae6bcf2f61fe8683182548","user":{"login":"larsoner","id":2365790,"avatar_url":"https://avatars1.githubusercontent.com/u/2365790?v=4","gravatar_id":"","url":"https://api.github.com/users/larsoner","html_url":"https://github.com/larsoner","followers_url":"https://api.github.com/users/larsoner/followers","following_url":"https://api.github.com/users/larsoner/following{/other_user}","gists_url":"https://api.github.com/users/larsoner/gists{/gist_id}","starred_url":"https://api.github.com/users/larsoner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/larsoner/subscriptions","organizations_url":"https://api.github.com/users/larsoner/orgs","repos_url":"https://api.github.com/users/larsoner/repos","events_url":"https://api.github.com/users/larsoner/events{/privacy}","received_events_url":"https://api.github.com/users/larsoner/received_events","type":"User","site_admin":false},"repo":{"id":5848067,"name":"mne-python","full_name":"larsoner/mne-python","owner":{"login":"larsoner","id":2365790,"avatar_url":"https://avatars1.githubusercontent.com/u/2365790?v=4","gravatar_id":"","url":"https://api.github.com/users/larsoner","html_url":"https://github.com/larsoner","followers_url":"https://api.github.com/users/larsoner/followers","following_url":"https://api.github.com/users/larsoner/following{/other_user}","gists_url":"https://api.github.com/users/larsoner/gists{/gist_id}","starred_url":"https://api.github.com/users/larsoner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/larsoner/subscriptions","organizations_url":"https://api.github.com/users/larsoner/orgs","repos_url":"https://api.github.com/users/larsoner/repos","events_url":"https://api.github.com/users/larsoner/events{/privacy}","received_events_url":"https://api.github.com/users/larsoner/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/larsoner/mne-python","description":"MNE scripting with Python","fork":true,"url":"https://api.github.com/repos/larsoner/mne-python","forks_url":"https://api.github.com/repos/larsoner/mne-python/forks","keys_url":"https://api.github.com/repos/larsoner/mne-python/keys{/key_id}","collaborators_url":"https://api.github.com/repos/larsoner/mne-python/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/larsoner/mne-python/teams","hooks_url":"https://api.github.com/repos/larsoner/mne-python/hooks","issue_events_url":"https://api.github.com/repos/larsoner/mne-python/issues/events{/number}","events_url":"https://api.github.com/repos/larsoner/mne-python/events","assignees_url":"https://api.github.com/repos/larsoner/mne-python/assignees{/user}","branches_url":"https://api.github.com/repos/larsoner/mne-python/branches{/branch}","tags_url":"https://api.github.com/repos/larsoner/mne-python/tags","blobs_url":"https://api.github.com/repos/larsoner/mne-python/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/larsoner/mne-python/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/larsoner/mne-python/git/refs{/sha}","trees_url":"https://api.github.com/repos/larsoner/mne-python/git/trees{/sha}","statuses_url":"https://api.github.com/repos/larsoner/mne-python/statuses/{sha}","languages_url":"https://api.github.com/repos/larsoner/mne-python/languages","stargazers_url":"https://api.github.com/repos/larsoner/mne-python/stargazers","contributors_url":"https://api.github.com/repos/larsoner/mne-python/contributors","subscribers_url":"https://api.github.com/repos/larsoner/mne-python/subscribers","subscription_url":"https://api.github.com/repos/larsoner/mne-python/subscription","commits_url":"https://api.github.com/repos/larsoner/mne-python/commits{/sha}","git_commits_url":"https://api.github.com/repos/larsoner/mne-python/git/commits{/sha}","comments_url":"https://api.github.com/repos/larsoner/mne-python/comments{/number}","issue_comment_url":"https://api.github.com/repos/larsoner/mne-python/issues/comments{/number}","contents_url":"https://api.github.com/repos/larsoner/mne-python/contents/{+path}","compare_url":"https://api.github.com/repos/larsoner/mne-python/compare/{base}...{head}","merges_url":"https://api.github.com/repos/larsoner/mne-python/merges","archive_url":"https://api.github.com/repos/larsoner/mne-python/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/larsoner/mne-python/downloads","issues_url":"https://api.github.com/repos/larsoner/mne-python/issues{/number}","pulls_url":"https://api.github.com/repos/larsoner/mne-python/pulls{/number}","milestones_url":"https://api.github.com/repos/larsoner/mne-python/milestones{/number}","notifications_url":"https://api.github.com/repos/larsoner/mne-python/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/larsoner/mne-python/labels{/name}","releases_url":"https://api.github.com/repos/larsoner/mne-python/releases{/id}","deployments_url":"https://api.github.com/repos/larsoner/mne-python/deployments","created_at":"2012-09-17T22:08:11Z","updated_at":"2015-05-07T15:05:01Z","pushed_at":"2017-11-15T14:10:01Z","git_url":"git://github.com/larsoner/mne-python.git","ssh_url":"git@github.com:larsoner/mne-python.git","clone_url":"https://github.com/larsoner/mne-python.git","svn_url":"https://github.com/larsoner/mne-python","homepage":"http://martinos.org/mne/","size":96879,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"archived":false,"open_issues_count":0,"forks":2,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"mne-tools:master","ref":"master","sha":"59f56acde198b1eba20b4411cf3d334ec328a36f","user":{"login":"mne-tools","id":583875,"avatar_url":"https://avatars3.githubusercontent.com/u/583875?v=4","gravatar_id":"","url":"https://api.github.com/users/mne-tools","html_url":"https://github.com/mne-tools","followers_url":"https://api.github.com/users/mne-tools/followers","following_url":"https://api.github.com/users/mne-tools/following{/other_user}","gists_url":"https://api.github.com/users/mne-tools/gists{/gist_id}","starred_url":"https://api.github.com/users/mne-tools/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mne-tools/subscriptions","organizations_url":"https://api.github.com/users/mne-tools/orgs","repos_url":"https://api.github.com/users/mne-tools/repos","events_url":"https://api.github.com/users/mne-tools/events{/privacy}","received_events_url":"https://api.github.com/users/mne-tools/received_events","type":"Organization","site_admin":false},"repo":{"id":1301584,"name":"mne-python","full_name":"mne-tools/mne-python","owner":{"login":"mne-tools","id":583875,"avatar_url":"https://avatars3.githubusercontent.com/u/583875?v=4","gravatar_id":"","url":"https://api.github.com/users/mne-tools","html_url":"https://github.com/mne-tools","followers_url":"https://api.github.com/users/mne-tools/followers","following_url":"https://api.github.com/users/mne-tools/following{/other_user}","gists_url":"https://api.github.com/users/mne-tools/gists{/gist_id}","starred_url":"https://api.github.com/users/mne-tools/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mne-tools/subscriptions","organizations_url":"https://api.github.com/users/mne-tools/orgs","repos_url":"https://api.github.com/users/mne-tools/repos","events_url":"https://api.github.com/users/mne-tools/events{/privacy}","received_events_url":"https://api.github.com/users/mne-tools/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mne-tools/mne-python","description":"MNE : Magnetoencephalography (MEG) and Electroencephalography (EEG) in Python","fork":false,"url":"https://api.github.com/repos/mne-tools/mne-python","forks_url":"https://api.github.com/repos/mne-tools/mne-python/forks","keys_url":"https://api.github.com/repos/mne-tools/mne-python/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mne-tools/mne-python/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mne-tools/mne-python/teams","hooks_url":"https://api.github.com/repos/mne-tools/mne-python/hooks","issue_events_url":"https://api.github.com/repos/mne-tools/mne-python/issues/events{/number}","events_url":"https://api.github.com/repos/mne-tools/mne-python/events","assignees_url":"https://api.github.com/repos/mne-tools/mne-python/assignees{/user}","branches_url":"https://api.github.com/repos/mne-tools/mne-python/branches{/branch}","tags_url":"https://api.github.com/repos/mne-tools/mne-python/tags","blobs_url":"https://api.github.com/repos/mne-tools/mne-python/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mne-tools/mne-python/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mne-tools/mne-python/git/refs{/sha}","trees_url":"https://api.github.com/repos/mne-tools/mne-python/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mne-tools/mne-python/statuses/{sha}","languages_url":"https://api.github.com/repos/mne-tools/mne-python/languages","stargazers_url":"https://api.github.com/repos/mne-tools/mne-python/stargazers","contributors_url":"https://api.github.com/repos/mne-tools/mne-python/contributors","subscribers_url":"https://api.github.com/repos/mne-tools/mne-python/subscribers","subscription_url":"https://api.github.com/repos/mne-tools/mne-python/subscription","commits_url":"https://api.github.com/repos/mne-tools/mne-python/commits{/sha}","git_commits_url":"https://api.github.com/repos/mne-tools/mne-python/git/commits{/sha}","comments_url":"https://api.github.com/repos/mne-tools/mne-python/comments{/number}","issue_comment_url":"https://api.github.com/repos/mne-tools/mne-python/issues/comments{/number}","contents_url":"https://api.github.com/repos/mne-tools/mne-python/contents/{+path}","compare_url":"https://api.github.com/repos/mne-tools/mne-python/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mne-tools/mne-python/merges","archive_url":"https://api.github.com/repos/mne-tools/mne-python/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mne-tools/mne-python/downloads","issues_url":"https://api.github.com/repos/mne-tools/mne-python/issues{/number}","pulls_url":"https://api.github.com/repos/mne-tools/mne-python/pulls{/number}","milestones_url":"https://api.github.com/repos/mne-tools/mne-python/milestones{/number}","notifications_url":"https://api.github.com/repos/mne-tools/mne-python/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mne-tools/mne-python/labels{/name}","releases_url":"https://api.github.com/repos/mne-tools/mne-python/releases{/id}","deployments_url":"https://api.github.com/repos/mne-tools/mne-python/deployments","created_at":"2011-01-28T03:31:13Z","updated_at":"2017-11-14T20:25:16Z","pushed_at":"2017-11-15T14:46:33Z","git_url":"git://github.com/mne-tools/mne-python.git","ssh_url":"git@github.com:mne-tools/mne-python.git","clone_url":"https://github.com/mne-tools/mne-python.git","svn_url":"https://github.com/mne-tools/mne-python","homepage":"http://martinos.org/mne/","size":97078,"stargazers_count":358,"watchers_count":358,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":328,"mirror_url":null,"archived":false,"open_issues_count":220,"forks":328,"open_issues":220,"watchers":358,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/4743"},"html":{"href":"https://github.com/mne-tools/mne-python/pull/4743"},"issue":{"href":"https://api.github.com/repos/mne-tools/mne-python/issues/4743"},"comments":{"href":"https://api.github.com/repos/mne-tools/mne-python/issues/4743/comments"},"review_comments":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/4743/comments"},"review_comment":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mne-tools/mne-python/pulls/4743/commits"},"statuses":{"href":"https://api.github.com/repos/mne-tools/mne-python/statuses/5c38ad3b74dad10630ae6bcf2f61fe8683182548"}},"author_association":"OWNER"}}
{"actor":{"display_login":"larsoner"}}