1. MobaXterm学习与使用

  2. DOT1X 认证过程解析  
  3. [ 您没有编辑权限 ]

摘要:DOT1x 认证过程解析 1、802.11i协议简介 IEEE 802.11i标准是为了增强WLAN的数据加密和认证性能,定义了RSN(Robust Security Network:强健安全网络)的概念,并且针对WEP加密机制的各种缺陷做了多方面的改进。 IEEE 802.11i规定使用802.1X认证和密钥管理方式,在数据加密方面,定义了TKIP (Temporal Key Integrity Protocol:临时密钥完整性协议)和CCMP(Counter-Mode/CBC-MAC Protocol:计数器模式密码块链消息完整码协议)和WRAP(Wirel

  1. DOT1x 认证过程解析
  2. 1、802.11i协议简介
  3.       IEEE 802.11i标准是为了增强WLAN的数据加密和认证性能,定义了RSN(Robust Security Network:强健安全网络)的概念,并且针对WEP加密机制的各种缺陷做了多方面的改进。
  4.       IEEE 802.11i规定使用802.1X认证和密钥管理方式,在数据加密方面,定义了TKIP (Temporal  Key Integrity Protocol:临时密钥完整性协议)和CCMP(Counter-Mode/CBC-MAC Protocol:计数器模式密码块链消息完整码协议)和WRAP(Wireless Robust Authenticated Protocol:无线强制认证协议)三种加密机制。
  5. 由于打造安全的加密协议并不简单,而市场对于提高WLAN安全的需求又十分的紧迫,IEEE 802.11i的进展并不能满足这一需要。在这种情况下,Wi-Fi联盟加快了脚步致力于TKIP部署,提出了WPA(Wi-Fi Protected Access:wifi保护访问)的过渡性标准。这一标准采用了IEEE802.11i的草案(2003年中期),保证了与未来出现的协议的前向兼容。
  6.        WPA包含了两种认证方式:一是使用预共享密钥(pre-shared key)认证;另一种是使用802.1x协议认证;IEEE802.11i的终稿(2004年中期)称为WPA2,它全面采用AES机制。本文仅对802.1x协议认证进行总结。
  7. 2、Dot1x设计
  8.       802.1x 是一种基于端口的认证协议,整个802.1x 的实现设计三个部分,请求者、认证系统和认证服务器。下面分别介绍三者的具体内容:
  9.       请求者是位于局域网链路一端的实体,由连接到该链路另一端的认证系统对其进行认证。请求者通常是支持802.1x认证的用户终端设备,用户通过启动客户端软件发起802.lx认证,后文的认证请求者和客户端二者表达相同含义。
  10.       认证系统对连接到链路对端的认证请求者进行认证。认证系统通常为支持802. Lx协议的网络设备,它为请求者提供服务端口,该端口可以是物理端口也可以 是逻辑端口,一般在用户接入设备 (如LAN Switch和AP) 上实现802.1x认证。
  11.        认证服务器是为认证系统提供认证服务的实体,一般使用RADIUS服务器来实现认证服务器的认证和授权功能。请求者和认证系统之间运行802.1x定义的EAPOL (Extensible Authentication Protocol over LAN)协议。当认证系统工作于中继方式时,认证系统与认证服务器之间也运行EAP协议,EAP帧中封装认证数据,将该协议承载在其它高层次协议中(如 RADIUS),以便穿越复杂的网络到达认证服务器;当认证系统工作于终结方式时,认证系统终结EAPoL消息,并转换为其它认证协议(如 RADIUS),传递用户认证信息给认证服务器系统。
  12.        认证系统每个物理端口内部包含有受控端口和非受控端口。非受控端口始终处于双向连通状态,主要用来传递EAPoL协议帧,可随时保证接收认证请求者发出的EAPoL认证报文;受控端口只有在认证通过的状态下才打开,用于传递网络资源和服务。
  13. 3、Dot1x认证过程分析
  14.        证书主要用来进行终端和网络的相互认证。 Radius服务器首先向CA证书颁发机构申请服务器证书,用来代表Radius服务器的合法性。 客户端向CA证书颁发机构下载CA 根证书,用来验证Radius服务器下发的证书是否合法(一般情况下,如果终端不需要对网络进行认证的情况下,根证书可以不用下载和安装),客户端安装用户证书和根证书(可选)。上图展示的是dot1x认证的全过程,接下来将对整个过程逐一解释:
  15.        1)Client通过开放系统接入的方法(OPEN SYSTEM)关联上AP。
  16.        2)Client发送一个EAPoL-Start组播报文,触发802.1x接入的开始。认证系统(AC)将响应该消息。
  17.        3)AC 向客户端发送EAP-Request/Identity单播报文,要求客户端将用户信息送上来。
  18.        4)Client回应一个EAP-Response/Identity单播报文给AC的请求,其中包括用户名称,域名信息。 
  19.        5)AC以EAP Over RADIUS的报文格式将EAP-Response/Identity封装在Radius-Access-Request发送给认证服务器Radius,并且带上相关的RADIUS的属性。
  20.        6)Radius收到客户端发来的EAP-Response/Identity,根据配置确定使用EAP-PEAP认证,并向AP发送RADIUS-Access-Challenge报文,里面含有Radius发送给客户端的EAP-Request/Peap/Start的报文,表示希望开始进行EAP-PEAP的认证。
  21.        7)AC设备收到RADIUS-Access-Challenge报文后解析出EAP-Request/PEAP/Start发送给客户端。
  22.        8)Client收到EAP-Request/Peap/Start报文后,产生一个随机数、客户端支持的加密算法列表、TLS协议版本、会话ID封装在EAP-Response/Client Hello报文中发送给AC设备。AC以EAP Over RADIUS的报文格式将EAP-Response/Client Hello封装在Radius-Access-Request发送给认证服务器Radius Server,并且带上相关的RADIUS的属性。
  23.        9)Radius收到Client发来的Client Hello报文后,会从Client 的Hello报文的加密算法列表中选择自己支持的一组加密算法+Server产生的随机数+Server 证书(包含服务器的名称和公钥)+证书请求+Server_Hello_Done属性形成一个Server Hello报文封装在Access-Challenge报文中发送给AC,.AC设备收到RADIUS-Access-Challenge报文后解析出EAP-Request/ server hello发送给客户端。由于证书比较大,一个报文是无法承载的,后面还有3个续传的IP分片报文,目的是把Server证书发送到客户端,如果采用的认证系统无法完成报文的分片,将导致认证失败。
  24.       10)Client收到报文后,进行验证Server的证书是否合法(使用从CA证书颁发机构获取的根证书进行验证,主要验证证书时间是否合法,名称是否合法),即对网络进行认证,从而可以保证Server的合法。如果合法则提取Server证书中的公钥,client同时产生一个随机密码串 pre-master-secret,并使用服务器的公钥对其进行加密,最后将加密的信息ClientKeyExchange+客户端的证书(如果没有证书,可以把属性置为0)+TLS finished属性封装成EAP-Rsponse/TLS OK报文发送给认证系统.如果client没有安装根证书或者用户设置为不验证,则不会对Server证书的合法性进行认证,即不能对网络进行认证。
  25.       这里要说明:dot1x认证可以采用3种组合:用户名+密码,用户名+密码+根证书,根证书+用户证书。 AP以EAP Over RADIUS的报文格式将EAP-Response/TLS OK发送给认证服务器Radius Server,并且带上相关的RADIUS的属性。
  26.      11)Radius收到客户端发了的报文后,用自己的证书对应的私钥对ClientKeyExchange进行解密,从而获取到pre-master-secret,然后将pre-master-secret进行运算处理,加上Client和Server产生的随机数,生成加密密钥、加密初始化向量和hmac的密钥,这时双方已经安全的协商出一套加密办法了,至此TLS通道已经建立成功,以后的认证过程将使用协商出的密钥进行加密和校验。Radius Server借助hmac的密钥,对要在TLS通道内进行认证的消息做安全的摘要处理,然后和认证消息放到一起。借助加密密钥,加密初始化向量加密上面的消息,封装在Access-Challenge报文中,发送给AC,AC解除封装后转发Encrypted Handshake Message 给Client。
  27.      12)客户端收到Radius server发来报文后,用服务器相同的方法生成加密密钥,加密初始化向量和hmac的密钥,并用相应的密钥及其方法对报文进行解密和校验,然后产生认证回应报文,用密钥进行加密和校验,最后封装成EAP-response报文发送给AC。
  28.      13)AC以EAP Over RADIUS的报文格式将EAP-Response封装在radius-access-request报文中发送给认证服务器Radius Server,并且带上相关的RADIUS的属性。
  29.      14)服务器认证客户端成功,会发送Access-Accept报文给AC,报文中包含了认证服务器所提供的MPPE属性。
  30.      15)AC收到RADIUS-Access-Accept报文,会提取MPPE属性中的密钥做为WPA加密用的PMK,并且会发送EAP-success报文给客户端。
  31.      16)如果认证通过,AC和client将进行4次握手和组密钥更新协商加密密钥(加密协商这个过程比较复杂,还没有完全弄懂,继续学习中)。用户通过标准的DHCP协议 (可以是DHCP Relay) ,通过接入设备获取规划的IP地址; 接入设备发起计费开始请求给RADIUS用户认证服务器;  RADIUS用户认证服务器回应计费开始请求报文。用户上线完毕。

