《写给程序员的数据挖掘实践指南》——5.4一个编程的例子

5.4一个编程的例子

回到上一章当中提到的来自卡内基梅隆大学的汽车MPG数据集,该数据集的格式如下:

下面试图基于气缸的数目、排水量(立方英寸)、功率、重量和加速时间预测汽车的MPG。我将所有392个实例放到mpgData.txt文件中,然后编写了如下的短Python程序,该程序利用分层采样方法将数据分到10个桶中(数据集及Python代码都可以从网站guidetodatamining.com下载)。

import random
def buckets(filename, bucketName, separator, classColumn):
  """the original data is in the file named filename
  bucketName is the prefix for all the bucket names
  separator is the character that divides the columns
  (for ex., a tab or comma) and classColumn is the column
  that indicates the class"""

  # put the data in 10 buckets
  numberOfBuckets = 10
  data = {}
  # first read in the data and divide by category
  with open(filename) as f:
    lines = f.readlines()
  for line in lines:
    if separator != '\t':
      line = line.replace(separator, '\t')
    # first get the category
    category = line.split()[classColumn]
    data.setdefault(category, [])
    data[category].append(line)
  # initialize the buckets
  buckets = []
  for i in range(numberOfBuckets):
    buckets.append([])
  # now for each category put the data into the buckets
  for k in data.keys():
    #randomize order of instances for each class
  random.shuffle(data[k])
  bNum = 0
  # divide into buckets
  for item in data[k]:
    buckets[bNum].append(item)
    bNum = (bNum + 1) % numberOfBuckets
  # write to file
  for bNum in range(numberOfBuckets):
    f = open("%s-%02i" % (bucketName, bNum + 1), 'w')
    for item in buckets[bNum]:
      f.write(item)
    f.close()

buckets("mpgData.txt", 'mpgData','\t',0)

执行上述代码会产生10个分别为mpgData01、mpgData02… mpgData10的文件。

能否修改上一章中近邻算法的代码,以使test函数能够在刚刚构建的10个文件上进行10折交叉验证(该数据集可以从网站guidetodatamining.com下载)?

你的程序应该输出类似如下矩阵的混淆矩阵:

该解答只涉及如下方面:

修改initializer方法以便从9个桶中读取数据;

加入一个新的方法对一个桶中的数据进行测试;

加入一个新的过程来执行10折交叉验证过程。

下面依次来考察上述修改。

initializer方法的签名看起来如下:

def __init__(self, bucketPrefix, testBucketNumber, dataFormat):

每个桶的文件名类似于mpgData-01、mpgData-02,等等。这种情况下,bucketPrefix将是“mpgData”,而testBucketNumber是包含测试数据的桶。如果testBucketNumber为3,则分类器将会在桶1、2、4、5、6、7、8、9、10上进行训练。dataFormat是一个如何解释数据中每列的字符串,比如:

"class  num  num  num  num  num  comment"

它表示第一列代表实例的类别,下面5列代表实例的数值型属性,最后一列会被看成注释。

新的初始化方法的完整代码如下:

import copy

class Classifier:
  def __init__(self, bucketPrefix, testBucketNumber, dataFormat):

    """ a classifier will be built from files with the bucketPrefix
    excluding the file with textBucketNumber. dataFormat is a
    string that describes how to interpret each line of the data
    files. For example, for the mpg data the format is:
    "class num  num  num  num  num  comment"
    """
    self.medianAndDeviation = []

    # reading the data in from the file
    self.format = dataFormat.strip().split('\t')
    self.data = []
    # for each of the buckets numbered 1 through 10:
    for i in range(1, 11):
      # if it is not the bucket we should ignore, read the data
      if i != testBucketNumber:
        filename = "%s-%02i" % (bucketPrefix, i)
        f = open(filename)
        lines = f.readlines()
        f.close()
        for line in lines:
          fields = line.strip().split('\t')
          ignore = []
          vector = []
          for i in range(len(fields)):
            if self.format[i] == 'num':
              vector.append(float(fields[i]))
            elif self.format[i] == 'comment':
              ignore.append(fields[i])
            elif self.format[i] == 'class':
              classification = fields[i]
          self.data.append((classification, vector, ignore))
  self.rawData = copy.deepcopy(self.data)
  # get length of instance vector
  self.vlen = len(self.data[0][1])
  # now normalize the data
  for i in range(self.vlen):
    self.normalizeColumn(i)

testBucket方法
下面编写一个新的方法来测试一个桶中的数据。

