C4.5算法总结

C4.5是一系列用在机器学习和数据挖掘的分类问题中的算法。它的目标是监督学习:给定一个数据集,其中的每一个元组都能用一组属性值来描述,每一个元组属于一个互斥的类别中的某一类。C4.5的目标是通过学习,找到一个从属性值到类别的映射关系,并且这个映射能用于对新的类别未知的实体进行分类。

C4.5由J.Ross Quinlan在ID3的基础上提出的。ID3算法用来构造决策树。决策树是一种类似流程图的树结构,其中每个内部节点(非树叶节点)表示在一个属性上的测试,每个分枝代表一个测试输出,而每个树叶节点存放一个类标号。一旦建立好了决策树,对于一个未给定类标号的元组,跟踪一条有根节点到叶节点的路径,该叶节点就存放着该元组的预测。决策树的优势在于不需要任何领域知识或参数设置,适合于探测性的知识发现。

从ID3算法中衍生出了C4.5和CART两种算法,这两种算法在数据挖掘中都非常重要。下图就是一棵典型的C4.5算法对数据集产生的决策树。

比如我们判断一个人能不能结婚,那么每个人就可以作为一个具体的对象,该对象有着很多属性,比如年龄,性别,帅不帅,工作NB不,有没有女朋友,是不是富二代6个属性,而结婚也作为该对象的一个属性,而”结婚”属性就可以作为我们的预测属性!然后根据其他属性来预测我们的目标属性--结婚属性,比如说,年龄:30,性别:男,长的帅,工作不错,又女朋友,还是富二代!根据这些属性我们就可以得出该人今年可以结婚!当然这是预测出来的!这时,我们肯定有个疑问了,这是如何预测的呢?这实质上是根据我们的统计数据得出的,比如我们统计10000个人,根据这一万个人的6个属性以及目标属性(结婚)最终得出一组数据,我们用这组数据做成一个决策树!而其中这10000个人的样本我们则称为训练样本!

我们还是拿”打高尔夫球”这个经典的例子来作具体研究吧!该例其实就是通过一些列的属性来决定是否适合打高尔夫!刚刚说了训练样本,我们就来看看训练样本吧!图1是我用WPF做了一个简单的CRUD界面,用来把我们的样本显示的展现出来。具体看图1。。

图1  数据集

我们从图中可以看出,该表中共有6列,而每一列中的列名对应一个属性,而我们以实践经验知道,“Day”即日期这个属性并不能帮我们预测今天是否适合去打Golf.故该属性我们就应该选择摒弃!再看该系统中的其他5给属性。很显然,图1中我用红笔画出来的属性“Play Golf”该属性就是我们的预测属性。而其他4个属性“Outlook”(天气)”、Temperature”(温度) 、“Humdity”(湿度)、“Windy”(是否刮风)这四个属性进行判断今天去 Play Golf。

那我们接下来的工作自然就是根据属性1-4得出我们的决策树了!那么我们来想想该决策树的算法,实质上其遵循一种统一的递归模式:即,首先用根节点表示一个给定的数据集(比如在这,就是我们的14个样本);然后,从根节点开始在每个节点上测试一个特定的属性,把节点数据集划分成更小的子集(这一步,比如根据属性Outlook划分,可以划分出三个子集出来,即属于Sunny的一个子集样本,属于Overcast的子集样本,属于Rainy的子集样本),该子集并用子树进行表示;该过程就开始一直进行,直到子集称为“纯的”,也就是说直到子集中的所有实例都属于同一个类别,树才停止生长。

根据算法产生的决策树:

图2   在数据集上通过C4.5生成的决策树

算法描述

C4.5并不一个算法,而是一组算法—C4.5,非剪枝C4.5和C4.5规则。下图中的算法将给出C4.5的基本工作流程:

图3  C4.5算法流程

我们可能有疑问,一个元组本身有很多属性,我们怎么知道首先要对哪个属性进行判断,接下来要对哪个属性进行判断?换句话说,在图2中,我们怎么知道第一个要测试的属性是Outlook,而不是Windy?其实,能回答这些问题的一个概念就是属性选择度量。

属性选择度量

属性选择度量又称分裂规则,因为它们决定给定节点上的元组如何分裂。属性选择度量提供了每个属性描述给定训练元组的秩评定,具有最好度量得分的属性被选作给定元组的分裂属性。目前比较流行的属性选择度量有--信息增益、增益率和Gini指标。

