前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >最近面试太难了。

最近面试太难了。

作者头像
朱小五
发布于 2022-05-26 06:13:09
发布于 2022-05-26 06:13:09
1.2K00
代码可运行
举报
文章被收录于专栏:凹凸玩数据凹凸玩数据
运行总次数:0
代码可运行

在面试数据分析师时,往往会考察一下SQL的掌握程度。

最近有位同学面试了几家,分享了一些觉得有些难度的SQL面试题:比如会让你用SQL实现行转列和列转行操作、用SQL计算留存、用SQL计算中位数、还有如何统计用户最大连续登录天数?

当然这种题变形也很多,连续打卡天数、连续学习天数,连续点击天数等等都是同一个类型,今天我们将会给大家分享SQL和Pandas的多种做法。让大家一次搞懂,下次面试不难!

作者简介

小小明,数据、Python爱好者,CSDN博客专家。个人博客地址:https://blog.csdn.net/as604049322

计算每一个用户的最大连续登录天数,由左变换到右边。

SQL 8.0窗口函数

实现思路:

  1. 对用户ID和登录日期去重
  2. 对每个用户ID按照日期顺序进行编号
  3. 将登录日期减去编号对应的天数,使连续的日期转换为同一天

将连续日期转换为同一个日期之后就可以按照这个字段分组,后面就简单了。下面我们一步步看:

对用户ID和登录日期去重

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT DISTINCT role_id,$part_date `date` FROM role_login;

对每个用户ID按照日期顺序进行编号,并将登录日期减去该编号对应的天数(可以一步到位):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT
 role_id,`date`,
 DATE_SUB(`date`,INTERVAL (row_number() OVER(PARTITION BY role_id ORDER BY `date`)) DAY) data_group
FROM(
 SELECT DISTINCT role_id,$part_date `date` FROM role_login
) a;

从结果我们可以看到已经成功的使连续的日期都转换到同一天。

然后我们就可以基于该结果统计每个用户的所有连续日期段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT
 role_id,
 MIN(DATE) begin_date,
 MAX(DATE) end_date,
 COUNT(*) continuous_days
FROM(
 SELECT
  role_id,`date`,
  DATE_SUB(`date`,INTERVAL (row_number() OVER(PARTITION BY role_id ORDER BY `date`)) DAY) data_group
 FROM(
  SELECT DISTINCT role_id,$part_date `date` FROM role_login
 ) a
) b
GROUP BY role_id,data_group;

结果:

然后通过窗口函数标注每个用户的连续日期排名:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT
 role_id,begin_date,end_date,
 continuous_days ,
 row_number() OVER (PARTITION BY role_id ORDER BY continuous_days DESC) rk
FROM(
 SELECT
  role_id,
  MIN(DATE) begin_date,
  MAX(DATE) end_date,
  COUNT(*) continuous_days
 FROM(
  SELECT
   role_id,`date`,
   DATE_SUB(`date`,INTERVAL (row_number() OVER(PARTITION BY role_id ORDER BY `date`)) DAY) data_group
  FROM(
   SELECT DISTINCT role_id,$part_date `date` FROM role_login
  ) a
 ) b
 GROUP BY role_id,data_group
) c;

注意:有时同一个用户的最大连续日期可能存在多个,这里以第一个为准;如果需要获取全部的最大日期可以使用rankdense_rank窗口函数,可以保证天数一致时排名一致。

完整SQL脚本:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT
 role_id,begin_date,end_date,continuous_days max_continuous_days
FROM(
 SELECT
  role_id,begin_date,end_date,
  continuous_days ,
  row_number() OVER (PARTITION BY role_id ORDER BY continuous_days DESC) rk
 FROM(
  SELECT
   role_id,
   MIN(DATE) begin_date,
   MAX(DATE) end_date,
   COUNT(*) continuous_days
  FROM(
   SELECT
    role_id,`date`,
    DATE_SUB(`date`,INTERVAL (row_number() OVER(PARTITION BY role_id ORDER BY `date`)) DAY) data_group
   FROM(
    SELECT DISTINCT role_id,$part_date `date` FROM role_login
   ) a
  ) b
  GROUP BY role_id,data_group
 ) c
) d
WHERE rk=1
ORDER BY max_continuous_days DESC,role_id;

成功得到结果:

sql变量(5.0也可以使用)

前面方法使用的是SQL 8.0窗口函数,但在低版本sql5.0中并没有窗口函数,如果面试官提出不让用窗口函数,应该怎么办呢?

其实我们可以通过变量来实现,思路仍然与上述相同,首先我们对数据集去重并排序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT DISTINCT role_id,$part_date `date` FROM role_login
ORDER BY role_id,$part_date;

