R語言學習系列(資料採礦之決策樹演算法實現–ID3代碼篇)

來源:互聯網
上載者:User

1、輔助類,用於計算過程和結果儲存

/// <summary>    /// 決策樹節點.    /// </summary>    public class DecisionTreeNode    {        /// <summary>        /// 類型:分支或葉子        /// </summary>        public string Type { get; set; }        /// <summary>        /// 關鍵字一般存當前屬性因子        /// </summary>        public string Key { get; set; }        /// <summary>        /// 判斷值,葉子節點有效.        /// </summary>        public string DecisionValue { get; set; }        /// <summary>        /// 前一個屬性因子,可以看作是分支條件.        /// </summary>        public string ParentFactor { get; set; }        /// <summary>        /// 當前節點的樣本數量,        /// </summary>        public int CalcCount { get; set; }        /// <summary>        /// 當前節點的樣本索引集合.        /// </summary>        public List<int> DataIndexes {get;set;}        /// <summary>        /// 分支節點集合.        /// </summary>        public Dictionary<string, DecisionTreeNode> Children { get; private set; }        /// <summary>        /// 父節點        /// </summary>        public DecisionTreeNode Parent { get; set; }        public DecisionTreeNode()        {            DataIndexes = new List<int>();            Children = new Dictionary<string, DecisionTreeNode>();        }           }    /// <summary>    /// 用於計算過程存放資料.用數組不是很方便,這裡採用字典,可以減少迴圈次數.    /// </summary>    public class CalcNode    {        public string Key { get; set; }        public string Type { get; set; }        public int CalcCount { get; set; }        public List<int> DataIndexes {get;set;}        public Dictionary<string, CalcNode> Children { get; private set; }        public CalcNode()        {            DataIndexes = new List<int>();            Children = new Dictionary<string, CalcNode>();        }        public void AddChildren(string Key,string AType,int AIndex, int Count = 1)        {            if (Children.ContainsKey(Key) == false)            {                Children.Add(Key, new CalcNode());            }            Children[Key].Key = Key;            Children[Key].Type = AType;            Children[Key].CalcCount += Count;            Children[Key].DataIndexes.Add(AIndex);        }          }

