摘要:采用 SQL 作為數據查詢和分析的入口是一種數據全棧的思路。
本文分享自華為雲社區《如何使用 SQL 對數據進行分析?》,作者:zuozewei 。
前言
我們通過 OLTP(聯機事務處理)系統實時處理用戶數據,還需要在 OLAP(聯機分析處理)系統中對它們進行分析,今天我們來看下如何使用 SQL 分析數據。
使用 SQL 進行數據分析的幾種方式
在 DBMS(數據庫管理系統) 中,有些數據庫很好地集成了 BI 工具,可以方便我們對收集的數據進行商業分析。
比如在SQL Server 中提供了 BI 分析工具,我們可以通過使用 SQL Server中的 Analysis Services 完成數據挖掘任務。SQL Server 內置了多種數據挖掘算法,比如常用的 EM、K-Means 聚類算法、決策樹、朴素貝葉斯和邏輯回歸等分類算法,以及神經網絡等模型。我們還可以對這些算法模型進行可視化效果呈現,幫我們優化和評估算法模型的好壞。
圖片來源::https://docs.microsoft.com/en-us/analysis-services/analysis-services-features-supported-by-the-editions-of-sql-server-2016
另外 PostgreSQL 是一個免費開源的關系數據庫(ORDBMS),它的穩定性非常強,功能強大,在 OLTP 和 OLAP 系統上表現都非常出色。同時在機器學習上,配合 Madlib 項目可以讓 PostgreSQL 如虎添翼。Madlib 包括了多種機器學習算法,比如分類、聚類、文本分析、回歸分析、關聯規則挖掘和驗證分析等功能。這樣我們可以通過使用 SQL,在 PostgreSQL 中使用各種機器學習算法模型,幫我們進行數據挖掘和分析。
圖片來源:https://cwiki.apache.org/confluence/display/MADLIB/Architecture
2018 年 Google 將機器學習(Machine Learning)工具集成到了 BigQuery 中,發布了 BigQuery ML,這樣開發者就可以在大型的結構化或半結構化的數據集上構建和使用機器學習模型。通過 BigQuery 控制台,開發者可以像使用 SQL 語句一樣來完成機器學習模型的訓練和預測。
SQLFlow 是螞蟻金服於 2019 年開源的機器學習工具,我們可以通過使用 SQL 就可以完成機器學習算法的調用,你可以將 SQLFlow 理解為機器學習的翻譯器。我們在 SELECT 之后加上 TRAIN 從句就可以完成機器學習模型的訓練,在 SELECT 語句之后加上 PREDICT 就可以使用模型來進行預測。這些算法模型既包括了傳統的機器學習模型,也包括了基於 Tensorflow、PyTorch 等框架的深度學習模型。
從上圖中你能看出 SQLFlow 的使用過程,首先我們可以通過 Jupyter notebook 來完成 SQL 語句的交互。SQLFlow 支持了多種 SQL 引擎,包括 MySQL、Oracle、Hive、SparkSQL 和 Flink 等,這樣我們就可以通過 SQL 語句從這些 DBMS 數據庫中抽取數據,然后選擇想要進行的機器學習算法(包括傳統機器學習和深度學習模型)進行訓練和預測。不過這個工具剛剛上線,工具、文檔、社區還有很多需要完善的地方。
最后一個最常用方法是 SQL+Python,也是我們今天要重點講解的內容。上面介紹的工具可以說既是 SQL 查詢數據的入口,也是數據分析、機器學習的入口。不過這些模塊耦合度高,也可能存在使用的問題。一方面工具會很大,比如在安裝 SQLFlow 的時候,采用 Docker 方式進行安裝,整體需要下載的文件會超過 2G。同時,在進行算法調參、優化的時候也存在靈活度差的情況。因此最直接的方式,還是將 SQL 與數據分析模塊分開,采用 SQL 讀取數據,然后通過 Python 來進行數據分析的處理。
案例:挖掘購物數據中的頻繁項集與關聯規則
下面我們通過一個案例來進行具體的講解。
我們要分析的是購物問題,采用的技術為關聯分析。它可以幫我們在大量的數據集中找到商品之間的關聯關系,從而挖掘出經常被人們購買的商品組合,一個經典的例子就是“啤酒和尿布”的例子。
今天我們的數據集來自於一個購物樣本數據,字段包括了 trans_id(交易 ID)以及 product(商品名稱),具體的數據集參考下面的初始化 sql:
DROP TABLE IF EXISTS test_data; CREATE TABLE test_data ( trans_id INT, product TEXT ); INSERT INTO test_data VALUES (1, 'beer'); INSERT INTO test_data VALUES (1, 'diapers'); INSERT INTO test_data VALUES (1, 'chips'); INSERT INTO test_data VALUES (2, 'beer'); INSERT INTO test_data VALUES (2, 'diapers'); INSERT INTO test_data VALUES (3, 'beer'); INSERT INTO test_data VALUES (3, 'diapers'); INSERT INTO test_data VALUES (4, 'beer'); INSERT INTO test_data VALUES (4, 'chips'); INSERT INTO test_data VALUES (5, 'beer'); INSERT INTO test_data VALUES (6, 'beer'); INSERT INTO test_data VALUES (6, 'diapers'); INSERT INTO test_data VALUES (6, 'chips'); INSERT INTO test_data VALUES (7, 'beer'); INSERT INTO test_data VALUES (7, 'diapers');
這里我們采用的關聯分析算法是 Apriori 算法,它幫我們查找頻繁項集,首先我們需要先明白什么是頻繁項集。
頻繁項集就是支持度大於等於最小支持度閾值的項集,小於這個最小值支持度的項目就是非頻繁項集,而大於等於最小支持度的項集就是頻繁項集。支持度是個百分比,指的是某個商品組合出現的次數與總次數之間的比例。支持度越高,代表這個組合出現的頻率越大。
我們再來看下 Apriori 算法的基本原理。
Apriori 算法其實就是查找頻繁項集 (frequent itemset) 的過程:
0.設置一個最小支持度,
1.從K=1開始,篩選頻繁項集。
2.在結果中,組合K+1項集,再次篩選
3.循環1、2步。直到找不到結果為止,K-1項集的結果就是最終結果。
我們來看下數據理解一下,下面是所有的訂單,以及每筆訂單購買的商品:
在這個例子中,“啤酒”出現了 7 次,那么這 7 筆訂單中“牛奶”的支持度就是 7/7=1。同樣“啤酒 + 尿布”出現了 5 次,那么這 7 筆訂單中的支持度就是 5/7=0.71。
同時,我們還需要理解一個概念叫做“置信度”,它表示的是當你購買了商品 A,會有多大的概率購買商品 B,在這個例子中,置信度(啤酒→尿布)=5/7=0.71,代表如果你購買了啤酒,會有 71% 的概率會購買尿布;置信度(啤酒→薯條)=3/7=0.43,代表如果你購買了啤酒,有 43% 的概率會購買薯條。
所以說置信度是個條件概念,指的是在 A 發生的情況下,B 發生的概率是多少。
我們在計算關聯關系的時候,往往需要規定最小支持度和最小置信度,這樣才可以尋找大於等於最小支持度的頻繁項集,以及在頻繁項集的基礎上,大於等於最小置信度的關聯規則。
使用 MADlib+PostgreSQL 完成購物數據的關聯分析
針對上面的購物數據關聯分析的案例我們可以使用工具自帶的關聯規則進行分析,下面我們演示使用 PostgreSQL 數據庫在 Madlib 工具中都可以找到相應的關聯規則,通過寫 SQL 的方式就可以完成關聯規則的調用分析。
開發環境
- Windows/MacOS
- Navicat Premium 11.2.7及以上
服務器環境
- Centos 7.6
- Docker
- PostgreSQL 9.6
- MADlib 1.4及以上
使用 Docker 安裝 MADlib+PostgreSQL
拉取 docker 鏡像(這個鏡像提供了需要的 postgres 等環境,並沒有安裝 madlib) :
docker pull madlib/postgres_9.6:latest
下載 MADlib github 源碼. 假定下載的源碼位置為 /home/git-repo/github/madlib:
cd /home/git-repo/github && git clone git@github.com:apache/madlib.git
啟動容器,並建立本機目錄與容器中系統的路徑映射,共享的目錄在容器和本機之間是讀寫共享的。
docker run -d -it --name madlib -v /home/git-repo/github/madlib:/incubator-madlib/ madlib/postgres_9.6
啟動容器后,連接容器編譯 MADlib 組件,編譯用時約 30 分鍾:
docker exec -it madlib bash mkdir /incubator-madlib/build-docker cd /incubator-madlib/build-docker cmake .. make make doc make install
在容器中安裝 MADlib:
src/bin/madpack -p postgres -c postgres/postgres@localhost:5432/postgres install
運行 MADlib 測試:
# Run install check, on all modules: src/bin/madpack -p postgres -c postgres/postgres@localhost:5432/postgres install-check # Run install check, on a specific module, say svm: src/bin/madpack -p postgres -c postgres/postgres@localhost:5432/postgres install-check -t svm # Run dev check, on all modules (more comprehensive than install check): src/bin/madpack -p postgres -c postgres/postgres@localhost:5432/postgres dev-check # Run dev check, on a specific module, say svm: src/bin/madpack -p postgres -c postgres/postgres@localhost:5432/postgres dev-check -t svm # 如果需要,重新安裝 Reinstall MADlib: src/bin/madpack -p postgres -c postgres/postgres@localhost:5432/postgres reinstall
如果需要,先關掉並刪除容器,刪完再起新容器需要重新安裝:
docker kill madlib
docker rm madlib
用配置好的容器制作新鏡像,先查看容器 ID, 在用容器 ID 創建新鏡像:
docker ps -a
docker commit <container id> my/madlib_pg9.6_dev
用新鏡像創建新容器:
docker run -d -it -p 5432:5432 --name madlib_dev -v /home/my/git-repo/github/madlib:/incubator-madlib/ madlib/postgres_9.6
連接容器進行交互(發現新容器還是沒有安裝,但是不用編譯了,安裝也很快,裝完測試一下)
docker exec -it madlib_dev bash cd /incubator-madlib/build-docker src/bin/madpack -p postgres -c postgres/postgres@localhost:5432/postgres install src/bin/madpack -p postgres -c postgres/postgres@localhost:5432/postgres install-check
使用 Navicat 遠程連接 PostgreSQL(假定沒有修改登錄用戶和密碼,默認沒有密碼)
最后,新建表並初始化數據:
使用 SQL 完成關聯規則的調用分析
最后使用 SQL + MADlib 進行關聯分析,這里我們設定了參數最小支持度為 0.25,最小置信度為 0.5。根據條件生成 transactions 中的關聯規則,如下所示:
SELECT * FROM madlib.assoc_rules( .25, -- 支持度 .5, -- 置信度 'trans_id', -- Transaction id 字段 'product', -- Product 字段 'test_data', -- 輸入數據 NULL, -- 輸出模式 TRUE -- 詳細輸出 );
查詢結果:
關聯規則存儲在 assoc_rules 表中:
SELECT * FROM assoc_rules
ORDER BY support DESC, confidence DESC;
注意:
關聯規則會始終創建一個名為的表 assoc_rules。如果要保留多個關聯規則表,請在再次運行之前復制該表。
使用 SQL+Python 完成購物數據的關聯分析
除此以外,我們還可以直接使用 SQL 完成數據的查詢,然后通過 Python 的機器學習工具包完成關聯分析。
開發環境
- Windows/MacOS
- Navicat Premium 11.2.7及以上
- Python 3.6
服務器環境
- Centos 7.6
- Docker
- MySQL 5.7
使用 Docker 安裝 MySQL
拉取官方鏡像(我們這里選擇5.7,如果不寫后面的版本號則會自動拉取最新版):
docker pull mysql:5.7
檢查是否拉取成功:
docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/mysql 5.7 db39680b63ac 2 days ago 437 MB
啟動容器:
docker run -p 3306:3306 --name mymysql -v $PWD/conf:/etc/mysql/conf.d -v $PWD/logs:/logs -v $PWD/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
- –name:容器名,此處命名為 mymysql;
- -e:配置信息,此處配置 mysql 的 root 用戶的登陸密碼;
- -p:端口映射,此處映射 主機 3306 端口到容器的 3306 端口;
- -d:源鏡像名,此處為 mysql:5.7;
- -v:主機和容器的目錄映射關系,":"前為主機目錄,之后為容器目錄。
檢查容器是否正常運行:
[root@VM_0_10_centos ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d1e682cfdf76 mysql:5.7 "docker-entrypoint..." 14 seconds ago Up 13 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp mymysql
可以看到容器 ID、容器的源鏡像、啟動命令、創建時間、狀態、端口映射信息、容器名字。
進入 docker 本地連接 MySQL 客戶端:
sudo docker exec -it mymysql bash
mysql -u root -p
設置遠程訪問賬號,並授權遠程連接:
CREATE USER 'zuozewei'@'%' IDENTIFIED WITH mysql_native_password BY 'zuozewei'; GRANT ALL PRIVILEGES ON *.* TO 'zuozewei'@'%';
使用 Navicat 遠程連接 MySQL,新建數據庫並初始化數據。
編寫 Python 腳本完成數據分析
首先我們通過 SQLAlchemy 來完成 SQL 查詢,使用 efficient_apriori 工具包的 Apriori 算法。
整個工程一共包括 3 個部分:
- 第一個部分為數據加載,首先我們通過 sql.create_engine 創建 SQL 連接,然后從數據集表中讀取全部的數據加載到 data 中。這里需要配置 MySQL 賬戶名和密碼;
- 第二步為數據預處理。我們還需要得到一個 transactions 數組,里面包括了每筆訂單的信息,其中每筆訂單是以集合的形式進行存儲的,這樣相同的訂單中 item 就不存在重復的情況,同時也可以使用 Apriori 工具包直接進行計算;
- 最后一步,使用 Apriori 工具包進行關聯分析,這里我們設定了參數 min_support=0.25,min_confidence=0.5,也就是最小支持度為 0.25,最小置信度為 0.5。根據條件找出 transactions 中的頻繁項集 itemsets 和關聯規則 rules。
下載依賴庫:
#pip3 install 包名 -i 源的url 臨時換源 #清華大學源:https://pypi.tuna.tsinghua.edu.cn/simple/ # 強大的數據結構庫,用於數據分析,時間序列和統計等 pip3 install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple/ # python的orm程序 pip3 install SQLAlchemy -i https://pypi.tuna.tsinghua.edu.cn/simple/ # Apriori算法的高效純Python實現 pip3 install efficient-apriori -i https://pypi.tuna.tsinghua.edu.cn/simple/ # MySQL驅動 pip3 install mysql-connector -i https://pypi.tuna.tsinghua.edu.cn/simple/
具體的代碼如下:
from efficient_apriori import apriori import sqlalchemy as sql import pandas as pd ''' 數據加載 ''' # 創建數據庫連接 engine = sql.create_engine('mysql+mysqlconnector://zuozewei:zuozewei@server_ip/SQLApriori') # 查詢數據 query = 'SELECT * FROM test_data' # 加載到 data 中 data = pd.read_sql_query(query, engine) ''' 數據預處理 ''' # 得到一維數組 orders_series,並且將 Transaction 作為 index, value 為 Item 取值 orders_series = data.set_index('trans_id')['product'] # 將數據集進行格式轉換 transactions = [] temp_index = 0 for i, v in orders_series.items(): if i != temp_index: temp_set = set() temp_index = i temp_set.add(v) transactions.append(temp_set) else: temp_set.add(v) ''' 數據分析 ''' # 挖掘頻繁項集和頻繁規則 itemsets, rules = apriori(transactions, min_support=0.25, min_confidence=0.5) print('頻繁項集:', itemsets) print('關聯規則:', rules)
運行結果:
頻繁項集: { 1: {('beer',): 7, ('chips',): 3, ('diapers',): 5}, 2: {('beer', 'chips'): 3, ('beer', 'diapers'): 5, ('chips', 'diapers'): 2}, 3: {('beer', 'chips', 'diapers'): 2} } 關聯規則: [ {chips} -> {beer}, {diapers} -> {beer}, {beer} -> {diapers}, {chips} -> {diapers}, {chips, diapers} -> {beer}, {beer, chips} -> {diapers}, {chips} -> {beer, diapers} ]
從結果中我們能看到購物組合中:
- 商品個數為 1 的頻繁項集有 3 種,分別為 beer(啤酒)、chips(薯條)、diapers(尿布) 等;
- 商品個數為 2 的頻繁項集有 3 種,包括{beer(啤酒), chips(薯條)},{beer(啤酒), diapers(尿布)},{chips(薯條), diapers(尿布)}等;
- 其中關聯規則有 7 種,包括了購買 chips(薯條) 的人也會購買 beer(啤酒),購買 diapers(尿布)的同時也會 beer(啤酒) 等。
總結
通過 SQL 完成數據分析、機器學習還是推薦使用到 Python,因為這是 Python 所擅長的。通過今天的例子我們應該能看到采用 SQL 作為數據查詢和分析的入口是一種數據全棧的思路,對於數據開發人員來說降低了數據分析的技術門檻。相信在當今的 DT 時代,我們的業務增長會越來越依靠於 SQL 引擎 + AI 引擎。
參考文獻:
- [1]:http://madlib.apache.org/docs/latest/group__grp__assoc__rules.html
- [2]:https://sql-machine-learning.github.io/
- [3]:https://www.jianshu.com/p/8e1e64c08cb7
- [4]:《數據分析實戰45講》陳暘 清華大學計算機博士