Кадр данных Pandas – сохраняйте значения, находящиеся на определенном расстоянии друг от друга

У меня есть столбец 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(), но я не смог в этом разобраться.

Каким должен быть результат, если строка в индексе 4 была 0?

mozway 28.06.2024 21:23
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
87
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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
'''

Другие вопросы по теме