@ braml1 ответ работает, но здесь я предоставил альтернативу, которая подправляет несколько вещей. Во-первых, вот альтернативное решение:
df = df.sort_values(by=['OrgID', 'Year'], ascending = True)
df['startCounter'] = df.groupby('OrgID')['Program'].apply(lambda x:
((x.shift(1)==0)&(x.shift(2) == 0) & (x == 1))).values
df['stopCounter'] = df.groupby('OrgID')['Total_Fees_for_Services_binary'].apply(lambda x: x==0).values
df['counting'] = np.where(df['startCounter'] & ~df['stopCounter'],1,np.NaN)
df['counting'] = np.where(df['stopCounter'], 0, df['counting'])
df['counting'] = df.groupby('OrgID')['counting'].ffill().fillna(0)
a = df.groupby('OrgID')['counting'].fillna(0).eq(1)
b = a.cumsum()
df['cumsum'] = b-b.where(~a).ffill().fillna(0).astype(int)
Вот ключевые отличия. Сначала я сортирую по OrgID и Год :
df = df.sort_values(by=['OrgID', 'Year'], ascending = True)
Затем с startCounter и stopCounter Я различаюсь на включая groupby операторы:
df['startCounter'] = df.groupby('OrgID')['Program'].apply(lambda x:
((x.shift(1)==0)&(x.shift(2) == 0) & (x == 1))).values
df['stopCounter'] = df.groupby('OrgID)['Total_Fees_for_Services_binary'].apply(lambda x: x==0).values
С помощью этих команд я могу пропустить создание двухэтапных промежуточных переменных program1Ybefore и program2Ybefore .
Далее, первые две строки в создании переменной count такие же, как в ответе @ braml1:
df['counting'] = np.where(df['startCounter'] & ~df['stopCounter'],1,np.NaN)
df['counting'] = np.where(df['stopCounter'], 0, df['counting'])
Третья строка, тем не менее, снова включает groupby :
df['counting'] = df.groupby('OrgID')['counting'].ffill().fillna(0)
Однако самое большое изменение наступает на последнем шаге - создание переменной cumsum . Здесь я был вдохновлен другим SO ответом
В частности, вместо применения функции * bra421 cumsumWithReset (которая использует al oop по всем строкам кадра данных ), Я применяю кумулятивную сумму со сбросом при соблюдении определенного c условия. Сначала a преобразует двоичный (0/1) столбец , считая , в столбец True / False. Напомним, что столбец count - это столбец, в котором указаны все строки, в которых есть действительная «новая программа», и именно для этих строк мы хотим получить накопительную сумму.
a = df.groupby('OrgID')['counting'].fillna(0).eq(1)
b затем принимает кумулятивную сумму для значений в a
b = a.cumsum()
Наконец, мы присваиваем значения новой переменной cumsum со значениями b , где выполняется условие a , и ноль в противном случае (и затем заполнение нулями столбца вперед, пока мы не найдем a снова):
df['cumsum'] = b-b.where(~a).ffill().fillna(0).astype(int)
Именно этот последний шаг действительно помогает производительности. Не выполняя функцию iterrows , входящую в функцию cumsumWithReset , мы можем реально повысить производительность, особенно с большим набором данных.
Опять же, спасибо @ braml1 за помощь. Ваше решение сработало! Мое альтернативное решение - только некоторые постепенные улучшения.