首页 > 试题广场 >

推荐内容准确的用户平均评分

[编程题]推荐内容准确的用户平均评分
  • 热度指数:70489 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
某产品2022年2月8日系统推荐内容给部分用户的数据,以及用户信息和对推荐内容的评分交叉表部分数据如下:
推荐内容表recommend_tb(rec_id-推荐信息id,rec_info_l-推荐信息标签,rec_user-推荐目标用户id,rec_time-推荐时间,如下所示:
rec_id rec_info_l rec_user rec_time
1 健身 101 2022-02-08 07:23:15
2 美妆 102 2022-02-08 07:24:15
3 体育 103 2022-02-08 07:25:15
4 美妆 103 2022-02-08 07:26:15
5 政要 104 2022-02-08 07:27:15
6 体育 104 2022-02-08 07:28:15
7 体育 105 2022-02-08 07:29:15
8 影视 106 2022-02-08 07:30:15
用户信息及评分交叉表user_action_tbuser_id-用户id,hobby_l-用户喜好标签,score-综合评分),如下所示:
注:该表score为对所有推荐给该用户的内容的综合评分,在计算用户平均评分切勿将推荐次数作为分母
user_id hobby_l score
101 健身 88
102 影视 81
103 美妆 78
104 健身 68
105 体育 90
106 影视 82

问题:请统计推荐内容准确的用户平均评分?(结果保留3位小数)
注:(1)准确的定义:推荐的内容标签与用户喜好标签一致;如推荐多次给同一用户,有一次及以上准确就归为准确。
示例数据结果如下:
avg_score
84.500
解释:一共推荐8条内容,其中推荐给101、103、105、106四位用户的内容准确,
四位用户的评分分别是88、78、90、82,故平均评分=(88+78+90+82)/4=84.500
(2)如果同一用户推荐同一个内容标签的话,计算的时候只算一次。
示例1

输入

drop table if exists  `recommend_tb` ; 
CREATE TABLE `recommend_tb` (
`rec_id` int(11) NOT NULL,
`rec_info_l` varchar(8) NOT NULL,
`rec_user` int(11) NOT NULL,
`rec_time` datetime NOT NULL,
PRIMARY KEY (`rec_id`));
INSERT INTO recommend_tb VALUES(1,'健身',101,'2022-02-08 07:23:15');
INSERT INTO recommend_tb VALUES(2,'美妆',102,'2022-02-08 07:24:15');
INSERT INTO recommend_tb VALUES(3,'体育',103,'2022-02-08 07:25:15');
INSERT INTO recommend_tb VALUES(4,'美妆',103,'2022-02-08 07:26:15');
INSERT INTO recommend_tb VALUES(5,'政要',104,'2022-02-08 07:27:15');
INSERT INTO recommend_tb VALUES(6,'体育',104,'2022-02-08 07:28:15');
INSERT INTO recommend_tb VALUES(7,'体育',105,'2022-02-08 07:29:15');
INSERT INTO recommend_tb VALUES(8,'影视',106,'2022-02-08 07:30:15');

drop table if exists  `user_action_tb` ;   
CREATE TABLE `user_action_tb` (
`user_id` int(11) NOT NULL,
`hobby_l` varchar(8) NOT NULL,
`score` int(11) NOT NULL,
PRIMARY KEY (`user_id`));
INSERT INTO user_action_tb VALUES(101,'健身',88);
INSERT INTO user_action_tb VALUES(102,'影视',81);
INSERT INTO user_action_tb VALUES(103,'美妆',78);
INSERT INTO user_action_tb VALUES(104,'健身',68);
INSERT INTO user_action_tb VALUES(105,'体育',90);
INSERT INTO user_action_tb VALUES(106,'影视',82);

输出

84.500
最后一题是真抽象,如果比如103喜欢美妆,如果重复推荐两次美妆给103,只统计一次,所以要用distinct
SELECT ROUND(AVG(score), 3) as avg_score
FROM (
    SELECT DISTINCT user_id,
    IF(rec_info_l = hobby_l, 1, 0) as flag,   # 判断推荐是否正确
    score
    FROM recommend_tb
    LEFT JOIN user_action_tb
    ON recommend_tb.rec_user = user_action_tb.user_id
) as t1
WHERE flag = 1


发表于 2024-09-12 21:28:30 回复(2)
with t1 as(
    select distinct rt.rec_info_l, rt.rec_user, uat.score
    from recommend_tb rt
    join 
    user_action_tb uat
    on rt.rec_info_l=uat.hobby_l
    and rt.rec_user=uat.user_id
)

select round(avg(score),3) as avg_score
from t1