一、网络数据处理

# -*- coding: utf-8 -*-
import os
import pdb
import re
import time
import csv
from datetime import datetime
import pandas as pd
from collections import defaultdict
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np




str_name_list=['ERRCODE','RESULT','SSID','MAC','TIME','ACNAME']
error_list=['130','147','84','176','205','206','34']
success_list=['4294967295','0']

data_file_path=r'D:\network_error\itdata'
#data_file_path=r'D:\network_error\itdata_test'
save_csv_path= r'D:\network_error\csv_itdata'
#save_csv_path=r'D:\network_error\csv_itdata_test'
csv_header="time_now_file","time_now","user_mac","connec_code","result","ac_name","ssid","net_state","in_file"
save_csv_path_all=r'D:\network_error'
csv_all_name='network_all_data_test'
time_interval=30 #最大容忍时间间隔,以转化后的20180420033009这样的str形式判断大小相减得

K_div=60#中位数在150左右,选取文本样本大小100为划分点

#将所有的结果存储起来
res_text_all=[]

if not os.path.isdir(save_csv_path): os.mkdir(save_csv_path)

#获得文件大小,单位KB,保留0位小数
def get_FileSize(filePath):
#    filePath = unicode(filePath,'utf8')
    fsize = os.path.getsize(filePath)
    fsize = fsize/float(1024)
    return round(fsize,0)


