Для каждого идентификатора удалите строку на основе ее отношения к другой строке: порядок появления (дата) и повторяющееся значение переменной Python

avatar
Renata Bastos Gottgtroy
8 августа 2021 в 23:38
61
1
1

У меня есть пример DataFrame ниже:

df = pd.DataFrame([[1, 1, 1,'2016-09-01','pay',1], [1, 2, 1, '2016-09-01','claims',1], [2, 3, 3, '2016-09-02','claims',1],[2,4,3,'2016-10-02','pay',2],[3,5,4,'2016-09-02','pay',1],[3,6,5,'2016-09-04','pay',2],[3,7,4,'2016-09-06','claims',3],[3,8,6,'2016-09-08','pay',4]], columns=['claim_id', 'payment_id', 'provider_id','date','dataset','date_rank'])
df['date'] = pd.to_datetime(df['date']) # this column ought to be date
df

df image

Есть повторяющиеся платежи, которые нельзя удалить с помощью простой функции drop_duplicates(), поскольку решение об удалении строки зависит от ее отношения к другим строкам платежей с тем же идентификатором Claim_id.

Я хотел бы создать новый столбец с именем 'dup', в котором помечаются повторяющиеся строки, чтобы я мог просмотреть их, прежде чем удалять из DataFrame.

Логика, необходимая для точного удаления дубликатов:

Для каждого ИД_заявки в df:
Для платежа, где df['dataset'] == 'claims', проверьте, есть ли другой платеж для того же Claim_id, который имеет тот же provider_id и происходит до или в тот же df['date']. Если есть, пометьте новый столбец df['dup'] как True для платежа, где df['dataset'] == 'claims'. В противном случае пометьте новый столбец df['dup'] как False.

.

В этом примере идентификаторы payment_id 2 и 7 должны иметь значение True в новом столбце 'dup', в то время как все остальные идентификаторы платежей должны иметь значение False:

df_out = pd.DataFrame([[1, 1, 1,'2016-09-01','pay',1,False], [1, 2, 1, '2016-09-01','claims',1,True], [2, 3, 3, '2016-09-02','claims',1,False],[2,4,3,'2016-10-02','pay',2,False],[3,5,4,'2016-09-02','pay',1,False],[3,6,5,'2016-09-04','pay',2,False],[3,7,4,'2016-09-06','claims',3,True],[3,8,6,'2016-09-08','pay',4,False]], columns=['claim_id', 'payment_id', 'provider_id','date','dataset','date_rank','dup'])
df_out['date'] = pd.to_datetime(df_out['date']) # this column ought to be date
df_out

df_out image

Я пробовал много разных вещей, в том числе пытался разбить это на шаги, но безуспешно. В одной из этих попыток я создал столбец date_rank, который помечает платежи в порядке их появления. Я включил это сюда на случай, если это будет полезно.

Источник
Dean Taler
9 августа 2021 в 05:33
0

Можете ли вы еще раз объяснить, почему 2 и 7 payment_id верны? что такое "дублированная" строка?

Renata Bastos Gottgtroy
9 августа 2021 в 22:27
0

Повторяться могут только строки набора данных «претензии». payment_id 2 является дубликатом payment_id 1, т.к. он взят из набора данных утверждений, имеет тот же Claim_id, provider_id И ту же дату, что и payment_id 1. provider_id И имеет более позднюю дату, чем payment_id 5. payment_id 3 не является дубликатом payment_id 4 bc, даже если он из набора данных утверждений, имеет тот же Claim_id и provider_id, что и payment_id 4, дата предшествует дате payment_id 4. Это ответило на ваш вопрос? Спасибо за помощь!

Ответы (1)

avatar
Dean Taler
10 августа 2021 в 05:39
0

это немного уродливо, но работает

import pandas as pd

df = pd.DataFrame([[1, 1, 1,'2016-09-01','pay',1], [1, 2, 1, '2016-09-01','claims',1], [2, 3, 3, '2016-09-02','claims',1],[2,4,3,'2016-10-02','pay',2],[3,5,4,'2016-09-02','pay',1],[3,6,5,'2016-09-04','pay',2],[3,7,4,'2016-09-06','claims',3],[3,8,6,'2016-09-08','pay',4]], columns=['claim_id', 'payment_id', 'provider_id','date','dataset','date_rank'])
df['date'] = pd.to_datetime(df['date']) # this column ought to be date



