#python #pandas #for-loop #rows #data-cleaning
Вопрос:
Я пытаюсь удалить повторяющиеся строки во фрейме данных на основе следующего условия: если значение пути к странице столбца такое же, как в предыдущей строке, и идентификатор сеанса такой же, мне нужно удалить эту строку. Если идентификатор сеанса отличается, то повторяющийся путь к странице не следует удалять. Это то, что я пытался:
data = data.sort_values(['SessionId', 'Datum'], ascending=True, ignore_index=True) i = 0 for i, _ in data.iterrows(): # i = index, _ = row if i != 0: try: while data.SessionId[i] == data.SessionId[i - 1] and data.pagePath[i] == data.pagePath[i - 1]: data = data.drop(i - 1) data = data.reset_index(drop=True) except KeyError: continue
Как вы можете видеть, я получаю исключение KeyError, хотя я не думаю, что это плохо, так как код делает то, что должен, с фреймом данных с 1000 строками. Единственная проблема заключается в том, что он не работает с большим набором данных с 6,5 миллионами строк. Либо это никогда не закончится, либо я получу СИГКИЛЛ. Я хорошо понимаю, что мне не следует использовать for-loop для наборов данных, но я не смог найти лучшего решения и был бы благодарен, если бы вы помогли мне улучшить мой код.
Комментарии:
1. Мне кажется, что вы изменяете
df
его во время итерации. Я прав? Это может привести к некоторому неожиданному поведению. Обычно лучше собрать расположение строк, которые вы хотите удалить во время цикла for, а затем удалить все выбранные строки за один раз.2. Рассматривали ли вы возможность использования
drop_duplicates
?3. @saiden да, но я также не уверен, как я могу сказать python, чтобы он тоже рассматривал SessionID? чтобы удалить повторяющиеся значения в столбце Путь к странице только внутри одного идентификатора сеанса
4. Что-то вроде
data.drop_duplicates(subset=['SessionId', 'pagePath'], keep='first')
? При условии, что это то, чего ты хочешь.5. @saiden это не так, ответил, почему ниже первого ответа
Ответ №1:
groupby
на SessionId
и pagePath
и найдите совокупное количество встречаемости каждой пары; затем найдите разницу в использовании последовательных элементов np.ediff1d
и назначьте ее df['cumcount']
, и поскольку мы хотим отфильтровать последовательные дубликаты, мы отфильтруем df['cumcount']!=1
:
cols = df.columns df['cumcount'] = np.concatenate(([0], np.ediff1d(df.groupby(['SessionId','pagePath']).cumcount()))) out = df.loc[df['cumcount']!=1, cols]
Комментарии:
1. нет, я не могу, потому что таким образом я теряю данные. подумайте об этом: просмотр только одного и того же идентификатора сеанса и удаление всех повторяющихся значений пути к странице (которые представляют поведение пользователя = какие страницы посещал пользователь) означало бы, что если бы пользователь вернулся на страницу, которую он уже посетил, я бы этого не увидел
2. @Kami Я отредактировал свой ответ, чтобы отразить то, что вы хотели. Кажется, теперь я понял. Взгляните на это.
3. да, он делает это сейчас! хотя я не совсем понимаю, что там происходит, результат, кажется, правильный! большое вам спасибо за явно более элегантное решение, чем мое 🙂
4. единственное, что вы можете добавить, это то, что сначала вам нужно отсортировать значения, иначе результат будет неправильным
Ответ №2:
В любом случае, как обычно, мне пришлось решать эту проблему самостоятельно, это было бы невозможно без комментария @np8. Для всех, кто может быть заинтересован:
locations = [] data = data.sort_values(['SessionId', 'Datum'], ascending=True, ignore_index=True) i = 0 for i, _ in data.iterrows(): # i = index, _ = row if i != 0: try: if data.SessionId[i] == data.SessionId[i - 1] and data.pagePath[i] == data.pagePath[i - 1]: locations.append(i) except KeyError as e: print(e) continue data_cleaned = data.drop(index=locations)
Это заняло 470 секунд для фрейма данных с 6,5 миллионами строк, что нормально, учитывая, что код вообще не заканчивал выполнение раньше.