先做一些假设,设D是类标记元组训练集,类标号属性具有m个不同值,m个不同类Ci(i=1,2,…,m),CiD是D中Ci类的元组的集合,|D|和|CiD|分别是D和CiD中的元组个数。

(1)信息增益

信息增益实际上是ID3算法中用来进行属性选择度量的。它选择具有最高信息增益的属性来作为节点N的分裂属性。该属性使结果划分中的元组分类所需信息量最小。对D中的元组分类所需的期望信息为下式:

 (1)

Info(D)又称为熵。

现在假定按照属性A划分D中的元组,且属性A将D划分成v个不同的类。在该划分之后,为了得到准确的分类还需要的信息由下面的式子度量:

       (2)

信息增益定义为原来的信息需求(即仅基于类比例)与新需求(即对A划分之后得到的)之间的差,即

       (3)

我想很多人看到这个地方都觉得不是很好理解,所以我自己的研究了文献中关于这一块的描述,也对比了上面的三个公式,下面说说我自己的理解。

一般说来,对于一个具有多个属性的元组,用一个属性就将它们完全分开几乎不可能,否则的话,决策树的深度就只能是2了。从这里可以看出,一旦我们选择一个属性A,假设将元组分成了两个部分A1和A2,由于A1和A2还可以用其它属性接着再分,所以又引出一个新的问题:接下来我们要选择哪个属性来分类?对D中元组分类所需的期望信息是Info(D) ,那么同理,当我们通过A将D划分成v个子集Dj(j=1,2,…,v)之后,我们要对Dj的元组进行分类,需要的期望信息就是Info(Dj),而一共有v个类,所以对v个集合再分类,需要的信息就是公式(2)了。由此可知,如果公式(2)越小,是不是意味着我们接下来对A分出来的几个集合再进行分类所需要的信息就越小?而对于给定的训练集,实际上Info(D)已经固定了,所以选择信息增益最大的属性作为分裂点。

但是,使用信息增益的话其实是有一个缺点,那就是它偏向于具有大量值的属性。什么意思呢?就是说在训练集中,某个属性所取的不同值的个数越多,那么越有可能拿它来作为分裂属性。例如一个训练集中有10个元组,对于某一个属相A,它分别取1-10这十个数,如果对A进行分裂将会分成10个类,那么对于每一个类Info(Dj)=0,从而式(2)为0,该属性划分所得到的信息增益(3)最大,但是很显然,这种划分没有意义。

(2)信息增益率

正是基于此,ID3后面的C4.5采用了信息增益率这样一个概念。信息增益率使用“分裂信息”值将信息增益规范化。分类信息类似于Info(D),定义如下:

    (4)

这个值表示通过将训练数据集D划分成对应于属性A测试的v个输出的v个划分产生的信息。信息增益率定义:

         (5)

选择具有最大增益率的属性作为分裂属性。

(3)Gini指标

Gini指标在CART中使用。Gini指标度量数据划分或训练元组集D的不纯度,定义为:

         (6)

C#实现代码如下:

计算信息增益率:

  1 using System;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 using System.Data;
  5 using System.Linq;
  6 using System.Text;
  7 using System.Threading.Tasks;
  8
  9 namespace C4._5.BLL
 10 {
 11     public class Entropy
 12     {
 13         public int[] statNum = new int[2];//训练统计结果:0->No  1->Yes
 14         public double EntropyValue = 0;
 15         private int mTotal = 0;
 16         private string mTargetAttribute = "PlayGolf";
 17
 18         public void getEntropy(DataTable samples)
 19         {
 20             CountTotalClass(samples,out statNum[0],out statNum[1]);
 21             EntropyValue = CalcEntropy(statNum[0],statNum[1]);
 22         }
 23         /// <summary>
 24         /// 统计各个样本集合中所包含的目标属性Yes或者No的数目
 25         /// </summary>
 26         public void CountTotalClass(DataTable samples,out int no,out int yes)
 27         {
 28             yes = no = 0;
 29             foreach (DataRow aRow in samples.Rows)
 30             {
 31                 if ((string)aRow[mTargetAttribute] == "Yes")
 32                     yes++;
 33                 else if ((string)aRow[mTargetAttribute] == "No")
 34                     no++;
 35                 else
 36                     throw new Exception("出错!");
 37             }
 38         }
 39         /// <summary>
 40         /// 计算熵值
 41         /// </summary>
 42         /// <returns></returns>
 43         public double CalcEntropy(int no,int yes)
 44         {
 45             double entropy = 0;
 46             double total = (double)(yes + no);
 47             double p = 0;
 48             if (no != 0)
 49             {
 50                 p = no / total;
 51                 entropy += -p * Math.Log(p,2);
 52             }
 53             if (yes != 0)
 54             {
 55                 p = yes / total;
 56                 entropy += -p * Math.Log(p, 2);
 57             }
 58             return entropy;
 59         }
 60         /// <summary>
 61         /// 该注释可能有问题,从属性中的样本集合中得到yes或者no的数目
 62         /// </summary>
 63         /// <param name="samples"></param>
 64         /// <param name="attribute"></param>
 65         /// <param name="value"></param>
 66         /// <param name="no"></param>
 67         /// <param name="yes"></param>
 68         public void GetValuesToAttribute(DataTable samples, Attribute attribute, string value, out int no, out int yes)
 69         {
 70             no = yes = 0;
 71             foreach (DataRow row in samples.Rows)
 72             {
 73                 if ((string)row[attribute.AttributeName] == value)
 74                 {
 75                     if ((string)row[mTargetAttribute] == "No")
 76                     {
 77                         no++;
 78                     }
 79                     else if ((string)row[mTargetAttribute] == "Yes")
 80                     {
 81                         yes++;
 82                     }
 83                     else
 84                     {
 85                         throw new Exception("出错");
 86                     }
 87                 }
 88             }
 89         }
 90         /// <summary>
 91         /// 计算信息收益
 92         /// </summary>
 93         /// <param name="samples"></param>
 94         /// <param name="attribute"></param>
 95         /// <returns></returns>
 96         public double Gain(DataTable samples, Attribute attribute)
 97         {
 98             mTotal = samples.Rows.Count;
 99             string[] values=attribute.values;
100             double sum=0.0;
101             for (int i = 0; i < values.Length; i++)
102             {
103                 int no, yes;
104                 no = yes = 0;
105                 GetValuesToAttribute(samples,attribute,values[i],out no,out yes);
106                 if (yes == (yes + no) || no == (yes + no))
107                 {
108                     sum += 0;
109                 }
110                 else
111                 {
112                     sum += (double)(yes + no) / (double)mTotal * (-(double)yes / (double)(yes + no) * Math.Log(((double)yes / (double)(yes + no)), 2) - (double)no / (double)(yes + no) * Math.Log(((double)no / (double)(yes + no)), 2));
113                 }
114             }
115             return SplitInfo(samples,mTargetAttribute)- sum;
116         }
117         /// <summary>
118         /// 获得targetAttribute属性下的所有属性值
119         /// </summary>
120         /// <param name="samples"></param>
121         /// <param name="targetAttribute"></param>
122         /// <returns></returns>
123         private ArrayList GetDistinctValues(DataTable samples, string targetAttribute)
124         {
125             ArrayList distinctValues = new ArrayList(samples.Rows.Count);
126             foreach (DataRow row in samples.Rows)
127             {
128                 if (distinctValues.IndexOf(row[targetAttribute]) == -1)
129                     distinctValues.Add(row[targetAttribute]);
130             }
131             return distinctValues;
132         }
133         /// <summary>
134         /// 按某个属性值计算该属性的熵值
135         /// </summary>
136         /// <param name="samples"></param>
137         /// <param name="attribute"></param>
138         /// <returns></returns>
139         public double SplitInfo(DataTable samples, string attribute)
140         {
141             ArrayList values = GetDistinctValues(samples,attribute);
142             for (int i = 0; i < values.Count; i++)
143             {
144                 if (values[i] == null || (string)values[i] == "")
145                 {
146                     values.RemoveAt(i);
147                 }
148             }
149             int[] count=new int[values.Count];
150             for (int i = 0; i < values.Count; i++)
151             {
152                 foreach (DataRow aRow in samples.Rows)
153                 {
154                     if ((string)aRow[attribute] == (string)values[i])
155                         count[i]++;
156                 }
157             }
158             double entropy = 0;
159             double total = samples.Rows.Count;
160             double p = 0;
161             for (int i = 0; i < values.Count; i++)
162             {
163                 if (count[i] != 0)
164                 {
165                     p = count[i] / total;
166                     entropy += -p * Math.Log(p,2);
167                 }
168             }
169             return entropy;
170         }
171         /// <summary>
172         /// 获得指定属性的信息增益率
173         /// </summary>
174         /// <param name="samples">样本集合</param>
175         /// <param name="attribute"></param>
176         /// <returns></returns>
177         public double GainRatio(DataTable samples, Attribute attribute)
178         {
179             double splitInfoA = this.SplitInfo(samples,attribute.AttributeName);//计算各个属性的熵值
180             double gainA = Gain(samples,attribute);//信息增益
181             double gainRatioA = gainA / splitInfoA;
182             return gainRatioA;
183         }
184     }
185 }

