Data Analysis Python操作MongoDB
Created at 2016-09-28 Updated at 2018-05-01 Category Data Analysis
MongoDB
其实Reinhard在2012年的时候就已经接触MongoDB了,这里再来熟悉下python操作MongoDB的方法。
这里主要使用PyMongo模块,看看官方的指南,可以了解基本的操作。
基本操作
1 | from pymongo import MongoClient |
使用pretty()输出结果,可以看到格式化的json文档
用less能更好地看输出结果
查询
1 | query={"name":"a","gender":"male"} |
这里通过多个查询条件进行查询,并且使用投影,只查看所需的字段。id设为0,说明不获取id。返回的结果集中只有name字段。
查询数组元素
MongoDB最棒的,是可以查询数组中的元素
1 | data={ |
含有1990的记录都会匹配到。
如果再years字段上建立索引,速度会更快。
插入
批量插入
1 |
|
也可以使用命令行直接从json文件导入
1 | mongoimport -d test_db -c test_collection --file student.json |
运算
常见的有下面这些,都非常简单
- $gt
- $lt
- $gte
- $lte
- $ne
- $exists
- $regex
- $in
- $all
用法如下
1 | query={"letter":{"$gt":"S","$lt":"Y"}} |
点表示法
查看大于18的名字1
2
3
4
5data={"girlfriend":{
"name":"coco",
"age":19
}}
query={"girlfriend.age":{"$gt":18},"girlfriend.name"}
更新
更新一个文档
find_one会取出匹配到的第一条记录
1 | student=db.students.find_one({"name":"reinhard"}) |
也可以使用update,默认也只更新一条记录
1 | student=db.students.update({"name":"reinhard"},{"$set":{"age":18}}) |
如果上面的代码中,忘记写$set,那么将会更新所有匹配到的记录!!!
也可以使用$unset来取消某个字段
1 | student=db.students.update({"name":"reinhard"},{"$unset":{"age":18}}) |
这样匹配到的第一条记录如果有age字段,将会删掉该字段
更新多个文档
1 | student=db.students.update({"name":"reinhard"},{"$set":{"age":18}},multi=True) |
删除
1 | db.students.remove({"name":"reinhard"}) |
分析
MongoDB的内置分析工具——聚合框架,产品定位应该对应于Hadoop的MapReduce。
社交分析
社交数据分析思路:
- 先按用户对数据进行分组
- 统计用户的发帖数量
- 降序排列
- 选择顶部的用户
数据结构
1 | { |
基本聚合操作
- $group
- $sort
- $project
- $match
- $skip
- $limit
- $unwind,展开运算,可以将字段值的数组元素展开,方便后面对元素进行分组。
group和sort
1 | db.weibos.aggregate([ |
这里,按照网名进行分组。用户前面的$,表示了,即使加了引号,这里也不会作为字符串去解释。也就是,不要让_id等同于user.screen_name,而是将网名相同的文档分为一组。
聚合管道
对数据的聚合,可以视为管道处理。聚合的过程都是在数据库端处理的,客户端只是接受一条最后的结果。
以上面的例子来说,首先从collection加载数据,第一阶段是进行分组聚合,然后输出结果。第二进行是排序,它的数据来源就是第一阶段输出的结果。第二阶段处理完也会输出结果。
match和project
这里的friend是指我关注的人,follower指关注我的人。
project,可以:
- 包含元文档中的字段
- 插入计算字段
- 重命名字段
- 创建自文件结构
1 | db.weibos.aggregate([ |
这里divide做除法,计算比率。输出的文件类似于
1 | { |
unwind是展开运算
1 | { |
对course展开后,会将变成
1 | { |
计算@别人最多次数的用户
1 | db.weibos.aggregate([ |
如果要查看用户微博中的所有标签,并且去除重复项,可以使用$addToSet
1 | db.weibos.aggregate([ |
而$push与$addToSet相反,它不会去除重复项。比如汇总用户的微博
1 | db.weibos.aggregate([ |
聚合管道可以添加很多个阶段,只要弄清楚每个阶段的输入和输出即可。比如上面计算@别人次数最多的用户,没有去除重复的@,下面这就用$addToSet来去除。
1 | db.weibos.aggregate([ |
索引
数据存储在大的文件中,数据的存储是没有顺序的,要查找数据,就要进行全表扫描(关系数据库),或Collection扫描(MongoDB),这非常得慢。
如果我们将数据的一个或多个关键信息有序存放,那么即使数据量很大,我们也能通过二分查找,快速地找到相关的数据。这就是索引。
与其他数据库一样,索引可以加快查找速度。索引的使用也是有代价的:
- 占用磁盘空间
- 更新数据的时候,也会花时间去更新索引
所以,只在需要查找的字段上建立索引。
索引并没有按照线性顺序存储,而是使用B-tree结构。比如使用(tags,date,username)三个字段创建的索引:
- 第一层是tags层,会首先对tags进行线性排序。
- 第二层是date层,每个tags下面对应的date,也会线性排序。
- 第三层是username层,也会线性排序。
tags | ||||||
---|---|---|---|---|---|---|
date | date | |||||
username | username | username | username | username | username |
- 如果查找时只给出了tags,那么时可以通过索引进行查找的。
- 如果查找时给出了tags和date,那么可以通过索引,查找某天某个标签的微博。
- 如果查找时给出了tags,date,username,那么可以通过索引,查找某个用户在某天的某个标签的微博。
- 如果只给出了date,那么无法使用索引,因为data处于索引的第二层。只有从第一层开始才能使用索引
建立索引
db.collection.createIndex
关于索引的说明,可以在MongoDB的文档中找到。
地理空间索引
可以是二维的点,也可以是三维的。比如要查询一个点附近的加油站或酒店,那么就可以这样查
1 | {'loc':[x,y]} |
其中(‘loc’:pymongo.GEO2D)是一个元组,pymongo.GEO2D是一个预先定一的常量,代表了x轴的方向,一般是正序还是逆序。