Repository: astropy/astropy

Issue:
sigma_clipped_stats fails with quantity input
Just what the title says really:
```
>>> stats.sigma_clipped_stats(np.random.randn(100)*u.kpc)
...
UnitConversionError: 'kpc(1/2)' and 'kpc' (length) are not convertible

During handling of the above exception, another exception occurred:
...
UnitConversionError: Can only apply 'subtract' function to quantities with compatible dimensions
```
Whereas I *expected* ``<Quantity [0.03102944, 0.13463141, 1.07249346] km>`` (or maybe ``(<Quantity 0.03102944 km>, <Quantity 0.13463141 km>, <Quantity 1.07249346 km>)``)


Discussion:
bsipocz: @crawfordsm - Failure looks related. With the new commit could you also please rebase?
crawfordsm: Looks like the failure  is due to bottleneck not returning a quantity.  Is
there a preferred method for wrapping functions to return quantities or
just to add an in function check?

pllim: @astrofrog , I think you added `bottleneck`?
astrofrog: I don't know if there is a preferred way to handle the bottleneck calls.
codecov[bot]: # [Codecov](https://codecov.io/gh/astropy/astropy/pull/8253?src=pr&el=h1) Report
> Merging [#8253](https://codecov.io/gh/astropy/astropy/pull/8253?src=pr&el=desc) into [master](https://codecov.io/gh/astropy/astropy/commit/e720bee8d0b1122ca79803b8c4d1366805389fda?src=pr&el=desc) will **decrease** coverage by `0.17%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/astropy/astropy/pull/8253/graphs/tree.svg?width=650&token=eEhbJxwU0j&height=150&src=pr)](https://codecov.io/gh/astropy/astropy/pull/8253?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #8253      +/-   ##
==========================================
- Coverage   86.91%   86.74%   -0.18%     
==========================================
  Files         383      385       +2     
  Lines       57889    58032     +143     
  Branches     1056     1060       +4     
==========================================
+ Hits        50313    50338      +25     
- Misses       6962     7079     +117     
- Partials      614      615       +1
```


| [Impacted Files](https://codecov.io/gh/astropy/astropy/pull/8253?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [astropy/stats/sigma\_clipping.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9zdGF0cy9zaWdtYV9jbGlwcGluZy5weQ==) | `98.2% <100%> (+0.07%)` | :arrow_up: |
| [...dinates/builtin\_frames/cirs\_observed\_transforms.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9jb29yZGluYXRlcy9idWlsdGluX2ZyYW1lcy9jaXJzX29ic2VydmVkX3RyYW5zZm9ybXMucHk=) | `53.84% <0%> (-46.16%)` | :arrow_down: |
| [astropy/coordinates/builtin\_frames/itrs.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9jb29yZGluYXRlcy9idWlsdGluX2ZyYW1lcy9pdHJzLnB5) | `80% <0%> (-20%)` | :arrow_down: |
| [...builtin\_frames/intermediate\_rotation\_transforms.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9jb29yZGluYXRlcy9idWlsdGluX2ZyYW1lcy9pbnRlcm1lZGlhdGVfcm90YXRpb25fdHJhbnNmb3Jtcy5weQ==) | `83.01% <0%> (-16.99%)` | :arrow_down: |
| [...coordinates/builtin\_frames/icrs\_cirs\_transforms.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9jb29yZGluYXRlcy9idWlsdGluX2ZyYW1lcy9pY3JzX2NpcnNfdHJhbnNmb3Jtcy5weQ==) | `78.1% <0%> (-14.6%)` | :arrow_down: |
| [astropy/coordinates/sky\_coordinate.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9jb29yZGluYXRlcy9za3lfY29vcmRpbmF0ZS5weQ==) | `82.28% <0%> (-8.4%)` | :arrow_down: |
| [astropy/coordinates/earth.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9jb29yZGluYXRlcy9lYXJ0aC5weQ==) | `75.09% <0%> (-3.35%)` | :arrow_down: |
| [astropy/coordinates/attributes.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9jb29yZGluYXRlcy9hdHRyaWJ1dGVzLnB5) | `92.14% <0%> (-2.86%)` | :arrow_down: |
| [astropy/coordinates/solar\_system.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9jb29yZGluYXRlcy9zb2xhcl9zeXN0ZW0ucHk=) | `70.06% <0%> (-1.28%)` | :arrow_down: |
| [astropy/coordinates/baseframe.py](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree#diff-YXN0cm9weS9jb29yZGluYXRlcy9iYXNlZnJhbWUucHk=) | `92.93% <0%> (-0.5%)` | :arrow_down: |
| ... and [18 more](https://codecov.io/gh/astropy/astropy/pull/8253/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/astropy/astropy/pull/8253?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/astropy/astropy/pull/8253?src=pr&el=footer). Last update [e720bee...de2f0d9](https://codecov.io/gh/astropy/astropy/pull/8253?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/astropy/stats/sigma_clipping.py b/astropy/stats/sigma_clipping.py
index afcaa853fc..5d94bb4d77 100644
--- a/astropy/stats/sigma_clipping.py
+++ b/astropy/stats/sigma_clipping.py
@@ -12,6 +12,7 @@ from astropy.utils.exceptions import AstropyUserWarning
 try:
     import bottleneck  # pylint: disable=W0611
     HAS_BOTTLENECK = True
+    from astropy.units import Quantity
 except ImportError:
     HAS_BOTTLENECK = False
 
@@ -54,7 +55,11 @@ def _nanmean(array, axis=None):
     if isinstance(axis, tuple):
         array = _move_tuple_axes_first(array, axis=axis)
         axis = 0
-    return bottleneck.nanmean(array, axis=axis)
+
+    if isinstance(array, Quantity):
+        return array.__array_wrap__(bottleneck.nanmean(array, axis=axis))
+    else:
+        return bottleneck.nanmean(array, axis=axis)
 
 
 def _nanmedian(array, axis=None):
@@ -63,7 +68,11 @@ def _nanmedian(array, axis=None):
     if isinstance(axis, tuple):
         array = _move_tuple_axes_first(array, axis=axis)
         axis = 0
-    return bottleneck.nanmedian(array, axis=axis)
+
+    if isinstance(array, Quantity):
+        return array.__array_wrap__(bottleneck.nanmedian(array, axis=axis))
+    else:
+        return bottleneck.nanmedian(array, axis=axis)
 
 
 def _nanstd(array, axis=None, ddof=0):
@@ -72,7 +81,12 @@ def _nanstd(array, axis=None, ddof=0):
     if isinstance(axis, tuple):
         array = _move_tuple_axes_first(array, axis=axis)
         axis = 0
-    return bottleneck.nanstd(array, axis=axis, ddof=ddof)
+
+    if isinstance(array, Quantity):
+        return array.__array_wrap__(bottleneck.nanstd(array, axis=axis,
+                                                      ddof=ddof))
+    else:
+        return bottleneck.nanstd(array, axis=axis, ddof=ddof)
 
 
 class SigmaClip:


Test patch:
diff --git a/astropy/stats/tests/test_sigma_clipping.py b/astropy/stats/tests/test_sigma_clipping.py
index 45f6fb69a3..4c0772dcf4 100644
--- a/astropy/stats/tests/test_sigma_clipping.py
+++ b/astropy/stats/tests/test_sigma_clipping.py
@@ -13,6 +13,7 @@ except ImportError:
 else:
     HAS_SCIPY = True
 
+from astropy import units as u
 from astropy.stats.sigma_clipping import sigma_clip, SigmaClip, sigma_clipped_stats
 from astropy.utils.misc import NumpyRNGContext
 
@@ -265,3 +266,9 @@ def test_sigmaclip_repr():
 
     assert repr(sigclip).startswith(sigclip_repr)
     assert str(sigclip).startswith(sigclip_str)
+
+
+def test_sigma_clippped_stats_unit():
+    data = np.array([1, 1]) * u.kpc
+    result = sigma_clipped_stats(data)
+    assert result == (1. * u.kpc, 1. * u.kpc, 0. * u.kpc)