Генерация последовательности на основе значения в другом столбце на Python

Вопрос или проблема

У меня есть следующий датафрейм:

введите описание изображения здесь

Я хотел бы добавить колонку со значением, равным 1, если flag равен 0, и поэтапно добавлять 1 в последующих строках, пока не встретится следующий 0 (как показано в примере ниже).

введите описание изображения здесь

Мне удалось сгенерировать последовательность, но код работает очень медленно, поэтому есть ли более быстрый способ сгенерировать последовательность?

Я не знаю, как вы проверяли это в первый раз, вот моя логика. Она предполагает, что первый элемент в flag равен 0!

df = pd.DataFrame({'memberid': [1]*11,
                   'flag': [0,0,1,1,0,1,0,0,0,1,1],
                   })
df['seq'] = ""
for i in range(0, len(df)):
    df.loc[i, 'seq'] = 1 if df.loc[i, 'flag'] == 0 else  df.loc[i - 1, 'seq'] + 1
print(df)

Другой вариант с использованием lambda:

df = pd.DataFrame({'memberid': [1] * 11,
                   'flag': [0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1]
                   })

def f(flag):
    global previous_seq
    previous_seq = 1 if flag == 0 else previous_seq + 1
    return previous_seq

previous_seq = 0
df['seq'] = df[['flag']].apply(lambda x: f(*x), axis=1)
print(df)

Я не уверен, быстрее ли это, чем первый способ….

Используйте эту одну строку кода, которая работает сверхбыстро, поскольку просто использует словарь с функцией update(), которая занимает O(1) времени, а лямбда-функция в общей сложности занимает O(N) времени, так что O(n) * O(1) = O(N)

b = {}
b[0] = 1
df['flags'].map(lambda x : [b.update({0 : 1}) , x+1][1] if x < 1 else (lambda : ([x+b[0] , b.update({0 : b[0]+1})][0]))())

Это сработает супербыстро.

Я пробовал это 3 способами, перебирая флаги (считая непрерывные 1) и это было самым быстрым для того же датафрейма (достаточно большим, чтобы пренебречь небольшими вариациями во времени при повторных попытках).

Я поддерживаю два списка (один для флагов, другой для последовательности) и переменную-счетчик.

Мы проходим через флаги и добавляем соответствующую последовательность в seq_list. Как вы описали, мы отслеживаем счет, если продолжаем видеть 1, и сбрасываем до 1, если видим 0 в flags_list.

После завершения мы добавляем seq_list как колонку в датафрейм.

seq_list = []
counter = 0
flag_list = list(df['flag'])
for flag in flag_list:
    if(flag == 0):
        counter = 1
        seq_list.append(counter)
    else:
        counter += 1
        seq_list.append(counter)
        
df['seq'] = seq_list

Другие варианты, которые я пробовал, включают

  • Прямое перебирание каждого элемента флага в датафрейме (используя .loc) и добавление в seq_list, который добавляется как колонка, но это заняло 16 раз больше времени, чем метод выше.
seq_list = []
counter = 0

for i in range(df.shape[0]):
    if(df.loc[i,'flag'] == 0):
        counter = 1
        seq_list.append(counter)
    else:
        counter += 1
        seq_list.append(counter)
        
df['seq'] = seq_list
  • Прямое перебирание каждого элемента флага в датафрейме (используя .loc) и немедленное изменение датафрейма (снова используя .loc), но это было еще медленнее и заняло от 5 до 10 раз больше времени, чем предыдущий метод.
counter = 1
df['seq'] = 0

for i in range(df.shape[0]):
    if(df.loc[i,'flag'] == 0):
        counter = 1
        df.loc[i,'seq'] = counter
    else:
        counter += 1
        df.loc[i,'seq'] = counter

Ответ или решение

Чтобы сгенерировать последовательность на основе значений в столбце flag в Python с использованием библиотеки pandas, мы можем рассмотреть несколько подходов к решению этой задачи, каждый из которых имеет свои плюсы и минусы касаемо скорости выполнения и читаемости кода. Рассмотрим основные шаги, как это можно сделать наиболее эффективно.

Постановка задачи

У нас есть DataFrame, содержащий столбец flag, где значения могут быть 0 или 1. Наша цель — добавить новый столбец, который будет равен 1, если flag равен 0, и будет инкрементально увеличиваться на 1 в следующих строках, до тех пор, пока не встретится следующий 0.

Эффективное решение

Одним из самых эффективных подходов является использование метода numpy или метод последовательного обхода с хранением состояния. В отличие от использования .apply() или for циклов, которые требуют большого количества обращений к индексам, мы можем использовать простой цикл, чтобы обновить значения.

Вот пример кода, который эффективно решает поставленную задачу:

import pandas as pd

# Создать DataFrame
df = pd.DataFrame({'memberid': [1] * 11,
                   'flag': [0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1]})

# Инициализация переменной для подсчета
counter = 0
seq_list = []

# Итерирование по значениям столбца 'flag'
for flag in df['flag']:
    if flag == 0:
        counter = 1  # Сбросить счетчик на 1, если встречаем 0
    else:
        counter += 1  # Увеличить счетчик, если 1
    seq_list.append(counter)

# Добавить новый столбец к DataFrame
df['seq'] = seq_list

print(df)

Объяснение работы кода

  • Инициализация: Мы начинаем с создания списков и переменной counter, которая будет отслеживать текущую последовательность.
  • Итерация: Проходим по каждому значению в столбце flag. Если значение — 0, мы сбрасываем счетчик, иначе увеличиваем его на 1.
  • Сбор результатов: Добавляем значение счетчика в список, который затем используется для добавления нового столбца в DataFrame.

Альтернативное решение с использованием Numpy

Если ваш DataFrame очень большой, вы также можете воспользоваться библиотекой numpy, которая предоставляет функции для обработки массивов гораздо быстрее, чем работа с обычными списками.

import pandas as pd
import numpy as np

# Создать DataFrame
df = pd.DataFrame({'memberid': [1] * 11,
                   'flag': [0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1]})

# Использовать numpy для создания последовательности
seq_array = np.zeros(len(df), dtype=int)
counter = 0

for i in range(len(df)):
    if df.iloc[i]['flag'] == 0:
        counter = 1
    else:
        counter += 1
    seq_array[i] = counter

# Добавить новый столбец к DataFrame
df['seq'] = seq_array

print(df)

Заключение

Оба предложенных метода являются эффективными, но с точки зрения производительности и чистоты кода, использование прямого обхода с хранением состояния является оптимальным (O(N)). Метод с использованием numpy также показывает хорошую производительность, особенно на больших наборах данных. Выбор подхода зависит от ваших требований по читаемости и среде выполнения.

Эти решения помогут вам увидеть, как можно эффективно работать с DataFrame и обрабатывать последовательности на основе условий в других столбцах.

Оцените материал
Добавить комментарий

Капча загружается...