<p dir="auto">Here is the test code:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="import pandas as pd
from numpy.random import randint, rand, seed

seed(10)
N_Group = 20
N_Rows = 1000
df = pd.DataFrame({&quot;g&quot;:randint(0, N_Group, N_Rows), &quot;a&quot;:rand(N_Rows), &quot;b&quot;:rand(N_Rows)})

def f(df):
    df[&quot;a&quot;] -= df[&quot;b&quot;].mean()
    return df

%time df1 = df.groupby(&quot;g&quot;).apply(f)
%time df2 = df.groupby(&quot;g&quot;).transform(f)"><pre class="notranslate"><code class="notranslate">import pandas as pd
from numpy.random import randint, rand, seed

seed(10)
N_Group = 20
N_Rows = 1000
df = pd.DataFrame({"g":randint(0, N_Group, N_Rows), "a":rand(N_Rows), "b":rand(N_Rows)})

def f(df):
    df["a"] -= df["b"].mean()
    return df

%time df1 = df.groupby("g").apply(f)
%time df2 = df.groupby("g").transform(f)
</code></pre></div>
<p dir="auto">the output is:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Wall time: 17 ms
Wall time: 682 ms"><pre class="notranslate"><code class="notranslate">Wall time: 17 ms
Wall time: 682 ms
</code></pre></div>
<p dir="auto">before the for-loop for every group in apply(), it disable the 'mode.chained_assignment' opition:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="    # ignore SettingWithCopy here in case the user mutates
    with option_context('mode.chained_assignment',None):
        return self._python_apply_general(f)"><pre class="notranslate"><code class="notranslate">    # ignore SettingWithCopy here in case the user mutates
    with option_context('mode.chained_assignment',None):
        return self._python_apply_general(f)
</code></pre></div>
<p dir="auto">but there is not such code for <code class="notranslate">transform()</code>.</p>
<p dir="auto"><code class="notranslate">to_records()</code> and the df constructor are broken for data containing categoricals when created with <code class="notranslate">dtype='category'</code> or <code class="notranslate">astype('category')</code>.</p>
<p dir="auto">Broken (<code class="notranslate">TypeError: data type not understood</code>):</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="pd.DataFrame(list('abc'),dtype='category')

df=pd.DataFrame(list('abc'), columns=['A'])
df['A'] = df['A'].astype('category')
df.to_records()"><pre class="notranslate"><span class="pl-s1">pd</span>.<span class="pl-v">DataFrame</span>(<span class="pl-en">list</span>(<span class="pl-s">'abc'</span>),<span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s">'category'</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-en">list</span>(<span class="pl-s">'abc'</span>), <span class="pl-s1">columns</span><span class="pl-c1">=</span>[<span class="pl-s">'A'</span>])
<span class="pl-s1">df</span>[<span class="pl-s">'A'</span>] <span class="pl-c1">=</span> <span class="pl-s1">df</span>[<span class="pl-s">'A'</span>].<span class="pl-en">astype</span>(<span class="pl-s">'category'</span>)
<span class="pl-s1">df</span>.<span class="pl-en">to_records</span>()</pre></div>
<p dir="auto">Works:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="pd.Series(list('abc'), dtype='category')

pd.DataFrame(list('abc'), dtype=pd.core.common.CategoricalDtype)

df=pd.DataFrame(list('abc'), columns=['A'])
df['A'] = df['A'].astype(pd.core.common.CategoricalDtype)
df.to_records()

pd.Series(list('abc'), dtype='category').to_frame().to_records()"><pre class="notranslate"><span class="pl-s1">pd</span>.<span class="pl-v">Series</span>(<span class="pl-en">list</span>(<span class="pl-s">'abc'</span>), <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s">'category'</span>)

<span class="pl-s1">pd</span>.<span class="pl-v">DataFrame</span>(<span class="pl-en">list</span>(<span class="pl-s">'abc'</span>), <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">pd</span>.<span class="pl-s1">core</span>.<span class="pl-s1">common</span>.<span class="pl-v">CategoricalDtype</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-en">list</span>(<span class="pl-s">'abc'</span>), <span class="pl-s1">columns</span><span class="pl-c1">=</span>[<span class="pl-s">'A'</span>])
<span class="pl-s1">df</span>[<span class="pl-s">'A'</span>] <span class="pl-c1">=</span> <span class="pl-s1">df</span>[<span class="pl-s">'A'</span>].<span class="pl-en">astype</span>(<span class="pl-s1">pd</span>.<span class="pl-s1">core</span>.<span class="pl-s1">common</span>.<span class="pl-v">CategoricalDtype</span>)
<span class="pl-s1">df</span>.<span class="pl-en">to_records</span>()

<span class="pl-s1">pd</span>.<span class="pl-v">Series</span>(<span class="pl-en">list</span>(<span class="pl-s">'abc'</span>), <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s">'category'</span>).<span class="pl-en">to_frame</span>().<span class="pl-en">to_records</span>()</pre></div>
<p dir="auto"><code class="notranslate">to_frame</code> seems to remove the category dtype though.</p>
<p dir="auto">Pandas version: 0.15.0-20-g2737f5a</p>