左手用R右手Python系列之——noSQL基礎與mongodb入門

12月的第一天,祝所有小夥伴兒的12月都能夠被溫柔以待。

能在學校悠哉寫推送的日子所剩不多了,為了珍惜剩下所剩不多的推送機會,打算12月寫一些實踐性強一些的內容,比如資料庫(包括關係型的和noSQL)。

前段時間一直在探索數據抓取的內容,那麼現在問題來了,抓完數據如何存儲呢?

保存成本地文件是一種方案,但是藉助關係型資料庫或者noSQL資料庫,我們可以給自己獲取的數據提供一個更為理想的安身之所。

今天這一篇粗淺的聊一聊非結構化數據存儲,以及R語言和Python與mongoDB之間的通訊。

寫這一篇是因為之前在寫web數據抓取的時候,涉及大量的json數據,當然我們可以直接將json轉換為R語言(dataframe/list)或者Python(dict/DataFrame)中的內置數據對象,但是保存原始數據往往也很重要,即便是list或者dict,如果不能轉化為關係型表格,通常也需要在本地保存成json格式的數據源。

那麼通過mongoDB這種專業的noSQL資料庫來保存非結構化數據,可以完成批量保存、批量讀取、條件查詢和更新,這樣可以集中維護,顯得更具有安全性、便利性、專業性。

mongo資料庫的數據對象是bson,這種數據結構相當於json標準的擴展,R語言中的list可以與json互轉,Python中的dict本身就與json高度兼容。

R語言

在R語言中,通常通過rmongodb包來進行非結構化數據存儲。(當然有替代的包,只是這個包資料相對較多一些!)

###下載:devtools::install_github("mongosoup/rmongodb")library("rmongodb")

創建/斷開連接

mongo <- mongo.create(host = "localhost")mongo.is.connected(mongo) #檢查是否連接成功mongo.destroy(mongo) #斷開連接

關於如何在系統中啟動mongodb服務,網路上有很多此類教程,照葫蘆畫瓢就好,如果你想使用一個類似MySQL的navicat那樣的可視化操作界面,可以考慮安裝Robo可視化界面,這樣基本就可以手動操作mongodb中的數據對象了。

mongodb中的數據對象,與MySQL中的數據對象略有不同,不過從層級上來看,仍然是分成資料庫 》集合(表) 》key-value.

一個資料庫中可以有很多個集合(相當於表),每一個集合中又包含很多的documents結構。每一個documents作為一條記錄,相當於SQL中的一行,而documents內是鍵值對結構,且允許包含嵌套結構。一個documents對象內嵌套的同一層級key-value對象,被稱為fileds,可以近似理解為SQL中的column。

mongodb的數據對象叫做bson,是Binary JSON Serialization的縮寫簡稱,關於詳細的json和bson的概念及其內含關係,可以查閱相關資料,或者通過W3C網站了解。

接下來進入R語言與mongodb鏈接的操作講解。

以上已經建立了一個名為mongo的鏈接(mongo.is.connected結果可以用於測試連接是否成功!)。

###查看本地資料庫文件mongo.get.databases(mongo) #查看本地資料庫名稱mongo.get.database.collections(mongo, db = "pymongo_test") #查看pymongo_test資料庫內的各個集合名稱mongo.count(mongo, ns = "pymongo_test") #查看pymongo_test資料庫內的集合數量mongo.rename(mongo, "pymongo_test.posts", "pymongo_test.post") #修改pymongo_test資料庫內posts表名稱

刪除操作

mongo.drop.database(mongo, db = "database") #移除資料庫及其內部所有集合mongo.drop(mongo, ns = "database.collection") #僅刪除資料庫內全部集合(collection)mongo.drop(mongo, ns = "rmongo_test.mydata1")#移除數據集合內的某一特定表mongo.remove(mongo, ns, criteria = mongo.bson.empty()) #移除集合內選定條件的記錄

其中ns是命名空間參數,格式為「資料庫名稱.集合名稱」。

rmongodb內沒有專門創建資料庫或者在資料庫中創建集合的函數,想要創建的話僅需在插入數據時指定一個不存在的ns參數即可。

R語言中的非結構化數據對象是list,因為list結構與json或者bson差別比較大,在插入mongo之前需要使用特定函數進行list/json與bson之間的相互轉化。

涉及轉化的函數有兩個:

mongo.bson.from.JSON #將json對象轉換為mongodb中的bson對象。mongo.bson.from.list #將list對象轉換為mongodb中的bson對象。

使用json格式數據插入mongo

#新建一個json對象json <- "{"A":1,"B":2,"C":{"D":3,"E":4}}"[1] "{"A":1,"B":2,"C":{"D":3,"E":4}}"#如果你不想手寫json,也可以使用jsonlite包中的toJSON函數(一定記得anto_unbox設置為RUE)json <- jsonlite::toJSON(list("A"=1,"B"=2,"C"=list("D"=3,"E"=4)),auto_unbox = TRUE){"A":1,"B":2,"C":{"D":3,"E":4}} #註:使用jsonlite::toJSON函數將一個list轉為一個json字元串,這個字元串擁有一個名為json的類,但是並未改變其內容,僅僅是添加了一個類,同時輸出的外觀優化了下。所以以上兩種list轉json的方法等價。#將json對象轉換為mongodb可識別的bson對象:bson <- mongo.bson.from.JSON(json) A : 16 1 B : 16 2 C : 3 D : 16 3 E : 16 4#轉化為basn後的數據結構內容未變,但是出現了樹狀層級結構。

插入mongo(注意這裡的rmongo_test.mydata是資料庫名+「.」+表名,而且資料庫名和表明都是不存在的,這樣會自動創建新資料庫及表)

mongo.get.databases(mongo)[1] "pymongo_test" mongo.insert(mongo,ns="rmongo_test.mydata",bson)[1] TRUEmongo.get.databases(mongo) [1] "pymongo_test" "rmongo_test"

使用list結構插入mongodb與使用json格式步驟差不多,不同的是要使用list轉bson的轉化函數。

list <- list(a=2, b=3, c=list(d=4, e=5))bson <- mongo.bson.from.list(list)mongo.insert(mongo,"rmongo_test.mydata",bson) #使用之前的資料庫+表名會將本次插入的記錄添加到mydata已經存在的記錄後面mongo.insert(mongo,"rmongo_test.mydata1",bson) #換一個表名則會在rmongo_test資料庫中新建一個表mongo.drop(mongo, ns = "rmongo_test.mydata1") #移除數據集合內的某一特定表(刪掉剛才新插的mydata1)

數據查詢

查詢其中一條記錄(第一條),使用mongo.find.one函數。

tmp <- mongo.find.one(mongo, ns = "rmongo_test.mydata") _id : 7 5a21346e5da941b6eb611cb7 A : 16 1 B : 16 2 C : 3 D : 16 3 E : 16 4

tmp對象是一個bson結構,需要轉化為list才能得到內置數據結構。

tmp <- mongo.bson.to.list(tmp)$`_id`{ $oid : "5a21346e5da941b6eb611cb7" }$A[1] 1$B[1] 2$C$C$D[1] 3$C$E[1] 4

查詢表中所有記錄要使用mongo.find.all函數。

find_all <- mongo.find.all(mongo, ns = "pymongo_test.post")#find_all直接是將post內的bson對象轉化為一個list,很奇怪,#為啥mongo.find.one輸出的是一個bson,需要使用函數轉為list,不是很理解設計的原因。

mongo.find函數可以支持條件查詢:

#創建索引條件:buf <- mongo.bson.buffer.create()mongo.bson.buffer.append(buf, name="gender", value="male")query <- mongo.bson.from.buffer(buf)構造查詢:cursor <- mongo.find(mongo,"pymongo_test.post",query)[1] "mongo.cursor"cursor對象類似SQL中的一個游標對象,不能直接查看內部結構,需要藉助迭代函數進行輸出while (mongo.cursor.next(cursor)) #判斷是否還有剩餘迭代次數 print(mongo.cursor.value(cursor)) #列印當前迭代記錄 mongo.cursor.destroy(cursor) #關閉查詢游標 _id : 7 5a21238873a36810a8c4896a id : 2 20170101 name : 2 Jordan age : 16 20 gender : 2 male _id : 7 5a2123d173a36810a8c4896b id : 2 20170101 name : 2 Jordan age : 16 20 gender : 2 male _id : 7 5a2123d173a36810a8c4896c id : 2 20170202 name : 2 Mike age : 16 21 gender : 2 male