#csv文件的写入(按行写入)
def getSortedValues(row):
    sortedValues=[]#初始化为空list
    keys=row.keys()
    keys.sort()
    for key in keys:
        sortedValues.append(row[key])
    return sortedValues

def formatTime_time(time_input):
    #例1524166200000前10位可转换为时间,time_file=2018-04-20 03:32:00,time_join=20180420033000
    time_str=str(time_input)
#    pdb.set_trace()
    time_file_num =("%.3f" %  float( ''.join(list(time_str)[0:10]) ) )
    time_file = datetime.fromtimestamp( float(time_file_num) )
    time_join=''.join( re.split(r"-|:| ",str(time_file)) )
    return time_file,time_join

def find_str(list_raw,str_name):
    for i in range(0,len(list_raw)):
        num_find=list_raw[i].find(str_name)
        if num_find != -1 and list_raw[i].split(":")[0]==str_name:
            num=list_raw[i].split(":")[1]
            break
    return num

def error_check(macnum_index_row,net_state,time_now,time_interval,reserve_index):
    for j in range(0,len(macnum_index_row)):
    #每次循环给网络状态赋值-100,时间是倒序,前面的序列时间晚
#        pdb.set_trace()
        #同一终端,时间段内多次错误码且最终为失败错误码
        if net_state[ macnum_index_row[0] ] ==0 and net_state[ macnum_index_row[j] ] == 0 and j !=0:
            reserve_index=macnum_index_row[0]
            break
        #同一终端,存在失败错误码且不存在成功错误码
        elif ( net_state[ macnum_index_row[j] ] == 0 and
              [ net_state[int(num_user)] ==1 for num_user in macnum_index_row ].count(True) == 0 ):
            reserve_index=macnum_index_row[j]
            break
        #同一终端,当有连接多次且距离最近错误码超过30s以上,时间是字符形式需要整型化
        elif net_state[ macnum_index_row[j] ] != 0:
             state_index=[num for num in macnum_index_row[j:] if net_state[num]==0]
             error_index_nums=state_index.count(0)
             if error_index_nums>0:
                 if int(time_now[ macnum_index_row[j] ]) - int(time_now[ state_index[0] ]) > time_interval :
                    reserve_index=macnum_index_row[j]
                    break#结束此次内层循环,调到外层循环去
    return reserve_index
