<pre name="code" class="java">private void handleNumericAttribute(Instances trainInstances)
<span style="white-space:pre"> </span>throws Exception {
int firstMiss;//最后一个有效instance的下标
int next = 1;//下一个instance的index
int last = 0;//当前instance的index
int splitIndex = -1;//分裂点
double currentInfoGain;//当前信息增益
double defaultEnt;//分割之前的信息熵
double minSplit;
Instance instance;
int i;
//首先新建一个分布,数值型默认处理为2维分布,也就可以理解为小于某个值放到一个Bag里,其余的放到另外一个Bag里
m_distribution = new Distribution(2,trainInstances.numClasses());
Enumeration enu = trainInstances.enumerateInstances();
i = 0;
//注意instances传入的时候是排好序的,这个排序保证了missingValue放在最后面,所以读到了missingValue其之后肯定都是miss//ingValue,换言之,firstMiss在循环之后代表了最后一个有效的instance的下标。
while (enu.hasMoreElements()) {
<span style="white-space:pre"> </span>instance = (Instance) enu.nextElement();
if (instance.isMissing(m_attIndex))
<span style="white-space:pre"> </span>break;
m_distribution.add(1,instance);
i++;
}
firstMiss = i;//循环结束后,m_distribution里放入了所有的有效instance,并全放入了bag1里。
<span style="white-space:pre"> </span>//minSplit是最后分类好每个Bag里最小的数据的量,也就是0.1*每个类的均值。
minSplit = 0.1*(m_distribution.total())/
<span style="white-space:pre"> </span>((double)trainInstances.numClasses());
if (Utils.smOrEq(minSplit,m_minNoObj))
minSplit = m_minNoObj;
else if (Utils.gr(minSplit,25))
minSplit = 25;
//如果有效数据总量不到2*minSplit,换言之无论怎么分均不能保证2个bag里的数量大于minSplit,就直接返回。
if (Utils.sm((double)firstMiss,2*minSplit))
return;
//defaultEnt代表旧的信息熵,也就是对该属性进行分类之前,Indexclass对应的信息熵。
defaultEnt = infoGainCrit.oldEnt(m_distribution);
while (next < firstMiss) {
if (trainInstances.instance(next-1).value(m_attIndex)+1e-5 <
trainInstances.instance(next).value(m_attIndex)) {
//Instances里的记录是升序排列的,加上这个条件默认把值相差很小的Instance就当做同一个instance处理了
<span style="white-space:pre"> </span>//last代表当前,next代表下一个,默认next=1,last=0,所以shiftRange可以理解成把当前记录从bag1移动到bag0中
//注意一开始初始化时候所有的都是在bag1里面的。
m_distribution.shiftRange(1,0,trainInstances,last,next);
//如果两个bag都满足最小数据集的数量minSplit
if (Utils.grOrEq(m_distribution.perBag(0),minSplit) &&
Utils.grOrEq(m_distribution.perBag(1),minSplit)) {
//算一下信息增益
currentInfoGain = infoGainCrit. splitCritValue(m_distribution,m_sumOfWeights,defaultEnt);
if (Utils.gr(currentInfoGain,m_infoGain)) {
m_infoGain = currentInfoGain;//如果信息增益比当前最大的要大,则替换当前最大的值,并记录splitIndex
splitIndex = next-1;
}
m_index++;
}
last = next;
}
next++;
}
if (m_index == 0)
return; //执行到这里说明没找到一个合适的分裂点,直接返回。
// 计算最佳信息增益
m_infoGain = m_infoGain-(Utils.log2(m_index)/m_sumOfWeights);
if (Utils.smOrEq(m_infoGain,0))
return; //如果信息增益是0也说明没找到合适的分裂点,直接返回。
//剩下的就是根据分裂点进行属性的划分。
m_numSubsets = 2;
m_splitPoint = (trainInstances.instance(splitIndex+1).value(m_attIndex)+
trainInstances.instance(splitIndex).value(m_attIndex))/2;
// In case we have a numerical precision problem we need to choose the
// smaller value
if (m_splitPoint == trainInstances.instance(splitIndex + 1).value(m_attIndex)) {
<span style="white-space:pre"> </span>m_splitPoint = trainInstances.instance(splitIndex).value(m_attIndex);
}
// Restore distributioN for best split.
m_distribution = new Distribution(2,trainInstances.numClasses());
m_distribution.addRange(0,trainInstances,0,splitIndex+1);
m_distribution.addRange(1,trainInstances,splitIndex+1,firstMiss);
// Compute modified gain ratio for best split.
m_gainRatio = gainRatioCrit.
splitCritValue(m_distribution,m_sumOfWeights,
m_infoGain);
}