Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
者: Terence Parr, Prince Grover
翻譯:王雨桐
校對:詹好
本文長度約為9500字,建議閱讀10+分鐘
本文分析了決策樹可視化中的關鍵因素,比較了現有的可視化工具。并通過大量的示例介紹了一個決策樹可視化工具的設計和實現過程。
目錄
在適用于結構化數據的機器學習模型中,梯度提升和隨機森林可以稱得上是明星級模型,而決策樹則是這兩者的基石。決策樹的可視化工作對于了解這些模型的工作原理有極大的幫助。然而目前的可視化軟件工具很基礎,對新手并不友好。例如,我們無法利用現有的庫展示每個節點是如何劃分變量的。此外,當把一個新樣本放到決策樹中進行預測時,我們只能生成一張可供展示的結果圖片,而很難運用現有工具將整個過程可視化。
因此我們創建了一個通用包來在scikit-learn上可視化決策樹模型以及解釋模型。并且我們將在馬上出版的機器學習書籍《The Mechanics of Machine Learning》(由JeremyHoward編寫)中大量使用該包。以下是一個簡單的決策樹可視化示例:
附書鏈接:
https://mlbook.explained.ai/
本文演示了這項工作的成果,詳細介紹了我們為此所做的嘗試,并概述了該工具的在使用過程中的基本框架和技術細節。該可視化軟件是dtreeviz的一部分,它是一個新興的Python機器學習庫。本文不會過多闡述決策樹的基本原理,但為了方便您熟悉相關的術語使用,我們會對此做一個簡短的概述。
決策樹概述
決策樹是一種基于二叉樹(最多有左右兩個子樹)的機器學習模型。決策樹遍歷訓練數據并將信息濃縮為二叉樹的內部節點和葉節點,從而學習訓練集中的觀測值之間的關系,這些觀測值表示為特征向量x和目標值y。(注:向量為粗體,標量為斜體。)
決策樹中的每個葉子都表示特定的預測結果。回歸樹中輸出的預測是一個(連續的)值,例如價格;而分類樹中輸出的預測是(離散的)目標類別(在scikit中表示為整數),例如是否患有癌癥。決策樹將觀測分為具有相似目標值的組,每個葉子代表其中一個分組。對于回歸而言,葉節點中觀測的相似性意味著目標值之間的差異很小;而對于分類而言,則意味著大多數或所有觀測屬于同一類別。
任何一個從樹根到葉節點的路徑都要經過一系列(內部)決策節點。在訓練過程中選出特定的分割點后,每個決策節點將x中的單個要素的值(xi)與分割點值進行比較。例如,在預測房租的模型中,決策節點會比較特征,如臥室數量和浴室數量等。(請參閱第3章,新樣本預測的可視化效果。)即使在具有目標值離散的分類模型中,決策節點仍會比較數值特征值,這是因為在scitkit中,假定決策樹模型的所有特征都是數值類型,因此分類變量必須要經過獨熱編碼、合并、標簽編碼等處理。
為了得到決策節點,模型將遍歷訓練集的子集(或根節點的完整訓練集)。在訓練過程中,根據相似性最大化的原則,決策樹將根據選擇節點的特征和該特征空間內的分割點,將觀察結果放入左右兩個桶中(子集)。(該選擇過程通常要對特征和特征值進行詳盡的比較)左子集中樣本的xi特征值均小于分割點,而右子集中樣本的xi均大于分割點。通過為左分支和右分支創建決策節點,遞歸地進行樹的構造。當達到某個停止標準(例如,節點中包含的觀測數少于5)時,決策樹終止生長。
決策樹可視化的關鍵因素
決策樹可視化應該突出以下重要元素,我們將在下文中具體闡述。
效果展示
在深入研究現有的可視化工具之前,想先介紹一下我們生成的效果圖。本節重點介紹一些可視化的案例,這些是我們利用一些數據集構造的scikit回歸和分類決策樹。你還可以利用完整的庫和代碼復現所有案例。
附代碼鏈接:
https://github.com/parrt/dtreeviz/blob/master/testing/gen_samples.py
與現有可視化工具的比較
如果搜索“可視化決策樹”,很快便能找到由scikit提供的基于Python語言的解決方案:sklearn.tree.export_graphviz。此外還可以找到R甚至SAS和IBM的可視化工具。在本節中,我們收集了現有的各種決策樹可視化效果,并將它們與我們使用dtreeviz庫制作的決策樹進行了比較。在下一部分中,我們將對可視化進行更詳細的討論。
讓我們使用默認設置下的scitkit可視化工具,在大家都很熟悉的的Iris數據集上繪制一個可視化的決策樹。
scikit樹很好地表現了樹的結構,但我們仍然發現了一些問題。首先顏色的運用不夠直觀,比如在為什么有些節點是彩色的而有些不是這一問題上:如果顏色代表該分類器的預測類別,那么我們可能會認為只有葉子才是彩色的,因為只有葉子才有預測。事實證明,沒有顏色的節點預測能力較弱。
除此之外,基尼系數(確定性得分)會占用圖中的空間,并且不利于解釋。在每個節點中體現各目標類別的樣本數很有用,但直方圖可以提供更多信息。此外,利用有顏色的目標類別圖例會很好。最后,將true和false用作邊緣標簽并不夠清晰,3和<看起來更清晰。最顯著的區別是我們的決策節點利用堆疊直方圖展示特征分布,每個目標類別都會用不同的顏色顯示。同樣,我們的葉子大小與該葉子中的樣本數成正比。
再來考察一個回歸的案例。下圖是在波士頓數據集的上使用scikit的可視化效果,我們將它與dtreeviz的版本進行比較。
同樣,在scikit樹中,我們不能直觀地理解顏色的用途,但是在進一步研究后,我們發現顏色較深的圖像表示較高的預測目標值。如前文所述,在我們的解決方案中,決策節點下能夠顯示特征空間分布,比如該案例就使用了特征-目標值散點圖,在葉節點總使用帶狀圖顯示目標值分布,其中點更密集的葉節點意味著該葉節點下有更多的樣本。
同樣,我們來考察以下R語言下的可視化決策樹的軟件包,該軟件包的結果與scikit類似,但邊緣標簽的效果更好:
SAS和IBM同樣提供(均不支持Python語言)決策樹可視化。我們發現SAS的決策節點包括與該節點的樣本目標值和其他詳細信息有關的條形圖:
在這一案例中有一個很好的idea,就是通過邊緣寬度來體現子樹中的樣本量。但是由于缺少水平軸,這些條形圖的解釋性并不如意。測試類別變量的決策節點(上圖)每個類別僅具有一個條形,因此它們只能表示簡單的類別計數,而不是特征分布。對于數字特征(下圖),SAS決策節點顯示目標值或特征值的直方圖(我們無法從圖像中分辨出)。SAS節點條形圖/直方圖似乎只是在說明目標值,這并沒有告訴我們有關特征分割的信息。
下側的SAS樹似乎突出顯示了新樣本的預測過程,但我們無法從其他工具和庫中找到任何其他示例,這樣的功能似乎并不常見。
再來考察IBM軟件的情況。它在泰坦尼克數據集中表現出了非常不錯的可視化效果,甚至結合IBM的Watson分析以條形圖的形式顯示了決策節點類別的計數:
再來看看IBM較早的SPSS產品中對決策樹可視化效果:
可見,在SPSS中,這些決策節點提供了與樣本目標類別計數相同的SAS條形圖。
以上所提及的所有可視化都提供了不錯的結果,但是給予我們啟發性最大的是來自《機器學習的可視化簡介》中的案例。它給我們展示了一個以動畫形式展示的決策樹可視化案例,如下所示:
附鏈接:
r2d3.us/visual-intro-to-machine-learning-part-1/(譯者注:很經典的可視化,建議看原網站動態圖)
除了動畫的要素之外,該可視化還具有此前提到的三個獨特特征:
盡管該案例是出于教學目的而開發的基于hardcoded技術的可視化動圖,但它給我們指明了正確的方向。
我們的決策樹可視化
除了《機器學習的可視化簡介》中的動畫外,我們找不到另一個能夠更好說明如何在決策節點(特征目標空間)處分割特征值的案例了。而這一點恰恰是在決策樹模型訓練期間進行操作的關鍵點,也是新手應該關注的點,因此我們借鑒這一方案,從檢查分類樹和回歸樹的決策節點開始我們的可視化工作。
同行,我們通過訓練數據學習到決策節點選擇特征xi并在xi的值域(特征空間)進行分割,將具有相似目標值的樣本分到兩個子樹中的一個中。準確地說,訓練過程中需要檢查特征和目標值之間的關系。因此,除非我們的可視化工作在決策節點上顯示了特征-目標空間,否則讀者很難根據可視化圖像來直觀理解得到預測結果的整個過程和原因。為了突出現實決策節點是如何分割特征空間的,我們以單特征(AGE)的回歸樹和分類樹作為展示。這個案例使用波士頓房價數據中的單個特征(age)來訓練回歸決策樹。為了便于討論,在下圖中加入了節點id:
附生成效果圖的代碼:
https://github.com/parrt/dtreeviz/blob/master/testing/paper_examples.py
水平虛線表示決策節點中左子樹和右子樹的目標均值;垂直虛線表示特征空間中的分割點。下方的黑色三角形突出顯示分割點并標出了確切的分割值。葉節點用虛線指示目標預測(這里使用的是平均值)。
如圖所示,為了便于決策節點的比較,AGE特征軸都控制在了一個相同的值域上,而并沒有將數值集中的區域進行方法。因此,決策節附近的樣本的AGE值被限制在了一個狹窄的區域中。
例如,將節點0中的特征空間進一步被劃分為了為節點1和8的特征空間;節點1的特征空間又進一步劃分為節點2和5中所示的特征空間。當然,可以看到,這一決策樹模型的預測效果并不是很好,這是因為出于展示的方便,我們僅僅單一的變量來訓練模型,但是這個簡單的案例給我們演示了如何可視化決策樹劃分特征空間的過程。
盡管分類決策樹和回歸決策樹的在實現方式大致上相同,但對它們進行解釋方式卻大不相同,因此這兩種情況的可視化效果是不同的。對于回歸模型,最好使用特征與目標的散點圖來顯示特征-目標空間。但是,對于分類模型,目標是離散的類別而不是連續的數字,因此我們選擇使用直方圖來可視化特征目標空間。下圖是在USER KNOWLEDGE數據上訓練的分類樹,同樣我們只使用了單個特征(PEG)來進行訓練,并且同樣標記了節點id:
在這一案例中,直方圖顯示了PEG特征空間分布,而顏色則體現了特征與目標類別之間的關系。例如,在節點0中,我們可以看到具有very_low目標類別的樣本聚集在PEG特征空間的左端,而具有high目標類別的樣本聚集在右端。與回歸樹一樣,左子樹的特征空間和父節點上直方圖分割點左側的特征空間相同;右子樹也同理。
例如,將節點9和12的直方圖組合起來,可以得出節點8的直方圖。我們將PEG決策節點的水平軸限制在相同范圍,因此位于下方的直方圖中的范圍更窄,這也意味著分類更純凈。
為了更清楚地顯示不同類別的特征空間,我們使用了堆疊直方圖。值得注意的是,堆疊直方圖在Y軸上的高度是所有類別的樣本總數。多個類別的計數相互疊加。
當類別多于四或五個時,堆積直方圖可讀性很低,因此我們建議在這種情況下將直方圖類型參數設置為不堆積的直方圖。在基數較高的目標類別下,重疊的分布更難以可視化,并且出現問題,因此我們設置了10個目標類別的限制。使用10類Digits數據集(使用非堆疊直方圖)所構成的一個比較淺的決策樹的示例如下:
在前面的論述中,我們省略了一些最值得關注的可視化細節,在這里我們來對其中的一些關鍵要素展開分析。
在對于分類樹可視化中,我們使用了節點大小來指示每個節點包含的樣本數量。隨著節點中樣本數量的減少和葉節點直徑的變小,直方圖將按比例變短。
對于給定的特征,特征空間(水平軸)始終具有相同的寬度和相同的范圍,這更有利于比較不同節點的特征目標空間。所有直方圖的條具有相同的寬度(以像素為單位)。為了避免混亂,我們僅在水平和垂直軸上標示了最小值和最大值。
盡管在決策樹的可視化過程中一般不適用餅狀圖來進行呈現,但我們還是選擇了使用它來體現葉節點的分類。我們通過觀察圖中是否有明顯占主導的顏色,就可以評判分類結果的純度。餅圖最大的缺陷是無法體現元素間具體的關系。我們僅僅能從其中主導的顏色決定該節點的預測結果。
對于回歸樹而言,為了便于比較節點,我們將所有決策節點的目標(垂直)軸限制在相同的高度和相同的范圍。對于給定的特征,回歸要素空間(水平軸)始終是相同的寬度和范圍。將所有散點圖的透明度設置為較低水平,因此較高的目標值密度對應較深的顏色。
回歸葉節點在垂直方向上顯示相同范圍的目標空間。相比于箱狀圖,我們選擇了帶狀圖,因為帶狀圖能更好地表示分布,并隱含地以點數表示樣本數。(我們還用文字標示了葉子節點的樣本數。)關于葉節點的預測值,我們選擇了帶狀圖的質量(均值)分布中心,同時,我們使用了虛線來將其突出顯示。
除此之外,還有許多其他細節可以提高可視化圖表的效果:
通過了解拆分決策節點的特征空間的過程,我們能直觀地看到決策樹是如何得到具體的預測結果的。現在讓我們看一下如何將預測新樣本的過程可視化。此處的關鍵是可視化從根到葉節點的路上所做出的決策。
節點內的決策很簡單:如果測試向量x中的特征xi小于分割點,則歸類到左子樹,否則歸為右子樹。為了突出決策過程,我們必須重點強調比較操作。對于沿葉子預測變量節點路徑的決策節點,我們在水平特征空間中的位置xi處顯示了橙色三角形。
如果橙色三角形位于黑色三角形的左側,則沿左路徑,否則沿右路徑。預測過程中涉及的決策節點被帶有虛線框的框包圍,邊緣較粗且呈橙色。以下為兩個測試向量的示例樹:
帶有特征名稱和值的測試向量x出現在葉子預測變量節點的下方(或橫向決策樹的右端)。測試向量突出顯示了一個或多個決策節點中使用的特征。當特征數量達到閾值20(左右方向為10)時,測試向量不會顯示未使用的特征,以避免不必要的測試向量。
相比于縱向決策樹,一些用戶偏愛橫向圖像,有時樹的性質從左到右體現得更好。在預測過程中,樣本特征向量也可以從左向右延伸。以下是一些示例:
從更宏觀的角度評估決策樹,我們可以看到對分類過程的概述。這意味著比較樹的形狀和大小之類等,但更重要的是查看葉節點。我們想知道每個葉節點有多少個樣本,分類的純度如何,以及大多數樣本落在哪里。
當可視化文件太大時,很難獲得概述,因此我們提供了一個“non-fancy”選項。該選項可以生成較小的可視化文件,同時保留關鍵的葉子信息。以下案例是分別為non-fancy模式的回歸樹和分類樹:
前車之鑒
從設計的角度對這些樹可視化感興趣的人可能會發現閱讀我們嘗試過并拒絕的內容很有意思。在設計分類樹時,相比于塊狀的直方圖,我們預計核密度估計會給出更準確的圖像。我們完成了如下的案例:
問題在于只有一個或兩個樣本的決策節點中,所得到的這種分布極具誤導性:
我們還嘗試使用氣泡圖代替直方圖作為分類器決策節點:
這些可視化圖形看起來確實很酷,但比較下來,還是直方圖更易于閱讀。
關于回歸樹,我們考慮使用箱形圖顯示預測值的分布,還考慮使用簡單的條形圖顯示樣本數量:
與現在使用的條形圖相比,每片葉子的雙圖都不令人滿意。箱形圖無法像條形圖那樣顯示目標值的分布。在帶狀圖之前,我們只是使用樣本索引值作為水平軸來體現目標值:
這是一種誤導,因為水平軸通常是特征空間。我們將其壓縮為帶狀圖。
代碼示例
本節提供了波士頓回歸數據集和紅酒分類數據集的可視化示例。您還可以查看示例可視化的完整庫以及生成示例的代碼。
以下是一個代碼示例,用于加載Boston數據并訓練最大深度為3的回歸樹:
boston=load_boston()X_train=boston.datay_train=boston.targettestX=X_train[5,:]regr=tree.DecisionTreeRegressor(max_depth=3)regr=regr.fit(X_train, y_train)
可視化樹的代碼包括樹模型,訓練數據,特征和目標名以及新樣本(如果必要的話):
viz=dtreeviz(regr, X_train, y_train,target_name='price', feature_names=boston.feature_names, X=testX)viz.save("boston.svg") # suffix determines thegenerated image formatviz.view() # pop up window to display image
這是一個代碼案例,用于加載Wine數據并訓練最大深度為3的分類樹:
clf=tree.DecisionTreeClassifier(max_depth=3)wine=load_wine()clf.fit(wine.data,wine.target)
分類模型可視化與回歸模型相同,但需要目標類名稱:
viz=dtreeviz(clf, wine.data, wine.target, target_name='wine', feature_names=wine.feature_names, class_names=list(wine.target_names))viz.view()
在Jupyter notebooks中,從dtreeviz()返回的對象具有_repr_svg_()函數,Jupyter使用該函數自動顯示該對象。請參閱示例筆記本。
附鏈接:
https://github.com/parrt/dtreeviz/blob/master/notebooks/examples.ipynb
截至2018年9月,Jupyter notebooks無法正常顯示此庫生成的SVG,字體等會變得混亂:
好消息是github和JupyterLab可以正確地顯示圖像。
在Juypter notebooks中使用Image(viz.topng())的視覺效果較差;如果直接調用viz.view(),會彈出一個窗口,其中會恰當地顯示結果。
實踐經驗
我們在這個項目中遇到了很多問題,編程錯誤、參數設置、解決bug和各種庫的錯誤/限制以及如何更好地融合現有工具。唯一有趣的部分是可視化設計的無數次嘗試。期待這個可視化會對機器學習社區有很大的幫助,這也是我們堅持不懈完成項目的動力。結合stackoverflow,文檔和繁瑣的圖形編程,我們大概花了兩個月的時間完成了這個項目。
最終我們使用matplotlib生成決策和葉節點的圖像,并使用傳統的graphviz將它們組合成樹。我們還在graphviz樹的描述中廣泛使用了HTML標簽,以用于布局和字體規范。但我們遇到的最大麻煩是將所有組件組合成高質量的矢量圖形。
我們先創建了一個影子樹,它包括了scikit創建的決策樹,讓我們開始吧。
scikit-learn的分類樹和回歸決策樹是為提高效率而構建的,樹的延伸或提取節點信息并不是重點。我們創建了dtreeviz.shadow.ShadowDecTree和dtreeviz.shadow.ShadowDecTreeNode類,以便于使用所有樹信息(傳統的二叉樹)。以下是通過scikit分類樹或回歸樹模型創建影子樹的方法:
shadow_tree=ShadowDecTree(tree_model,X_train, y_train, feature_names, class_names)
影子樹/節點類具有許多方法,這些方法還可以用于需要遍歷scikit決策樹的其他庫。例如,predict()不僅可以運用樹來預測新樣本,而且還返回被訪問節點的路徑。可以通過node_samples()獲得與任意特定節點關聯的樣本。
工具箱
如果能掌握所有技巧,Graphviz和dot語言有助于設計合理的樹型布局,例如當子樹重疊時,如何隱藏圖象重疊的邊緣。如果要在同一水平上顯示兩個葉節點leaf4和leaf5,我們可以用到graphviz如下:
LSTAT3-> leaf4 [penwidth=0.3 color="#444443" label=<>]LSTAT3-> leaf5 [penwidth=0.3 color="#444443" label=<>]{rank=same;leaf4-> leaf5 [style=invis]}
我們通常在graphviz節點上使用HTML標簽,而不僅僅是文本標簽,因為它們能更好地控制文本顯示,并將表格數據顯示為實際表格。例如,當顯示沿著樹的測試向量時,使用HTML表顯示測試向量:
為了從graphviz文件生成圖像,我們使用graphvizpython軟件包,該軟件包最終是用程序例程之一(run())執行dot二進制可執行文件。有時,我們在dot命令上使用了略有不同的參數,因此我們可以更像這樣更靈活地直接調用run():
cmd=["dot", "-Tpng", "-o", filename, dotfilename]stdout,stderr=run(cmd, capture_output=True, check=True, quiet=False)
我們還將使用run()函數來執行pdf2svg(PDF轉SVG)工具,如下一節所述。
我們使用matplotlib生成決策和葉子節點,隨后生成graphviz /dot圖像和HTMLgraphviz標簽,最終通過img標簽引用生成的圖像,如下所示:
<imgsrc="/tmp/node3_94806.svg"/>
94806數字是進程ID,它有利于獨立運行同一臺計算機的多個dtreeviz實例。否則,多個進程可能會覆蓋相同的臨時文件。
因為需要可縮放的矢量圖形,我們先嘗試著導入SVG圖像,但無法通過graphviz插入這些文件(兩者都不是pdf)。隨后我們花了四個小時才發現生成和導入SVG是兩件事,需要在OS X上使用--with-librsvg進行如下操作:
$brew install graphviz --with-librsvg --with-app --with-pango
最初,當我們想從matplotlib生成PNG文件時,我們將每英寸的點數(dpi)設置為450,這樣它們在iMac這樣高分辨率屏幕上能有不錯的效果。不幸的是,這意味著我們必須使用<td>標簽的width和height參數和graphviz中的HTML表來設定整個樹的大小。這會帶來很多問題,因為我們必須了解matplotlib得到的寬高比。使用SVG文件后,我們不必再了解SVG 文件在HTML中的具體大小;在撰寫此文檔時,我們意識到沒有必要了解SVG文件的具體尺寸。
然而graphviz的SVG結果僅引用了我們導入的節點文件,而沒有將節點圖像嵌入到整個樹形圖像中。這是一種很不方便的形式,因為當發送可視化樹時,我們要發送文件的zip而不是單個文件。我們花了一些時間解析SVG XML,并將所有引用的圖像嵌入到單個大型meta-SVG文件中。有最終,得到了很好的效果。
然后我們注意到在生成SVG時,graphviz不能正確處理HTML標簽中的文本。例如,分類樹圖例的文本會被切除并重疊。
為了獲得獨立SVG文件的工作,我們首先從graphviz生成PDF文件,然后使用pdf2svg將PDF轉換為SVG(pdf2cairo也似乎起作用)。
我們注意到Jupyter notebook存在一個問題,它無法正確顯示這些SVG文件(請參見上文)。Jupyterlab確實可以像github一樣正確處理SVG。我們添加了一個topng()方法,這樣Jupyter Notebook的用戶就能使用Image(viz.topng())來獲取嵌入式圖像。還有一個跟好的方法,調用viz.view()將彈出一個窗口,也可以正確顯示圖像。
經驗總結
有時解決編程問題與算法無關,而與編程語言的限制和功能有關,例如構建一個工具和庫。決策樹可視化軟件也是這種類似的情況。編程并不難,我們是通過搭配適當的圖形工具和庫來得到最終的結果。
設計實際的可視化效果還需要進行無數次的實驗和調整。生成高質量的矢量圖還需要不斷試錯,對結果進行完善。
我們算不上可視化的狂熱者,但是對于這個特定的問題,我們一直堅持了下來,才收獲了理想的效果。在愛德華·塔夫特(Edward Tufte)的研討會上,我了解到,只要不是隨意的瞎搭配,我們就可以在人眼可以處理的限度下使用豐富的圖表呈現大量的信息。
在這個項目中,我們使用了設計面板中的許多元素:顏色,線條粗細,線條樣式,各種圖,大小(區域,長度,圖形高度,...),顏色透明度(alpha),文本樣式(顏色,字體,粗體,斜體,大小),圖形注釋和視覺流程。所有視覺元素都發揮了相應的作用。例如,我們不能僅因為某一個顏色漂亮就使用它,而是要考慮到如何使用這個顏色來突出顯示重要的維度(目標類別),因為人類能輕松且快速地發現顏色差異。節點大小的差異也應該很容易被人眼捕捉到,所以我們用節點的大小來表示葉子節點數據量的大小。
未來工作
本文檔中描述的可視化內容是dtreeviz機器學習庫的一部分,該庫還處于起步階段。我很快會將rfpimp庫移至dtreeviz。到目前為止,我們只在OS X上測試過該工具。我們期待其他平臺上的程序員提供更多執導,以便包括更豐富的安裝步驟。
我們還在考慮幾個細節的調整,例如使直方圖和分類樹底部對齊,會更利于比較節點。另外,某些三角形標簽與軸標簽重疊。最后,如果邊緣寬度和子樹中的樣本量成比例就更好了(如SAS)。
原文標題:
How to visualize decision trees
原文鏈接:
https://explained.ai/decision-tree-viz/index.html
編輯:黃繼彥
校對:林亦霖
譯者簡介
王雨桐,UIUC統計學在讀碩士,本科統計專業,目前專注于Coding技能的提升。理論到應用的轉換中,敬畏數據,持續進化。
—完—
關注清華-青島數據科學研究院官方微信公眾平臺“ THU數據派 ”及姊妹號“ 數據派THU ”獲取更多講座福利及優質內容。
者 | Erik-Jan van Baaren
譯者 | 彎月,責編 | 屠敏
以下為譯文:
元旦過完了,我們都紛紛回到了各自的工作崗位。新的一年新氣象,我想借本文為大家獻上 Python 語言的30個最佳實踐、小貼士和技巧,希望能對各位勤勞的程序員有所幫助,并希望大家工作順利!
1. Python 版本
在此想提醒各位:自2020年1月1日起,Python 官方不再支持 Python 2。本文中的很多示例只能在 Python 3 中運行。如果你仍在使用 Python 2.7,請立即升級。
2. 檢查 Python 的最低版本
你可以在代碼中檢查 Python 的版本,以確保你的用戶沒有在不兼容的版本中運行腳本。檢查方式如下:
if not sys.version_info > (2, 7):
# berate your user for running a 10 year
# python version
elif not sys.version_info >=(3, 5):
# Kindly tell your user (s)he needs to upgrade
# because you're using 3.5 features
3. IPython
IPython 本質上就是一個增強版的shell。就沖著自動補齊就值得一試,而且它的功能還不止于此,它還有很多令我愛不釋手的命令,例如:
%cd:改變當前的工作目錄
%edit:打開編輯器,并關閉編輯器后執行鍵入的代碼
%env:顯示當前環境變量
%pip install [pkgs]:無需離開交互式shell,就可以安裝軟件包
%time 和 %timeit:測量執行Python代碼的時間
完整的命令列表,請點擊此處查看(https://ipython.readthedocs.io/en/stable/interactive/magics.html)。
還有一個非常實用的功能:引用上一個命令的輸出。In 和 Out 是實際的對象。你可以通過 Out[3] 的形式使用第三個命令的輸出。
IPython 的安裝命令如下:
pip3 install ipython
4. 列表推導式
你可以利用列表推導式,避免使用循環填充列表時的繁瑣。列表推導式的基本語法如下:
[ expression for item in list if conditional ]
舉一個基本的例子:用一組有序數字填充一個列表:
mylist=[i for i in range(10)]
print(mylist)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
由于可以使用表達式,所以你也可以做一些算術運算:
squares=[x**2 for x in range(10)]
print(squares)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
甚至可以調用外部函數:
def some_function(a):
return (a + 5) / 2
my_formula=[some_function(i) for i in range(10)]
print(my_formula)
# [2, 3, 3, 4, 4, 5, 5, 6, 6, 7]
最后,你還可以使用 ‘if’ 來過濾列表。在如下示例中,我們只保留能被2整除的數字:
filtered=[i for i in range(20) if i%2==0]
print(filtered)
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
5. 檢查對象使用內存的狀況
你可以利用 sys.getsizeof 來檢查對象使用內存的狀況:
import sys
mylist=range(0, 10000)
print(sys.getsizeof(mylist))
# 48
等等,為什么這個巨大的列表僅包含48個字節?
因為這里的 range 函數返回了一個類,只不過它的行為就像一個列表。在使用內存方面,range 遠比實際的數字列表更加高效。
你可以試試看使用列表推導式創建一個范圍相同的數字列表:
import sys
myreallist=[x for x in range(0, 10000)]
print(sys.getsizeof(myreallist))
# 87632
6. 返回多個值
Python 中的函數可以返回一個以上的變量,而且還無需使用字典、列表或類。如下所示:
def get_user(id):
# fetch user from database
# ....
return name, birthdate
name, birthdate=get_user(4)
如果返回值的數量有限當然沒問題。但是,如果返回值的數量超過3個,那么你就應該將返回值放入一個(數據)類中。
7. 使用數據類
Python從版本3.7開始提供數據類。與常規類或其他方法(比如返回多個值或字典)相比,數據類有幾個明顯的優勢:
數據類的代碼量較少
你可以比較數據類,因為數據類提供了 __eq__ 方法
調試的時候,你可以輕松地輸出數據類,因為數據類還提供了 __repr__ 方法
數據類需要類型提示,因此可以減少Bug的發生幾率
數據類的示例如下:
from dataclasses import dataclass
@dataclass
class Card:
rank: str
suit: str
card=Card("Q", "hearts")
print(card==card)
# True
print(card.rank)
# 'Q'
print(card)
Card(rank='Q', suit='hearts')
詳細的使用指南請點擊這里(https://realpython.com/python-data-classes/)。
8. 交換變量
如下的小技巧很巧妙,可以為你節省多行代碼:
a=1
b=2
a, b=b, a
print (a)
# 2
print (b)
# 1
9. 合并字典(Python 3.5以上的版本)
從Python 3.5開始,合并字典的操作更加簡單了:
dict1={ 'a': 1, 'b': 2 }
dict2={ 'b': 3, 'c': 4 }
merged={ **dict1, **dict2 }
print (merged)
# {'a': 1, 'b': 3, 'c': 4}
如果 key 重復,那么第一個字典中的 key 會被覆蓋。
10. 字符串的首字母大寫
如下技巧真是一個小可愛:
mystring="10 awesome python tricks"
print(mystring.title)
'10 Awesome Python Tricks'
11. 將字符串分割成列表
你可以將字符串分割成一個字符串列表。在如下示例中,我們利用空格分割各個單詞:
mystring="The quick brown fox"
mylist=mystring.split(' ')
print(mylist)
# ['The', 'quick', 'brown', 'fox']
12. 根據字符串列表創建字符串
與上述技巧相反,我們可以根據字符串列表創建字符串,然后在各個單詞之間加入空格:
mylist=['The', 'quick', 'brown', 'fox']
mystring=" ".join(mylist)
print(mystring)
# 'The quick brown fox'
你可能會問為什么不是 mylist.join(" "),這是個好問題!
根本原因在于,函數 String.join 不僅可以聯接列表,而且還可以聯接任何可迭代對象。將其放在String中是為了避免在多個地方重復實現同一個功能。
13. 表情符
有些人非常喜歡表情符,而有些人則深惡痛絕。我在此鄭重聲明:在分析社交媒體數據時,表情符可以派上大用場。
首先,我們來安裝表情符模塊:
pip3 install emoji
安裝完成后,你可以按照如下方式使用:
import emoji
result=emoji.emojize('Python is :thumbs_up:')
print(result)
# 'Python is '
# You can also reverse this:
result=emoji.demojize('Python is ')
print(result)
# 'Python is :thumbs_up:'
更多有關表情符的示例和文檔,請點擊此處(https://pypi.org/project/emoji/)。
14. 列表切片
列表切片的基本語法如下:
a[start:stop:step]
start、stop 和 step 都是可選項。如果不指定,則會使用如下默認值:
start:0
end:字符串的結尾
step:1
示例如下:
# We can easily create a new list from
# the first two elements of a list:
first_two=[1, 2, 3, 4, 5][0:2]
print(first_two)
# [1, 2]
# And if we use a step value of 2,
# we can skip over every second number
# like this:
steps=[1, 2, 3, 4, 5][0:5:2]
print(steps)
# [1, 3, 5]
# This works on strings too. In Python,
# you can treat a string like a list of
# letters:
mystring="abcdefdn nimt"[::2]
print(mystring)
# 'aced it'
15. 反轉字符串和列表
你可以利用如上切片的方法來反轉字符串或列表。只需指定 step 為 -1,就可以反轉其中的元素:
revstring="abcdefg"[::-1]
print(revstring)
# 'gfedcba'
revarray=[1, 2, 3, 4, 5][::-1]
print(revarray)
# [5, 4, 3, 2, 1]
16. 顯示貓貓
我終于找到了一個充分的借口可以在我的文章中顯示貓貓了,哈哈!當然,你也可以利用它來顯示圖片。首先你需要安裝 Pillow,這是一個 Python 圖片庫的分支:
pip3 install Pillow
接下來,你可以將如下圖片下載到一個名叫 kittens.jpg 的文件中:
然后,你就可以通過如下 Python 代碼顯示上面的圖片:
from PIL import Image
im=Image.open("kittens.jpg")
im.show
print(im.format, im.size, im.mode)
# JPEG (1920, 1357) RGB
Pillow 還有很多顯示該圖片之外的功能。它可以分析、調整大小、過濾、增強、變形等等。完整的文檔,請點擊這里(https://pillow.readthedocs.io/en/stable/)。
17. map
Python 有一個自帶的函數叫做 map,語法如下:
map(function, something_iterable)
所以,你需要指定一個函數來執行,或者一些東西來執行。任何可迭代對象都可以。在如下示例中,我指定了一個列表:
def upper(s):
return s.upper
mylist=list(map(upper, ['sentence', 'fragment']))
print(mylist)
# ['SENTENCE', 'FRAGMENT']
# Convert a string representation of
# a number into a list of ints.
list_of_ints=list(map(int, "1234567")))
print(list_of_ints)
# [1, 2, 3, 4, 5, 6, 7]
你可以仔細看看自己的代碼,看看能不能用 map 替代某處的循環。
18. 獲取列表或字符串中的唯一元素
如果你利用函數 set 創建一個集合,就可以獲取某個列表或類似于列表的對象的唯一元素:
mylist=[1, 1, 2, 3, 4, 5, 5, 5, 6, 6]
print (set(mylist))
# {1, 2, 3, 4, 5, 6}
# And since a string can be treated like a
# list of letters, you can also get the
# unique letters from a string this way:
print (set("aaabbbcccdddeeefff"))
# {'a', 'b', 'c', 'd', 'e', 'f'}
19. 查找出現頻率最高的值
你可以通過如下方法查找出現頻率最高的值:
test=[1, 2, 3, 4, 2, 2, 3, 1, 4, 4, 4]
print(max(set(test), key=test.count))
# 4
你能看懂上述代碼嗎?想法搞明白上述代碼再往下讀。
沒看懂?我來告訴你吧:
max 會返回列表的最大值。參數 key 會接受一個參數函數來自定義排序,在本例中為 test.count。該函數會應用于迭代對象的每一項。
test.count 是 list 的內置函數。它接受一個參數,而且還會計算該參數的出現次數。因此,test.count(1) 將返回2,而 test.count(4) 將返回4。
set(test) 將返回 test 中所有的唯一值,也就是 {1, 2, 3, 4}。
因此,這一行代碼完成的操作是:首先獲取 test 所有的唯一值,即{1, 2, 3, 4};然后,max 會針對每一個值執行 list.count,并返回最大值。
這一行代碼可不是我個人的發明。
20. 創建一個進度條
你可以創建自己的進度條,聽起來很有意思。但是,更簡單的方法是使用 progress 包:
pip3 install progress
接下來,你就可以輕松地創建進度條了:
from progress.bar import Bar
bar=Bar('Processing', max=20)
for i in range(20):
# Do some work
bar.next
bar.finish
21. 在交互式shell中使用_(下劃線運算符)
你可以通過下劃線運算符獲取上一個表達式的結果,例如在 IPython 中,你可以這樣操作:
In [1]: 3 * 3
Out[1]: 9In [2]: _ + 3
Out[2]: 12
Python Shell 中也可以這樣使用。另外,在 IPython shell 中,你還可以通過 Out[n] 獲取表達式 In[n] 的值。例如,在如上示例中,Out[1] 將返回數字9。
22. 快速創建Web服務器
你可以快速啟動一個Web服務,并提供當前目錄的內容:
python3 -m http.server
當你想與同事共享某個文件,或測試某個簡單的HTML網站時,就可以考慮這個方法。
23. 多行字符串
雖然你可以用三重引號將代碼中的多行字符串括起來,但是這種做法并不理想。所有放在三重引號之間的內容都會成為字符串,包括代碼的格式,如下所示。
我更喜歡另一種方法,這種方法不僅可以將多行字符串連接在一起,而且還可以保證代碼的整潔。唯一的缺點是你需要明確指定換行符。
s1="""Multi line strings can be put
between triple quotes. It's not ideal
when formatting your code though"""
print (s1)
# Multi line strings can be put
# between triple quotes. It's not ideal
# when formatting your code though
s2=("You can also concatenate multiple\n" +
"strings this way, but you'll have to\n"
"explicitly put in the newlines")
print(s2)
# You can also concatenate multiple
# strings this way, but you'll have to
# explicitly put in the newlines
24. 條件賦值中的三元運算符
這種方法可以讓代碼更簡潔,同時又可以保證代碼的可讀性:
[on_true] if [expression] else [on_false]
示例如下:
x="Success!" if (y==2) else "Failed!"
25. 統計元素的出現次數
你可以使用集合庫中的 Counter 來獲取列表中所有唯一元素的出現次數,Counter 會返回一個字典:
from collections import Counter
mylist=[1, 1, 2, 3, 4, 5, 5, 5, 6, 6]
c=Counter(mylist)
print(c)
# Counter({1: 2, 2: 1, 3: 1, 4: 1, 5: 3, 6: 2})
# And it works on strings too:
print(Counter("aaaaabbbbbccccc"))
# Counter({'a': 5, 'b': 5, 'c': 5})
26. 比較運算符的鏈接
你可以在 Python 中將多個比較運算符鏈接到一起,如此就可以創建更易讀、更簡潔的代碼:
x=10
# Instead of:
if x > 5 and x < 15:
print("Yes")
# yes
# You can also write:
if 5 < x < 15:
print("Yes")
# Yes
27. 添加顏色
你可以通過 Colorama,設置終端的顯示顏色:
from colorama import Fore, Back, Style
print(Fore.RED + 'some red text')
print(Back.GREEN + 'and with a green background')
print(Style.DIM + 'and in dim text')
print(Style.RESET_ALL)
print('back to normal now')
28. 日期的處理
python-dateutil 模塊作為標準日期模塊的補充,提供了非常強大的擴展,你可以通過如下命令安裝:
pip3 install python-dateutil
你可以利用該庫完成很多神奇的操作。在此我只舉一個例子:模糊分析日志文件中的日期:
from dateutil.parser import parse
logline='INFO 2020-01-01T00:00:01 Happy new year, human.'
timestamp=parse(log_line, fuzzy=True)
print(timestamp)
# 2020-01-01 00:00:01
你只需記住:當遇到常規 Python 日期時間功能無法解決的問題時,就可以考慮 python-dateutil !
29.整數除法
在 Python 2 中,除法運算符(/)默認為整數除法,除非其中一個操作數是浮點數。因此,你可以這么寫:
# Python 2
5 / 2=2
5 / 2.0=2.5
在 Python 3 中,除法運算符(/)默認為浮點除法,而整數除法的運算符為 //。因此,你需要這么寫:
Python 3
5 / 2=2.5
5 // 2=2
這項變更背后的動機,請參閱 PEP-0238(https://www.python.org/dev/peps/pep-0238/)。
30. 通過chardet 來檢測字符集
你可以使用 chardet 模塊來檢測文件的字符集。在分析大量隨機文本時,這個模塊十分實用。安裝方法如下:
pip install chardet
安裝完成后,你就可以使用命令行工具 chardetect 了,使用方法如下:
chardetect somefile.txt
somefile.txt: ascii with confidence 1.0
你也可以在編程中使用該庫,完整的文檔請點擊這里(https://chardet.readthedocs.io/en/latest/usage.html)。
如上就是我為各位奉上的新年禮物,希望各位喜歡!如果你有其他的技巧、貼士和實踐,請在下方留言!
原文:https://towardsdatascience.com/30-python-best-practices-tips-and-tricks-caefb9f8c5f5
本文為 CSDN 翻譯,轉載請注明來源出處。
者 | 李秋鍵
責編 | 伍杏玲
封圖 | CSDN 付費下載自東方 IC
出品 | CSDN(ID:CSDNnews)
隨著中國工業和科技的發展,中國的一些發達城市的空氣質量問題變得越來越嚴重,其中最為嚴重的便是PM2.5帶來的惡劣環境問題。
本文在根據網絡公開空氣質量數據的基礎上進行爬取相關數據,主要針對環境較為惡劣的城市,天津、北京、廣州等幾個城市,尤其是針對天津的質量數據進行對比分析。在分析的基礎上得出空氣質量變化情況,提出一些意見。并借助機器學習算法根據數據預測空氣質量,以達到分析預測的典型大數據分析模式效果。
整體分析的流程圖如下:
實驗前的準備
我們這里所得到的數據來源于網絡公開的空氣質量數據,數據來源于“天氣后報”網站,網址為:http://www.tianqihoubao.com/aqi/tianjin.html。網址內容如下圖可見:
圖1-1 網址數據圖
整個數據的獲取使用python進行爬取。流程如下:
(1) 導入爬蟲所需要的的庫:
在air_tianjin_2019.py程序中。
其中Requests 是用Python語言編寫,基于urllib,采用 Apache2 Licensed開源協議的 HTTP 庫。它比 urllib 更加方便,可以節約我們大量的工作,完全滿足 HTTP 測試需求。
其中BeautifulSoup庫是一個靈活又方便的網頁解析庫,處理高效,支持多種解析器。利用它就不用編寫正則表達式也能方便的實現網頁信息的抓取
對應代碼如下:
import time
import requests
from bs4 import BeautifulSoup
(2)為了防止網站的反爬機制,我們設定模擬瀏覽器進行訪問獲取數據:
headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
(3)然后獲取2019年全年的空氣質量數據:
for i in range(1, 13):
time.sleep(5)
url='http://www.tianqihoubao.com/aqi/tianjin-2019' + str("%02d" % i) + '.html'
response=requests.get(url=url, headers=headers)
soup=BeautifulSoup(response.text, 'html.parser')
tr=soup.find_all('tr')
如果僅僅是從網站上得到的數據會有一些標簽等干擾項,我們針對一些標簽進行去除即可:
for j in tr[1:]:
td=j.find_all('td')
Date=td[0].get_text.strip
Quality_grade=td[1].get_text.strip
AQI=td[2].get_text.strip
AQI_rank=td[3].get_text.strip
PM=td[4].get_text
with open('air_tianjin_2019.csv', 'a+', encoding='utf-8-sig') as f:
f.write(Date + ',' + Quality_grade + ',' + AQI + ',' + AQI_rank + ',' + PM + '\n')
最終爬取下來的部分數據如下:
表1-1 部分天津爬取數據表
這幾個數據分別對應著AQI指數、當天AQI排名和PM2.5值
這里的數據分析主要通過可視化的方法得到圖像來進行分析。
(1) 天津AQI全年走勢圖
代碼在air_tianjin_2019_AQI.py中
通過導入pyecharts 庫來進行繪制走勢圖
首先通過已經獲取到的數據進行讀取:
df=pd.read_csv('air_tianjin_2019.csv', header=None, names=["Date", "Quality_grade", "AQI", "AQI_rank", "PM"])
然后獲取日期和AQI數據,儲存在列表變量中,以方便繪制圖像:
attr=df['Date']v1=df['AQI']
接著定義標題,繪制曲線并保存為網頁即可:
line=Line("2019年天津AQI全年走勢圖", title_pos='center', title_top='18', width=800, height=400)
line.add("", attr, v1, mark_line=['average'], is_fill=True, area_color="#000", area_opacity=0.3, mark_point=["max", "min"], mark_point_symbol="circle", mark_point_symbolsize=25)
line.render("2019年天津AQI全年走勢圖.html")
最終的效果圖如下可見
圖2-2 2019年天津AQI全年走勢圖
根據圖2-2可知,在2019年度,天津的空氣質量峰值分別是在1月、2月、11月和12月,即主要集中在春冬季,考慮到可能是春冬季通風較差,且節日較多,過多的節日煙花和汽車人員流動造成了空氣質量變差。
(2)天津月均AQI走勢圖
air_tianjin_2019_AQI_month.py
為了體現出每月的平均空氣質量變化,我們繪制了月均走勢圖。
首先同樣的是讀取數據:
df=pd.read_csv('air_tianjin_2019.csv', header=None, names=["Date", "Quality_grade", "AQI", "AQI_rank", "PM"])
接著獲取日期和空氣質量數據,并加以處理,去除日期中間的“-”:
dom=df[['Date', 'AQI']]
list1=
for j in dom['Date']:
time=j.split('-')[1]
list1.append(time)
df['month']=list1
接著計算每月空氣質量的平均值
month_message=df.groupby(['month'])
month_com=month_message['AQI'].agg(['mean'])
month_com.reset_index(inplace=True)
month_com_last=month_com.sort_index
attr=["{}".format(str(i) + '月') for i in range(1, 13)]
v1=np.array(month_com_last['mean'])
v1=["{}".format(int(i)) for i in v1]
然后繪制走勢圖:
line=Line("2019年天津月均AQI走勢圖", title_pos='center', title_top='18', width=800, height=400)
line.add("", attr, v1, mark_point=["max", "min"])
line.render("2019年天津月均AQI走勢圖.html")
最終的效果圖如下可見:
圖2-3 2019年天津月均AQI走勢圖
(3)天津季度AQI箱形圖
代碼在air_tianjin_2019_AQI_season.py中
繪制天津季度空氣質量箱型圖,步驟如下:
讀取爬取下來的數據:
df=pd.read_csv('air_tianjin_2019.csv', header=None, names=["Date", "Quality_grade", "AQI", "AQI_rank", "PM"])
接著按照月份分季,可以分為四個季度:
dom=df[['Date', 'AQI']]
data=[[], , , ]
dom1, dom2, dom3, dom4=data
for i, j in zip(dom['Date'], dom['AQI']):
time=i.split('-')[1]
if time in ['01', '02', '03']:
dom1.append(j)
elif time in ['04', '05', '06']:
dom2.append(j)
elif time in ['07', '08', '09']:
dom3.append(j)
else:
dom4.append(j)
然后定義箱型圖的標題,橫縱坐標等繪制箱型圖:
boxplot=Boxplot("2019年天津季度AQI箱形圖", title_pos='center', title_top='18', width=800, height=400)
x_axis=['第一季度', '第二季度', '第三季度', '第四季度']
y_axis=[dom1, dom2, dom3, dom4]
_yaxis=boxplot.prepare_data(y_axis)
boxplot.add("", x_axis, _yaxis)
boxplot.render("2019年天津季度AQI箱形圖.html")
最終得到繪制的箱型圖如下可見:
圖2-4 2019年天津季度AQI箱形圖
整體的代碼流程分為兩個部分,一部分是建立test.py程序用來將CSV文件轉為符合標準的TXT數據存儲;另一部分是K均值聚類的數據分類。
(1) 數據生成TXT
代碼在test.py中
首先讀入數據,存出入列表為x何y。同時因為y的值為漢字,需要轉換為數字:
# 文件的名字
FILENAME1="air_tianjin_2019.csv"
# 禁用科學計數法
pd.set_option('float_format', lambda x: '%.3f' % x)
np.set_printoptions(threshold=np.inf)
# 讀取數據
data=pd.read_csv(FILENAME1)
rows, clos=data.shape
# DataFrame轉化為array
DataArray=data.values
Y=
y=DataArray[:, 1]
for i in y:
if i=="良":
Y.append(0)
if i=="輕度污染":
Y.append(1)
if i=="優":
Y.append(2)
if i=="嚴重污染":
Y.append(3)
if i=="重度污染":
Y.append(4)
print(Y)
print(len(y))
X=DataArray[:, 2:5]
print(X[1])
然后將存儲的數據寫入TXT,其中要注意換行和加“,”:
for i in range(len(Y)):
f=open("data.txt","a+")
for j in range(3):
f.write(str(X[i][j])+",")
f.write(str(Y[i])+"\n")
print("data.txt數據生成")
(2)K均值聚類
代碼在KNearestNeighbor.py中。
首先是讀取數據:
def loadDataset(self,filename, split, trainingSet, testSet): # 加載數據集 split以某個值為界限分類train和test
with open(filename, 'r') as csvfile:
lines=csv.reader(csvfile) #讀取所有的行
dataset=list(lines) #轉化成列表
for x in range(len(dataset)-1):
for y in range(3):
dataset[x][y]=float(dataset[x][y])
if random.random < split: # 將所有數據加載到train和test中
trainingSet.append(dataset[x])
else:
testSet.append(dataset[x])
定義計算距離的函數
def calculateDistance(self,testdata, traindata, length): # 計算距離
distance=0 # length表示維度 數據共有幾維
for x in range(length):
distance +=pow((int(testdata[x])-traindata[x]), 2)
return math.sqrt(distance)
對每個數據文檔測量其到每個質心的距離,并把它歸到最近的質心的類。
def getNeighbors(self,trainingSet, testInstance, k): # 返回最近的k個邊距
distances=
length=len(testInstance)-1
for x in range(len(trainingSet)): #對訓練集的每一個數計算其到測試集的實際距離
dist=self.calculateDistance(testInstance, trainingSet[x], length)
print('訓練集:{}-距離:{}'.format(trainingSet[x], dist))
distances.append((trainingSet[x], dist))
distances.sort(key=operator.itemgetter(1)) # 把距離從小到大排列
print(distances)
neighbors=
for x in range(k): #排序完成后取前k個距離
neighbors.append(distances[x][0])
print(neighbors)
return neighbors
決策函數,根據少數服從多數,決定歸類到哪一類:
def getResponse(self,neighbors): # 根據少數服從多數,決定歸類到哪一類
classVotes={}
for x in range(len(neighbors)):
response=neighbors[x][-1] # 統計每一個分類的多少
if response in classVotes:
classVotes[response] +=1
else:
classVotes[response]=1
print(classVotes.items)
sortedVotes=sorted(classVotes.items, key=operator.itemgetter(1), reverse=True) #reverse按降序的方式排列
return sortedVotes[0][0]
計算模型準確度
def getAccuracy(self,testSet, predictions): # 準確率計算
correct=0
for x in range(len(testSet)):
if testSet[x][-1]==predictions[x]: #predictions是預測的和testset實際的比對
correct +=1
print('共有{}個預測正確,共有{}個測試數據'.format(correct,len(testSet)))
return (correct/float(len(testSet)))*100.0
接著整個模型的訓練,種子數定義等等:
def Run(self):
trainingSet=
testSet=
split=0.75
self.loadDataset(r'data.txt', split, trainingSet, testSet) #數據劃分
print('Train set: ' + str(len(trainingSet)))
print('Test set: ' + str(len(testSet)))
#generate predictions
predictions=
k=5 # 取最近的5個數據
# correct=
for x in range(len(testSet)): # 對所有的測試集進行測試
neighbors=self.getNeighbors(trainingSet, testSet[x], k) #找到5個最近的鄰居
result=self.getResponse(neighbors) # 找這5個鄰居歸類到哪一類
predictions.append(result)
# print('predictions: ' + repr(predictions))
# print('>predicted=' + repr(result) + ', actual=' + repr(testSet[x][-1]))
# print(correct)
accuracy=self.getAccuracy(testSet,predictions)
print('Accuracy: ' + repr(accuracy) + '%')
最終模型的準確度為90%。
圖2-10 模型運行結果圖
源碼地址:https://pan.baidu.com/s/1Vcc_bHQMHmQpe-F6A-mFdQ
提取碼:qvy7
作者簡介:李秋鍵,CSDN博客專家,CSDN達人課作者。碩士在讀于中國礦業大學,開發有taptap競賽獲獎等。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。