构造决策树:

 public class DTree_ID3
    {
        private string mTargetAttribute = "result";
        public Entropy en = new Entropy();
        public TreeNode roots;
        /// <summary>
        /// 获得信息增益率最大的属性
        /// </summary>
        /// <param name="samples"></param>
        /// <param name="attributes"></param>
        /// <returns></returns>
        private Attribute getBestAttribute(DataTable samples,Attribute[] attributes)
        {
            double maxGain = 0.0;
            Attribute bestAttribute = null;
            foreach (Attribute attribute in attributes)
            {
                double aux = en.GainRatio(samples,attribute);
                if (aux > maxGain)
                {
                    maxGain = aux;
                    bestAttribute = attribute;
                }
            }
            return bestAttribute;
        }
        /// <summary>
        /// 判断样例集是否属于同一类,即该样例集是否是"纯"的,是则返回此属性值,否则返回Null
        /// </summary>
        /// <param name="samples"></param>
        /// <param name="targetAttribute"></param>
        /// <returns></returns>
        public string AllSamplesSameClass(DataTable samples, string targetAttribute)
        {
            DataRow row = samples.Rows[0];
            string targetValue = (string)row[targetAttribute];
            for (int i = 1; i < samples.Rows.Count; i++)
            {
                if (targetValue!=samples.Rows[i][targetAttribute].ToString())
                {
                    return null;
                }
            }
            return targetValue;
        }
        /// <summary>
        /// 获得属性的目标属性的值(解释有可能错误)
        /// </summary>
        /// <param name="samples"></param>
        /// <param name="targetAttribute"></param>
        /// <returns></returns>
        private ArrayList GetDistinctValues(DataTable samples, string targetAttribute)
        {
            ArrayList distinctValues = new ArrayList(samples.Rows.Count);
            foreach (DataRow row in samples.Rows)
            {
                if (distinctValues.IndexOf(row[targetAttribute]) == -1)
                    distinctValues.Add(row[targetAttribute]);
            }
            return distinctValues;
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="samples"></param>
        /// <param name="targetAttribute"></param>
        /// <returns></returns>
        private object GetMostCommonValue(DataTable samples, string targetAttribute)
        {
            ArrayList distinctValues = GetDistinctValues(samples,targetAttribute);
            int[] count=new int[distinctValues.Count];
            foreach (DataRow row in samples.Rows)
            {
                int index = distinctValues.IndexOf(row[targetAttribute]);
                count[index]++;
            }
            int MaxIndex = 0;
            int MaxCount = 0;
            for (int i = 0; i < count.Length; i++)
            {
                if (count[i] > MaxCount)
                {
                    MaxCount = count[i];
                    MaxIndex = i;
                }
            }
            return distinctValues[MaxIndex];
        }
        /// <summary>
        /// 构造决策树
        /// </summary>
        /// <param name="samples">样本集合</param>
        /// <param name="targetAttribute">目标属性</param>
        /// <param name="attributes">该样本所含的属性集合</param>
        /// <returns></returns>
        private TreeNode BuildTree(DataTable samples, string targetAttribute, Attribute[] attributes)
        {
            TreeNode temp = new TreeNode();
            //如果samples中的元祖是同一类C
            string c = AllSamplesSameClass(samples,targetAttribute);
            if (c != null)   //返回N作为叶节点,以类C标记
                return new TreeNode(new Attribute(c).AttributeName + c);

            //if attributes为空,then
            if (attributes.Length == 0)//返回N作为叶子节点,标记为D中的多数类,多数表决
            {
                return new TreeNode(new Attribute(GetMostCommonValue(samples,targetAttribute)).AttributeName);
            }
            //计算目标属性的熵值,即PlayGolf的熵值
            mTargetAttribute = targetAttribute;
            en.getEntropy(samples);
            //找出最好的分类属性,即信息熵最大的
            Attribute bestAttribute = getBestAttribute(samples,attributes);
            //标记为节点root
            DTreeNode root = new DTreeNode(bestAttribute);
            temp.Text = bestAttribute.AttributeName;

            DataTable aSample = samples.Clone();
            //为bestAttribute的每个输出value划分元祖并产生子树
            foreach (string value in bestAttribute.values)
            {
                aSample.Rows.Clear();
                //aSamples为满足输出value的集合,即一个划分(分支)
                DataRow[] rows = samples.Select(bestAttribute.AttributeName+"="+"‘"+value+"‘");
                foreach (DataRow row in rows)
                {
                    aSample.Rows.Add(row.ItemArray);
                }
                //删除划分属性
                ArrayList aArributes = new ArrayList(attributes.Length-1);
                for (int i = 0; i < attributes.Length; i++)
                {
                    if (attributes[i].AttributeName != bestAttribute.AttributeName)
                    {
                        aArributes.Add(attributes[i]);
                    }
                }
                //如果aSample为空,加一个树叶到节点N,标记为aSample中的多数类
                if (aSample.Rows.Count == 0)
                {
                    TreeNode leaf = new TreeNode();
                    leaf.Text = GetMostCommonValue(samples, targetAttribute).ToString() + "(" + value + ")";
                    temp.Nodes.Add(leaf);
                }
                else  //加一个由BulidTree(samples,targetAttribute,attributes)返回的节点到节点N
                {
                    DTree_ID3 dc3 = new DTree_ID3();
                    TreeNode ChildNode = dc3.BuildTree(aSample,targetAttribute,(Attribute[])aArributes.ToArray(typeof(Attribute)));
                    ChildNode.Text += "(" + value + ")";
                    temp.Nodes.Add(ChildNode);
                }
            }
            roots = temp;
            return temp;
        }
        public TreeNode MountTree(DataTable samples, string targetAttribute, Attribute[] attributes)
        {
            return BuildTree(samples, targetAttribute, attributes);
        }
    }
时间: 2024-11-08 23:00:32

C4.5算法总结的相关文章

决策树-预测隐形眼镜类型 (ID3算法,C4.5算法,CART算法,GINI指数,剪枝,随机森林)

1. 1.问题的引入 2.一个实例 3.基本概念 4.ID3 5.C4.5 6.CART 7.随机森林 2. 我们应该设计什么的算法,使得计算机对贷款申请人员的申请信息自动进行分类,以决定能否贷款? 一个女孩的母亲要给这个女孩介绍男朋友,于是有了下面的对话: 女儿:多大年纪了? 母亲:26. 女儿:长的帅不帅? 母亲:挺帅的. 女儿:收入高不? 母亲:不算很高,中等情况. 女儿:是公务员不? 母亲:是,在税务局上班呢. 女儿:那好,我去见见. 决策过程: 这个女孩的决策过程就是典型的分类树决策.

决策树之C4.5算法学习

决策树<Decision Tree>是一种预测模型,它由决策节点,分支和叶节点三个部分组成.决策节点代表一个样本测试,通常代表待分类样本的某个属性,在该属性上的不同测试结果代表一个分支:分支表示某个决策节点的不同取值.每个叶节点代表一种可能的分类结果. 使用训练集对决策树算法进行训练,得到一个决策树模型,利用模型对未知样本(类别未知)的类别判断时,从决策树根节点开始,从上到下搜索,直到沿某分支到达叶节点,叶节点的类别标签就是该未知样本的类别. 网上有个例子可以很形象的说明利用决策树决策的过程(

决策树-C4.5算法(三)

在上述两篇的文章中主要讲述了决策树的基础,但是在实际的应用中经常用到C4.5算法,C4.5算法是以ID3算法为基础,他在ID3算法上做了如下的改进: 1) 用信息增益率来选择属性,克服了用信息增益选择属性时偏向选择取值多的属性的不足,公式为GainRatio(A): 2) 在树构造过程中进行剪枝: 3) 能够完成对连续属性的离散化处理: 4) 能够对不完整数据进行处理. C4.5算法与其它分类算法如统计方法.神经网络等比较起来有如下优点:产生的分类规则易于理解,准确率较高.其缺点是:在构造树的过