然后利用变量逐行扫描数据集:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT
 role_id,`date`,
 IF(DATE_ADD(`date`,INTERVAL -1 DAY)=@prev_date,@r,@r:=@r+1) group_id, -- 日期变化大于1天(不连续)改变r值
 @prev_date:=`date` -- 记录前一条记录的日期
FROM(
 SELECT DISTINCT role_id,$part_date `date` FROM role_login
 ORDER BY role_id,$part_date
) a,(SELECT @prev_date:=NULL,@r:=0) t;

从结果可以看到,对于每个用户下连续的日期都给出了完全相同的分组编号:

然后就可以计算连续天数了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT
 role_id,
 MIN(DATE) begin_date,
 MAX(DATE) end_date,
 COUNT(*) continuous_days
FROM(
 SELECT
  role_id,`date`,
  IF(DATE_ADD(@prev_date,INTERVAL 1 DAY)=`date`,@r,@r:=@r+1) group_id, -- 日期变化大于1天(不连续)改变r值
  @prev_date:=`date` -- 记录前一条记录的日期
 FROM(
  SELECT DISTINCT role_id,$part_date `date` FROM role_login
  ORDER BY role_id,$part_date
 ) a,(SELECT @prev_date:=NULL,@r:=0) t
) b
GROUP BY role_id,group_id;

然后借助变量标注排名:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT
 role_id,begin_date,end_date,continuous_days ,
 IF(@prev_id=role_id,@r2:=@r2+1,@r2:=1) rk,
 @prev_id:=role_id -- 记录前一条记录的用户ID
FROM(
 SELECT
  role_id,
  MIN(DATE) begin_date,
  MAX(DATE) end_date,
  COUNT(*) continuous_days
 FROM(
  SELECT
   role_id,`date`,
   IF(DATE_ADD(@prev_date,INTERVAL 1 DAY)=`date`,@r,@r:=@r+1) group_id, -- 日期变化大于1天(不连续)改变r值
   @prev_date:=`date` -- 记录前一条记录的日期
  FROM(
   SELECT DISTINCT role_id,$part_date `date` FROM role_login
   ORDER BY role_id,$part_date
  ) a,(SELECT @prev_date:=NULL,@r:=0) t1
 ) b
 GROUP BY role_id,group_id
) c,(SELECT @prev_id:=NULL,@r2:=0) t2
ORDER BY role_id,continuous_days DESC;

可以看到变量已经成功实现了row_number的效果。

最后再进行一次过滤即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT
 role_id,begin_date,end_date,continuous_days max_continuous_days
FROM(
 SELECT
  role_id,begin_date,end_date,continuous_days ,
  IF(@prev_id=role_id,@r2:=@r2+1,@r2:=1) rk,
  @prev_id:=role_id -- 记录前一条记录的用户ID
 FROM(
  SELECT
   role_id,
   MIN(DATE) begin_date,
   MAX(DATE) end_date,
   COUNT(*) continuous_days
  FROM(
   SELECT
    role_id,`date`,
    IF(DATE_ADD(@prev_date,INTERVAL 1 DAY)=`date`,@r,@r:=@r+1) group_id, -- 日期变化大于1天(不连续)改变r值
    @prev_date:=`date` -- 记录前一条记录的日期
   FROM(
    SELECT DISTINCT role_id,$part_date `date` FROM role_login
    ORDER BY role_id,$part_date
   ) a,(SELECT @prev_date:=NULL,@r:=0) t1
  ) b
  GROUP BY role_id,group_id
 ) c,(SELECT @prev_id:=NULL,@r2:=0) t2
 ORDER BY role_id,continuous_days DESC
) d
WHERE rk=1
ORDER BY max_continuous_days DESC,role_id;

成功得到结果:

Pandas

下面我们用sql窗口函数的实现思路,用Pandas实现一遍。

首先读取数据集并去重:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import pandas as pd

df = pd.read_excel("role_login.xlsx")
df = df[["role_id", "$part_date"]].drop_duplicates()
df

对每个用户ID按照日期顺序进行编号,并将登录日期减去该编号对应的天数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
data_group = df["$part_date"]-pd.to_timedelta(df.groupby("role_id")["$part_date"].rank(method="dense"), unit='d')

统计每个用户的所有连续日期段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
data_group = df["$part_date"]-pd.to_timedelta(df.groupby("role_id")["$part_date"].rank(method="dense"), unit='d')
df = df.groupby(["role_id", data_group], as_index=False).agg(
    begin_date=("$part_date", "min"),
    end_date=("$part_date", "max"),
    max_continuous_days=("$part_date", "count")
)
df

然后我们过滤出每个用户的最大连续日期:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ids = df.groupby("role_id")["max_continuous_days"].idxmax()
r1 = df.loc[ids].sort_values(
    ["max_continuous_days", "role_id"], ascending=[False, True])