def testBucket(self, bucketPrefix, bucketNumber):
  """Evaluate the classifier with data from the file
  bucketPrefix-bucketNumber"""

  filename = "%s-%02i" % (bucketPrefix, bucketNumber)
  f = open(filename)
  lines = f.readlines()
  totals = {}
  f.close()
  for line in lines:
    data = line.strip().split('\t')
    vector = []
    classInColumn = -1
    for i in range(len(self.format)):
      if self.format[i] == 'num':
        vector.append(float(data[i]))
      elif self.format[i] == 'class':
        classInColumn = i
    theRealClass = data[classInColumn]
    classifiedAs = self.classify(vector)
    totals.setdefault(theRealClass, {})
    totals[theRealClass].setdefault(classifiedAs, 0)
    totals[theRealClass][classifiedAs] += 1
  return totals

它以bucketPrefix和bucketNumber为输入,如果前者为“mpgData”、后者为3的话,测试数据将会从文件mpgData-03中读取,而testBucket将会返回如下格式的字典:

{'35':   {'35': 1, '20': 1, '30': 1},
 '40':   {'30': 1},
 '30':   {'35': 3, '30': 1, '45': 1, '25': 1},
 '15':   {'20': 3, '15': 4, '10': 1},
 '10':   {'15': 1},
 '20':   {'15': 2, '20': 4, '30': 2, '25': 1},
 '25':   {'30': 5, '25': 3}}

字典的键代表的是实例的真实类别。例如,上面第一行表示真实类别为35mpg的实例的结果。每个键的值是另一部字典,该字典代表分类器对实例进行分类的结果。例如行

'15':    {'20': 3, '15': 4, '10': 1},

表示实际为15mpg的3个实例被错分到20mpg类别中,而有4个实例被正确分到15mpg中,1个实例被错分到10mpg中。

10折交叉验证的执行流程
最后,我们需要编写一个过程来实现10折交叉验证。也就是说,我们要构造10个分类器。每个分类器利用9个桶中的数据进行训练,而将其余数据用于测试。

def tenfold(bucketPrefix, dataFormat):
  results = {}
  for i in range(1, 11):
    c = Classifier(bucketPrefix, i, dataFormat)
    t = c.testBucket(bucketPrefix, i)
    for (key, value) in t.items():
      results.setdefault(key, {})
      for (ckey, cvalue) in value.items():
        results[key].setdefault(ckey, 0)
        results[key][ckey] += cvalue

    # now print results
  categories = list(results.keys())
  categories.sort()
  print(   "\n   Classified as: ")
  header = "     "
  subheader = "   +"
  for category in categories:
    header += category + "  "
    subheader += "----+"
  print (header)
  print (subheader)
  total = 0.0
  correct = 0.0
  for category in categories:
    row = category + " |"
    for c2 in categories:
      if c2 in results[category]:
        count = results[category][c2]
      else:
        count = 0
      row += " %2i |" % count
      total += count
      if c2 == category:
        correct += count
    print(row)
  print(subheader)
  print("\n%5.3f percent correct" %((correct * 100) / total))
  print("total of %i instances" % total)

tenfold("mpgData", "class num num num num num comment")

运行上述程序会产生如下结果:

时间: 2024-10-25 20:09:46

《写给程序员的数据挖掘实践指南》——5.4一个编程的例子的相关文章

《写给程序员的数据挖掘实践指南》——第5章 分类的进一步探讨—算法评估及kNN

第5章 分类的进一步探讨-算法评估及kNN 写给程序员的数据挖掘实践指南 回到上一章中关于运动员的例子.在那个例子中我们构建了一个分类器,输入为运动员的身高.体重,输出为其从事的体育项目-体操.田径或篮球. 因此,左图的Marissa Coleman身高6英尺1英寸,体重160磅.我们的分类器能够将她正确判断为篮球运动员: >>> cl = Classifier('athletesTrainingSet.txt') >>> cl.classify([73, 160])

《写给程序员的数据挖掘实践指南》——1.4本书体例