df['dup'] = False
provider_list = list(set(df['provider_id'].tolist()))
for provider in provider_list:
  temp_df = df.loc[df['provider_id'] == provider]
  claim_list = list(set(df['claim_id'].tolist()))
  for claim in claim_list:
      temp_df2 = temp_df.loc[temp_df['claim_id'] == claim]
      if len(set(temp_df2['dataset'].tolist())) == 2:
        pay_date = temp_df2.loc[temp_df2['dataset'] == 'pay', 'date'].iloc[0]
        claim_date = temp_df2.loc[temp_df2['dataset'] == 'claims', 'date'].iloc[0]
        if pay_date <= claim_date:
          payment_id = temp_df2.loc[temp_df2['dataset'] == 'claims', 'payment_id'].iloc[0]
          df.loc[df['payment_id'] == payment_id, 'dup'] = True
      else:
        continue
df

вывод:

    claim_id    payment_id  provider_id date    dataset date_rank   dup
0      1           1           1       2016-09-01   pay     1       False
1      1           2           1       2016-09-01   claims  1       True
2      2           3           3       2016-09-02   claims  1       False
3      2           4           3       2016-10-02   pay     2       False
4      3           5           4       2016-09-02   pay     1       False
5      3           6           5       2016-09-04   pay     2       False
6      3           7           4       2016-09-06   claims  3       True
7      3           8           6      2016-09-08    pay     4       False

РЕДАКТИРОВАТЬ поскольку вам нужно перебирать каждый provider_id (другого способа я не нашел) и у вас более 1 млн строк, я рекомендую разбить DataFrame на куски по ~ 1000. Кроме того, я бы сохранил результат в виде файла csv для каждого фрагмента на случай, если запуск рухнет (в этом случае я бы продолжил с места, где он рухнул)

import pandas as pd
import itertools

chunk_n = 1000
df = pd.DataFrame([[1, 1, 1,'2016-09-01','pay',1], [1, 2, 1, '2016-09-01','claims',1], [2, 3, 3, '2016-09-02','claims',1],[2,4,3,'2016-10-02','pay',2],[3,5,4,'2016-09-02','pay',1],[3,6,5,'2016-09-04','pay',2],[3,7,4,'2016-09-06','claims',3],[3,8,6,'2016-09-08','pay',4]], columns=['claim_id', 'payment_id', 'provider_id','date','dataset','date_rank'])
df['date'] = pd.to_datetime(df['date']) # this column ought to be date
df['dup'] = False
list_of_df = []

provider_list = list(set(df['provider_id'].tolist()))
provider_groups = [list(group) for key, group in itertools.groupby(provider_list, lambda k: k//chunk_n)]
i = 0
for group in provider_groups:
  print('starting group', i)
  temp_df = df.loc[df['provider_id'].isin(group)]
  for provider in group:
    claim_list = list(set(temp_df.loc[temp_df['provider_id'] == provider, 'claim_id'].tolist()))  
    for claim in claim_list:
        temp_df2 = temp_df.loc[(temp_df['claim_id'] == claim) & (temp_df['provider_id'] == provider)]
        if len(set(temp_df2['dataset'].tolist())) == 2:
          pay_date = temp_df2.loc[temp_df2['dataset'] == 'pay', 'date'].iloc[0]
          claim_date = temp_df2.loc[temp_df2['dataset'] == 'claims', 'date'].iloc[0]
          if pay_date <= claim_date:
            payment_id = temp_df2.loc[temp_df2['dataset'] == 'claims', 'payment_id'].iloc[0]
            temp_df.loc[temp_df['payment_id'] == payment_id, 'dup'] = True
        else:
          continue
  list_of_df.append(temp_df)
  # temp_df.to_csv('group_{}.csv'.format(i))
  # i += 1


df = pd.concat(list_of_df)

Renata Bastos Gottgtroy
10 августа 2021 в 22:01
0

Дин, это сработало отлично! Большое спасибо, и я также найду время, чтобы изучить ваш код.

Dean Taler
11 августа 2021 в 05:51
0

Рад помочь! обратите внимание, что вы можете сделать это лучше, используя меньше строк, но это интуитивно понятный способ, уровень за уровнем. Кроме того, я буду признателен, если вы проголосуете за мой ответ и примете мой ответ в качестве решения, спасибо!

Renata Bastos Gottgtroy
12 августа 2021 в 22:37
0

Привет, Дин, у меня возникли проблемы с его запуском на моем фактическом наборе данных, размер которого составляет примерно 1 миллион строк. Код работал в течение очень долгого времени без вывода. Есть ли у вас какие-либо предложения, которые могли бы помочь? Я проголосовал за ответ, но, поскольку я новичок в Stack Overflow, у меня недостаточно очков репутации, чтобы он отображался как проголосовавший, но он сказал, что отзыв был записан. Ваше здоровье!