Edit 2024-03-24
with t1 as(
    SELECT DISTINCT user_id,
    IF(rec_info_l = hobby_l, 1, 0) as flag,   # 判断推荐是否正确
    score
    FROM recommend_tb
    LEFT JOIN user_action_tb
    ON recommend_tb.rec_user = user_action_tb.user_id
)

SELECT ROUND(AVG(score), 3) as avg_score
from t1
WHERE flag = 1



编辑于 2025-03-24 19:02:21 回复(2)
想问下佬们这么写为什么有组用例不通过?
select sum(score)/count(distinct a.rec_user) as avg_score
from recommend_tb a
left join user_action_tb b
on a.rec_user = b.user_id
where a.rec_info_l = b.hobby_l
发表于 2025-03-18 13:00:01 回复(6)
select avg(score)
from user_action_tb u
inner join recommend_tb r
on r.rec_user = u.user_id
and r.rec_info_l=u.hobby_l
编辑于 2024-04-07 14:15:52 回复(4)
select distinct r.rec_user from recommend_tb r
left join user_action_tb u on r.rec_user	 = u.user_id
where r.rec_info_l = u.hobby_l
先得到推荐准确的这部分用户的ID:

然后在用户表中,计算平均得分。where筛选出第一段代码中的这部分用户即可
select avg(score) avg_score from user_action_tb
where user_id in 
(select distinct r.rec_user from recommend_tb r
left join user_action_tb u on r.rec_user	 = u.user_id
where r.rec_info_l = u.hobby_l)


发表于 2025-01-01 16:11:09 回复(0)
select round(avg(score),3) avg_score
from (
        select distinct(rec_user),score
        from 
            recommend_tb rt join user_action_tb uat 
            on rt.rec_user=uat.user_id
        where rec_info_l=hobby_l
) t ;

发表于 2024-12-18 10:21:03 回复(0)
select round(avg(score),3) avg_score
from user_action_tb
where (user_id,hobby_l)
in
(select rec_user user_id,rec_info_l hobby_l from recommend_tb)
发表于 2024-08-26 21:24:18 回复(2)
题目第二个注释的意思是,假如向同一个用户推送同一种内容的次数多于1次,也算作1次。
具体来说就是在recommend_tb中,出现了rec_info_l和rec_user都相同的多笔不同的记录。
发表于 2025-03-03 16:30:24 回复(1)
with t1 as (
    select
    distinct r.rec_info_l,r.rec_user, u.score
    from
    recommend_tb as r
    join
    user_action_tb as u
    on r.rec_info_l = u.hobby_l and r.rec_user = u.user_id
)

select
round(avg(t1.score),3) as avg_score
from t1
发表于 2025-02-14 16:34:45 回复(0)
select round(avg(t1.score),3) as avg_score
from(
    select
    u.user_id,u.score,sum(case when u.hobby_l = r.rec_info_l then 1 else 0 end) as num
    from
    user_action_tb as u
    join recommend_tb as r
    on u.user_id = r.rec_user
    group by u.user_id, u.score
)as t1
where t1.num >= 1
order by t1.user_id

发表于 2025-11-12 16:21:24 回复(0)
简便版,但由于distinct是给全局数据过滤,当不同的用户有相同的分数时依旧会被过滤
select
 round(avg(distinct case when rt.rec_info_l = ut.hobby_l then ut.score end ),3)
from recommend_tb as rt
left join user_action_tb as ut
on rt.rec_user = ut.user_id
可以使用窗口函数,将相同的数划分到同一个区域
select round(avg(score),3)
from (
    select
    rt.rec_user,
    ut.score,
    row_number() over(PARTITION BY rt.rec_user,ut.score order by rt.rec_id) as distincscore
    from recommend_tb as rt
    left join user_action_tb as ut 
    on rt.rec_user = ut.user_id
    where rt.rec_info_l = ut.hobby_l	
) as t
where distincscore =1



发表于 2025-11-06 16:48:40 回复(0)
select round(sum(distinct score) /count(distinct user_id),3)
from recommend_tb a
left join user_action_tb b
on a.rec_user=b.user_id
where rec_info_l = hobby_l
发表于 2025-10-07 19:37:30 回复(0)
select
    round(avg(score), 3) as avg_score