C4.5算法

C4.5是一套用来处理分类问题的算法,属于有监督学习的类型,每个实例由一组属性来描述,每个实例仅属于一个类别. 如下是一个数据集 算法的发展历史 J.Ross Quinlan设计的C4.5算法源于名为ID3的一种决策树诱导算法. 而ID3是迭代分解器(iterative dichotomizers)系列算法的第3代. 除了可以分类之外,C4.5还可以具有良好可理解性的规则. Friedman的Original Tree算法. Breiman,Olshen和Stone的呢个人参与下发展为CART算

决策分类树算法之ID3,C4.5算法系列

一.引言 在最开始的时候,我本来准备学习的是C4.5算法,后来发现C4.5算法的核心还是ID3算法,所以又辗转回到学习ID3算法了,因为C4.5是他的一个改进.至于是什么改进,在后面的描述中我会提到. 二.ID3算法 ID3算法是一种分类决策树算法.他通过一系列的规则,将数据最后分类成决策树的形式.分类的根据是用到了熵这个概念.熵在物理这门学科中就已经出现过,表示是一个物质的稳定度,在这里就是分类的纯度的一个概念.公式为: 在ID3算法中,是采用Gain信息增益来作为一个分类的判定标准的.他的定

C4.5算法(摘抄)

1. C4.5算法简介 C4.5是一系列用在机器学习和数据挖掘的分类问题中的算法.它的目标是监督学习:给定一个数据集,其中的每一个元组都能用一组属性值来描述,每一个元组属于一个互斥的类别中的某一类.C4.5的目标是通过学习,找到一个从属性值到类别的映射关系,并且这个映射能用于对新的类别未知的实体进行分类. C4.5由J.Ross Quinlan在ID3的基础上提出的.ID3算法用来构造决策树.决策树是一种类似流程图的树结构,其中每个内部节点(非树叶节点)表示在一个属性上的测试,每个分枝代表一个测

