I'm trying to create a user retention table using Pyspark which I can transfer to AWS Glue to create an ETL job that I can query using Athena in QuickSight.
Basically, I have two tables, one with the user registration date and one with the user activity date. This registration date is then compared with the activity date to calculate how long after the registration the user is active. Thereafter I wanna track how many of the users that was registered on a certain month are active after, 0, 1, 2 weeks etc. I therefore wanna calculate the distinct count of users after week 0, after week 1, etc, i.e. not the normal cohort table where they are grouped by month and then tracked which could result in a scenario where the user activity is larger at 3 months after registration then after 2 months.
A snip of the table and the desired outcome can be seen below:
- user_id 1 has 5 activities, 2 at week 0, 2 at week 2 and 1 at week 6.
- user_id 2 has 5 activities, 1 at week 0, 2 at week 1, 1 at week 2 and 1 at week 3.
- user_id 3 has 3 activities, 1 at week 0, 1 at week 1 and 1 at week 4
However,
- There are 3 unique users (id: 1, 2, 3) seen at 0 weeks or later after registrations in August.
- There are 3 unique users (user_id: 1, 2, 3) seen at 1 weeks or later after registrations in August.
- ...
- There are 2 unique users (user_id: 1, 3) seen at 4 weeks or later after registrations in August.
- There are 1 unique user (user_id: 1) seen at 5 weeks or later after registration in August.
- There are 1 unique user (user_id: 1) seen at 6 weeks or later after registration in August.
- There are 0 unique users seen at 7 weeks or later after registration in August.
To get the number of registrations per month I just do a simple groupBy:
df_reg = df\
.sort(col('user_id').asc(), col('created_at').asc())\
.groupBy('registered_at_month')\
.agg(countDistinct('user_id').alias('reg'))
To get the distinct count of users after each week I apply a filter to the dataframe and loop through the weeks and thereafter apply a pivot function to get the table:
retention = []
for week in weeks:
print(week)
df_out = df\
.filter((col('diff_week') >= week))\
.sort(col('user_id').asc(), col('created_at').asc())\
.groupBy('registered_at_month')\
.agg(countDistinct('user_id').alias('countDistinct'))\
.withColumn('week', lit(week))
retention.append(df_out)
df_retention = functools.reduce(DataFrame.union, retention)
df_retention_2 = df_retention\
.groupBy('registered_at_month')\
.pivot('week')\
.agg(first('countDistinct'))\
.orderBy('registered_at_month')
Is there a cleaner way of doing this? Preferably without a for loop. Also, the pivot function takes forever when the input data gets large and there are thousands of users registered per month and hundreds of weeks of activity? Finally, could this be done directly in QuickSight using some calculated fields?
Very thankful for any help! Thank you!