def success_check(macnum_index_row,net_state,time_now,time_interval,reserve_index):
    for j in range(0,len(macnum_index_row)):
#        pdb.set_trace()
        #同一终端,连接多次均为成功
        if net_state[ macnum_index_row[0] ] ==1 and net_state[ macnum_index_row[j] ] == 1 and j !=0:
            reserve_index=macnum_index_row[0]
            break
        #同一终端,存在成功错误码且不存在失败错误码
        elif ( net_state[ macnum_index_row[j] ] == 1 and
              [ net_state[int(num_user)] ==0 for num_user in macnum_index_row ].count(True) == 0 ):
            reserve_index=macnum_index_row[j]
            break
        #同一终端,失败错误码距离成功错误码的时间间隔小于30s,且最终不为失败状态
        elif ( net_state[ macnum_index_row[0] ] !=0 and
              net_state[ macnum_index_row[j] ] == 1 ):
              #判断成功之前有没有失败,有失败的话看失败的最近的时间间隔是否小于time_interval,如果是保存此成功状态
              state_index=[num for num in macnum_index_row[j:] if net_state[num]==0]
              error_index_nums=state_index.count(0)
              if 0<error_index_nums:
                  #state_index[-1]是最早时间的索引
                  if int(time_now[ macnum_index_row[j] ]) - int(time_now[ state_index[-1] ]) < time_interval:
                      reserve_index=macnum_index_row[j]
                      break#结束此次内层循环,调到外层循环去

    return reserve_index

def warning_check(macnum_index_row,net_state,time_now,time_interval,reserve_index):
#    pdb.set_trace()
    for j in range(0,len(macnum_index_row)):
        #同一终端,错误码中无失败错误码和成功错误码
        if ( [ net_state[int(num_user)] ==0 for num_user in macnum_index_row ].count(True) == 0 and
             [ net_state[int(num_user)] ==1 for num_user in macnum_index_row ].count(True) == 0 ):
            reserve_index=macnum_index_row[0]
            break
    return reserve_index

def result_check(connec_code):

    res_state=[]
    for i in range(0,len(connec_code)):
        if connec_code[i] in error_list:
            res=0
        elif connec_code[i] in success_list:
            res=1
        else:
            res=2
        res_state.append(res)
    return res_state

def error_code_select(error_index,success_index,warning_index):
    if error_index !=-100 or success_index != -100 or warning_index != -100:
                if error_index !=-100:
                    reserve_index=error_index
                    return reserve_index#找到则终止本次循环,直接跳到下次循环,但不能跳过保存res_usermac_index这一步
                else:
                    if success_index != -100:
                        reserve_index=success_index
                        return reserve_index
                    else:
                        if warning_index != -100:
                            reserve_index=warning_index
                            return reserve_index
    else:
        return -100

def del_times_mac(user_mac,net_state,time_now,time_interval):
    #根据user_mac地址去重
