How to use multiple cross-section observations per subject for churn prediction?
If you know SQL, then my method might work. I'll break it out into subqueries to make it a little more understandable. I don't know your columns so this might not translate perfectly.
The point is to have 1 query identify all active students at a particular point in time. The point of time you choose is arbitrary, but it must have enough time forwards enough to the end of your data so that you can establish a forward-looking window to satisfy churn or not. I am calling this the snapshot_date. Don't have students who churned before the shapshot_date in this resultset at all.
Then, you join this to a subquery which uses that shapshot_date and looks forward for the churn condition. This returns only students who churned, but churned within the window that you decide. If they churned in the future beyond your window, treat them as if they have not churned.
The very first query that returns a list of active students might still return multiple rows for each student. You handle this by deciding how to aggregate those rows into a single row for each customer. You might end up creating a lot of good variables for your model this way. I added a few as an example.
Then, your SQL would look similar to this:
SELECT active_students.*, 
case when student_churned is not null then 1 else 0 end as target_student_churn
FROM
    (
        SELECT Key,
        DateDiff(snapshot_date, date_of_enrollment, &quot;Days&quot;) as time_since_enrollment,
        min(date_of_enrollment) as date_of_enrollment,
        max(felev) as max_felev,
        COUNT(DISTINCT course) as distinct_courses_taken,
        ...
        FROM table
        WHERE date_of_enrollment &lt; snapshot_date
        AND {student has not churned as of snapshot date}
        GROUP BY Key
    ) as active_students
LEFT OUTER JOIN 
    (
    SELECT Key
    FROM table
    WHERE {student churned within hard coded time period AFTER snapshot date}
    GROUP BY Key
    ) as student_churned
ON active_students.Key = student_churned.Key;

You may wonder then how this accounts for seasonality over time if you chose your snapshot date randomly. Well, just change the snapshot_date and repeat while you continue to stack the results on top of one another. Having the snapshot date as a column will help you extract the &quot;season&quot; from each resultset.
Now the students might be repeated again in the final dataset. So, when you build the model you want to make sure that the students' rows fall into the same validation or holdout dataset. So use GroupKFold or something like that. I hope this helps.