1.4本书体例 本书秉承践行学习法.我建议大家用书中提供的Python代码来进行习题练习和实验,而不是被动地阅读本书.多多实验.深入代码并在多个不同数据集上尝试本书中的方法是真正理解技术的关键. 我努力做到如下两个方面之间的平衡:一方面,我详细介绍实用的数据挖掘Python代码以便读者可以使用并对其进行修改:另一方面,读者也能了解背后的数据挖掘技术.为防止读者阅读理论.数学及Python代码时大脑的思维停滞,我加入插图和图片来刺激大脑的另一部分. Google研究院院长彼得·诺维德(Peter

《写给程序员的数据挖掘实践指南》导读

前言 写给程序员的数据挖掘实践指南 在你面前是一个学习基本的数据挖掘技术的工具.绝大多数数据挖掘教材关注数据挖掘的基础理论知识,因此众所周知给读者带来理解上的困难.当然,不要误解我的意思,那些书中的知识相当重要.但是,如果你是一名想学习一点数据挖掘知识的程序员,你可能会对入门者实用手册感兴趣.而这正是本书的宗旨所在. 本书内容采用"做中学"的思路来组织.我希望读者不是被动地阅读本书,而是通过课后习题和本书提供的Python代码进行实践.我也希望读者积极参与到数据挖掘技术的编程当中.本书

《写给程序员的数据挖掘实践指南》——第1章 数据挖掘简介及本书使用方法

第1章 数据挖掘简介及本书使用方法写给程序员的数据挖掘实践指南假想150年前一个美国小镇的生活情形:大家都互相认识:百货店某天进了一批布料,店员注意到这批布料中某个特定毛边的样式很可能会引起Clancey夫人的高度兴趣,因为他知道Clancey夫人喜欢亮花纹样:于是他在心里记着等Clancey夫人下次光顾时将该布料拿给她看看:Chow Winkler告诉酒吧老板Wilson先生,他考虑将多余的雷明顿(Renmington)1来福枪出售:Wilson先生将这则消息告诉Bud Barclay,因为他

《写给程序员的数据挖掘实践指南》——5.3混淆矩阵

5.3混淆矩阵 到目前为止,通过计算下列精确率百分比,我们对分类器进行评估: 有时,我们可能希望得到分类器算法的更详细的性能.能够详细揭示性能的一种可视化方法是引入一个称为混淆矩阵(confusion matrix)的表格.混淆矩阵的行代表测试样本的真实类别,而列代表分类器所预测出的类别. 它之所以名为混淆矩阵,是因为很容易通过这个矩阵看清楚算法产生混淆的地方.下面以女运动员分类为例来展示这个矩阵.假设我们有一个由100名女子体操运动员.100名WNBA篮球运动员及100名女子马拉松运动员的属性

《写给程序员的数据挖掘实践指南》——1.3TB级挖掘是现实不是科幻

1.3TB级挖掘是现实不是科幻 20世纪末,100万词的数据集被认为很大.20世纪90年代我在读研究生时(是的,我有那么老)在Greek New Testament做了一年程序员.大约20万单词的分析就大到不能放在主机内存里而不得不把结果缓存在磁带上,因此必须要安装磁带机. Barbara Friberg依据该结果写成Analytical Greek New Testament一书并公开出版(可以从亚马逊网站上订购).我是当时在明尼苏达大学完成该项目的3名程序员之一. 现在在数T信息上进行数据挖

《写给程序员的数据挖掘实践指南》——5.1训练集和测试集

5.1训练集和测试集 前一章的最后部分中,我们使用了3个不同的数据集:女子运动员数据集.Iris数据集以及汽车MPG数据集.我们把每个数据集分成两个子集,一个用于构建分类器,该数据集称为训练集(training set).另一个数据集用于评估分类器,该数据集称为测试集(test set).训练集和测试集是数据挖掘中的常用术语. 数据挖掘领域的人永远不会在用于训练系统的数据上进行测试!下面以近邻算法为例来解释为什么不能使用训练数据来测试.如果上述例子中的篮球运动员Marissa Coleman在训

《写给程序员的数据挖掘实践指南》——5.6近邻算法的改进

5.6近邻算法的改进 一个普通的分类器的例子是Rote分类器,它只记忆所有的训练集,仅当实例与训练样本精确匹配时才对实例进行分类.如果只在训练集上进行评估,那么Rote分类器的精确率一直是100%.在实际中,由于有些待分类的实例不在训练集中出现,因此Rote分类器并不是一个好的选择.我们可以将前面介绍的近邻分类器看成是Rote分类器的一个扩展.与Rote分类器寻找精确匹配不同的是,近邻方法寻找近似的匹配.Pang Ning Tan.Michael Steinbach和Vipin Kumar在他们

《写给程序员的数据挖掘实践指南》——5.7一个新数据集及挑战

5.7一个新数据集及挑战 现在到考察一个新数据集的时候了,该数据集是美国国立糖尿病.消化和肾脏疾病研究所(United States National Institute of Diabetes and Digestive and Kidney Diseases,简称NIDDK)所开发的皮马印第安人糖尿病数据集(Pima Indians Diabetes Data Set). 令人吃惊的是,有超过30%的皮马人患有糖尿病.与此形成对照的是,美国糖尿病的患病率为8.3%,中国为4.2%. 数据集中