Creating a pandas pivot table to count number of times items appear in a list together

Here is another way by using numpy broadcasting to create a matrix which is obtained by comparing each value in user_id with every other value, then create a new dataframe from this matrix with index and columns set to page_view_page_slug and take sum on level=0 along axis=0 and axis=1 to count the user_ids of the cross section of slugs:

a = df['user_id'].values
i = list(df['page_view_page_slug'])

pd.DataFrame(a[:, None] == a, index=i, columns=i)\
   .sum(level=0).sum(level=0, axis=1).astype(int)

       slug1  slug2  slug3  slug4  slug5
slug1      2      2      2      1      1
slug2      2      2      2      1      1
slug3      2      2      2      1      1
slug4      1      1      1      1      0
slug5      1      1      1      0      1

Let's try groupby and reduce:

from functools import reduce

dfs = [pd.DataFrame(1, index=list(s), columns=list(s)) 
      for _, s in df.groupby('user_id')['page_view_page_slug']]
      
df_out = reduce(lambda x, y: x.add(y, fill_value=0), dfs).fillna(0).astype(int)

Details:

group the dataframe on user_id then for each group in page_view_page_slug per user_id create an adjacency dataframe with index and columns corresponding to the slugs in that group.

>>> dfs

[       slug1  slug2  slug3  slug4
 slug1      1      1      1      1
 slug2      1      1      1      1
 slug3      1      1      1      1
 slug4      1      1      1      1,
        slug5  slug3  slug2  slug1
 slug5      1      1      1      1
 slug3      1      1      1      1
 slug2      1      1      1      1
 slug1      1      1      1      1]

Now reduce the above adjacency dataframes using a reduction function DataFrame.add with optional parameter fill_value=0 so as to count the user_ids of the cross section of slugs.

>>> df_out

       slug1  slug2  slug3  slug4  slug5
slug1      2      2      2      1      1
slug2      2      2      2      1      1
slug3      2      2      2      1      1
slug4      1      1      1      1      0
slug5      1      1      1      0      1

Optionally you can wrap the above code in a function as follows:

def count():
    df_out = pd.DataFrame()
    for _, s in df.groupby('user_id')['page_view_page_slug']:
        df_out = df_out.add(
            pd.DataFrame(1, index=list(s), columns=list(s)), fill_value=0)

    return df_out.fillna(0).astype(int)

>>> count()

       slug1  slug2  slug3  slug4  slug5
slug1      2      2      2      1      1
slug2      2      2      2      1      1
slug3      2      2      2      1      1
slug4      1      1      1      1      0
slug5      1      1      1      0      1