I have the following strange error with pandas(pandas==0.23.1) :
import pandas as pd df = pd.DataFrame({'t1': ["a","b","c"]*10000, 't2': ["x","y","z"]*10000, 'i1': list(range(5000))*6, 'i2': list(range(5000))*6, 'dummy':0}) # works fast with less memory piv = df.pivot_table(values='dummy', index=['i1','i2'], columns=['t1','t2']) d2 = df.copy() d2.t1 = d2.t1.astype('category') d2.t2 = d2.t2.astype('category') # needs > 20GB of memory and takes for ever piv2 = d2.pivot_table(values='dummy', index=['i1','i2'], columns=['t1','t2'])
I am wondering if this is expected and I am doing something wrong, or if this is a bug in pandas. Should dtype category
for str
not be very transparent (for this use case)?
Advertisement
Answer
This is not a bug. What’s happening is pandas.pivot_table
is calculating the Cartesian product of grouper categories.
This is a known intended behaviour. In Pandas v0.23.0, we saw the introduction of the observed
argument for pandas.groupby
. Setting observed=True
only includes observed combinations; it is False
by default. This argument has not yet now been rolled out to related methods such as pandas.pivot_table
. In my opinion, it should be.
But now let’s see what this means. We can use an example dataframe and see what happens when we print
the result.
Setup
We make the dataframe substantially smaller:
import pandas as pd n = 10 df = pd.DataFrame({'t1': ["a","b","c"]*n, 't2': ["x","y","z"]*n, 'i1': list(range(int(n/2)))*6, 'i2': list(range(int(n/2)))*6, 'dummy':0})
Without categories
This is likely what you are looking for. Unobserved combinations of categories are not represented in your pivot table.
piv = df.pivot_table(values='dummy', index=['i1','i2'], columns=['t1','t2']) print(piv) t1 a b c t2 x y z i1 i2 0 0 0 0 0 1 1 0 0 0 2 2 0 0 0 3 3 0 0 0 4 4 0 0 0
With categories
With categories, all combinations of categories, even unobserved combinations, are accounted for in the result. This is expensive computationally and memory-hungry. Moreover, the dataframe is dominated by NaN
from unobserved combinations. It’s probably not what you want.
Update: you can now set the observed
parameter to True
to only show observed values for categorical groupers.
d2 = df.copy() d2.t1 = d2.t1.astype('category') d2.t2 = d2.t2.astype('category') piv2 = d2.pivot_table(values='dummy', index=['i1','i2'], columns=['t1','t2']) print(piv2) t1 a b c t2 x y z x y z x y z i1 i2 0 0 0.0 NaN NaN NaN 0.0 NaN NaN NaN 0.0 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN NaN NaN NaN NaN NaN NaN NaN 3 NaN NaN NaN NaN NaN NaN NaN NaN NaN 4 NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 0.0 NaN NaN NaN 0.0 NaN NaN NaN 0.0 2 NaN NaN NaN NaN NaN NaN NaN NaN NaN 3 NaN NaN NaN NaN NaN NaN NaN NaN NaN 4 NaN NaN NaN NaN NaN NaN NaN NaN NaN 2 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN 2 0.0 NaN NaN NaN 0.0 NaN NaN NaN 0.0 3 NaN NaN NaN NaN NaN NaN NaN NaN NaN 4 NaN NaN NaN NaN NaN NaN NaN NaN NaN 3 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN NaN NaN NaN NaN NaN NaN NaN 3 0.0 NaN NaN NaN 0.0 NaN NaN NaN 0.0 4 NaN NaN NaN NaN NaN NaN NaN NaN NaN 4 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN NaN NaN NaN NaN NaN NaN NaN 3 NaN NaN NaN NaN NaN NaN NaN NaN NaN 4 0.0 NaN NaN NaN 0.0 NaN NaN NaN 0.0