Get the last activity per user per day in a dataframe
Instead of using tail, if you only need one item there are the first and last methods, which do exactly what you think they would do with grouped dataframes:
df_example.groupby(['id', 'date']).last()

I doubt there is a faster way to create the activity column. And you have to create a new one because of your requirement with what counts to which day.
But you can speed up getting the date. Using apply with a lambda is about the second slowest way to work with pandas (manual Python for loops are slower). Instead use the vectorized datetime functions:
df_example['date'] = df_example['activity'].dt.date


Potential bug:
Note that df_example.sort_values(by=['id', 'activity']) returns the sorted dataframe, it does not modify it inplace. Either assign it back to df_example, or use inplace=True.
The same is true for groupby, you probably want to assign the result to a variable as well in order to do something else with it afterwards.

Python has an official style-guide, PEP8. While it recommends using spaces around the = operator when using it for assignment, it recommends using no spaces when using it for keyword arguments.