Итерация по строкам и применение функции на основе условия

Я изучаю итерацию и применяю функцию в пандах. У меня есть пример ниже. для каждых 4 строк примените функцию (1-я строка + 0, 2-я строка + 0, 3-я строка = 0, 4-я строка = 0) и так далее. Есть ли ссылки на ресурсы для выполнения этой операции? Большое спасибо.

data = {'DATE': ['2023-12-29', '2023-12-29', '2023-12-29', '2023-12-29','2024-01-31','2024-01-31','2024-01-31','2024-01-31',
                '2024-02-27','2024-02-27','2024-02-27','2024-02-27'],
        'score': [10, 5, 30, 41,12,7,32,43,14,9,34,45]}

df = pd.DataFrame(data=data)


        DATE      Score Result
    0   2023-12-29  10  10
    1   2023-12-29  5   5
    2   2023-12-29  30  0   
    3   2023-12-29  41  0   
    4   2024-01-31  12  12
    5   2024-01-31  7   7
    6   2024-01-31  32  0   
    7   2024-01-31  43  0   
    8   2024-02-27  14  14
    9   2024-02-27  9   9   
    10  2024-02-27  34  0    
    11  2024-02-27  45  0

83
4

Ответы:

Решено

Код

cond = df.index.to_series().mod(4).isin([2, 3])
df['result'] = df['score'].mask(cond, 0)

дф

          DATE  score  result
0   2023-12-29     10      10
1   2023-12-29      5       5
2   2023-12-29      8       0
3   2023-12-29      2       0
4   2023-12-29      7       7
5   2023-12-27     10      10
6   2023-12-27     12       0
7   2023-12-27      7       0
8   2023-12-27      9       9
9   2024-01-31     13      13
10  2024-02-27     14       0
11  2024-02-27      9       0

Обновить ответ на дополнительный вопрос

(1-я строка +5, 2-я строка +8, 3-я строка = 0, 4-я строка -10)

s = pd.Series([5, 8, 0, -10])[df.index % 4].reset_index(drop=True)
df['result'] = df['score'] + s

дф

          DATE  score  result
0   2023-12-29     10      15
1   2023-12-29      5      13
2   2023-12-29      8       8
3   2023-12-29      2      -8
4   2023-12-29      7      12
5   2023-12-27     10      18
6   2023-12-27     12      12
7   2023-12-27      7      -3
8   2023-12-27      9      14
9   2024-01-31     13      21
10  2024-02-27     14      14
11  2024-02-27      9      -1

Индексировать массив numpy проще, но вы, возможно, не знаете numpy, поэтому я выполнил индексацию в серии pandas.


numpy.select может сослужить вам хорошую службу:

import numpy as np

n = 4
index_n = df.index.to_series().mod(n)
conditions = [
    index_n.eq(i)
    for i in range(n)
]
choices = [
    df.score.add(5),  # 1st row +5
    df.score.add(8),  # 2nd row +8
    0,                # 3rd row = 0
    df.score.sub(10), # 4th row -10
]

df["result"] = np.select(conditions, choices)

Выход:

          DATE  score  result
0   2023-12-29     10      15
1   2023-12-29      5      13
2   2023-12-29     30       0
3   2023-12-29     41      31
4   2024-01-31     12      17
5   2024-01-31      7      15
6   2024-01-31     32       0
7   2024-01-31     43      33
8   2024-02-27     14      19
9   2024-02-27      9      17
10  2024-02-27     34       0
11  2024-02-27     45      35

Если каждые 4 строки расположены по столбцу DATE, можно использовать GroupBy.transform со специальной функцией:

def f(x):
    x.iat[0] += 5
    x.iat[1] += 8
    x.iat[2] = 0
    x.iat[3] -= 10
    return x

df['result'] = df.groupby('DATE')['score'].transform(f)
print (df)
          DATE  score  result
0   2023-12-29     10      15
1   2023-12-29      5      13
2   2023-12-29     30       0
3   2023-12-29     41      31
4   2024-01-31     12      17
5   2024-01-31      7      15
6   2024-01-31     32       0
7   2024-01-31     43      33
8   2024-02-27     14      19
9   2024-02-27      9      17
10  2024-02-27     34       0
11  2024-02-27     45      35

Если каждые 4 строки не соответствуют столбцу DATE:

def f(x):
    x.iat[0] += 5
    x.iat[1] += 8
    x.iat[2] = 0
    x.iat[3] -= 10
    return x

df['result'] = df.groupby(np.arange(len(df.index)) // 4)['score'].transform(f)

Если возможно, в какой-то группе есть 1,2 или 3 строки, одна идея — пропустить эту строку, например. автор try-except:

df = pd.DataFrame(data=data).drop(11)

def f(x):
    try:
        x.iat[0] += 5
        x.iat[1] += 8
        x.iat[2] = 0
        x.iat[3] -= 10
    except IndexError:
        pass
    return x

df['result'] = df.groupby(np.arange(len(df.index)) // 4)['score'].transform(f)
print (df)
          DATE  score  result
0   2023-12-29     10      15
1   2023-12-29      5      13
2   2023-12-29     30       0
3   2023-12-29     41      31
4   2024-01-31     12      17
5   2024-01-31      7      15
6   2024-01-31     32       0
7   2024-01-31     43      33
8   2024-02-27     14      19
9   2024-02-27      9      17
10  2024-02-27     34       0

Для эффективного подхода не зацикливайтесь, создавайте массивы numpy с помощью numpy.tile, добавляйте значения и умножайте на 1/0 строки, которые вы хотите сохранить/сбросить:

import numpy as np

add = [5, 8, 0, -10] # value to add
mul = [1, 1, 0,   1] # values to reset (0 = reset)
n = int(np.ceil(len(df)/len(add))) # 3

df['Result'] = (df['score']
                .mul(np.tile(mul, n)[:len(df)])
                .add(np.tile(add, n)[:len(df)])
               )

Примечание. если количество строк всегда кратно количеству операций, вы можете использовать n = len(df)//len(add) и удалить [:len(df)].

n = len(df)//len(add) # 3

df['Result'] = df['score'].mul(np.tile(mul, n)).add(np.tile(add, n))

Выход:

          DATE  score  Result
0   2023-12-29     10      15
1   2023-12-29      5      13
2   2023-12-29     30       0
3   2023-12-29     41      31
4   2024-01-31     12      17
5   2024-01-31      7      15
6   2024-01-31     32       0
7   2024-01-31     43      33
8   2024-02-27     14      19
9   2024-02-27      9      17
10  2024-02-27     34       0
11  2024-02-27     45      35

вариант

Если ваши группы нерегулярны, вы можете объединить это с groupby.cumcount и индексацией numpy:

add = np.array([5, 8, 0, -10])
mul = np.array([1, 1, 0,   1])
n = df.groupby('DATE').cumcount()
df['Result'] = df['score'].mul(mul[n]).add(add[n])

тайминги