When working with data, it is very useful to be able to group and aggregate data by multiple columns to understand the various segments of our data.

With pandas, we can easily find the frequencies of columns in a dataframe using the pandas value_counts() function, and we can do cross tabulations very easily using the pandas crosstab() function.

However, what is not obvious is how to use pandas to create a crosstab for 3 columns or a crosstab for an arbitrary number of columns and make it easy to work with.

In the rest of this post, you’ll find exactly the code you need to be able to produce a pandas crosstab on multiple columns.

Pandas Crosstab with Multiple Columns

Let’s say I have the following data:

   animal_type  gender  weight  age state trained
0          cat    male      10    1    CA      no
1          dog    male      20    4    FL      no
2          dog    male      30    5    NY      no
3          cat  female      40    3    FL     yes
4          cat  female      10    2    NY     yes
5          dog  female      20    4    TX     yes
6          cat  female      50    6    TX     yes
7          dog    male      60    1    CA      no
8          dog    male      70    5    NY      no
9          cat  female      80    4    FL     yes
10         cat  female      90    3    TX     yes
11         cat    male     100    2    TX      no
12         dog  female      80    4    FL      no

To calculate a simple two column crosstab, we can do the following:

pd.crosstab(data["gender"],data["state"])

#output
state   CA  FL  NY  TX
gender
female   0   3   1   3
male     2   1   2   1

While this is nice, it would be better to have something that we can work with more easily.

The key to creating a generalized function which will create a crosstab for any number of columns using pandas and Python is to utilize the pandas unstack() function with the pandas reset_index() function like shown below:

pd.crosstab(data["gender"],data["state"]).unstack().reset_index().rename(columns={0:'Count'})

#output:
  state  gender  Count
0    CA  female      0
1    CA    male      2
2    FL  female      3
3    FL    male      1
4    NY  female      1
5    NY    male      2
6    TX  female      3
7    TX    male      1

With this in mind, we can easily create a function which will produce a crosstab for multiple columns.

Below is a function which takes a dataframe and a list of column names and produces the frequencies for each of the groups we want.

def frequency(ds, vars):
    if len(vars) > 1:
        c1 = ds[vars[0]]
        c2 = []
        for i in range(1,len(vars)):
            c2.append(ds[vars[i]])
        dfs = []
        dfs.append(pd.crosstab(c1,c2).unstack().reset_index().rename(columns={0:'Count'}))
        dfs.append(pd.crosstab(c1,c2, normalize='all').unstack().reset_index().rename(columns={0:'Percent'}))
        dfs = [df.set_index(vars) for df in dfs]
        df = dfs[0].join(dfs[1:]).reset_index()
        return df

First, we need to create a list of columns which we will do the crosstab with. Then, we will call the pandas crosstab() function, unstack the result, and reset the index.

I’ve also included the ability to calculate the percentages for each group which is easily done by passing the “normalize=’all'” option to the crosstab() function.

Here’s the output of our frequency function which gives us the counts and percentages of each segment from our crosstab:

frequency(data,["animal_type","gender","state","trained"])

#output
   animal_type  gender state trained  Count   Percent
0          cat  female    FL      no      0  0.000000
1          dog  female    FL      no      1  0.076923
2          cat  female    FL     yes      2  0.153846
3          dog  female    FL     yes      0  0.000000
4          cat  female    NY     yes      1  0.076923
5          dog  female    NY     yes      0  0.000000
6          cat  female    TX     yes      2  0.153846
7          dog  female    TX     yes      1  0.076923
8          cat    male    CA      no      1  0.076923
9          dog    male    CA      no      1  0.076923
10         cat    male    FL      no      0  0.000000
11         dog    male    FL      no      1  0.076923
12         cat    male    NY      no      0  0.000000
13         dog    male    NY      no      2  0.153846
14         cat    male    TX      no      1  0.076923
15         dog    male    TX      no      0  0.000000

Finding Row Percentages and Column Percentages in Crosstab

Many times when we are analyzing data, we want to find the row percentages and column percentages in a crosstab.

To do this, we can add two lines to our function and utilize the “normalize” option of the crosstab() function.

Below is the new function which will allow us to calculate the row and column percentages in a crosstab using pandas and Python:

def frequency(ds, vars):
    if len(vars) > 1:
        c1 = ds[vars[0]]
        c2 = []
        for i in range(1,len(vars)):
            c2.append(ds[vars[i]])
        dfs = []
        dfs.append(pd.crosstab(c1,c2).unstack().reset_index().rename(columns={0:'Count'}))
        dfs.append(pd.crosstab(c1,c2, normalize='all').unstack().reset_index().rename(columns={0:'Percent'}))
        dfs.append(pd.crosstab(c1,c2, normalize='columns').unstack().reset_index().rename(columns={0:'Column Percent'}))
        dfs.append(pd.crosstab(c1,c2, normalize='index').unstack().reset_index().rename(columns={0:'Row Percent'}))
        dfs = [df.set_index(vars) for df in dfs]
        df = dfs[0].join(dfs[1:]).reset_index()
        return df

Here’s the output of our frequency function which gives us the counts and percentages of each segment in our dataframe, and also the row and column percentages in our crosstab:

frequency(data,["animal_type","gender","state","trained"])

#output
   animal_type  gender state trained  Count   Percent  Column Percent  Row Percent
0          cat  female    FL      no      0  0.000000        0.000000     0.000000
1          dog  female    FL      no      1  0.076923        1.000000     0.166667
2          cat  female    FL     yes      2  0.153846        1.000000     0.285714
3          dog  female    FL     yes      0  0.000000        0.000000     0.000000
4          cat  female    NY     yes      1  0.076923        1.000000     0.142857
5          dog  female    NY     yes      0  0.000000        0.000000     0.000000
6          cat  female    TX     yes      2  0.153846        0.666667     0.285714
7          dog  female    TX     yes      1  0.076923        0.333333     0.166667
8          cat    male    CA      no      1  0.076923        0.500000     0.142857
9          dog    male    CA      no      1  0.076923        0.500000     0.166667
10         cat    male    FL      no      0  0.000000        0.000000     0.000000
11         dog    male    FL      no      1  0.076923        1.000000     0.166667
12         cat    male    NY      no      0  0.000000        0.000000     0.000000
13         dog    male    NY      no      2  0.153846        1.000000     0.333333
14         cat    male    TX      no      1  0.076923        1.000000     0.142857
15         dog    male    TX      no      0  0.000000        0.000000     0.000000

Hopefully this article has been helpful for you to learn how to use pandas to create a crosstab across multiple columns in Python.

Categorized in:

Python,

Last Update: March 21, 2024