#    pdb.set_trace()
    user_mac_only=list(set(user_mac))
    #将每个重复的user_mac都根据user_mac_only放入在原来的所有数据的列表中的索引
    macnum_index_row=[]
    for i_mac in user_mac_only:
        num_index=[]
        for i in range(0,len(user_mac)):
            num_find=str(user_mac[i]).find(i_mac)
            if num_find != -1:
                num_index.append(i)
        macnum_index_row.append(num_index)


    #将重复的usemac取如果这段时间里有连接成功的,则保留此索引,否则保存时间最晚的连接状态
    res_usermac_index=[]
    for i in range(0,len(macnum_index_row)):
        reserve_index=-100

        error_index=error_check(macnum_index_row[i],net_state,time_now,time_interval,reserve_index)
        success_index=success_check(macnum_index_row[i],net_state,time_now,time_interval,reserve_index)
        warning_index=warning_check(macnum_index_row[i],net_state,time_now,time_interval,reserve_index)

        #根据状态优先级判断输出状态的索引
        reserve_index=error_code_select(error_index,success_index,warning_index)
        #如果最终没有匹配到状态,则取最终的状态为准(如果没有重复mac,则也可以取这个)
        if reserve_index == -100:
            reserve_index=macnum_index_row[i][0]
#        pdb.set_trace()
        res_usermac_index.append(reserve_index)

    return res_usermac_index

def csv_write(save_csv_path,file_name,data,csv_header):
    with open(os.path.join(save_csv_path,file_name+'.csv'),'w',encoding='utf8' ) as file:
        writer=csv.writer(file)
        writer.writerow(csv_header)
        for i_row in data:
            writer.writerow(i_row)
        file.close()


file_size_all=[]
for file_name in os.listdir(data_file_path):
    file_abs_path=os.path.join(data_file_path,file_name)
    file_size=get_FileSize(file_abs_path)
    file_size_all.append(file_size)
'''
#画文件大小的箱线图
# 设置图形的显示风格
plt.style.use('ggplot')

# 设置中文和负号正常显示
plt.rcParams['font.sans-serif'] = 'Microsoft YaHei'
plt.rcParams['axes.unicode_minus'] = False

# 绘图:整体文件大小箱线图
plt.boxplot(x = file_size_all, # 指定绘图数据
            patch_artist=True, # 要求用自定义颜色填充盒形图,默认白色填充
            showmeans=True, # 以点的形式显示均值
            boxprops = {'color':'black','facecolor':'#9999ff'}, # 设置箱体属性,填充色和边框色
            flierprops = {'marker':'o','markerfacecolor':'red','color':'black'}, # 设置异常值属性,点的形状、填充色和边框色
            meanprops = {'marker':'D','markerfacecolor':'indianred'}, # 设置均值点的属性,点的形状、填充色
            medianprops = {'linestyle':'--','color':'orange'}) # 设置中位数线的属性,线的类型和颜色
# 设置y轴的范围
plt.ylim(0,2000)
# 去除箱线图的上边框与右边框的刻度标签
plt.tick_params(top='off', right='off')
# 显示图形
plt.show()
'''

