Удаление повторяющихся строк во фрейме данных на основе условия pandas

#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 миллионами строк, что нормально, учитывая, что код вообще не заканчивал выполнение раньше.