b=df.groupby(["ID","Class"])["Class"].count().unstack()
Вы группируетесь как по идентификатору, так и по классу, что означает, что вы получите количество каждого класса для каждого идентификатора. Затем вы вызываете unstack, который берет самые левые метки из индекса и вставляет их как столбцы.
После того, как вы создадите другую группу, по которой вы определяете последнее появление (это решение предполагает, что ваши данные упорядочены по дате, если не использовать функцию max).
c=df.groupby("ID").agg({"Date":"last","Class":"last"})
После объединения двух фреймов данных.
b.merge(c, on="ID")
И вы получите то, что просили.