У меня есть столбец 0 и 1. Я хочу сохранять единицы в выходном столбце только в том случае, если они находятся на расстоянии не менее 4 строк друг от друга.
Обратите внимание, что простое использование diff()
не является решением, поскольку это устранит слишком много 1
. Вот пример:
df = pd.DataFrame.from_dict({'ix':list(range(12)), 'in':[1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1]})
df['out'] = [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0] # desred output
ix in out
0 0 1 1
1 1 0 0
2 2 0 0
3 3 1 0 # this 1 needs to become 0
4 4 1 1 # we keep this 1, because the previously kept one is sufficiently far
5 5 0 0
6 6 1 0
7 7 0 0
8 8 1 1
9 9 0 0
10 10 0 0
11 11 1 0
Интуитивно кажется, что это должно быть решено с помощью некоторой комбинации группировки diff
и cumsum()
, но я не смог в этом разобраться.
IIUC, вы не можете векторизовать свою операцию, поскольку судьба одной строки зависит от судьбы предыдущих строк.
Таким образом, вы должны зациклиться. Один из вариантов — использовать numba для улучшения скорости:
from numba import jit
@jit(nopython=True)
def keep_apart(a, N=3):
out = []
wait = 0
for x in a:
if x == 1:
if wait > 0:
out.append(0)
wait -= 1
else:
out.append(1)
wait = N
else:
out.append(0)
wait -= 1
return out
df['out'] = keep_apart(df['in'].to_numpy())
Выход:
ix in out
0 0 1 1
1 1 0 0
2 2 0 0
3 3 1 0
4 4 1 1
5 5 0 0
6 6 1 0
7 7 0 0
8 8 1 1
9 9 0 0
10 10 0 0
11 11 1 0
Альтернативный пример:
ix in out
0 0 1 1
1 1 0 0
2 2 0 0
3 3 1 0
4 4 0 0
5 5 0 0
6 6 1 1
7 7 1 0
8 8 1 0
9 9 0 0
10 10 0 0
11 11 1 1
Поскольку вы меняете по мере продвижения по столбцу, строка за строкой, группировка или совокупная сумма или что-то подобное не может дать вам правильный ответ. Вы можете использовать простой цикл for.
in_col = "in"
out_col = "out"
distance = 4
df[out_col] = df[in_col]
idxs = df[in_col].eq(1).index.tolist()
if idxs:
prev = idxs[0]
for idx in idxs[1:]:
if idx - prev + 1 <= distance:
df.loc[idx, out_col] = 0
else:
df.loc[idx, out_col] = 1
prev = idx
print(df)
ix in out
0 0 1 1
1 1 0 0
2 2 0 0
3 3 1 0
4 4 1 1
5 5 0 0
6 6 1 0
7 7 0 0
8 8 1 1
9 9 0 0
10 10 0 0
11 11 1 0
import pandas as pd
import numpy as np
from numba import njit
df = pd.DataFrame({'id': list(range(12)), 'val': [1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1]})
val = df['val'].values
@njit
def filter_ones(val):
res = np.zeros_like(val)
indices_for_1 = np.where(val == 1)[0]#[ 0 3 4 6 8 11]
last_kept_idx = -np.inf
for idx in indices_for_1:
if idx - last_kept_idx >= 4:
res[idx] = 1
last_kept_idx = idx
return res
res = filter_ones(val)
df['res'] = res
print(df)
'''
id val res
0 0 1 1
1 1 0 0
2 2 0 0
3 3 1 0
4 4 1 1
5 5 0 0
6 6 1 0
7 7 0 0
8 8 1 1
9 9 0 0
10 10 0 0
11 11 1 0
'''
Каким должен быть результат, если строка в индексе
4
была0
?