from
(
select
    distinct u.user_id,
    u.score
from recommend_tb r
join user_action_tb u
on r.rec_user=u.user_id
where r.rec_info_l=u.hobby_l
) as sub;
发表于 2025-09-20 16:08:14 回复(0)
select sum(distinct u.score)/count(distinct user_id) as avg_score
from user_action_tb u
join recommend_tb r
on u.hobby_l=r.rec_info_l and u.user_id=r.rec_user
取巧了,正好分数没有重复的,不然通不过
发表于 2025-07-17 10:19:53 回复(0)
select round(avg(c.score),3) from (select  distinct user_id,score from recommend_tb join user_action_tb on rec_user=user_id where rec_info_l=hobby_l) as c ;
发表于 2025-02-25 15:53:08 回复(0)
select avg(a.score) from (select distinct u.user_id,score from
recommend_tb as r inner join user_action_tb as u on r.rec_info_l=u.hobby_l and r.rec_user=u.user_id) as a
发表于 2024-10-29 17:24:18 回复(0)
select 
avg(case when t.score>0 then t.score end) as avg_score
from
(select distinct
user_id, score
from recommend_tb
join user_action_tb
    on recommend_tb.rec_user = user_action_tb.user_id
    and recommend_tb.rec_info_l = user_action_tb.hobby_l
) t
-- 是出现了相同用户相同记录,出现了两条记录吧,这种是数仓的问题吧,但我们还是需要尽量去避免?

发表于 2024-08-22 10:44:06 回复(3)
select
  round(avg(u.score),3) as avg_score
from
    (select distinct r.rec_user
    from recommend_tb r
    inner join user_action_tb u
    on r.rec_user=u.user_id
    and r.rec_info_l=u.hobby_l) as vaild_users
inner join user_action_tb u
on u.user_id=vaild_users.rec_user
发表于 2025-12-18 18:28:20 回复(0)
select round(avg(u.score),3) as avg_score
from user_action_tb u
join (select distinct rec_info_l,rec_user from recommend_tb) r on r.rec_user = u.user_id
where r.rec_info_l = u.hobby_l
发表于 2025-12-13 04:38:42 回复(0)
后面user_action_tb才是这个用户的画像标签,所以如果给101用户她的爱好是影视,即便在recommend_tb中推三次,一次是健身,一次是音乐,一次是影视,而在user_action_tb,只会有101 她对影视的打分是88,所以她算推荐准确,她的打分就会被算在里面是88分贡献1票,如果是102用户爱好健身,recommend_tb中推两次,但两次都没有,也就是在JOIN这个表中她根本不会出现,所以这两种情况用后者我的写法都不会出现问题,问题就只会出现在103用户爱好音乐,然后recommend_tb中推三次,三次都是音乐,而在user_action_tb,有103 她对音乐的打分是98,所以她算推荐准确,但是在JOIN表格中这个103的记录就有三行,所以如果我用后者的写法,就会出现我把这个用户对于音乐的打分98用了三次,虽然单算这个用户的打分情况,三次均分也是98,但是因为这个是求和的的,所有的用户打分情况算在一起,所以三次就会导致错误,

✅ 题目要求的算法(一人一票)

不管系统推了多少次,每个用户只代表“一种体验”。

  • 计算公式: $(101的88分 + 103的98分) \div 2人$

  • 计算过程: $(88 + 98) / 2 = \mathbf{93}$

  • 含义: 用户的平均满意度是 93 分。

❌ JOIN 后的错误算法(按次加权)

因为 User 103 被推准了3次,在 JOIN 出来的大表里,她的分数出现了3行。

  • 计算公式: $(101的88分 \times 1 + 103的98分 \times \mathbf{3}) \div \mathbf{4条记录}$

  • 计算过程: $(88 + 294) / 4 = 382 / 4 = \mathbf{95.5}$

虽然 User 103 自己的均分还是 98($98 \times 3 / 3$),但在全局计算时,因为她出现的次数多,她把整个大盘的平均分**“拉高”**了(因为她是高分用户)。

如果 User 103 是个打低分(比如 10 分)的喷子,且系统给她推了 100 次,你的 JOIN 算法就会导致整个公司的平均评分暴跌,仅仅因为这个低分用户被统计了 100 次。

这就是数据分析中常说的**“粒度” (Granularity)** 问题。你的需求是“用户粒度”,但 JOIN 后的表变成了“推荐事件粒度”,两者的统计口径完全不同。

虽然 User 103 自己的均分还是 98($98 \times 3 / 3$),但在全局计算时,因为她出现的次数多,她把整个大盘的平均分**“拉高”**了(因为她是高分用户)。

如果 User 103 是个打低分(比如 10 分)的喷子,且系统给她推了 100 次,你的 JOIN 算法就会导致整个公司的平均评分暴跌,仅仅因为这个低分用户被统计了 100 次。

这就是数据分析中常说的**“粒度” (Granularity)** 问题。你的需求是“用户粒度”,但 JOIN 后的表变成了“推荐事件粒度”,两者的统计口径完全不同。


发表于 2025-12-12 18:14:31 回复(0)