r1

按照窗口函数的思路代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mask=df.groupby("role_id")["max_continuous_days"].rank(method="first", ascending=False) == 1
r2 = df[mask].sort_values(["max_continuous_days", "role_id"], ascending=[False, True])
r2

两种思路的结果一致:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(r1 == r2).all()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
role_id                True
begin_date             True
end_date               True
max_continuous_days    True
dtype: bool

但是窗口函数的思路相对idxmax快了近百倍,效果如下图所示。

所以针对这取分组最大的问题还是使用rank函数效果更高一些。

RANK、DENSE_RANK差异

本题在一个用户存在多个最大连续日期时只要求取第一个,如果需要取每个用户所有的最大连续日期,则需要使用rankdense_rank窗口函数。

我们看看Pandas中rank函数的几种method的差异:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import pandas as pd

t1 = pd.DataFrame(data={'num': [2, 4, 4, 8, 8]})
t1['default_rank'] = t1['num'].rank()
t1['min_rank'] = t1['num'].rank(method='min')
t1['max_rank'] = t1['num'].rank(method='max')
t1['dense_rank'] = t1['num'].rank(method='dense')
t1['first_rank'] = t1['num'].rank(method='first')
t1

结果:

可以看到sql的rank函数相当于Pandas的min_rank,row_number相当于first_rank:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT
    cookieid,
    createtime,
    pv,
    RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn1,
    DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn2,
    ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3
FROM xxm_t2
WHERE cookieid = 'cookie1';

