我有一个数据框,其中包含两列UserId和movieId。不同的用户看过不同的电影。我想获取(例如,两个普通用户之间的三部常见电影(。
df = DataFrame({'userId' : [1,2,3,1,3,6,2,4,1,2], 'movieId' : [222,222,900,555,555,888,555,222,666,666]})
所需的结果应该是这样的
userId movieId
1 222
1 555
1 666
2 222
2 555
2 666
我不需要其他不包含用户三部常见电影的条目。例如,如果有另一个用户观看了所有三部电影,则应考虑。
按get_dummies
创建指标,然后按max
获取每user
秒1
值,并通过boolean indexing
sum
和DataFrame.all
过滤具有N
常见值的行,最后按DataFrame.stack
重塑并将MultiIndex
转换为列Index.to_frame
:
df1 = pd.get_dummies(df.set_index('userId')['movieId']).max(level=0)
df1 = df1[df1.sum(axis=1).ge(N)]
df1 = (df1.loc[:, df1.eq(1).all()]
.iloc[:, :N]
.rename_axis('movieId', axis=1))
df1 = pd.DataFrame({'userId': np.repeat(df1.index, len(df1.columns)),
'movieId': np.tile(df1.columns, len(df1.index))})
对于测试,只有 2 个用户可以使用另一个具有pivot_table
和dropna
的解决方案:
N = 3
df = df[df['userId'].isin([1,2])]
df1 = (df.pivot_table(index='userId',columns='movieId', aggfunc=len)
.dropna(axis=1)
.iloc[:, :N])
print (df1)
movieId 222 555 666
userId
1 1 1 1
2 1 1 1
df1 = pd.DataFrame({'userId': np.repeat(df1.index, len(df1.columns)),
'movieId': np.tile(df1.columns, len(df1.index))})
print (df1)
userId movieId
0 1 222
1 1 555
2 1 666
3 2 222
4 2 555
5 2 666
这是一个执行一些数据帧操作的方法。
-
设置一些变量:
n_common_movies = 3 n_users = 2
-
创建包含电影组的列:
df1 = df.groupby('userId')['movieId'].apply(list).reset_index(name='movies')
输出:
df1
userId movies
0 1 [222, 555, 666]
1 2 [222, 555, 666]
2 3 [900, 555]
3 4 [222]
4 6 [888]
- 将该
列表减少到等于
n_common_movies
的电影数量,因为这是我们想要的总数。 如果不满足,那么我们可以跳过其余的过程。df2 = df1.loc[df1['movies'].apply(lambda x: len(x))== n_common_movies,:]
输出:
df2
userId movies
0 1 [222, 555, 666]
1 2 [222, 555, 666]
使用 pd.explode(( "堆叠"步骤 2 的结果:
df3 = df2.explode('movies')
输出:
df3
userId movies
0 1 222
0 1 555
0 1 666
1 2 222
1 2 555
1 2 666
创建另一个分组以获取每部电影的观看次数:
df4 = df3.groupby('movies').size().reset_index(name='viewer_count')
输出:
df4
movies viewer_count
0 222 2
1 555 2
2 666 2
最后,检查基于预期用户数的过滤结果是否等于常见电影数量的长度,并打印...用户ID,我猜。 打印任何你想要的东西,哈哈。
if len(df4[df4['viewer_count'] == n_users]) == n_common: tmp = 'nt'.join([str(i) for i in list(set(df3['userId']))]) print('Users with three common movies: nt{}'.format(tmp))
输出:
Users with three common movies:
1
2
我认为最好定义一个函数来获取两个用户u和v之间的k部常见电影,例如:
def common_movies(d, u, v, k=3):
"""Fetch common movies between users u and v"""
# create filter by the specified users
mask = d['userId'].isin((u, v))
# group by movieId, aggregate into a list and then explode on userId
values = d[mask].groupby('movieId').agg({'userId': list}).explode('userId')
# filter by the first k movies
return values.loc[values.index.unique()[:k]].sort_values('userId').reset_index()
print(common_movies(df, 1, 2))
输出
movieId userId
0 222 1
1 555 1
2 666 1
3 222 2
4 555 2
5 666 2
请注意,在上面的函数中,默认值是 3,如指定的那样,这个函数也是健壮的,因为如果没有指定值的数量,它不会失败,例如,如果你删除电影222
它返回:
movieId userId
0 555 1
1 666 1
2 555 2
3 666 2