Используйте GroupBy.pct_change
по группам для каждых 2 строк, созданных целочисленным делением массива 1d, созданного numpy.arange
:
print (np.arange(len(df)) // 2)
[0 0 1 1 2 2 3 3]
df['Z1'] = df['X'].div(df['Y']).groupby(np.arange(len(df)) // 2).pct_change().mul(-100)
print (df)
X Y Z Z1
A1 41 1 NaN NaN
A2 52 3 57.72% 57.723577
B1 74 6 NaN NaN
B2 74 4 -50.00% -50.000000
C1 5 0 NaN NaN
C2 5 0 nan% NaN
D1 120 7 NaN NaN
D2 131 7 -9.17% -9.166667
Другое решение:
s1 = df['X'].div(df['Y'])
df['Z1'] = (1-s1[1::2] / s1[::2].values).mul(100)
print (df)
X Y Z Z1
A1 41 1 NaN NaN
A2 52 3 57.72% 57.723577
B1 74 6 NaN NaN
B2 74 4 -50.00% -50.000000
C1 5 0 NaN NaN
C2 5 0 nan% NaN
D1 120 7 NaN NaN
D2 131 7 -9.17% -9.166667
Если для пропущенных значений нужны пропущенные значения, используйте map
с if-else
, чтобы избежать преобразования пропущенных значений в строки:
s = df['X'].div(df['Y']).groupby(np.arange(len(df)) // 2).pct_change().mul(-100)
df['Z1'] = s.map(lambda x: '{:,.2f}%'.format(x) if x == x else np.nan)
print (df)
X Y Z Z1
A1 41 1 NaN NaN
A2 52 3 57.72% 57.72%
B1 74 6 NaN NaN
B2 74 4 -50.00% -50.00%
C1 5 0 NaN NaN
C2 5 0 nan% NaN
D1 120 7 NaN NaN
D2 131 7 -9.17% -9.17%