python - 在Pandas中的条件下删除重复的行

时间:2016-09-05 02:37:52

标签: python pandas dataframe duplicates

我有一个像这样的DataFrame:

  NoDemande   NoUsager  Sens  IdVehiculeUtilise  Fait  HeurePrevue  HeureDebutTrajet
0 42191000823  001208    +         246Véh         1    08:20:04     08:22:26 
1 42191000822  001208    +         246Véh         1    08:20:04     08:18:56 
2 42191000822  001208    -         246Véh        -99   09:05:03     08:56:26 
3 42191000823  001208    -         246Véh         1    09:05:03     08:56:26 
4 42191000834  001208    +         246Véh         1    16:50:04     16:39:26 
5 42191000834  001208    -         246Véh         1    17:45:03     17:25:10 
6 42192000761  001208    +         246Véh        -1    08:20:04     08:15:07 
7 42192000762  001208    +         246Véh         1    08:20:04     08:18:27 
8 42192000762  001208    -         246Véh        -99   09:05:03     08:58:29 
9 42192000761  001208    -         246Véh        -11   09:05:03     08:58:29 

我从df[df.duplicated(['NoUsager','NoDemande'],keep=False)]获取此数据框,以确保我的行成对。当NoDemande是连续数字(如42191000822和42191000823,42192000761和42192000762)并且列HeurePrevue相同时,我想删除一对行,这意味着记录被记录两次。我必须删除一对,我想在列Fait中预先设置一个具有更多正数的那个(至少一个大于0)

所以我的结果应该是这样的:

  NoDemande   NoUsager  Sens  IdVehiculeUtilise  Fait  HeurePrevue  HeureDebutTrajet
0 42191000823  001208    +         246Véh         1    08:20:04     08:22:26 
3 42191000823  001208    -         246Véh         1    09:05:03     08:56:26 
4 42191000834  001208    +         246Véh         1    16:50:04     16:39:26 
5 42191000834  001208    -         246Véh         1    17:45:03     17:25:10 
7 42192000762  001208    +         246Véh         1    08:20:04     08:18:27 
8 42192000762  001208    -         246Véh        -99   09:05:03     08:58:29 

我知道这是关于OR逻辑的,但我不知道如何实现它。

任何帮助将不胜感激〜

3 个答案:

答案 0 :(得分:1)

我对这个问题的处理方法是制作两个包含检查条件的列(相同的heure和连续增加的NoDemande)。然后迭代数据帧,根据Fait列删除不需要的对。

这有点像hacky代码,但这似乎可以解决问题:

# Recreate DataFrame
df = pd.DataFrame({
    'NoDemande': [23, 22, 22, 23, 34, 34, 61, 62, 62, 61],
    'HeurePrevue': [84, 84, 93, 93, 64, 73, 84, 84, 93, 93],
    'Fait': [1, 1, -99, 1, 1, 1, -1, 1, -99, -11]
    }, columns=['NoDemande', 'Fait', 'HeurePrevue'])

# Make columns which contain conditions for inspection
df['sameHeure'] = df.HeurePrevue.iloc[1:] == df.HeurePrevue.iloc[:-1]
df['cont'] = df.NoDemande.diff()

# Cycle over rows
for prev_row, row in zip(df.iloc[:-1].itertuples(), df.iloc[1:].itertuples()):
    if row.sameHeure and (row.cont == 1):  # If rows are continuous and have the same Heure delete a pair
        pair_1 = df.loc[df.NoDemande == row.NoDemande]
        pair_2 = df.loc[df.NoDemande == prev_row.NoDemande]
        if sum(pair_1.Fait > 0) < sum(pair_2.Fait > 0):  # Find which pair to delete
            df.drop(pair_1.index, inplace=True)
        else:
            df.drop(pair_2.index, inplace=True)

df.drop(['cont', 'sameHeure'], 1, inplace=True)  # Throw away the added columns

结果:

print(df)

   NoDemande  Fait  HeurePrevue
0         23     1           84
3         23     1           93
4         34     1           64
5         34     1           73
7         62     1           84
8         62   -99           93

答案 1 :(得分:0)

我在这里看到两个解决方案。 第一个是基于这样的建议:您的数据集中始终有连续的条目对 - 如果任何条目有一对,则此对出现在此条目之后。然后你应该使用步长= 2循环数据帧:

for i in range(0,x,2):
  your action