for file_name in os.listdir(data_file_path):
    time_file,time_join=formatTime_time(file_name)
    file=os.path.join(data_file_path,file_name)
    text=[]
    for line in open(file,'rb'):
        data = str(line).split(";")
        text.append(data)
    time_now=[]
    time_now_file=[]
    user_mac=[]
    ssid=[]
    connec_code=[]
    ac_name=[]
    result=[]
    for num in range(0,len(text)):
        time_now_num=find_str(text[num],str_name_list[4])
        time_now_file_one,time_now_join_one=formatTime_time(time_now_num)

        time_now_file.append(time_now_file_one)
        time_now.append(time_now_join_one)
        #查找的索引(并不一定在列表的第几行,且有例如acmac的干扰),以:分割的前一部分如果与所需要的字符串完全匹配,则保留此值
        connec_code.append( find_str(text[num],str_name_list[0]) )   #error的code值
        result.append(find_str(text[num],str_name_list[1]))  #最终网络状态
        ssid.append( find_str(text[num],str_name_list[2]) )  #网络类型
        user_mac.append(find_str(text[num],str_name_list[3]))   #用户mac地址
        ac_name.append(find_str(text[num],str_name_list[5])) #AC设备名称

    #网络连接的状态,失败为0,成功为1,无关为2
    net_state=result_check(connec_code)
    #根据user_mac去重,返回去重之后的索引
    res_usermac_index=del_times_mac(user_mac,net_state,time_now,time_interval)

    #去重user_mac的保留的索引
    time_now_file_only=[]#实际时间文本形式
    time_now_only=[]#实际时间数字字符串形式
    user_mac_only=[]#user mac地址
    connec_code_only=[]#错误码
    result_only=[]#连接的状态
    ac_name_only=[]#ac名称
    ssid_only=[]#连接网络名称
    net_state_only=[]#连接的最终判别状态 0失败 1成功 2无关
    in_file_name=[]#在原本的哪个文件
    #获得不重复的usermac的所有数据,根据res_usermac_index中的序列取原来序列的值,按照时间降序排列
    for i in range(0,len(res_usermac_index)):

        time_now_file_only.append(time_now_file[res_usermac_index[i]])
        time_now_only.append(time_now[res_usermac_index[i]])
        user_mac_only.append(user_mac[res_usermac_index[i]])
        connec_code_only.append(connec_code[res_usermac_index[i]])
        result_only.append(result[res_usermac_index[i]])
        ac_name_only.append(ac_name[res_usermac_index[i]])
        ssid_only.append(ssid[res_usermac_index[i]])
        net_state_only.append(net_state[res_usermac_index[i]])
        in_file_name.append(file_name)

    res_text=[]
    res_text=list( zip(time_now_file_only,time_now_only,user_mac_only,connec_code_only,result_only,ac_name_only,ssid_only,net_state_only,in_file_name) )
    res_text.sort(key=lambda x:x[1],reverse=True)#按时间大小倒序排列
    csv_write(save_csv_path,file_name,res_text,csv_header)


#    pdb.set_trace()
    res_text_all.extend(res_text)
csv_write(save_csv_path_all,csv_all_name,res_text_all,csv_header)

 

二、故障率计算

# -*- coding: utf-8 -*-
import os
import pdb
import re
import time
import csv
from datetime import datetime
import pandas as pd
from collections import defaultdict
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

csv_file_path=r'D:\network_error\csv_itdata'
#csv_file_path=r'D:\network_error\csv_itdata_test'
list_use=['net_state']
list_ac_num=['lgh31dcthw10','nkg41acthw07','lga90acthw07','lgh31dcthw01']
csv_head='error_rate','success_rate','warning_rate','k_num','file_index'
save_csv_path_all=r'D:\network_error'
#csv_all_name='data_rate_test'
csv_all_name='data_rate_all'

k_div=50#最小样本量

def csv_read(save_csv_path,file_name):
    data_lines = []
    with open(os.path.join(save_csv_path,file_name),'r',encoding='utf8' ,newline="") as file:
#        pdb.set_trace()
        reader = csv.reader(file)
        fields = next(reader)  # 过滤掉表头(如果有的话)
        for row in reader:
            items = dict(zip(fields, row))
            if items!={}:
                data_lines.append(items)
    file.close()
    return data_lines

def csv_write(save_csv_path,file_name,data,csv_header):
    with open(os.path.join(save_csv_path,file_name+'.csv'),'w',encoding='utf8' ) as file:
        writer=csv.writer(file)
        writer.writerow(csv_header)
        for i_row in data:
            writer.writerow(i_row)
        file.close()

def rate_cal(error_num,success_num,warning_num,k):
    if k!=0:
        error_rate=float(error_num)/k
        success_rate=float(success_num)/k
        warning_rate=float(warning_num)/k
    else:
        error_rate=0.0
        success_rate=0.0
        warning_rate=0.0
    return error_rate,success_rate,warning_rate



