Pandas - 按id分组并根据阈值删除重复项

7

我有以下数据:

userid itemid
  1       1
  1       1
  1       3
  1       4
  2       1
  2       2
  2       3

我希望删除查看同一物品ID两次或以上的用户ID。 例如,userid=1已经两次查看了itemid=1,因此我想要删除userid=1的整个记录。然而,由于userid=2没有查看过同一物品两次,因此我将保留userid=2的记录。
所以我希望我的数据如下:
userid itemid
  2       1
  2       2
  2       3

有人可以帮助我吗?

import pandas as pd    
df = pd.DataFrame({'userid':[1,1,1,1, 2,2,2],
                   'itemid':[1,1,3,4, 1,2,3] })

1
请发布可重现的代码以创建该数据。 - smci
5个回答

11
你可以使用duplicated函数来查找行级别的重复数据,然后通过对 'userid' 进行 groupby 来查找 'userid' 级别的重复数据,最后根据需要删除相应的数据。
要删除没有阈值的数据,请执行以下操作:
df = df[~df.duplicated(['userid', 'itemid']).groupby(df['userid']).transform('any')]

如果想按阈值删除,请在 duplicated 中使用 keep=False,并对布尔列求和并与阈值进行比较。例如,如果阈值为3:

df = df[~df.duplicated(['userid', 'itemid'], keep=False).groupby(df['userid']).transform('sum').ge(3)]

无阈值的结果输出:

   userid  itemid
4       2       1
5       2       2
6       2       3

1
如果我想根据阈值删除怎么办?也就是说,如果我想删除具有超过3个重复项的用户ID,该怎么做? - Mansumen
1
这是一个特殊情况的技巧,用于 THRESHOLD = 2。对于更大的 THRESHOLD,请使用 @DYZ 更通用的代码。 - smci

7

筛选

这个功能是专门为此而设计的。你可以传递一个返回布尔值的函数,该函数确定分组是否通过筛选。

筛选计数值
最通用和直观

df.groupby('userid').filter(lambda x: x.itemid.value_counts().max() < 2)

筛选去重
当寻找n < 2时的特殊情况

df.groupby('userid').filter(lambda x: x.itemid.is_unique)

   userid  itemid
4       2       1
5       2       2
6       2       3

3

按照用户和物品对数据框进行分组:

views = df.groupby(['userid','itemid'])['itemid'].count()
#userid  itemid
#1       1         2 <=== The offending row
#        3         1
#        4         1
#2       1         1
#        2         1
#        3         1
#Name: dummy, dtype: int64

查找只看过一次某个物品的人:

THRESHOLD = 2
viewed = ~(views.unstack() >= THRESHOLD).any(axis=1)
#userid
#1    False
#2     True
#dtype: bool

合并结果,保留“好”的行:

combined = df.merge(pd.DataFrame(viewed).reset_index())
combined[combined[0]][['userid','itemid']]
#   userid  itemid
#4       2       1
#5       2       2
#6       2       3

2
# group userid and itemid and get a count
df2 = df.groupby(by=['userid','itemid']).apply(lambda x: len(x)).reset_index()
#Extract rows where the max userid-itemid count is less than 2.
df2 = df2[~df2.userid.isin(df2[df2.ix[:,-1]>1]['userid'])][df.columns]
print(df2)
   itemid  userid
3       1       2
4       2       2
5       3       2

如果你想在特定阈值下放弃,只需设置:
df2.ix[:,-1]>threshold]

0

我不知道在 Pandas 中是否有可用的函数来完成此任务。但是,我尝试了一种解决方法来处理你的问题。

以下是完整的代码。

import pandas as pd
dictionary = {'userid':[1,1,1,1,2,2,2],
              'itemid':[1,1,3,4,1,2,3]}

df = pd.DataFrame(dictionary, columns=['userid', 'itemid'])

selected_user = []

for user in df['userid'].drop_duplicates().tolist():

    items = df.loc[df['userid']==user]['itemid'].tolist()
    if len(items) != len(set(items)): continue
    else: selected_user.append(user)

result = df.loc[(df['userid'].isin(selected_user))]

这段代码将会产生以下结果。

    userid  itemid
4   2       1
5   2       2
6   2       3

希望能对你有所帮助。

for user in df['userid'].drop_duplicates().tolist() can always be replaced by df.groupby('userid') - smci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接