在此循环中,您可以比较两个条目并删除具有负值的条目。

我的第二个主张有点复杂。

首先,您应该复制和延迟(按特定行数移动)所有列。这可以通过以下函数完成(仅适用于NoDemande,为每个列使用循环执行此操作):

df.NoDemande = df.NoDemande.shift(-1)

看起来像:

  NoDemande      NoDemande_lagged

0 42191000823    42191000822
1 42191000822    42191000822 
2 42191000822    42191000823
3 42191000823    42191000834

然后比较NoDemande和NoDemande_lagged列中同一行中的两个值。如果42191000822中的数字大于或小于NoDemande中的值,则比较 Fait Fait_lagged 并选择更正值,您应该将其粘贴到新值中列 Fait_selected 。您应该对其他列执行相同操作,以便每列都具有滞后副本和选定副本。之后你应该删除你的下一行,因为你已经将它与前一行进行了比较。 最后,您应该删除原始和滞后的列,只留下&#34; _selected&#34;。

对不起复杂的解释,希望,无论如何这对你有所帮助。如果您熟悉RapidMiner,我可以解释如何在那里做到这一点,它会更容易。我为各种概念提供了一些想法,可以帮助您解决问题。

答案 2 :(得分:0)

这是一个冗长的解决方案,可能会有较短的解决方案。 frame0是您在上面发布的确切框架。

首先获取数据,按NoDemande对其进行排序,拆分并重新组合,以便在同一行中有两个配对。让事情变得更容易:

frame0.HeurePrevue = pd.to_datetime(frame0.HeurePrevue)
frame0 = frame0.sort_values('NoDemande').reset_index(drop=True)
frameA = frame0.iloc[::2].reset_index(drop=True)
frameB = frame0.iloc[1::2].reset_index(drop=True)
frame1 = pd.concat([frameA,frameB],axis=1,join='inner')
frame1.columns = [u'NoDemande1', u'NoUsager1', u'Sens1', u'IdVehiculeUtilise1', u'Fait1',\
                  u'HeurePrevue1', u'HeureDebutTrajet1', u'NoDemande2', u'NoUsager2', u'Sens2',\
                  u'IdVehiculeUtilise2', u'Fait2', u'HeurePrevue2', u'HeureDebutTrajet2']
frame1 = frame1[[u'NoDemande1', u'Fait1',u'HeurePrevue1', u'NoDemande2',u'Fait2',\
                 u'HeurePrevue2']]

接下来做一些比较,以查看在给定行中该行是否重复该行:

frame2 = frame1[['NoDemande1','NoDemande2','HeurePrevue1','HeurePrevue2']].diff()
frame2['lastColumnsPartner'] = (frame2.NoDemande1 == 1) & (frame2.NoDemande2 == 1) &\
                               (frame2.HeurePrevue1 == pd.Timedelta(0)) &\
                               (frame2.HeurePrevue2 == pd.Timedelta(0))
frame2 = frame2['lastColumnsPartner'].to_frame()
frame1 = pd.merge(frame1,frame2,left_index=True,right_index=True)

现在检查Fait

的值
frame1['Fait1Pos'] = 0
frame1['Fait2Pos'] = 0
frame1.ix[frame1.Fait1>0,'Fait1Pos'] = 1
frame1.ix[frame1.Fait2>0,'Fait2Pos'] = 1
frame1['FaitPos'] = frame1.Fait1Pos+frame1.Fait2Pos
frame1['FaitBool'] = (frame1.Fait1 > 0)|(frame1.Fait2 > 0)

迭代所有行并使用布尔lastColumnsPartner创建一个标识重复行的新索引:

frame1['newIndex'] = 0
j = -1
for i,row in frame1.iterrows():
  if frame1.ix[i,'lastColumnsPartner'] == False:
    j+=1
  frame1.ix[i,'newIndex'] = j

仅采用FaitFaitBool)中至少有一个正值的行,按FaitFaitPos)的正值数排序,删除重复项({ {1}})仅保留newIndex的最高值,然后返回Fait

NoDemande

最后在初始帧上使用布尔索引来过滤所有内容。

tokeep = frame1[frame1.FaitBool][['NoDemande1','newIndex','FaitPos']]\
 .sort_values('FaitPos',ascending=False).drop_duplicates('newIndex')['NoDemande1']

我无法确定它是否适用于所有情况,它适用于您的示例。也有改进的余地。