data=[]
data_rate=[]
for file_index,file_name in enumerate( os.listdir(csv_file_path) ):
#    pdb.set_trace()
    data=csv_read(csv_file_path,file_name)
    #列表表达式,判断每行中的键值代表的网络状态的数量
    k=len(data)
    #当样本量大于60以上时才保存
    if k >= k_div:
        error_num=[int(row[list_use[0]]) ==0  for row in data ].count(True)
        success_num=[int(row[list_use[0]]) ==1  for row in data ].count(True)
        warning_num=[int(row[list_use[0]]) ==2  for row in data ].count(True)
        error_rate,success_rate,warning_rate=rate_cal(error_num,success_num,warning_num,k)
        one_data=[error_rate,success_rate,warning_rate,k,file_index]#按照错误率,成功率,无关率,样本量和原始文件中的第几个的索引
        data_rate.append(one_data)
    csv_write(save_csv_path_all,csv_all_name,data_rate,csv_head)

#    pdb.set_trace()

#将样本量画出箱线图
polt_box_data=[row[3] for row in data_rate]
#画文件大小的箱线图
# 设置图形的显示风格
plt.style.use('ggplot')

# 设置中文和负号正常显示
plt.rcParams['font.sans-serif'] = 'Microsoft YaHei'
plt.rcParams['axes.unicode_minus'] = False

# 绘图:整体文件大小箱线图
plt.boxplot(x = polt_box_data, # 指定绘图数据
            patch_artist=True, # 要求用自定义颜色填充盒形图,默认白色填充
            showmeans=True, # 以点的形式显示均值
            boxprops = {'color':'black','facecolor':'#9999ff'}, # 设置箱体属性,填充色和边框色
            flierprops = {'marker':'o','markerfacecolor':'red','color':'black'}, # 设置异常值属性,点的形状、填充色和边框色
            meanprops = {'marker':'D','markerfacecolor':'indianred'}, # 设置均值点的属性,点的形状、填充色
            medianprops = {'linestyle':'--','color':'orange'}) # 设置中位数线的属性,线的类型和颜色
# 设置y轴的范围
plt.ylim(0,200)
# 去除箱线图的上边框与右边框的刻度标签
plt.tick_params(top='off', right='off')
# 显示图形
plt.show()

三、故障率和样本聚类

# -*- coding: utf-8 -*-
import numpy as np
from numpy.linalg import cholesky
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import tensorflow as tf
from random import choice, shuffle
from numpy import array
import csv
import pdb


def csv_read(save_csv_path,file_name):
    data_lines = []
    with open(os.path.join(save_csv_path,file_name),'r',encoding='utf8' ,newline="") as file:
#        pdb.set_trace()
        reader = csv.reader(file)
        fields = next(reader)  # 过滤掉表头(如果有的话)
        for row in reader:
            items = dict(zip(fields, row))
            if items!={}:
                data_lines.append(items)
    file.close()
    return data_lines


############Sachin Joglekar的基于tensorflow写的一个kmeans模板###############
def KMeansCluster(vectors, noofclusters):
    """
    K-Means Clustering using TensorFlow.
    `vertors`应该是一个n*k的二维的NumPy的数组,其中n代表着K维向量的数目
    'noofclusters' 代表了待分的集群的数目,是一个整型值
    """
