實習中同事分給我的一個需求:把sklearn中的kmeans算法封裝起來,使用界面可視化,提供給不會改代碼的領導使用
對pyqt5一竅不通的我經過幾天的摸索終於完成任務!參考的內容大多來自於這個帖子:
https://mp.weixin.qq.com/s/Wy1iTYoX7_O81ChMflXXfg 感謝!
首先展示一下封裝-可視化後的最終效果:
如上圖,我在使用界面暴露了聚類個數、最大迭代次數、初始化質心次數
這幾個參數並設置了默認值,以供使用者自行修改
點擊【開始】後執行kmeans算法過程,執行成功之後將結果輸出爲聚類結果.csv和聚類中心.csv
在這個過程中,使用者不需要去代碼中修改數據集位置和kmeans算法運行的參數,只需要在可視化界面裏面點點點就可以了,下面貼出代碼。
首先是可視化頁面的代碼,首先是界面主體
#界面主體
self.resize(600, 400)
self.setWindowTitle('Kmeans聚類')
插入文件按鈕的寫法
#選擇文件部分設置三個對象:標籤1、標籤1_、按鈕1
#標籤1顯示“文件位置4個字”
self.lb1 = QLabel("文件位置:", self)
self.lb1.move(20, 40)
#標籤1_用來儲存文件路徑並顯示
self.lb1_ = QLabel(self)
self.lb1_.setGeometry(110, 40, 300, 20)
#按鈕1用來連接打開文件函數、選擇文件位置
self.bt1 = QPushButton('選擇文件位置', self)
self.bt1.move(400, 35)
self.bt1.clicked.connect(self.openfile)#連接到打開文件函數
這個按鈕對應的打開文件函數
#解析上傳文件
def openfile(self):
inputfile = QFileDialog.getOpenFileName(self, '打開文件','./')
path=inputfile[0]
self.lb1_.setText(path)
#print(str(path))
#return path
我不會截動態圖,效果不方便展示,讀者可自行復制上述代碼運行觀察效果。
其他的幾個修改參數按鈕的設計規則和上個按鈕一樣,都是兩個標籤、一個按鈕對應一個函數
整個界面主體的函數
def initUI(self):
# 界面主體
self.resize(600, 400)
self.setWindowTitle('Kmeans聚類')
#選擇文件部分設置三個對象:標籤1、標籤1_、按鈕1
self.lb1 = QLabel("文件位置:", self)
self.lb1.move(20, 40)
self.lb1_ = QLabel(self)
self.lb1_.setGeometry(110, 40, 300, 20)
self.bt1 = QPushButton('選擇文件位置', self)
self.bt1.move(400, 35)
self.bt1.clicked.connect(self.openfile)#連接到打開文件函數
# 選擇聚類個數設置三個對象:標籤2、標籤2_、按鈕2
self.lb2 = QLabel("聚類個數:", self)
self.lb2.move(20, 80)
self.lb2_ = QLabel("8",self)
self.lb2_.setGeometry(110, 80, 180, 20)
self.bt2 = QPushButton('選擇聚類個數', self)
self.bt2.move(400, 75)
self.bt2.clicked.connect(self.choice_clusters) # 連接到選擇聚類中心函數
# 選擇最大迭代次數,設置三個對象:標籤3、標籤3_、按鈕3
self.lb3= QLabel("最大迭代次數:", self)
self.lb3.move(20, 120)
self.lb3_ = QLabel("300",self)
self.lb3_.setGeometry(140, 120, 180, 20)
self.bt3 = QPushButton('修改最大迭代次數', self)
self.bt3.move(400, 115)
self.bt3.clicked.connect(self.choice_max_iter) # 連接到選擇最大迭代次數函數
# 選擇初始化質心次數,設置三個對象:標籤4、標籤4_、按鈕4
self.lb4 = QLabel("初始化質心次數:", self)
self.lb4.move(20, 160)
self.lb4_ = QLabel("10", self)
self.lb4_.setGeometry(160, 160, 180, 20)
self.bt4 = QPushButton('修改初始化質心次數', self)
self.bt4.move(400, 155)
self.bt4.clicked.connect(self.choice_n_init) # 連接到選擇最大迭代次數函數
#執行聚類過程
self.bt5 = QPushButton('開始', self)
self.bt5.move(200,300)
self.bt5.clicked.connect(self.kmeans_building)
self.show()
它們對應的操作函數,這幾個函數與上面幾個按鈕一一對應,注意觀察
#解析上傳文件
def openfile(self):
inputfile = QFileDialog.getOpenFileName(self, '打開文件','./')
path=inputfile[0]
self.lb1_.setText(path)
#print(str(path))
#return path
#選擇聚類個數
def choice_clusters(self):
text, ok = QInputDialog.getInt(self, '聚類個數', '請輸入聚類個數:', min=1)
if ok:
types_num =int(text)
self.lb2_.setText(str(types_num))
#print(types_num)
#return types_num
#選擇最大迭代次數
def choice_max_iter(self):
text, ok = QInputDialog.getInt(self, '修改最大迭代次數', '請輸入最大迭代次數:', min=10)
if ok:
iter_num =int(text)
self.lb3_.setText(str(iter_num))
#選擇初始化質心次數
def choice_n_init(self):
text, ok = QInputDialog.getInt(self, '修改初始化質心次數', '請輸入初始化質心次數:', min=1)
if ok:
init_num =int(text)
self.lb4_.setText(str(init_num))
# kmeans使用函數
def kmeans_building(self):
#取標籤1_ 2_ 3_ 4_裏面的值
inputfile=str(self.lb1_.text())
types_num=int(self.lb2_.text())
iter_num = int(self.lb3_.text())
init_num=int(self.lb4_.text())
#讀取數據
data = pd.read_csv(inputfile, encoding='gb18030', error_bad_lines=True, engine='python')
data2=data.copy()
#數據空值填充
data = self.fillNan(data)
#數據標準化
data = self.standardize(data)
#sklearn中的kmeans算法執行
kmodel = KMeans(n_clusters=types_num,max_iter=iter_num,n_init=init_num).fit(data) # 訓練模型
r1 = pd.Series(kmodel.labels_).value_counts() # 統計各個類別的數目
r2 = pd.DataFrame(kmodel.cluster_centers_) # 找出聚類中心
r3 = self.reStandardize(data2,r2)#將聚類中心還原爲標準化前的數值
result_1 = pd.concat([r3, r1], axis=1) # 橫向連接(0是縱向),得到聚類中心對應的類別下的數目
result_1.columns = list(data.columns) + [u'聚類個數'] # 重命名錶頭
# 把聚類中心文件輸出到原始文件的路徑下
path = inputfile.split('/')[:-1]
path.append('')
path = '/'.join(path)
result_1.to_csv(path + '聚類中心.csv', encoding='gb18030', index=True)
# 把聚類結果文件輸出到原始文件的路徑下
result_2 = pd.concat([data2, pd.Series(kmodel.labels_, index=data.index)], axis=1) # 詳細輸出每個樣本對應的類別
result_2.columns = list(data.columns) + [u'聚類類別'] # 重命名錶頭
result_2.to_csv(path + '聚類結果.csv', encoding='gb18030', index=True) # 保存分類結果
#加入進度條
progress = QProgressDialog(self)
progress.setWindowTitle("請稍等")
progress.setLabelText("正在操作...")
progress.setCancelButtonText("取消")
progress.setMinimumDuration(5)
progress.setWindowModality(Qt.WindowModal)
progress.setRange(0, len(result_2))
for i in range(len(result_2)):
progress.setValue(i)
if progress.wasCanceled():
QMessageBox.warning(self, "提示", "操作失敗")
break
else:
progress.setValue(len(result_2))
QMessageBox.information(self, "提示", "操作成功")
其中kmeans使用函數主要跟sklearn的使用有關,非常簡單,可以去找別的帖子看一下,在此不再詳細講。
最後附上完整代碼
#!/usr/bin/python
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import QLabel, QWidget, QApplication, QPushButton, QFileDialog, QInputDialog, QProgressDialog,QMessageBox
from PyQt5.QtCore import Qt
import sys
import pandas as pd
from sklearn.cluster import KMeans
class Kmeans_(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# 界面主體
self.resize(600, 400)
self.setWindowTitle('Kmeans聚類')
#選擇文件部分設置三個對象:標籤1、標籤1_、按鈕1
self.lb1 = QLabel("文件位置:", self)
self.lb1.move(20, 40)
self.lb1_ = QLabel(self)
self.lb1_.setGeometry(110, 40, 300, 20)
self.bt1 = QPushButton('選擇文件位置', self)
self.bt1.move(400, 35)
self.bt1.clicked.connect(self.openfile)#連接到打開文件函數
# 選擇聚類個數設置三個對象:標籤2、標籤2_、按鈕2
self.lb2 = QLabel("聚類個數:", self)
self.lb2.move(20, 80)
self.lb2_ = QLabel("8",self)
self.lb2_.setGeometry(110, 80, 180, 20)
self.bt2 = QPushButton('選擇聚類個數', self)
self.bt2.move(400, 75)
self.bt2.clicked.connect(self.choice_clusters) # 連接到選擇聚類中心函數
# 選擇最大迭代次數,設置三個對象:標籤3、標籤3_、按鈕3
self.lb3= QLabel("最大迭代次數:", self)
self.lb3.move(20, 120)
self.lb3_ = QLabel("300",self)
self.lb3_.setGeometry(140, 120, 180, 20)
self.bt3 = QPushButton('修改最大迭代次數', self)
self.bt3.move(400, 115)
self.bt3.clicked.connect(self.choice_max_iter) # 連接到選擇最大迭代次數函數
# 選擇初始化質心次數,設置三個對象:標籤4、標籤4_、按鈕4
self.lb4 = QLabel("初始化質心次數:", self)
self.lb4.move(20, 160)
self.lb4_ = QLabel("10", self)
self.lb4_.setGeometry(160, 160, 180, 20)
self.bt4 = QPushButton('修改初始化質心次數', self)
self.bt4.move(400, 155)
self.bt4.clicked.connect(self.choice_n_init) # 連接到選擇最大迭代次數函數
#執行聚類過程
self.bt5 = QPushButton('開始', self)
self.bt5.move(200,300)
self.bt5.clicked.connect(self.kmeans_building)
self.show()
# 標準化函數
def standardize(self,df):
for i in df.columns:
df[i] = (df[i] - df[i].mean(axis=0)) / (df[i].std(axis=0))
return df
#反標準化函數
def reStandardize(self,df1,df2):
mean_value=[]
std_value=[]
for i in df1.columns:
mean=df1[i].mean(axis=0)
mean_value.append(mean)
std=df1[i].std(axis=0)
std_value.append(std)
for indexs in df2.index:
df2.loc[indexs]=df2.loc[indexs]*std_value+mean_value
return df2
# 填充空值函數
def fillNan(self,dataframe):
for i in dataframe.columns:
dataframe[i] = dataframe[i].fillna(dataframe[i].mean())
return dataframe
#解析上傳文件
def openfile(self):
inputfile = QFileDialog.getOpenFileName(self, '打開文件','./')
path=inputfile[0]
self.lb1_.setText(path)
#print(str(path))
#return path
#選擇聚類個數
def choice_clusters(self):
text, ok = QInputDialog.getInt(self, '聚類個數', '請輸入聚類個數:', min=1)
if ok:
types_num =int(text)
self.lb2_.setText(str(types_num))
#print(types_num)
#return types_num
#選擇最大迭代次數
def choice_max_iter(self):
text, ok = QInputDialog.getInt(self, '修改最大迭代次數', '請輸入最大迭代次數:', min=10)
if ok:
iter_num =int(text)
self.lb3_.setText(str(iter_num))
#選擇初始化質心次數
def choice_n_init(self):
text, ok = QInputDialog.getInt(self, '修改初始化質心次數', '請輸入初始化質心次數:', min=1)
if ok:
init_num =int(text)
self.lb4_.setText(str(init_num))
# kmeans使用函數
def kmeans_building(self):
#取標籤裏面的值
inputfile=str(self.lb1_.text())
types_num=int(self.lb2_.text())
iter_num = int(self.lb3_.text())
init_num=int(self.lb4_.text())
#讀取數據
data = pd.read_csv(inputfile, encoding='gb18030', error_bad_lines=True, engine='python')
data2=data.copy()
#數據空值填充
data = self.fillNan(data)
#數據標準化
data = self.standardize(data)
#sklearn中的kmeans算法執行
kmodel = KMeans(n_clusters=types_num,max_iter=iter_num,n_init=init_num).fit(data) # 訓練模型
r1 = pd.Series(kmodel.labels_).value_counts() # 統計各個類別的數目
r2 = pd.DataFrame(kmodel.cluster_centers_) # 找出聚類中心
r3 = self.reStandardize(data2,r2)#將聚類中心還原爲標準化前的數值
result_1 = pd.concat([r3, r1], axis=1) # 橫向連接(0是縱向),得到聚類中心對應的類別下的數目
result_1.columns = list(data.columns) + [u'聚類個數'] # 重命名錶頭
# 把聚類中心文件輸出到原始文件的路徑下
path = inputfile.split('/')[:-1]
path.append('')
path = '/'.join(path)
result_1.to_csv(path + '聚類中心.csv', encoding='gb18030', index=True)
# 把聚類結果文件輸出到原始文件的路徑下
result_2 = pd.concat([data2, pd.Series(kmodel.labels_, index=data.index)], axis=1) # 詳細輸出每個樣本對應的類別
result_2.columns = list(data.columns) + [u'聚類類別'] # 重命名錶頭
result_2.to_csv(path + '聚類結果.csv', encoding='gb18030', index=True) # 保存分類結果
#加入進度條
progress = QProgressDialog(self)
progress.setWindowTitle("請稍等")
progress.setLabelText("正在操作...")
progress.setCancelButtonText("取消")
progress.setMinimumDuration(5)
progress.setWindowModality(Qt.WindowModal)
progress.setRange(0, len(result_2))
for i in range(len(result_2)):
progress.setValue(i)
if progress.wasCanceled():
QMessageBox.warning(self, "提示", "操作失敗")
break
else:
progress.setValue(len(result_2))
QMessageBox.information(self, "提示", "操作成功")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Kmeans_()
sys.exit(app.exec_())
這個帖子寫的比較匆忙,後續會不斷完善,有問題可以提出來互相探討。