pymongo中group by的操作方法教程
前言
使用pymongo进行groupby操作有两种基本方式,他们都是mongodb的原生命令,于Collection对象上调用。
defaggregate(self,pipeline,**kwargs): defgroup(self,key,condition,initial,reduce,finalize=None,**kwargs):
示例数据
演示用的数据为一个订单表,含有以下字段:
Order
_id:ObjectID
userid:int
itemid:int
amount:int
time: string
主要任务为:
- 统计某个时间区间内每个userid的订单数
- 统计某个时间区间内每组(userid,itemid)共售出多少amount
即分别为:单键分组和多键分组
aggregate
聚合操作只接受一个列表类型的参数——pipeline。其每一个元素都是一步操作(stage)。全部可用的stage可参见:
https://docs.mongodb.com/manual/meta/aggregation-quick-reference/#stages
注意pipline里面的stage是有序且可重复的,mongodb会顺序执行,因此一定要记得把像$match这样的stage放前面。
单键分组
start_time='2010-10-1000:00:00' end_time='2010-10-1023:59:59' match={ 'time':{ '$gte':start_time, '$lte':end_time, } } groupby='userid' group={ '_id':"$%s"%(groupbyifgroupbyelseNone), 'count':{'$sum':1} } ret=collection.aggregate( [ {'$match':match}, {'$group':group}, ] ) >>>ret [{'_id':123,'count':500},...]
$group指定了返回数据的格式,其中_id字段是分组的键。
多键分组
groupby=['itemid','userid'] group={ '_id':{key:('$%s'%key)forkeyingroupby}or{'None':'$None'}, 'count':{'$sum':'$amount'} } ret=collection.aggregate( [ {'$match':match}, {'$group':group}, ] ) >>>ret [{'_id':{'itemid':111,'user_id':123},'count':100},...]
这里与单键分组的区别仅在于_id的类型,改成了一个字典,从而允许多键组合。
为了提高通用性,建议始终使用字典的格式。
另外,既然字符串和字典都可以做键,那么列表行不行呢?答案是不行,列表里的元素,(如'$userid')并不会被自动识别为字段,而是仅作一般字符串处理。
最后关于aggregate中可用的运算操作符,可参见:
https://docs.mongodb.com/manual/reference/operator/aggregation/#accumulators
如其中的$addToSet也是颇有用处,可以用来实现“统计每个人都买过哪些itemid”这样的功能:
group={ '_id':{'userid':'$userid'}, 'dist_itemids':{'$addToSet':'$itemid'}, }
group
相较于aggregate的全能,group是专门处理分组操作的一个命令,因此这个方法的参数也更明确,主要参数为:
- keylist,分组的键
- conditiondict,过滤条件
- initialdict,初始值
- reducestring/bson.Code,js的reduce函数
例:
key=['userid','itemid'] condition={ 'time':{ '$gte':start_time, '$lte':end_time, } } initial={'count':0} reducer=Code(""" function(obj,prev){ prev.count=prev.count+obj.amount } """) ret=collection.group(key,condition,initial,reducer) >>>ret [{'userid':110,'itemid':123,'count':500.0},...]
这里的分组数据聚合,是通过reduce函数实现的,这个函数与python的reduce不同,它不需要返回值,而是直接修改prev参数即可,这个参数会自动代入下一次调用。这可能是js的实现。
须注意的是js默认返回浮点数。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。