#    pdb.set_trace()
    noofclusters = int(noofclusters)
    assert noofclusters < len(vectors)
    #找出每个向量的维度
    dim = len(vectors[0])
    #辅助随机地从可得的向量中选取中心点
    vector_indices = list(range(len(vectors)))
    shuffle(vector_indices)
    #计算图
    #我们创建了一个默认的计算流的图用于整个算法中,这样就保证了当函数被多次调用      #时,默认的图并不会被从上一次调用时留下的未使用的OPS或者Variables挤满
    graph = tf.Graph()
    with graph.as_default():
        #计算的会话
        sess = tf.Session()
        ##构建基本的计算的元素
        ##首先我们需要保证每个中心点都会存在一个Variable矩阵
        ##从现有的点集合中抽取出一部分作为默认的中心点
        centroids = [tf.Variable((vectors[vector_indices[i]]))
                     for i in range(noofclusters)]
        ##创建一个placeholder用于存放各个中心点可能的分类的情况
        centroid_value = tf.placeholder("float64", [dim])
        cent_assigns = []
        for centroid in centroids:
            cent_assigns.append(tf.assign(centroid, centroid_value))
        ##对于每个独立向量的分属的类别设置为默认值0
        assignments = [tf.Variable(0) for i in range(len(vectors))]
        ##这些节点在后续的操作中会被分配到合适的值
        assignment_value = tf.placeholder("int32")
        cluster_assigns = []
        for assignment in assignments:
            cluster_assigns.append(tf.assign(assignment,
                                             assignment_value))
        ##下面创建用于计算平均值的操作节点
        #输入的placeholder
        mean_input = tf.placeholder("float", [None, dim])
        #节点/OP接受输入,并且计算0维度的平均值,譬如输入的向量列表
        mean_op = tf.reduce_mean(mean_input, 0)
        ##用于计算欧几里得距离的节点
        v1 = tf.placeholder("float", [dim])
        v2 = tf.placeholder("float", [dim])
        euclid_dist = tf.sqrt(tf.reduce_sum(tf.pow(tf.subtract (
            v1, v2), 2)))
        ##这个OP会决定应该将向量归属到哪个节点
        ##基于向量到中心点的欧几里得距离
        #Placeholder for input
        centroid_distances = tf.placeholder("float", [noofclusters])
        cluster_assignment = tf.argmin(centroid_distances, 0)
        ##初始化所有的状态值
         ##这会帮助初始化图中定义的所有Variables。Variable-initializer应该定
         ##义在所有的Variables被构造之后,这样所有的Variables才会被纳入初始化
        init_op = tf.global_variables_initializer()
        #初始化所有的变量
        sess.run(init_op)
        ##集群遍历
        #接下来在K-Means聚类迭代中使用最大期望算法。为了简单起见,只让它执行固
        #定的次数,而不设置一个终止条件
        noofiterations = 20
        for iteration_n in range(noofiterations):

            ##期望步骤
            ##基于上次迭代后算出的中心点的未知
            ##the _expected_ centroid assignments.
            #首先遍历所有的向量
            for vector_n in range(len(vectors)):
                vect = vectors[vector_n]
                #计算给定向量与分配的中心节点之间的欧几里得距离
                distances = [sess.run(euclid_dist, feed_dict={
                    v1: vect, v2: sess.run(centroid)})
                             for centroid in centroids]
                #下面可以使用集群分配操作,将上述的距离当做输入
                assignment = sess.run(cluster_assignment, feed_dict = {
                    centroid_distances: distances})
                #接下来为每个向量分配合适的值
                sess.run(cluster_assigns[vector_n], feed_dict={
                    assignment_value: assignment})

            ##最大化的步骤
            #基于上述的期望步骤,计算每个新的中心点的距离从而使集群内的平方和最小
            for cluster_n in range(noofclusters):
                #收集所有分配给该集群的向量
                assigned_vects = [vectors[i] for i in range(len(vectors))
                                  if sess.run(assignments[i]) == cluster_n]
                #计算新的集群中心点
                new_location = sess.run(mean_op, feed_dict={
                    mean_input: array(assigned_vects)})
                #为每个向量分配合适的中心点
                sess.run(cent_assigns[cluster_n], feed_dict={
                    centroid_value: new_location})

        #返回中心节点和分组
        centroids = sess.run(centroids)
        assignments = sess.run(assignments)
        return centroids, assignments
############生成测试数据###############
sampleNo = 100;#数据数量
mu =3
# 二维正态分布
mu = np.array([[1, 5]])
Sigma = np.array([[1, 0.5], [1.5, 3]])
R = cholesky(Sigma)
srcdata= np.dot(np.random.randn(sampleNo, 2), R) + mu
plt.plot(srcdata[:,0],srcdata[:,1],'bo')




############kmeans算法计算###############
k=4
center,result=KMeansCluster(srcdata,k)
print(center)
############利用seaborn画图###############

res={"x":[],"y":[],"kmeans_res":[]}
for i in range(0,len(result)):
    res["x"].append(srcdata[i][0])
    res["y"].append(srcdata[i][1])
    res["kmeans_res"].append(result[i])
pd_res=pd.DataFrame(res)
sns.lmplot("x","y",data=pd_res,fit_reg=False,size=5,hue="kmeans_res")
plt.show()

 

10-07 17:14