What’s the most efficient way to iterate by rows for each group of rows?

I’m wondering how to efficiently loop through rows by groups. So like the following sample dataset shows, it includes 3 different students with their pass records in 3 months.

import pandas as pd
import numpy as np
df = pd.DataFrame({'student':'A A A B B B C C C'.split(),
                  'month':[1, 2, 3, 1, 2, 3, 1, 2, 3],
                  'pass':[0, 1, 0, 0, 0, 0, 1, 0, 0]})
print(df)
 student  month  pass
0       A      1     0
1       A      2     1
2       A      3     0
3       B      1     0
4       B      2     0
5       B      3     0
6       C      1     1
7       C      2     0
8       C      3     0

I’d like to have a new column “pass_patch”, which should be equal to “pass” at first. But when a student has “pass” as 1 then all of his “pass_patch” in the following months should be 1, like the following:

df = pd.DataFrame({'student':'A A A B B B C C C'.split(),
                   'month':[1, 2, 3, 1, 2, 3, 1, 2, 3],
                   'pass':[0, 1, 0, 0, 0, 0, 1, 0, 0],
                   'pass_patch':[0, 1, 1, 0, 0, 0, 1, 1, 1]})
print(df)
  student  month  pass  pass_patch
0       A      1     0           0
1       A      2     1           1
2       A      3     0           1
3       B      1     0           0
4       B      2     0           0
5       B      3     0           0
6       C      1     1           1
7       C      2     0           1
8       C      3     0           1

I did some searches and found iterrows might work, but was concerned it would be too slow to run for the whole dataset (around million of records). Would there be more efficient ways to realize that?

Any suggestions would be greatly appreciated.

Answer

Try with cummax

df['new'] = df.groupby('student')['pass'].cummax()
df
Out[78]: 
  student  month  pass  new
0       A      1     0    0
1       A      2     1    1
2       A      3     0    1
3       B      1     0    0
4       B      2     0    0
5       B      3     0    0
6       C      1     1    1
7       C      2     0    1
8       C      3     0    1