机器学习实战2(决策树篇)
目录
1、决策树
2、决策树的构造
3、决策树的可视化
4、测试和存储决策树
1、决策树
你是否玩过二十个问题的游戏,游戏的规则很简单:参与游戏的一方在脑海里想某个事物,其他参与者向他提问题,只允许提20个问题,问题的答案也只能用对或错回答。问问题的人通过推断分解,逐步缩小待猜测事物的范围。决策树的工作原理与20个问题类似,用户输人一系列数据,然后给出游戏的答案。
我们经常使用决策树处理分类问题,近来的调查表明决策树也是最经常使用的数据挖掘算法R。它之所以如此流行,一个很重要的原因就是使用者基本上不用了解机器学习算法,也不用深究它是如何工作的。
本文构造的决策树算法能够读取数据集合,构建类似于图下的决策树。
①优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
②缺点:可能会产生过度匹配问题。
③适用数据类型:数值型和标称型。
④决策树的一般流程:
(1)决策树的一般流程
(2)准备数据;树构造算法只适用于标称型数据,因此数值型数据必须离散化。
(3)分析数据:可以使用任何方法,构造树完成之后我们应该检查图形是否符合预期。
(4)训练算法:构造树的数据结构。
(5)测试算法:使用经验树计算错误率。
(6)使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好地理解数据
的内在含义。
2、决策树的构造
在构造决策树时,我们需要解决的第一个问题就是,当前数据集上哪个特征在划分数据分类时起决定性作用。为了找到决定性的特征,划分出最好的结果,我们必须评估每个特征。完成测试之后,原始数据集就被划分为几个数据子集。这些数据子集会分布在第一个决策点的所有分支上。如果某个分支下的数据属于同一类型,则当前无需阅读的垃圾邮件已经正确地划分数据分类,无需进一步对数据集进行分割。如果数据子集内的数据不属于同一类型,则需要重复划分数据子集的过程。如何划分数据子集的算法和划分原始数据集的方法相同,直到所有具有相同类型的数据均在一个数据子集内。
下表的数据包含5个海洋动物,特征包括:不浮出水面是否可以生存,以及是否有脚践。我们可以将这些动物分成两类:鱼类和非鱼类。现在我们想要决定依据第一个特征还是第二个特征划分数据。在回答这个问题之前,我们必须采用量化的方法判断如何划分数据。
划分数据集的大原则是:将无序的数据变得更加有序。我们可以使用多种方法划分数据集,但是每种方法都有各自的优缺点。组织杂乱无章数据的一种方法就是使用信息论度量信息,信息论是量化处理信息的分支科学。我们可以在划分数据之前使用信息论量化度量信息的内容。
在划分数据集之前之后信息发生的变化称为信息增益,知道如何计算信息增益,我们就可以计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。
在可以评测哪种数据划分方式是最好的数据划分之前,我们必须学习如何计算信息增益。集合信息的度量方式称为香农嫡或者简称为嫡,这个名字来源于信息论之父克劳德·香农。
为了计算嫡,我们需要计算所有类别所有可能值包含的信息期望值,通过下面的公式得到:
1、计算给定数据集的熵
先利用createDataSet()函数得到简单鱼的鉴定数据集。
#创建数据
def createDataSet():dataSet = [[1,1,1],[1,1,1],[1,0,0],[0,1,0],[0,1,0]]#最后一列数据1代表为鱼类,0代表非鱼类labels = ['no surfacing','flippers']return dataSet,labels
首先,计算数据集中实例的总数。我们也可以在需要时再计算这个值,但是由于代码中多次用到这个值,为了提高代码效率,我们显式地声明一个变量保存实例总数。然后,创建一个数据字典,它的键值是最后一列的数值。如果当前键值不存在,则扩展字典并将当前键值加人字典。每个键值都记录了当前类别出现的次数。最后,使用所有类标签的发生频率计算类别出现的概率。我们将用这个概率计算香农嫡,统计所有类标签发生的次数。
#计算给定数据的香农熵
def calShannonEnt(dataSet):numEntries = len(dataSet)#返回数据集的行数labelCounts = {}#保存每个标签(Label)出现次数的字典for featVec in dataSet:#对每组特征向量进行统计currentLabel = featVec[-1]if currentLabel not in labelCounts.keys(): #如果标签没有放入统计次数的字典,添加进去labelCounts[currentLabel] = 0labelCounts[currentLabel]+=1shannonEnt = 0.0for key in labelCounts:prob = float(labelCounts[key])/numEntries#选择该标签(Label)的概率shannonEnt = shannonEnt - prob*log(prob,2)#香农熵return shannonEnt
2、划分数据集
我们学习了如何度量数据集的无序程度,分类算法除了需要测量信息嫡,还需要划分数据集,度量花费数据集的嫡,以便判断当前是否正确地划分了数据集。我们将对每个特征划分数据集的结果计算一次信息嫡,然后判断按照哪个特征划分数据集是最好的划分方式。
#划分数据集
#我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照那个特征划分数据集是最好的划分方式
def splitDataSet(dataSet,axis,value):retDataSet = []#创建返回的数据集列表for featVec in dataSet:if featVec[axis] ==value:reduceFeatVec = featVec[:axis] #去掉axis特征reduceFeatVec.extend(featVec[axis+1:])#将符合条件的添加到返回的数据集retDataSet.append(reduceFeatVec)return retDataSet#返回划分后的数据集
我们输出检查一下:
dataSet,labels = createDataSet()#创建数据
ans1 = splitDataSet(dataSet,0,1)
print(ans1)
可以得到输出的结果为:
在开始划分数据集之前,我们需要计算整个数据集的原始香农嫡,我们保存最初的无序度量值,用于与划分完之后的数据集计算的嫡值进行比较。第1个for循环遍历数据集中的所有特征。使用列表推导(List Comprehension)来创建新的列表,将数据集中所有第i个特征值或者所有可能存在的值写入这个新list中。然后使用Python语言原生的集合(set)数据类型。集合数据类型与列表类型相似,不同之处仅在于集合类型中的每个值互不相同。从列表中创建集合是Python语言得到列表中唯一元素值的最快方法。
遍历当前特征中的所有唯一属性值,对每个特征划分一次数据集,然后计算数据集的新嫡值,并对所有唯一特征值得到的嫡求和。信息增益是嫡的减少或者是数据无序度的减少,大家肯定对于将嫡用于度量数据无序度的减少更容易理解。最后,比较所有特征中的信息增益,返回最好特征划分的索引值。
#选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):numFeature = len(dataSet[0])-1#特征数量,-1是因为最后一个是类别baseEntopy = calShannonEnt(dataSet)#计算数据集的香农熵bestInfoGain = 0.0#信息增益bestFeature = -1 #最优特征的索引值for i in range(numFeature):#获取dataSet的第i个所有特征featList = [example[i] for example in dataSet]uniqueVals = set(featList) #创建set集合{},元素不可重复newEntropy = 0.0#计算每种划分方式的信息熵for value in uniqueVals:subDataSet = splitDataSet(dataSet,i,value)#subDataSet划分后的子集prob = len(subDataSet)/float(len(dataSet)) #计算子集的概率newEntropy += prob*calShannonEnt(subDataSet)InfoGain = baseEntopy -newEntropy #信息增益if (bestInfoGain<InfoGain):bestInfoGain = InfoGainbestFeature = ireturn bestFeature
输出检查一下:
dataSet,labels = createDataSet()#创建数据
# ans1 = splitDataSet(dataSet,0,1)
bestFeature = chooseBestFeatureToSplit(dataSet)
print(bestFeature)
得到的输出结果为:
代码运行结果告诉我们,第0个特征是最好的用于划分数据集的特征。结果是否正确呢?这个结果又有什么实际意义呢?数据集中的数据来源于表3-1,如果我们按照第一个特征属性划分数据,也就是说第一个特征是1的放在一个组,第一个特征是0的放在另一个组,数据一致性如何?按照上述的方法划分数据集,第一个特征为1的海洋生物分组将有两个属于鱼类,一个属于非鱼类;另一个分组则全部属于非鱼类。如果按照第二个特征分组,结果又是怎么样呢?第一个海洋动物分组将有两个属于鱼类,两个属于非鱼类;另一个分组则只有一个非鱼类。
3、递归构建决策树
目前我们已经学习了从数据集构造决策树算法所需要的子功能模块,其工作原理如下:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将被向下传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。
递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类。如果所有实例具有相同的分类,则得到一个叶子节点或者终止块。任何到达叶子节点的数据必然属于叶子节点的分类。
第一个结束条件使得算法可以终止,我们甚至可以设置算法可以划分的最大分组数目。
#出现次数最多的分类名称
def majorityCnt(classList):classCount = {}for vote in classList:if vote not in classCount.keys():classCount[vote] = 0classCount[vote]+=1sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse = True)return sortedClassCount[0][0]
递归函数的第一个停止条件是所有的类标签完全相同,则直接返回该类标签。递归函数的第二个停止条件是使用完了所有特征,仍然不能将数据集划分成仅包含唯一类别的分组。由于第二个条件无法简单地返回唯一的类标签,这里使用函数挑选出现次数最多的类别作为返回值。
#创建树
def createTree(dataSet,labels):classList = [example[-1] for example in dataSet]if classList.count(classList[0])==len(classList): #如果类别完全相同则停止继续划分return classList[0]if len(dataSet[0])==1: #遍历完所有特征时返回出现次数最多的类标签return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #选择最优特征bestFeatLabel = labels[bestFeat] #最优特征的标签myTree = {bestFeatLabel:{}} #根据最优特征的标签生成树del (labels[bestFeat]) #删除已经使用特征标签featValues = [example[bestFeat] for example in dataSet] #得到训练集中所有最优特征的属性值uniqueVals = set(featValues) #去掉重复的属性值for valus in uniqueVals: #遍历特征,创建决策树。subLabels = labels[:] #复制类标签myTree[bestFeatLabel][valus] = createTree(splitDataSet(dataSet,bestFeat,valus),subLabels)return myTree
dataSet,labels = createDataSet()#创建数据
myTree = createTree(dataSet,labels)
print(myTree)
构造的树为:
至此,我们的决策树已经构造完成了,接下来讲的是怎么样将决策树进行可视化处理。
3、决策树的可视化
本节我们将使用Matplotlib库创建树形图。决策树的主要优点就是直观易于理解,如果不能将其直观地显示出来,就无法发挥其优势。Python并没有提供绘制树的工具,因此我们必须自己绘制树形图。可视化这里我就不做过多的讲解了,大家可以直接看代码部分。
import matplotlib.pyplot as plt
import operator#定义文本框和箭头格式
decisionNode = dict(boxstyle = 'sawtooth',fc = '0.8')
leafNode = dict(boxstyle = 'round4',fc = '0.8')
arrow_args = dict(arrowstyle = '<-')#绘制带箭头的注解
def plotNode(nodeTxt,centerPt,parentPt,nodeType):createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',xytext=centerPt, textcoords='axes fraction',va="center", ha="center", bbox=nodeType, arrowprops=arrow_args )def createPlot(inTree):fig = plt.figure(1,facecolor='white')fig.clf()axprops = dict(xticks=[], yticks=[])createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) #no ticksplotTree.totalW = float(getNumLeafs(inTree))plotTree.totalD = float(getTreeDepth(inTree))plotTree.xOff = -0.5/plotTree.totalW; plotTree.yOff = 1.0;plotTree(inTree, (0.5,1.0), '')plt.show()#获取叶节点的数目
def getNumLeafs(myTree):numLeafs = 0firstStr = list(myTree.keys())[0] #python3中myTree.keys()返回的是dict_keys,不是list,所以不能使用myTree.keys()[0]的方法获取结点属性,可以使用secondDict = myTree[firstStr] #获取下一组字典for key in secondDict.keys():if type(secondDict[key]).__name__=='dict': #测试节点的数据类型是否为字典numLeafs += getNumLeafs(secondDict[key])else:numLeafs+=1return numLeafs#获取树的层数
def getTreeDepth(myTree):maxDepth = 0firstStr = list(myTree.keys())[0]secondDict = myTree[firstStr]for key in secondDict.keys():if type(secondDict[key]).__name__=='dict':thisDepth =1 + getTreeDepth(secondDict[key])else:thisDepth =1if thisDepth >maxDepth:maxDepth = thisDepthreturn maxDepthdef retrieveTree(i):listOfTrees =[{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}},{'no surfacing': {0: 'no', 1: {'flippers': {0: {'head': {0: 'no', 1: 'yes'}}, 1: 'no'}}}}]return listOfTrees[i]#在父子节点之间填充文本信息
def plotMidText(cntrPt,parentPt,txtString):xMid = (parentPt[0]-cntrPt[0])/2.0 +cntrPt[0]#计算节点yMid = (parentPt[1]-cntrPt[1])/2.0 +cntrPt[1]createPlot.ax1.text(xMid,yMid,txtString)def plotTree(myTree,parentPt,nodeTree):#计算宽与高numLeafs = getNumLeafs(myTree)depth = getTreeDepth(myTree)firstStr = list(myTree.keys())[0]cntrPt = (plotTree.xOff+(1.0+float(numLeafs))/2.0/plotTree.totalW,plotTree.yOff)plotMidText(cntrPt,parentPt,nodeTree)plotNode(firstStr, cntrPt, parentPt, decisionNode)secondDict = myTree[firstStr]plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalDfor key in secondDict.keys():if type(secondDict[key]).__name__=='dict':plotTree(secondDict[key],cntrPt,str(key))else:plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalWplotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalDTree = retrieveTree(0)
createPlot(Tree)
我们建造出来的树形图像为:
4、测试和存储决策树
依靠训练数据构造了决策树之后,我们可以将它用于实际数据的分类。在执行数据分类时,需要决策树以及用于构造树的标签向量。然后,程序比较测试数据与决策树上的数值,递归执行该过程直到进入叶子节点;最后将测试数据定义为叶子节点所属的类型。
#分类函数
def classify(inputTree,featLabel,TestVec):firstStr = next(iter(inputTree))secondDict = inputTree[firstStr]featIndex = featLabel.index(firstStr)for key in secondDict.keys():if TestVec[featIndex] == key:if type(secondDict[key]).__name__=='dict':classLabel = classify(secondDict[key],featLabel,TestVec)else :classLabel = secondDict[key]return classLabel
测试代码:
dataSet,labels = createDataSet()#创建数据
T = labels[:] #复制labels
myTree = createTree(dataSet,labels)
ans = classify(myTree,T,[1,0])
print(ans)
输出为:
构造决策树是很耗时的任务,即使处理很小的数据集,如前面的样本数据,也要花费几秒的时间,如果数据集很大,将会耗费很多计算时间。然而用创建好的决策树解决分类问题,则可以很快完成。因此,为了节省计算时间,最好能够在每次执行分类时调用已经构造好的决策树。为了解决这个问题,需要使用Python模块pickle序列化对象。序列化对象可以在磁盘上保存对象,并在需要的时候读取出来。任何对象都可以执行序列化操作,字典对象也不例外。
#决策树的存储
def storeTree(inputTree,filename):import picklefw = open(filename,'wb')pickle.dump(inputTree,fw)fw.close()def grabTree(filename):import picklefr = open(filename, 'rb')return pickle.load(fr)
完整代码:
import numpy as np
from math import log
import operator#创建数据
def createDataSet():dataSet = [[1,1,1],[1,1,1],[1,0,0],[0,1,0],[0,1,0]]#最后一列数据1代表为鱼类,0代表非鱼类labels = ['no surfacing','flippers']return dataSet,labels#计算给定数据的香农熵
def calShannonEnt(dataSet):numEntries = len(dataSet)#返回数据集的行数labelCounts = {}#保存每个标签(Label)出现次数的字典for featVec in dataSet:#对每组特征向量进行统计currentLabel = featVec[-1]if currentLabel not in labelCounts.keys(): #如果标签没有放入统计次数的字典,添加进去labelCounts[currentLabel] = 0labelCounts[currentLabel]+=1shannonEnt = 0.0for key in labelCounts:prob = float(labelCounts[key])/numEntries#选择该标签(Label)的概率shannonEnt = shannonEnt - prob*log(prob,2)#香农熵return shannonEnt#划分数据集
#我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照那个特征划分数据集是最好的划分方式
def splitDataSet(dataSet,axis,value):retDataSet = []#创建返回的数据集列表for featVec in dataSet:if featVec[axis] ==value:reduceFeatVec = featVec[:axis] #去掉axis特征reduceFeatVec.extend(featVec[axis+1:])#将符合条件的添加到返回的数据集retDataSet.append(reduceFeatVec)return retDataSet#返回划分后的数据集#选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):numFeature = len(dataSet[0])-1#特征数量,-1是因为最后一个是类别baseEntopy = calShannonEnt(dataSet)#计算数据集的香农熵bestInfoGain = 0.0#信息增益bestFeature = -1 #最优特征的索引值for i in range(numFeature):#获取dataSet的第i个所有特征featList = [example[i] for example in dataSet]uniqueVals = set(featList) #创建set集合{},元素不可重复newEntropy = 0.0#计算每种划分方式的信息熵for value in uniqueVals:subDataSet = splitDataSet(dataSet,i,value)#subDataSet划分后的子集prob = len(subDataSet)/float(len(dataSet)) #计算子集的概率newEntropy += prob*calShannonEnt(subDataSet)InfoGain = baseEntopy -newEntropy #信息增益if (bestInfoGain<InfoGain):bestInfoGain = InfoGainbestFeature = ireturn bestFeature#出现次数最多的分类名称
def majorityCnt(classList):classCount = {}for vote in classList:if vote not in classCount.keys():classCount[vote] = 0classCount[vote]+=1sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse = True)return sortedClassCount[0][0]#创建树
def createTree(dataSet,labels):classList = [example[-1] for example in dataSet]if classList.count(classList[0])==len(classList): #如果类别完全相同则停止继续划分return classList[0]if len(dataSet[0])==1: #遍历完所有特征时返回出现次数最多的类标签return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #选择最优特征bestFeatLabel = labels[bestFeat] #最优特征的标签myTree = {bestFeatLabel:{}} #根据最优特征的标签生成树del (labels[bestFeat]) #删除已经使用特征标签featValues = [example[bestFeat] for example in dataSet] #得到训练集中所有最优特征的属性值uniqueVals = set(featValues) #去掉重复的属性值for valus in uniqueVals: #遍历特征,创建决策树。subLabels = labels[:] #复制类标签myTree[bestFeatLabel][valus] = createTree(splitDataSet(dataSet,bestFeat,valus),subLabels)return myTree#分类函数
def classify(inputTree,featLabel,TestVec):firstStr = next(iter(inputTree))secondDict = inputTree[firstStr]featIndex = featLabel.index(firstStr)for key in secondDict.keys():if TestVec[featIndex] == key:if type(secondDict[key]).__name__=='dict':classLabel = classify(secondDict[key],featLabel,TestVec)else :classLabel = secondDict[key]return classLabel#决策树的存储
def storeTree(inputTree,filename):import picklefw = open(filename,'wb')pickle.dump(inputTree,fw)fw.close()def grabTree(filename):import picklefr = open(filename, 'rb')return pickle.load(fr)dataSet,labels = createDataSet()#创建数据
T = labels[:] #复制labels
myTree = createTree(dataSet,labels)
ans = classify(myTree,T,[1,0])
print(ans)
storeTree(myTree,'store.txt')
AnsTree = grabTree('store.txt')
机器学习实战2(决策树篇)相关推荐
- 机器学习实战之决策树熵的概述
机器学习实战之决策树熵的概述 一.决策树简介 二.决策树的一般流程 三.决策树构建的准备工作 1.特征选择 (1)香农熵 (2)编写代码计算经验熵 (3)信息增益 (4)编写代码计算信息增益 2.决策 ...
- 机器学习实战——绘制决策树(代码)
最近在学习Peter Harrington的<机器学习实战>,代码与书中的略有不同,但可以顺利运行. import matplotlib.pyplot as plt# 定义文本框和箭头格式 ...
- 机器学习实战之决策树(一)构造决策树
决策树(一)构造决策树 1.简介 1.1 优缺点 1.2 流程 1.3 决策树的构造 1.4 海洋生物数据 2.信息增益 2.1 几个概念 2.2 计算给定数据集的熵 3 划分数据集 3.1 按照给定 ...
- 刻意练习:机器学习实战 -- Task01. 决策树
背景 这是我们为拥有 Python 基础的同学推出的精进技能的"机器学习实战" 刻意练习活动,这也是我们本学期推出的第三次活动了. 我们准备利用8周时间,夯实机器学习常用算法,完成 ...
- 《机器学习实战》—— 决策树
目录 一.决策树的构造 1. 信息增益 2. 划分数据集 3. 递归构建决策树 二.在 Python 中使用 Matplotlib 注解绘制树形图 1. Matplotlib 2. 构造注解树 三.测 ...
- 《机器学习实战》——决策树
一 决策树 决策树是什么?决策树(decision tree)是一种基本的分类与回归方法.举个通俗易懂的例子,如下图所示的流程图就是一个决策树,长方形代表判断模块(decision block),椭圆 ...
- 机器学习实战之决策树
你是否玩过二十个问题的游戏,游戏的规则很简单:参与游戏的一方在脑海里想某个事物,其他参与者向他提问题,只允许提20个问题,问题的答案也只能用对或错回答.问问题的人通过 推断分解,逐步缩小待猜测事物的范 ...
- 《机器学习实战》决策树的应用
课本中给出的是一个预测隐形眼镜的例子. 数据集样式如下: young myope no reduced no lenses young myope no no ...
- 机器学习实战之决策树(四)示例:预测隐形眼镜类型(含数据集)
决策树(四)示例:预测隐形眼镜类型 流程 代码 决策树小结 转载请注明作者和出处:https://blog.csdn.net/weixin_45814668 微信公众号:qiongjian0427 知 ...
- 机器学习实战(二)决策树DT(Decision Tree、ID3算法)
目录 0. 前言 1. 信息增益(ID3) 2. 决策树(Decision Tree) 3. 实战案例 3.1. 隐形眼镜案例 3.2. 存储决策树 3.3. 决策树画图表示 学习完机器学习实战的决策 ...
最新文章
- 民族、学历学位、所学专业、、专业技术职务 对应表
- 基于Manim针对FT教学过程的一种可行性设计
- SQL进阶随笔--case用法(一)
- http GET 和 POST 请求的优缺点和误区 --前端优化
- dubbo指定服务提供者ip_使用指定IP调用Dubbo服务
- SpringCloud Eureka自我保护机制介绍及配置
- facebook 开源_Facebook开源主管开放
- 【译】5 个你需要知道的 JavaScript 小技巧
- Mac使用Docker搭建python测试执行环境
- 计算机应用与基础app,手机APP支持下《计算机应用基础》课程探究性学习活动设计与实验研究...
- R语言--Cox模型校准曲线原理(一)数据来源
- VirtualBox如何添加ISO文件
- 企查查的批量公司查找
- FPGA学习:Verilog基本语法
- 圈内著名ts_央视为电竞发声:AG和estar当选著名战队,梦泪,猫神被官方肯定
- RCNN到faster RCNN 简介
- DICOM协议学习笔记(二)
- 建筑八大员培训湖北劳务员培训建筑劳务人员实名制管理的问题
- 将小写金额转换成中文大写
- 计算机网络各层设备及功能讲解大汇总~
热门文章
- python 路由_静态路由配置
- 关于“前台根据后台值,操作字段、显示或select选中状态”的几种做法
- 什么是H3C的BFD MAD
- win7防火墙怎么关_电脑防火墙怎么关?这两招你得会
- duplicate symbol _OBJC_CLASS 错误处理方法
- 计算机科学与生命科学的关系,计算机科学与生命科学论文
- ipad使用的PDF书籍没有目录怎么办?
- SQL Server ansi_null_default | ansi_null_dflt_on
- 2021巢湖第一中学高考成绩查询,2021年巢湖高中学校排名及录取分数线排名
- 「macOS无法验证此APP不包含恶意软件」的处理方式