所以也可以把cursor當成是一個迭代器,想要提取裡面的查詢數據,需要構造循環與迭代函數,自行提取,而mongo.find.one函數和mongo.find.all函數相當於兩個快捷函數,直接提取符合條件的記錄或者所有記錄。

rmongosb的mongo.find函數可以支持mongodb原生的複雜查詢,支持很多高級符號函數,這一點兒我暫未深入了解,留待以後再做探討。如果你想要詳細的了解mongodb的用法, 最好參考關於mongodb的專業操作書,rmongodb內的函數與mongodb的原生函數相比,還有很多地方不完善,無法支持,不過對於平時的數據存儲而言最夠了,用的最頻繁的就是插入、讀取操作了。

Python:

from pymongo import MongoClient,ASCENDING, DESCENDINGimport pymongo,json

之前說到過,因為Python中的dict與json高度兼容(並不代表一模一樣),而bson結構又是基於json的擴展,所以在Python中可以直接將dict插入mongodb資料庫,而基本無需做類型轉換,這一點兒Python完勝R語言。

#連接MongDB:client = MongoClient() client = MongoClient(host="localhost",port= 27017)client = MongoClient("mongodb://localhost:27017")

以上三種連接方法等價。

#連接資料庫:db = client.pymongo_testdb = client["pymongo_test"]

以上兩句等價,用於連接資料庫,與Python中訪問屬性的操作相同。

#指定集合(相當於SQL中的table)collection = db.postcollection = db["post"]

以上兩句等價,db的基礎上連接mongodb中的集合(相當於表)。

使用本地的json數據,創建一個帶插入的臨時dict結構:

mydata = json.load(open("D:/R/File/indy.json")) mydata = mydata["indy movies"]mydata1 = mydata[1];mydata1mydata2 = mydata[-2:];mydata2

為了防止數據混亂,現將之前在R語言中添加的表記錄刪除:

collection.remove({})collection.insert_one(mydata1)results = collection.find_one(){"_id": ObjectId("5a2143c573a36810a8c4896f"), "academy_award_ve": True, "actors": {"Indiana Jones": "Harrison Ford", "Mola Ram": "Amish Puri"}, "budget": 28170000, "name": "Indiana Jones and the Temple of Doom", "producers": ["Robert Watts"], "year": 1984}

當然也可以一次插入多條記錄,不過將記錄構造成一個列表即可。

type(mydata2)listcollection.remove({})collection.insert_many(mydata2)for item in collection.find(): print(item){"_id": ObjectId("5a2143c573a36810a8c4896f"), "name": "Indiana Jones and the Temple of Doom", "year": 1984, "actors": {"Indiana Jones": "Harrison Ford", "Mola Ram": "Amish Puri"}, "producers": ["Robert Watts"], "budget": 28170000, "academy_award_ve": True}{"_id": ObjectId("5a21451673a36810a8c48970"), "name": "Indiana Jones and the Last Crusade", "year": 1989, "actors": {"Indiana Jones": "Harrison Ford", "Walter Donovan": "Julian Glover"}, "producers": ["Robert Watts", "George Lucas"], "budget": 48000000, "academy_award_ve": False}

查詢函數可以直接提供給for循環進行記錄的遍歷。

mangodb不允許插入重複記錄,還有一些保留字元要注意。(比如英文句點「.」)

查詢則提供了更為豐富的函數及可選參數。

#查詢一條記錄:results = collection.find_one({"budget": 28170000}){"_id": ObjectId("5a2143c573a36810a8c4896f"), "academy_award_ve": True, "actors": {"Indiana Jones": "Harrison Ford", "Mola Ram": "Amish Puri"}, "budget": 28170000, "name": "Indiana Jones and the Temple of Doom", "producers": ["Robert Watts"], "year": 1984}

條件查詢:

results = collection.find({"year": 1984})for result in results: print(result){"_id": ObjectId("5a2143c573a36810a8c4896f"), "name": "Indiana Jones and the Temple of Doom", "year": 1984, "actors": {"Indiana Jones": "Harrison Ford", "Mola Ram": "Amish Puri"}, "producers": ["Robert Watts"], "budget": 28170000, "academy_award_ve": True}

查詢條件支持符號函數以及正則表達式:

results = collection.find({"budget": {"$gt":30000000}})#budget大於30000000的記錄for result in results: print(result){"_id": ObjectId("5a21451673a36810a8c48970"), "name": "Indiana Jones and the Last Crusade", "year": 1989, "actors": {"Indiana Jones": "Harrison Ford", "Walter Donovan": "Julian Glover"}, "producers": ["Robert Watts", "George Lucas"], "budget": 48000000, "academy_award_ve": False}

