<p dir="auto">For certain well-chosen inputs, <code class="notranslate">group_var_float64</code> may return small negative values due to roundoff error. This then interferes with e.g. computing aggregate standard deviations:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&gt;&gt;&gt; import pandas as pd
&gt;&gt;&gt; import numpy as np
&gt;&gt;&gt; df = pd.DataFrame({'a': (0, 0, 0), 'b': 0.832845131556193 * np.ones(3)})
&gt;&gt;&gt; df.groupby('a').std()
    b
a
0 NaN"><pre class="notranslate"><span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-k">import</span> <span class="pl-s1">pandas</span> <span class="pl-k">as</span> <span class="pl-s1">pd</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">df</span> <span class="pl-c1">=</span> <span class="pl-s1">pd</span>.<span class="pl-v">DataFrame</span>({<span class="pl-s">'a'</span>: (<span class="pl-c1">0</span>, <span class="pl-c1">0</span>, <span class="pl-c1">0</span>), <span class="pl-s">'b'</span>: <span class="pl-c1">0.832845131556193</span> <span class="pl-c1">*</span> <span class="pl-s1">np</span>.<span class="pl-en">ones</span>(<span class="pl-c1">3</span>)})
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">df</span>.<span class="pl-en">groupby</span>(<span class="pl-s">'a'</span>).<span class="pl-en">std</span>()
    <span class="pl-s1">b</span>
<span class="pl-s1">a</span>
<span class="pl-c1">0</span> <span class="pl-v">NaN</span></pre></div>
<p dir="auto">To see the cause of this in isolation, consider</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import numpy as np
from pandas.algos import group_var_float64

out = np.array([[0.0]], dtype=np.float64)
counts = np.array([0])
values = 0.832845131556193 * np.ones((3, 1), dtype=np.float64)
labels = np.zeros(3, dtype=np.int)

group_var_float64(out, counts, values, labels)

print out  # This prints [[ -1.48029737e-16]] on my machine."><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>
<span class="pl-k">from</span> <span class="pl-s1">pandas</span>.<span class="pl-s1">algos</span> <span class="pl-k">import</span> <span class="pl-s1">group_var_float64</span>

