上个月空降了个总监,听说200万从某大厂挖来的,他一来要大换血。团队开始站队:一波人疯狂表忠心,一波人消极抵抗,我选择观望。
刚看到个贴子,说公司空降来个高薪总监,一来就要“换血”,有人抢着表忠心,有人摆烂对着干,楼主选择先看戏。三个月一盘点:喊得最响的那批,确实被“重用”了,但基本都在干又累又难背锅的活;原地硬刚的,慢慢被边缘化。
我觉得这事吧,关键不是站哪队,而是先想明白:你能提供什么价值,底线在哪。一上来就跪舔,短期看是机会,长期容易变成“免费苦力”;硬刚领导,在大多数公司又是九死一生。
从我的角度看,楼主现在的“观望”没问题,但别只是吃瓜,要悄悄把几件事做扎实:手里的活做到不可或缺,多记录自己的成果,少卷情绪,多留后路。
面试题:活跃用户
有天产品拍着我肩膀说:“能不能给我拉一个‘活跃用户列表’,就是那种连续好多天都来玩的用户?”听起来挺简单的,对吧?但你真要写代码,就得先把“活跃”这俩字说清楚。
我下面就按 Python 来聊一版常见定义:连续打卡型活跃用户,顺带把思路和代码都走一遍。
有一堆登录日志,每条长这样:
(user_id, login_date)
比如:
("u1", "2025-01-01")
("u1", "2025-01-02")
...
要求:找出在某段时间内,至少有 k 天连续登录的用户。 比如 k = 5,那 u1 在 1 日、2 日、3 日、4 日、5 日都来了,就算“活跃用户”。
现实里这个定义很好用:游戏的“连续登录奖励”,社区的“连续签到”,本质都是同一个事。
你想象一下用肉眼看日志:
先把同一个用户的日期拎在一起
把这些日期按时间排好序
从前往后扫,数一数连续的天数
如果今天是昨天 + 1 天,那 streak++ 否则 streak 从 1 重新算
一旦某个 streak ≥ k,这个用户就可以标记为“活跃”
这个过程,其实就是我们要写进代码里的逻辑,没有什么高深的算法名词,就是“按人分组 + 排序 + 扫一遍”。
直接上代码,先看整体,再拆细节:
from collections import defaultdict
from datetime import datetime, timedelta
deffind_active_users(logs, k=5):
"""
logs: 列表,每个元素是 (user_id, date_str)
date_str 形如 "2025-01-01"
k: 连续登录的天数阈值,比如 5 天
返回: 满足条件的 user_id 列表
"""
# 1. 先把每个用户对应的所有登录日期收集起来,用 set 去重
user_dates = defaultdict(set)
for user_id, date_str in logs:
# 把字符串转成 date 对象,方便做加减
d = datetime.strptime(date_str, "%Y-%m-%d").date()
user_dates[user_id].add(d)
active_users = []
# 2. 对每个用户,检查它的连续登录情况
for user_id, date_set in user_dates.items():
# set 里是无序的,先排个序
days = sorted(date_set)
if len(days) < k:
# 连登录记录都没 k 天,不可能连续 k 天,直接跳过
continue
streak = 1# 当前连续天数,从第一天开始算
# 3. 从第二个日期开始往后扫
for i in range(1, len(days)):
# 如果这一天刚好是前一天 + 1 天,说明连续
if days[i] == days[i - 1] + timedelta(days=1):
streak += 1
else:
# 中断了,重新计数
streak = 1
# 只要达到 k,就可以认定是活跃用户,没必要再往后算了
if streak >= k:
active_users.append(user_id)
break
return active_users
你可以简单跑个小例子:
if __name__ == "__main__":
logs = [
("u1", "2025-01-01"),
("u1", "2025-01-02"),
("u1", "2025-01-03"),
("u1", "2025-01-04"),
("u1", "2025-01-05"), # u1 连续 5 天
("u2", "2025-01-01"),
("u2", "2025-01-03"),
("u2", "2025-01-04"), # u2 有断档
("u3", "2025-01-10"),
("u3", "2025-01-11"),
("u3", "2025-01-12"),
("u3", "2025-01-13"),
("u3", "2025-01-14"),
("u3", "2025-01-20"), # u3 也连续 5 天
]
print(find_active_users(logs, k=5))
# 输出类似:['u1', 'u3']
到这儿,一个“连续 k 天登录用户”的算法就算落地了。
别看代码挺朴素,其实复杂度也可以算一算:
收集数据那一段:遍历所有日志一次,假设日志条数是 n,就是 O(n) 对每个用户的日期排序:假设用户总数是 m,每个用户有 d_i 条记录 总体复杂度大致是 Σ d_i log d_i,大约可以看成 O(n log n) 每个用户再线性扫一遍自己的日期:总和还是 O(n)
所以主导复杂度就是那一轮排序:**O(n log n)**。 大部分业务场景,这个级别已经挺能打了,几百万行日志都没啥问题。
随便提几个经常踩坑的点:
同一天重复多条登录记录
比如用户一天打开了十几次 app,日志就一大堆 我们用 set去重,就是为了只保留“这天来过就行”,不按次数算
日志可能没按时间排序
所以每个用户自己的那一堆日期都要 sorted一下,不要迷信“日志本来就是按时间写的”
活跃定义随业务变
有时候是“连续 k 天登录” 有时候是“最近 30 天里活跃 ≥ 15 天” 有时候甚至是“最近一周下过单就算活跃” 其实改起来也不难,关键是把“活跃”这个条件从代码里抽出来,写成清晰的 if/规则就行。
比如如果你要的是“最近 7 天里登录过就算活跃用户”,代码可以变成这样:
deffind_active_users_in_last_n_days(logs, n=7, today_str="2025-01-15"):
today = datetime.strptime(today_str, "%Y-%m-%d").date()
threshold = today - timedelta(days=n - 1)
active_set = set()
for user_id, date_str in logs:
d = datetime.strptime(date_str, "%Y-%m-%d").date()
if d >= threshold:
active_set.add(user_id)
return list(active_set)
这个版本就更简单:不用排序,也不用算连续,时间复杂度直接 O(n),就是扫一遍。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB