Skip to content
Advertisement

pivot_table requires more memory if dtype is category (MemoryError)

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

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement