使用Redis位图实现强大的分析库。
项目描述
全新! 尝试使用我们全新的独立版 bitmapist-server,它将内存效率提升了443倍,使得您的设置运行成本更低(并且更具可扩展性)。它与运行在Redis上的bitmapist完全兼容。
bitmapist:Redis强大的分析库
这个Python库使得实现实时、高度可扩展的分析成为可能,可以回答以下问题:
- 用户123今天、这周、这个月在线了吗?
- 用户123执行了操作“X”吗?
- 这个月有多少用户活跃?这个小时有多少用户活跃?
- 这个周有多少唯一用户执行了操作“X”?
- 上周活跃的用户中还有多少%仍然活跃?
- 上个月活跃的用户中还有多少%这个月仍然活跃?
- 哪些用户执行了操作“X”?
这个库非常易于使用,并允许您轻松创建自己的报告。
使用Redis位图,您可以在非常小的内存量(兆字节)中存储数百万用户的操作事件。您应该小心使用大ID,因为这可能需要更多的内存。ID应该在范围[0, 2^32)内。
此外,bitmapist可以生成以下 cohort 图表:
- 用户留存率 cohort
- 上周有多少%的用户仍然活跃([天、周、月])?
- 执行了操作X的用户中还有多少%也执行了操作Y(并且随着时间的推移)?
- 还有很多其他的东西!
如果您想了解更多关于位图的信息,请阅读以下内容:
- http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/
- https://redis.ac.cn/commands/setbit
- http://en.wikipedia.org/wiki/Bit_array
- http://www.slideshare.net/crashlytics/crashlytics-on-redis-analytics
安装
可以通过以下方式轻松安装:
$ pip install bitmapist
端口
示例
设置环境
from datetime import datetime, timedelta
from bitmapist import setup_redis, delete_all_events, mark_event,\
MonthEvents, WeekEvents, DayEvents, HourEvents,\
BitOpAnd, BitOpOr
now = datetime.utcnow()
last_month = datetime.utcnow() - timedelta(days=30)
将用户123标记为活跃并且已播放歌曲
mark_event('active', 123)
mark_event('song:played', 123)
回答用户123这个月是否活跃
assert 123 in MonthEvents('active', now.year, now.month)
assert 123 in MonthEvents('song:played', now.year, now.month)
assert MonthEvents('active', now.year, now.month).has_events_marked() == True
这个周有多少用户活跃?
print(len(WeekEvents('active', now.year, now.isocalendar()[1])))
迭代本周所有活跃的用户
for uid in WeekEvents('active'):
print(uid)
如果您对“当前事件”感兴趣,可以省略额外的now.whatever
参数。事件将自动用当前时间填充。
例如,这两个调用是等效的
MonthEvents('active') == MonthEvents('active', now.year, now.month)
此外,为了统一,您可以使用from_date
静态方法从任何datetime对象创建事件。
MonthEvents('active').from_date(now) == MonthEvents('active', now.year, now.month)
获取这些用户(用户ID)的列表
print(list(WeekEvents('active', now.year, now.isocalendar()[1])))
有一些特殊的方法prev
和next
返回“兄弟”事件,并允许您在不使用复杂的迭代器的情况下遍历事件。一个delta
方法允许您“跳过”多个步骤。统一的API允许您使用所有类型的基事件(从小时到年)使用相同的代码。
current_month = MonthEvents()
prev_month = current_month.prev()
next_month = current_month.next()
year_ago = current_month.delta(-12)
每个事件对象都有period_start
和period_end
方法来查找事件的时间范围。这可能对缓存值很有用,当不希望缓存“未来的事件”时。
ev = MonthEvent('active', dt)
if ev.period_end() < now:
cache.set('active_users_<...>', len(ev))
作为新功能,每小时跟踪已禁用(为了节省内存!)要默认启用它,请执行以下操作:
import bitmapist
bitmapist.TRACK_HOURLY = True
此外,您还可以向mark_event
提供额外的参数以绕过默认值:
mark_event('active', 123, track_hourly=False)
唯一事件
有时事件日期几乎没有意义,例如,过滤您的付费账户,或者在A/B测试中。为此目的有一个UniqueEvents
模型。该模型只创建一个Redis密钥,并且不依赖于日期。
您可以将唯一事件与其他类型的事件结合使用。
A/B测试示例
active_today = DailyEvents('active')
a = UniqueEvents('signup_form:classic')
b = UniqueEvents('signup_form:new')
print("Active users, signed up with classic form", len(active & a))
print("Active users, signed up with new form", len(active & b))
通用过滤器示例
def premium_up(uid):
# called when user promoted to premium
...
mark_unique('premium', uid)
def premium_down(uid):
# called when user loses the premium status
...
unmark_unique('premium', uid)
active_today = DailyEvents('active')
premium = UniqueEvents('premium')
# Add extra Karma for all premium users active today,
# just because today is a special day
for uid in premium & active_today:
add_extra_karma(uid)
为了达到两全其美,您可以在同一时间标记唯一事件和常规位图事件。
def premium_up(uid):
# called when user promoted to premium
...
mark_event('premium', uid, track_unique=True)
执行位运算
上个月活跃的用户有多少人这个月仍然活跃?
active_2_months = BitOpAnd(
MonthEvents('active', last_month.year, last_month.month),
MonthEvents('active', now.year, now.month)
)
print(len(active_2_months))
# Is 123 active for 2 months?
assert 123 in active_2_months
或者,您可以使用标准的Python位运算语法。
last_month_event = MonthEvents('active', last_month.year, last_month.month)
this_month_event = MonthEvents('active', now.year, now.month)
active_two_months = last_month_event & this_month_event
支持操作符 &
、|
、^
和 ~
。
处理嵌套位运算(想象一下你可以用它做什么 ;-))!
active_2_months = BitOpAnd(
BitOpAnd(
MonthEvents('active', last_month.year, last_month.month),
MonthEvents('active', now.year, now.month)
),
MonthEvents('active', now.year, now.month)
)
print(len(active_2_months))
assert 123 in active_2_months
# Delete the temporary AND operation
active_2_months.delete()
删除
如果您想永久删除任何时间段内标记的事件,可以使用 delete()
方法
last_month_event = MonthEvents('active', last_month.year, last_month.month)
last_month_event.delete()
如果您想删除所有位图事件,请使用
bitmapist.delete_all_events()
在使用位运算(例如 BitOpAnd
)时,您可以(并可能应该)删除结果,除非您希望它们被缓存。有几种不同的方法可以实现这一点
active_2_months = BitOpAnd(
MonthEvents('active', last_month.year, last_month.month),
MonthEvents('active', now.year, now.month)
)
# Delete the temporary AND operation
active_2_months.delete()
# delete all bit operations created in runtime up to this point
bitmapist.delete_runtime_bitop_keys()
# delete all bit operations (slow if you have many millions of keys in Redis)
bitmapist.delete_temporary_bitop_keys()
位图群体
使用位图群体,您可以获取您在位图群体中保存的数据的表单和表格渲染。如果这听起来很复杂,请查看Mixpanel。
以下是一个如何生成位图群体内部数据的表单和渲染的简单示例
from bitmapist import cohort
html_form = cohort.render_html_form(
action_url='/_Cohort',
selections1=[ ('Are Active', 'user:active'), ],
selections2=[ ('Task completed', 'task:complete'), ]
)
print(html_form)
dates_data = cohort.get_dates_data(select1='user:active',
select2='task:complete',
time_group='days')
html_data = cohort.render_html_data(dates_data,
time_group='days')
print(html_data)
# All the arguments should come from the FORM element (html_form)
# but to make things more clear I have filled them in directly
这将渲染类似的内容
贡献
请参阅我们的指南这里
本地开发
我们使用Poetry进行依赖管理和打包。请参阅这里的设置说明。
安装Poetry后,您可以在虚拟环境中运行以下命令来安装依赖项
poetry install
测试
要运行我们的测试,需要确保已安装本地redis服务器。
我们使用pytest运行unittest,您可以在Poetry shell中使用以下命令运行
poetry run pytest
发布新版本
- 在
pyproject.toml
中提升版本 - 更新CHANGELOG
- 使用提交消息“版本X.X.X”提交更改
- 使用
vX.X.X
标记当前提交 - 在GitHub上创建名为
vX.X.X
的新版本 - GitHub Actions将为您将新版本发布到PIP
法律
版权:2012年Doist Ltd.
许可:BSD
项目详情
下载文件
下载您平台的文件。如果您不确定选择哪个,请了解安装包的更多信息。