C4.5算法(一)代码实现

入门学习机器学习的十大算法,第一站就是C4.5算法.C4.5是一种决策树算法,属于监督学习.先给一个样本集,从而建立一棵决策树,然后根据这个决策树来对后续的数据做决策. 作为没有相关背景知识和系统学习过的人,当然要边学边记啦.C4.5算法我的学习步骤是这样: step 1: 了解清楚算法的逻辑,以及编程实现 step 2: 其中对连续变量的离散化处理 step 3: C4.5的剪枝 step 4: C4.5算法的spark实现 因为个人认为C4.5算法中比较难和重要的两个点就是对连续变量的离散化

机器学习总结(八)决策树ID3,C4.5算法,CART算法

本文主要总结决策树中的ID3,C4.5和CART算法,各种算法的特点,并对比了各种算法的不同点. 决策树:是一种基本的分类和回归方法.在分类问题中,是基于特征对实例进行分类.既可以认为是if-then规则的集合,也可以认为是定义在特征空间和类空间上的条件概率分布. 决策树模型:决策树由结点和有向边组成.结点一般有两种类型,一种是内部结点,一种是叶节点.内部结点一般表示一个特征,而叶节点表示一个类.当用决策树进行分类时,先从根节点开始,对实例的某一特征进行测试,根据测试结果,将实例分配到子结点.而

C4.5算法学习

C4.5属于决策树算法的分类树决策树更是常见的机器学习方法,可以帮助我们解决分类与回归两类问题.以决策树作为起点的原因很简单,因为它非常符合我们人类处理问题的方法,而且逻辑清晰,可解释性好.从婴儿到长者,我们每天都使用无数次! 决策树的总体流程: 总体流程 分而治之(devide and conquer) 自根结点的递归过程 从每一个中间结点寻找一个划分(split and test)的属性 三种停止条件: 当前结点包含的样本属于同一类别,无需划分 当前属性集为空,或是所有样本在所有属性值上取值