结果:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 凹凸数据 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
搭建一个你的文件共享站
说起文件共享,类似文件站这样的功能。在很久之前我使用过用apache httpd这个方案,这个的话就是太过于简单了。当然是满足需求的(又不是不能用) 今天来分享一个开源的文件共享平台。Pingvin Share 作者的github地址:https://github.com/stonith404/pingvin-share
huolong
2023/08/28
5360
搭建一个你的文件共享站
【Docker项目实战】使用Docker部署PicoShare共享文件平台
江湖有缘
2024/01/22
1.5K1
【Docker项目实战】使用Docker部署PicoShare共享文件平台
【Docker项目实战】使用Docker部署Jirafeau文件共享工具
江湖有缘
2025/01/06
4400
【Docker项目实战】使用Docker部署Jirafeau文件共享工具
【好玩儿的Docker项目】Pingvin Share——一个专注于文件分享的高颜值轻量小工具!
之前和大家介绍过的MicroBin主打的是文本代码分享功能,这周和大家推荐一个专注于文件分享的高颜值轻量小工具——Pingvin Share
二十五画生
2023/01/31
3.4K0
【好玩儿的Docker项目】Pingvin Share——一个专注于文件分享的高颜值轻量小工具!
用NAS搭建一个你的文件共享站,和基友分享你的那些好康的“东西” - 熊猫不是猫QAQ
大部分情况下大家分享nas中的文件都是通过直接生成连接的形式发给朋友,但如果有些东西我没有存在nas中,上传到nas再去生成链接再发给基友,多少有点不方便了,于是文件分享站变有了它存在的意义。我们可以将一些nas中没有存放的文件放上面供基友自行查看。
熊猫不是猫
2023/09/06
8180
用NAS搭建一个你的文件共享站,和基友分享你的那些好康的“东西” - 熊猫不是猫QAQ
【Docker项目实战】使用Docker部署FileGator文件管理器
江湖有缘
2024/02/03
3.1K0
【Docker项目实战】使用Docker部署FileGator文件管理器
【Docker项目实战】在Docker环境下部署go-file文件分享工具
江湖有缘
2023/12/09
1.6K1
【Docker项目实战】在Docker环境下部署go-file文件分享工具
【Docker项目实战】使用Docker部署FastSend文件传输工具
江湖有缘
2025/05/10
1650
【Docker项目实战】使用Docker部署FastSend文件传输工具
极简开源的文件共享系统,无需注册!
PicoShare 一款开源的文件共享系统,专注于提供图片、视频和其他文件的共享服务。项目基于Go语言编写,遵守AGPL开源协议。
永恒君
2023/09/02
8450
极简开源的文件共享系统,无需注册!
【docker】一款轻量的分享工具——Pingvin Share
项目地址:https://github.com/stonith404/pingvin-share
夜梦星尘
2024/08/20
3040
【docker】一款轻量的分享工具——Pingvin Share
PicoShare:轻量级文件共享系统的本地部署与远程访问指南
本篇文章介绍,如何在Linux系统本地部署轻量级文件共享系统PicoShare,并结合Cpolar内网穿透实现公网环境远程传输文件至本地局域网内文件共享系统。
FGGIT
2024/11/19
2640
PicoShare:轻量级文件共享系统的本地部署与远程访问指南
【Docker项目实战】使用Docker部署JmalCloud个人网盘
江湖有缘
2024/01/30
3.6K0
【Docker项目实战】使用Docker部署JmalCloud个人网盘
只使用简单的 JavaScript 创建文件共享型网站
Any Share 是一种简单、轻量、快速的文件共享服务。使用 Javascript 编写,并搭建在 Firebase 平台。
用户11164002
2024/06/17
8130
只使用简单的 JavaScript 创建文件共享型网站
FileCodeBox:Star4.9k,类似百度网盘,小团队、小公司都用得上的文件共享与分享工具,支持设置分享密码,界面清爽简单易用
嗨,大家好,我是小华同学,关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法
小华同学ai
2025/01/27
1540
FileCodeBox:Star4.9k,类似百度网盘,小团队、小公司都用得上的文件共享与分享工具,支持设置分享密码,界面清爽简单易用
【好用的个人工具】Docker环境下部署FileDrop文件共享工具
江湖有缘
2023/12/07
2.8K4
【好用的个人工具】Docker环境下部署FileDrop文件共享工具
【实用的开源项目】使用服务器部署Sharry:真的很好用的文件分享程序!
【实用的开源项目】系列出到第五期了鸭。小俊因为上学以及其他事务的原因已经快三个月没有更新了,那今天为大家介绍一下 Sharry 这一款文件分享程序吧,这篇文章是详细的文字版,若希望查看教程视频,请向下滑动,查看视频,或者直接到 BiliBili 搜索 我是小俊呀 ,查找视频哦!
小俊是我
2022/12/24
1.1K0
【实用的开源项目】使用服务器部署Sharry:真的很好用的文件分享程序!
飞牛 fnos 使用docker部署 Windows 系统:虚拟机的轻量级替代方案
飞牛系统暂缺官方虚拟机,而 dockur/windows 这个开源项目能让 Windows 系统在 Docker 容器中运行,支持多版本(如 win7、win8、win10、win11、Win Server、Tiny Win)和多语言,很适合开发、测试以及学习场景,可快速搭建且自动部署,稳定又高效,是飞牛系统下 Windows 环境搭建的理想之选。
季春二九
2024/12/17
1.8K0
飞牛 fnos 使用docker部署 Windows 系统:虚拟机的轻量级替代方案
【Docker项目实战】使用Docker部署Mikochi文件管理工具
江湖有缘
2023/12/22
1.2K0
【Docker项目实战】使用Docker部署Mikochi文件管理工具
【Docker项目实战】使用Docker部署MicroBin粘贴应用程序
江湖有缘
2024/12/28
1800
【Docker项目实战】使用Docker部署MicroBin粘贴应用程序
【Docker项目实战】使用Docker部署Filestash文件管理器
江湖有缘
2024/09/18
1.1K0
【Docker项目实战】使用Docker部署Filestash文件管理器
推荐阅读
搭建一个你的文件共享站
5360
【Docker项目实战】使用Docker部署PicoShare共享文件平台
1.5K1
【Docker项目实战】使用Docker部署Jirafeau文件共享工具
4400
【好玩儿的Docker项目】Pingvin Share——一个专注于文件分享的高颜值轻量小工具!
3.4K0
用NAS搭建一个你的文件共享站,和基友分享你的那些好康的“东西” - 熊猫不是猫QAQ
8180
【Docker项目实战】使用Docker部署FileGator文件管理器
3.1K0
【Docker项目实战】在Docker环境下部署go-file文件分享工具
1.6K1
【Docker项目实战】使用Docker部署FastSend文件传输工具
1650
极简开源的文件共享系统,无需注册!
8450
【docker】一款轻量的分享工具——Pingvin Share
3040
PicoShare:轻量级文件共享系统的本地部署与远程访问指南
2640
【Docker项目实战】使用Docker部署JmalCloud个人网盘
3.6K0
只使用简单的 JavaScript 创建文件共享型网站
8130
FileCodeBox:Star4.9k,类似百度网盘,小团队、小公司都用得上的文件共享与分享工具,支持设置分享密码,界面清爽简单易用
1540
【好用的个人工具】Docker环境下部署FileDrop文件共享工具
2.8K4
【实用的开源项目】使用服务器部署Sharry:真的很好用的文件分享程序!
1.1K0
飞牛 fnos 使用docker部署 Windows 系统:虚拟机的轻量级替代方案
1.8K0
【Docker项目实战】使用Docker部署Mikochi文件管理工具
1.2K0
【Docker项目实战】使用Docker部署MicroBin粘贴应用程序
1800
【Docker项目实战】使用Docker部署Filestash文件管理器
1.1K0
相关推荐
搭建一个你的文件共享站
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档