<span class="pl-s1">out</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([[<span class="pl-c1">0.0</span>]], <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">np</span>.<span class="pl-s1">float64</span>)
<span class="pl-s1">counts</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">0</span>])
<span class="pl-s1">values</span> <span class="pl-c1">=</span> <span class="pl-c1">0.832845131556193</span> <span class="pl-c1">*</span> <span class="pl-s1">np</span>.<span class="pl-en">ones</span>((<span class="pl-c1">3</span>, <span class="pl-c1">1</span>), <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">np</span>.<span class="pl-s1">float64</span>)
<span class="pl-s1">labels</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">zeros</span>(<span class="pl-c1">3</span>, <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">np</span>.<span class="pl-s1">int</span>)

<span class="pl-en">group_var_float64</span>(<span class="pl-s1">out</span>, <span class="pl-s1">counts</span>, <span class="pl-s1">values</span>, <span class="pl-s1">labels</span>)

<span class="pl-k">print</span> <span class="pl-s1">out</span>  <span class="pl-c"># This prints [[ -1.48029737e-16]] on my machine.</span></pre></div>
<p dir="auto">The fix for this should be easy (round up negative values to zero). I can provide a fix (+ tests) if needed.</p>
<h2 dir="auto">INSTALLED VERSIONS</h2>
<p dir="auto">commit: None<br>
python: 2.7.6.final.0<br>
python-bits: 64<br>
OS: Darwin<br>
OS-release: 14.3.0<br>
machine: x86_64<br>
processor: i386<br>
byteorder: little<br>
LC_ALL: None<br>
LANG: nl_BE.UTF-8</p>
<p dir="auto">pandas: 0.16.1<br>
nose: 1.3.4<br>
Cython: 0.22<br>
numpy: 1.9.2<br>
scipy: 0.15.1<br>
statsmodels: None<br>
IPython: 3.1.0<br>
sphinx: 1.3.1<br>
patsy: None<br>
dateutil: 2.4.2<br>
pytz: 2014.9<br>
bottleneck: None<br>
tables: 3.1.1<br>
numexpr: 2.4<br>
matplotlib: 1.4.3<br>
openpyxl: None<br>
xlrd: None<br>
xlwt: None<br>
xlsxwriter: None<br>
lxml: None<br>
bs4: 4.3.2<br>
html5lib: 0.999<br>
httplib2: None<br>
apiclient: None<br>
sqlalchemy: 1.0.4<br>
pymysql: None<br>
psycopg2: None</p>
<p dir="auto">I propose adding a pipe method to GroupBy objects, having the same interface as <code class="notranslate">DataFrame.pipe</code>/<code class="notranslate">Series.pipe</code>.</p>
<p dir="auto">The use case is reusing a GroupBy object when doing calculations, see use case example below.</p>
<h2 dir="auto">Use case</h2>
<p dir="auto">A pipe is useful for succintly reusing Groupby objects in calculations, for example calculating prices given a column of revenue and a columns of quantity sold:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&gt;&gt;&gt; from numpy.random import choice, random
&gt;&gt;&gt; n = 100_000
&gt;&gt;&gt; df = pd.DataFrame({'Store': choice(['Store_1', 'Store_2'], n),
                       'Year': choice(['Year_1', 'Year_2', 'Year_3', 'Year_4'], n),
                       'Revenue': (np.random.random(n)*50+10).round(2),
                       'Quantity': np.random.randint(1, 10, size=n)})
&gt;&gt;&gt; df.head(2)
   Quantity  Revenue    Store    Year
0         2    14.69  Store_1  Year_1
1         9    25.89  Store_2  Year_4"><pre class="notranslate"><span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-k">from</span> <span class="pl-s1">numpy</span>.<span class="pl-s1">random</span> <span class="pl-k">import</span> <span class="pl-s1">choice</span>, <span class="pl-s1">random</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">n</span> <span class="pl-c1">=</span> <span class="pl-c1">100_000</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">df</span> <span class="pl-c1">=</span> <span class="pl-s1">pd</span>.<span class="pl-v">DataFrame</span>({<span class="pl-s">'Store'</span>: <span class="pl-en">choice</span>([<span class="pl-s">'Store_1'</span>, <span class="pl-s">'Store_2'</span>], <span class="pl-s1">n</span>),
                       <span class="pl-s">'Year'</span>: <span class="pl-en">choice</span>([<span class="pl-s">'Year_1'</span>, <span class="pl-s">'Year_2'</span>, <span class="pl-s">'Year_3'</span>, <span class="pl-s">'Year_4'</span>], <span class="pl-s1">n</span>),
                       <span class="pl-s">'Revenue'</span>: (<span class="pl-s1">np</span>.<span class="pl-s1">random</span>.<span class="pl-en">random</span>(<span class="pl-s1">n</span>)<span class="pl-c1">*</span><span class="pl-c1">50</span><span class="pl-c1">+</span><span class="pl-c1">10</span>).<span class="pl-en">round</span>(<span class="pl-c1">2</span>),
                       <span class="pl-s">'Quantity'</span>: <span class="pl-s1">np</span>.<span class="pl-s1">random</span>.<span class="pl-en">randint</span>(<span class="pl-c1">1</span>, <span class="pl-c1">10</span>, <span class="pl-s1">size</span><span class="pl-c1">=</span><span class="pl-s1">n</span>)})
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">df</span>.<span class="pl-en">head</span>(<span class="pl-c1">2</span>)
   <span class="pl-v">Quantity</span>  <span class="pl-v">Revenue</span>    <span class="pl-v">Store</span>    <span class="pl-v">Year</span>
<span class="pl-c1">0</span>         <span class="pl-c1">2</span>    <span class="pl-c1">14.69</span>  <span class="pl-v">Store_1</span>  <span class="pl-v">Year_1</span>
<span class="pl-c1">1</span>         <span class="pl-c1">9</span>    <span class="pl-c1">25.89</span>  <span class="pl-v">Store_2</span>  <span class="pl-v">Year_4</span></pre></div>
<p dir="auto">Then having <code class="notranslate">.pipe</code>, we could for example get prices per store/year like so</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&gt;&gt;&gt; (df.groupby(['Store', 'Year'])
...    .pipe(lambda grp: grp.Revenue.sum()/grp.Quantity.sum())
...    .unstack().round(2))
Year     Year_1  Year_2  Year_3  Year_4
Store
Store_1    6.99    6.99    7.01    6.92
Store_2    6.95    6.98    6.97    6.96"><pre class="notranslate"><span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> (<span class="pl-s1">df</span>.<span class="pl-en">groupby</span>([<span class="pl-s">'Store'</span>, <span class="pl-s">'Year'</span>])
...    .<span class="pl-en">pipe</span>(<span class="pl-k">lambda</span> <span class="pl-s1">grp</span>: <span class="pl-s1">grp</span>.<span class="pl-v">Revenue</span>.<span class="pl-en">sum</span>()<span class="pl-c1">/</span><span class="pl-s1">grp</span>.<span class="pl-v">Quantity</span>.<span class="pl-en">sum</span>())
...    .<span class="pl-en">unstack</span>().<span class="pl-en">round</span>(<span class="pl-c1">2</span>))
<span class="pl-v">Year</span>     <span class="pl-v">Year_1</span>  <span class="pl-v">Year_2</span>  <span class="pl-v">Year_3</span>  <span class="pl-v">Year_4</span>
<span class="pl-v">Store</span>
<span class="pl-v">Store_1</span>    <span class="pl-c1">6.99</span>    <span class="pl-c1">6.99</span>    <span class="pl-c1">7.01</span>    <span class="pl-c1">6.92</span>
<span class="pl-v">Store_2</span>    <span class="pl-c1">6.95</span>    <span class="pl-c1">6.98</span>    <span class="pl-c1">6.97</span>    <span class="pl-c1">6.96</span></pre></div>
<p dir="auto">Note that the above is vectorized and piping makes the code succint and clear.</p>
<h2 dir="auto">Alternatives to .pipe</h2>
<p dir="auto">The alternatives would be:</p>
<ol dir="auto">
<li>use <code class="notranslate">.apply</code>,</li>
<li>create a function and call that with a GroupBy object as its argument</li>
<li>Create a price column</li>
</ol>
<p dir="auto">Option 1 is not good because of slowness.</p>
<p dir="auto">A pipe is just syntactic sugar for option 2, but would piping be more readable, especially it you're piping other stuff already.</p>
<p dir="auto">Creating a concrete calculated column is in some instances the right approach, but in other cases it is better to calculate stuff.</p>