布爾條件查詢:

results = collection.find({"academy_award_ve": True})for result in results: print(result){"_id": ObjectId("5a2143c573a36810a8c4896f"), "name": "Indiana Jones and the Temple of Doom", "year": 1984, "actors": {"Indiana Jones": "Harrison Ford", "Mola Ram": "Amish Puri"}, "producers": ["Robert Watts"], "budget": 28170000, "academy_award_ve": True}

正則表達式查詢:

results = collection.find({"name": {"$regex": "Doom$"}})for result in results: print(result){"_id": ObjectId("5a2143c573a36810a8c4896f"), "name": "Indiana Jones and the Temple of Doom", "year": 1984, "actors": {"Indiana Jones": "Harrison Ford", "Mola Ram": "Amish Puri"}, "producers": ["Robert Watts"], "budget": 28170000, "academy_award_ve": True}

更新操作:

student = collection.find_one({"year":1984}){"_id": ObjectId("5a2143c573a36810a8c4896f"), "academy_award_ve": True, "actors": {"Indiana Jones": "Harrison Ford", "Mola Ram": "Amish Puri"}, "budget": 28170000, "name": "Indiana Jones and the Temple of Doom", "producers": ["Robert Watts"], "year": 1984}collection.update_one({"year": 1984}, {"$set": {"name": "Indiana Jones and Doom"}}){"_id": ObjectId("5a2143c573a36810a8c4896f"), "academy_award_ve": True, "actors": {"Indiana Jones": "Harrison Ford", "Mola Ram": "Amish Puri"}, "budget": 28170000, "name": "Indiana Jones and Doom", "producers": ["Robert Watts"], "year": 1984}

刪除操作:

result = collection.delete_one({"name": "Indiana Jones and Doom"})data1 = collection.find()for result in data1: print(result){"_id": ObjectId("5a21451673a36810a8c48970"), "name": "Indiana Jones and the Last Crusade", "year": 1989, "actors": {"Indiana Jones": "Harrison Ford", "Walter Donovan": "Julian Glover"}, "producers": ["Robert Watts", "George Lucas"], "budget": 48000000, "academy_award_ve": False}

刪除之後只剩一個記錄了。

Python支持的符號運算符還有很多!

符號含義示例

{"age": {"$lt": 20}} #$lt小於{"age": {"$gt": 20}} #$gt大於{"age": {"$lte": 20}} #$lte小於等於{"age": {"$gte": 20}} #$gte大於等於{"age": {"$ne": 20}} #$ne不等於{"age": {"$in": [20, 23]}} #$in在範圍內{"age": {"$nin": [20, 23]}} #$nin不在範圍內

正則表達式含義:

{"name": {"$regex": "^M.*"}} #$regex,name以M開頭{"name": {"$exists": True}} #$exists,name屬性存在{"age": {"$type": "int"}} #$type,age的類型為int{"age": {"$mod": [5,0]}} #$mod數字模操作,年齡模5餘0{"$text": {"$search": "Mike"}} #$text文本查詢,text類型的屬性中包含Mike字元串{"$where": "obj.fans_count == obj.follows_count"}#$where高級條件查詢,自身粉絲數等於關注數

這些運算符號以及正則表達式可以用在查詢、更新、刪除等所有操作上。

最後吐槽一句,R語言的rmongodb包的查詢函數實在是太麻煩了,很難用,Pymongo的函數設計就很友好。

以上便是R語言、Python與mongodb資料庫通訊的基礎操作,如果想要了解更為詳細的高階查詢操作,可以參考關於mongodb的專業技術書籍及資料。

參考資料:

docs.mongodb.com/manual

api.mongodb.com/python/

api.mongodb.com/python/

在線課程請點擊文末原文鏈接:

Hellobi Live | R語言可視化在商務場景中的應用

往期案例數據請移步本人GitHub:

github.com/ljtyduyu/Dat

推薦閱讀:

OpenCV:圖片操作基本知識
Python 機器學習之 SVM 預測買賣(標的物:比特幣)
Python魔法方法指南
【深度技術】小試牛刀:使用Python模擬登錄知乎
認準目標,前進

TAG:R编程语言 | Python | mongo |