protected virtual IDecisionTreeNode BuildDecisionNode( IDataFrame dataFrame, string dependentFeatureName, IDecisionTreeModelBuilderParams additionalParams, IAlredyUsedAttributesInfo alreadyUsedAttributesInfo, int treeDepth, bool isFirstSplit = false) { if (dataFrame.GetColumnVector <object>(dependentFeatureName).DataItems.Distinct().Count() == 1 || MaximalTreeDepthHasBeenReached(additionalParams, treeDepth)) { return(BuildLeaf(dataFrame, dependentFeatureName)); } // TODO: later on add additional params indicating which features were already used ISplittingResult splitResult = BestSplitSelector.SelectBestSplit( dataFrame, dependentFeatureName, SplitQualityChecker, alreadyUsedAttributesInfo); if (SplitIsEmpty(splitResult)) { return(BuildLeaf(dataFrame, dependentFeatureName)); } if (additionalParams.UsePrunningHeuristicDuringTreeBuild && this.StatisticalSignificanceChecker != null) { var isSplitSignificant = StatisticalSignificanceChecker.IsSplitStatisticallySignificant( dataFrame, splitResult, dependentFeatureName); if (!isSplitSignificant) { return(BuildLeaf(dataFrame, dependentFeatureName)); } } var children = new ConcurrentDictionary <IDecisionTreeLink, IDecisionTreeNode>(); if (isFirstSplit) { Parallel.ForEach( splitResult.SplittedDataSets, splitData => { this.AddChildFromSplit(dependentFeatureName, additionalParams, splitData, children, alreadyUsedAttributesInfo, treeDepth + 1); }); } else { foreach (var splitData in splitResult.SplittedDataSets) { this.AddChildFromSplit(dependentFeatureName, additionalParams, splitData, children, alreadyUsedAttributesInfo, treeDepth + 1); } } return(BuildConcreteDecisionTreeNode(splitResult, children)); }