2、演算法類,注釋比較詳細,有時間再寫一篇原理文章

 /// <summary>    /// 決策樹演算法類,不適合連續性值。    /// </summary>    public class DecisionTreeAlg    {        private string PrefixString = "                                                                                                                                                                                                       ";        /// <summary>        /// 構建決策樹,決策分類屬性約定放在第1列。        /// </summary>        /// <param name="Inputs">行表示屬性,列為值,注意列等長</param>        /// <param name="PNode">父節點</param>        /// <param name="PropertyNames">測試屬性名稱</param>        /// <param name="TestProperties">當前可用測試屬性索引</param>        /// <param name="DefaultClassFactor">預設判別決策分類因子</param>        /// <param name="CallLevel">用來測試輸出控制,無實際作用</param>        /// <param name="OutContents">輸出內容,為調試用</param>        /// <param name="PropertyFactors">屬性因子</param>        public void BuildDecisionTree(int CallLevel, ref string OutContents, string[][] Inputs, DecisionTreeNode PNode, string[] PropertyNames, List<int> TestProperties, string DefaultClassFactor, Dictionary<string, List<string>> PropertyFactors)        {                        string thePrefix = PrefixString.Substring(0, CallLevel * 2);            CallLevel++;            //如果沒有測試屬性,將當前節點設為葉子節點,選擇高機率分類,然後返回            if (TestProperties.Count <= 1)            {                PNode.Type = "葉子";                PNode.DecisionValue = DefaultClassFactor;                return;            }            //如果沒有學習樣本集,將當前節點設為葉子節點,選擇高機率分類,然後返回            if (PNode.DataIndexes.Count <= 0)            {                PNode.Type = "葉子";                PNode.DecisionValue = DefaultClassFactor;                return;            }            if (PropertyFactors == null)            {                PropertyFactors = new Dictionary<string, List<string>>();            }            //準備儲存遍曆時的計數儲存結構            Dictionary<string, CalcNode> thePropertyCount = new Dictionary<string, CalcNode>();            foreach (var theProIndex in TestProperties)            {                thePropertyCount.Add(PropertyNames[theProIndex], new CalcNode() { Key = PropertyNames[theProIndex] });                if (PropertyFactors.ContainsKey(PropertyNames[theProIndex]) == false)                {                    PropertyFactors.Add(PropertyNames[theProIndex], new List<string>());                }            }            //遍曆當前可遍曆的資料,進行統計,為計算各屬性熵做準備            for (int n = 0; n < PNode.DataIndexes.Count; n++)            {                int theI = PNode.DataIndexes[n];                for (int k = 0; k < TestProperties.Count; k++)                {                    int theJ = TestProperties[k];                    var thePropertyCalcNode = thePropertyCount[PropertyNames[theJ]];                    //對當前屬性計數                    thePropertyCalcNode.CalcCount++;                    //對第j個屬性的當前因子計數                    thePropertyCalcNode.AddChildren(Inputs[theJ][theI], "測試屬性因子", theI, 1);                    //對第j個屬性的當前因子的主分類因子計數                    thePropertyCalcNode.Children[Inputs[theJ][theI]].AddChildren(Inputs[0][theI], "主分類因子", theI, 1);                    //統計歸納各屬性因子,採用這種方式可以減少迴圈.                    if (PropertyFactors[PropertyNames[theJ]].Contains(Inputs[theJ][theI]) == false)                    {                        PropertyFactors[PropertyNames[theJ]].Add(Inputs[theJ][theI]);                    }                }            }                        //計算資訊增益量,擷取具有最大資訊增益屬性            string theDefaultClassFactor = DefaultClassFactor;            //初始化最大測試屬性熵值.            double theMaxEA = double.MinValue;            //記錄具有最大熵值屬性的索引位置            int theMaxPropertyIndex = TestProperties[1];            //總資訊熵值,其實就是分類屬性的熵值.            double theTotalEA = 0.0;            //記錄總的樣本數,用於估算機率.            double theTotalSimple = 0;            for(int theI=0;theI<TestProperties.Count;theI++)            {                int thePIndex_1 = TestProperties[theI];                if (thePIndex_1 == 0)                {                    //主分類熵值計算,計算公式與測試屬性有所不同.                    CalcNode theCalcNode = thePropertyCount[PropertyNames[thePIndex_1]];                    double theCount = theCalcNode.CalcCount;                    theTotalSimple = theCount;                    double theMaxSubCount = -1;                    theTotalEA = 0.0;                    //求和(-Pj*log2(Pj))                    foreach (var theSubNode in theCalcNode.Children)                    {                        if (theSubNode.Value.CalcCount > 0)                        {                            double thePj = theSubNode.Value.CalcCount / theCount;                            theTotalEA += 0 - thePj * Math.Log(thePj, 2);                        }                        if (theMaxSubCount < theSubNode.Value.CalcCount)                        {                            theMaxSubCount = theSubNode.Value.CalcCount;                            theDefaultClassFactor = theSubNode.Key;                        }                        //測試輸出,跟蹤計算路徑.                        OutContents += "\r\n" + thePrefix + theCalcNode.CalcCount + ":: " + PropertyNames[thePIndex_1] + ":: " + theSubNode.Value.Type + " :: " + theSubNode.Key + " :: " + theSubNode.Value.CalcCount;                     }                }                else                {                    //測試屬性熵值計算。                    CalcNode theCalcNode = thePropertyCount[PropertyNames[thePIndex_1]];                    double theJEA = 0.0;                    foreach (var theSubNode_1 in theCalcNode.Children)                    {                        if (theSubNode_1.Value.CalcCount > 0)                        {                            double theSjCount = theSubNode_1.Value.CalcCount;                            double theSj_1 = theSjCount / theTotalSimple;                            double theSj_2 = 0.0;                                                        foreach (var theSubNode_2 in theSubNode_1.Value.Children)                            {                                if (theSubNode_2.Value.CalcCount > 0)                                {                                    double thePj_1 = Convert.ToDouble(theSubNode_2.Value.CalcCount) / theSjCount;                                    theSj_2 += 0.0 - thePj_1 * Math.Log(thePj_1, 2);                                }                                OutContents += "\r\n" + thePrefix + theCalcNode.CalcCount + ":: " + PropertyNames[thePIndex_1] + " :: " + theSubNode_1.Value.Type + " :: " + theSubNode_1.Key + " :: " + theSubNode_1.Value.CalcCount                                     + theSubNode_2.Value.Type + " :: "  + theSubNode_2.Key + " :: " + theSubNode_2.Value.CalcCount;                             }                            theJEA += theSj_1 * theSj_2;                        }                                            }                    theJEA = theTotalEA - theJEA;                    //只記錄最大熵值屬性資訊.                    if (theMaxEA < theJEA)                    {                        theMaxEA = theJEA;                        theMaxPropertyIndex = thePIndex_1;                    }                }            }            //如果分類因子只有一個,則置當前節點為葉子節點,設定判定為當前分類因子,然後返回            if (thePropertyCount[PropertyNames[0]].Children.Count <= 1)            {                PNode.Type = "葉子";                PNode.DecisionValue = theDefaultClassFactor;                return;            }            //具有多個分類因子,還剩有測試屬性,則設當前節點為分支節點,準備分支.            PNode.Type = "分支";            //1選取最大增益資訊量測試屬性,做分支處理,做處理,注意屬性一旦處理,將不在後續節點中再處理            //因此需要在測試屬性集合中刪除所選測試屬性.注意保持分類屬性在開始索引處(0).            PNode.Key = PropertyNames[theMaxPropertyIndex];             CalcNode theCalcNode_2 = thePropertyCount[PropertyNames[theMaxPropertyIndex]];             List<string> theFactors = PropertyFactors[PropertyNames[theMaxPropertyIndex]];             List<int> theAvailableTestPs = new List<int>();             for (int i = 0; i < TestProperties.Count; i++)             {                 if (theMaxPropertyIndex != TestProperties[i])                 {                     theAvailableTestPs.Add(TestProperties[i]);                 }             }             //對所選測試屬性的所有因子進行處理.             foreach (var theFactor_1 in theFactors)             {                 //如果當前因子不在計算中,則添加一個葉子節點,判定為高機率分類。                 if (theCalcNode_2.Children.ContainsKey(theFactor_1) == false)                 {                     DecisionTreeNode theNode_1 = new DecisionTreeNode();                     theNode_1.ParentFactor = theFactor_1;                     theNode_1.CalcCount = 0;                     theNode_1.DecisionValue = theDefaultClassFactor;                     theNode_1.Parent = PNode;                     theNode_1.Key = theFactor_1;                     theNode_1.Type = "葉子";                     PNode.Children.Add(theFactor_1, theNode_1);                     continue;                 }                 //如果當前因子存在,但不存在樣本,則添加一個葉子節點,判定為高機率分類。                 if (theCalcNode_2.Children[theFactor_1].CalcCount<=0)                 {                     DecisionTreeNode theNode_1 = new DecisionTreeNode();                     theNode_1.ParentFactor = theFactor_1;                     theNode_1.CalcCount = 0;                     theNode_1.DecisionValue = theDefaultClassFactor;                     theNode_1.Parent = PNode;                     theNode_1.Type = "葉子";                     theNode_1.Key = theFactor_1;                     PNode.Children.Add(theFactor_1, theNode_1);                     continue;                 }                 //如果存在,且有學習樣本,則添加一個節點,並以此節點遞迴處理.                 DecisionTreeNode theNode_2 = new DecisionTreeNode();                 theNode_2.ParentFactor = theFactor_1;                 theNode_2.Parent = PNode;                 theNode_2.Key = theFactor_1;                 theNode_2.CalcCount = theCalcNode_2.Children[theFactor_1].CalcCount;                 theNode_2.DataIndexes.AddRange(theCalcNode_2.Children[theFactor_1].DataIndexes);                 PNode.Children.Add(theFactor_1, theNode_2);                 BuildDecisionTree(CallLevel, ref OutContents, Inputs, theNode_2, PropertyNames, theAvailableTestPs, theDefaultClassFactor, PropertyFactors);             }        }    }

