Repository: scipy/scipy

Issue:
erroneous moment values for t-distribution
<<Please describe the issue in detail here, and for bug reports fill in the fields below.>>

Moments of the t-distribution return incorrect values.  See: https://en.wikipedia.org/wiki/Student%27s_t-distribution
- for df <= 1, the mean should be undefined
- for df < 1, the variance should be undefined 

Example:
```
from scipy import stats
X = stats.t(df=0.8)
X.mean() # returns 0
X.var() # returns inf
```

### Scipy/Numpy/Python version information:
`1.0.0 1.14.0 sys.version_info(major=3, minor=6, micro=4, releaselevel='final', serial=0)`



Discussion:
AFAIK, the variance is inf and not undefined.
kurtosis is undefined because it divides by var (inf / inf)
I take this back
because the mean is undefined for df <= 1, the var also has to be undefined

Resolving patch:
diff --git a/scipy/stats/_continuous_distns.py b/scipy/stats/_continuous_distns.py
--- a/scipy/stats/_continuous_distns.py
+++ b/scipy/stats/_continuous_distns.py
@@ -4652,7 +4652,7 @@ def _stats(self, dfn, dfd, nc):
 
 
 class t_gen(rv_continuous):
-    r"""A Student's T continuous random variable.
+    r"""A Student's t continuous random variable.
 
     %(before_notes)s
 
@@ -4662,18 +4662,23 @@ class t_gen(rv_continuous):
 
     .. math::
 
-        f(x, df) = \frac{\gamma((df+1)/2)}
-                        {\sqrt{\pi*df} \gamma(df/2) (1+x^2/df)^{(df+1)/2}}
+        f(x; \nu) = \frac{\Gamma((\nu+1)/2)}
+                        {\sqrt{\pi*\nu} \Gamma(\nu)}
+                    (1+x^2/\nu)^{-(\nu+1)/2}
 
-    for ``df > 0``.
-
-    `t` takes ``df`` as a shape parameter.
+    where ``x`` is a real number and the degrees of freedom parameter
+    :math:`\nu` (denoted ``df`` in the implementation) satisfies
+    :math:`\nu > 0``. :math:`\Gamma` is the gamma function
+    (`scipy.special.gamma`).
 
     %(after_notes)s
 
     %(example)s
 
     """
+    def _argcheck(self, df):
+        return df > 0
+
     def _rvs(self, df):
         return self._random_state.standard_t(df, size=self._size)
 
@@ -4705,14 +4710,17 @@ def _isf(self, q, df):
         return -sc.stdtrit(df, q)
 
     def _stats(self, df):
+        mu = np.where(df > 1, 0.0, np.inf)
         mu2 = _lazywhere(df > 2, (df,),
                          lambda df: df / (df-2.0),
                          np.inf)
+        mu2 = np.where(df <= 1, np.nan, mu2)
         g1 = np.where(df > 3, 0.0, np.nan)
         g2 = _lazywhere(df > 4, (df,),
                         lambda df: 6.0 / (df-4.0),
-                        np.nan)
-        return 0, mu2, g1, g2
+                        np.inf)
+        g2 = np.where(df <= 2, np.nan, g2)
+        return mu, mu2, g1, g2
 
 
 t = t_gen(name='t')


Test patch:
diff --git a/scipy/stats/tests/common_tests.py b/scipy/stats/tests/common_tests.py
--- a/scipy/stats/tests/common_tests.py
+++ b/scipy/stats/tests/common_tests.py
@@ -87,7 +87,7 @@ def check_kurt_expect(distfn, arg, m, v, k, msg):
         m4e = distfn.expect(lambda x: np.power(x-m, 4), arg)
         npt.assert_allclose(m4e, (k + 3.) * np.power(v, 2), atol=1e-5, rtol=1e-5,
                 err_msg=msg + ' - kurtosis')
-    else:
+    elif not np.isposinf(k):
         npt.assert_(np.isnan(k))
 
 
diff --git a/scipy/stats/tests/test_distributions.py b/scipy/stats/tests/test_distributions.py
--- a/scipy/stats/tests/test_distributions.py
+++ b/scipy/stats/tests/test_distributions.py
@@ -1091,6 +1091,22 @@ def test_rvgeneric_std():
     assert_array_almost_equal(stats.t.std([5, 6]), [1.29099445, 1.22474487])
 
 
+def test_moments_t():
+    # regression test for #8786
+    assert_equal(stats.t.stats(df=1, moments='mvsk'),
+                 (np.inf, np.nan, np.nan, np.nan))
+    assert_equal(stats.t.stats(df=1.01, moments='mvsk'),
+                 (0.0, np.inf, np.nan, np.nan))
+    assert_equal(stats.t.stats(df=2, moments='mvsk'),
+                 (0.0, np.inf, np.nan, np.nan))
+    assert_equal(stats.t.stats(df=2.01, moments='mvsk'),
+                 (0.0, 2.01/(2.01-2.0), np.nan, np.inf))
+    assert_equal(stats.t.stats(df=3, moments='sk'), (np.nan, np.inf))
+    assert_equal(stats.t.stats(df=3.01, moments='sk'), (0.0, np.inf))
+    assert_equal(stats.t.stats(df=4, moments='sk'), (0.0, np.inf))
+    assert_equal(stats.t.stats(df=4.01, moments='sk'), (0.0, 6.0/(4.01 - 4.0)))
+
+
 class TestRvDiscrete(object):
     def setup_method(self):
         np.random.seed(1234)