3、測試代碼:

private void button1_Click(object sender, EventArgs e)        {            DecisionTreeAlg theAlg = new DecisionTreeAlg();            string[][] theInputs = new string[4][];            theInputs[0] = new string[] { "no", "yes", "yes", "yes", "yes", "yes", "no", "yes", "yes", "no" };            theInputs[1] = new string[] { "s", "s", "l", "m", "l", "m", "m", "l", "m", "s" };            theInputs[2] = new string[] { "s", "l", "m", "m", "m", "l", "s", "m", "s", "s" };            theInputs[3] = new string[] { "no", "yes", "yes", "yes", "no", "no", "no", "no", "no", "yes" };            string[] thePropertyName = new string[] {"是否真實帳號","日誌密度","好友密度","是否真實頭像" };            DecisionTreeNode theRootNode = new DecisionTreeNode();            theRootNode.DataIndexes.AddRange(new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });            List<int> theTestPs = new List<int>() { 0, 1, 2, 3 };            string theOuts = "";            theAlg.BuildDecisionTree(0,ref theOuts, theInputs, theRootNode, thePropertyName, theTestPs, "", null);            this.treeView1.Nodes.Clear();            TreeNode theRoot = new TreeNode();            this.treeView1.Nodes.Add(theRoot);            VisitTree(theRoot, theRootNode);            this.textBox1.Text = theOuts;        }        private void VisitTree(TreeNode PNode, DecisionTreeNode PDNode)        {            PNode.Text = PDNode.Key + "(" + PDNode.Type + ")[判定:"+PDNode.DecisionValue +"]";            foreach (var theNode in PDNode.Children.Values)            {                TreeNode theTmpNode = new TreeNode();                PNode.Nodes.Add(theTmpNode);                VisitTree(theTmpNode, theNode);            }        }

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.