递归树生成器中的无限递归

时间:2014-10-24 04:43:59

标签: python algorithm recursion data-mining decision-tree

我的一个递归python函数有一个有趣的问题。我必须遗漏一些微妙的基础案例或其他东西!您将在下面看到的代码的目标是构建决策树(用于确定数据趋势的结构)。它是通过嵌套python列表来实现的,这是返回值和结果列表正在做的事情。结构本身并不是那么重要(至少看起来如此)。

 def tdidt(dataset,attr_list,class_index,class_labels):
    print attr_list

    #base case 1: all the same class
    if all_same_class(dataset, class_index):
        class_label = majority_vote(dataset, class_index)
        return ['c',class_label]
    #base case 2: no more attributes
    elif attr_list == []:
        print 'i see and empty list!!!'
        class_label = majority_vote(dataset, class_index)
        return ['c',class_label]
    else:
        #else select attribute and partition dataset 
        attr = select_attribute(dataset, list(attr_list), class_index, class_labels)
        partitions = partition_on_attr(dataset, attr)

        #base case 3: one of the partitions is empty
        if has_empty_partition(partitions):
            class_label = majority_vote(dataset, class_index)
            return ['c',class_label]
        else:
            #enable respective data attribute labels for easier to read tree
            #result = result + ['a',titanic_header[attr]]
            #result = result + ['a',auto_header[attr]]

            #add attribute node to tree
            result = ['a',attr]
            #remove used attribute
            removed_attr_list = [x for x in list(attr_list) if x != attr]

            for partition in partitions.values():
                #add value node for tree, recursive call for next attributes
                result.append(["v",partition[0][attr],tdidt(partition, list(removed_attr_list), class_index, class_labels)]) 
        return result

attr_list(或与attr_list有关的事情)似乎是最可能的嫌疑人(请参阅我的投诉和下面的错误消息)。 attr_list包含一个属性索引列表,我将"使用"一个接一个地从列表中一次选择一个而不替换。

递归函数的三个基本情况如下:

1(所有同一类):

数据集(行列表)中的每一行(属性列表)在class_index索引的行中共享一个公共值

2(没有更多属性):

参数attr_list为空。这应该是最活跃的基本情况,最常发生。

3(分区为空):

数据被partition_on_attr()分区,其中一个分区(行列表)为空,无法继续

递归调用是最长的行(导致水平滚动条的行),其中函数tdidt()被调用为附加列表的一个组件。分配给removed_attr_list的列表推导的目的是拥有一个属性列表,其中没有我们刚刚从attr_list中选择的属性。

好的,因为错误我得到它的巨大。我正在达到递归限制,并且出于一个非常奇怪的原因。我在函数调用时立即打印attr_list,并且每当出现一个空列表时。这是输出......

[0, 1, 2]
[0, 1]
[1]
[]
i see and empty list!!!
[] 
i see and empty list!!!
[1]
[]
i see and empty list!!!
[]
[1]
[1]
[1]
[1] 
[1]
[1]
.
.
.

强调省略号。 [1]一直持续到递归最大错误。

  

RuntimeError:cmp

中超出了最大递归深度

开始的行为正是我的预期; attr_list用完了,从递归调用的for循环中创建了一些分支,然后基本情况用"我看到一个空列表!",然后是[1]&# 39;开始流动!

我错过了一个递归案例吗? attr_list如何再次永久地重新设置为[1]?任何帮助都非常感谢,并感谢让它成为这本小说的底部!

-----------------------------更新10/24/2014 --------- --------------------

在每次递归调用上打印数据集变量时,我看到与attr_list参数类似的行为。事情就像预期的那样,然后数据集参数一遍又一遍地成为这条线......

  

[[' third',' adult',' male',' no'],[' third'] ;,'成人','男',' no'],'第三','成人',& #39;男性',' no'],['第三','成人','男','没有'],['第三','成人','男',' no'],['第三& #39;,'成人','男',' no'],['第三','成人' ,'男'' no'],'第三','成人','男',&# 39;没有'],['第三','成人','男',' no'],[' ;第三个','成人','男',' no'],'第三','成人和# 39;,'男','是'],['第三','成人''男', ' no'],['第三','成人','男','是'],[& #39;第三个','成人','男',' no'],'第三','成人','男',' no'],[&#3 9;第三个','成人','男'' no'],'第三','成人& #39;,'男',' no'],['第三','成人''男' ,' no'],['第三','成人','男','是'],[ '第三','成人','男',' no'],'第三',' ;成人','男',' no'],'第三','成人''男&# 39;,' no'],['第三','成人','男',' no'] ]

那只有一条线! (并查看一些奇怪的一些数据)

世界如何发生这种情况?一旦完成它的过程,该函数似乎会产生某种被零除的熔化......我的递归必定有一些错误。

-----------------------------更新10/25/2014 --------- --------------------

好的,我们现在正在做点什么。 @njlarsson建议在函数 partition_on_attr()返回它收到的相同数据集时检查行为,这意味着所选属性在数据集中是一致的,并且不会发生分区。我对这种情况的最初想法是下一个递归调用会有一个较小的attr_list,最后空的attr_list基本情况会捕获这个分区。我显然是错的,这里有几个基本的测试案例我已经运行了,他们分享的亮点......

第一个播放数据集有两个实例,前三个属性是要拆分的属性,第四个是我们试图猜测的属性。由于所有拆分属性都不相等,因此partition_on_attr()函数返回整个数据集,因为一个分区不会发生。

  

play = [[0,0,0,1],[1,1,1,0]]

输出是一个很棒的决策树(不太美观,但这不是重点)

  

[' a',0,[' v',0,[' c',1]],[' v' ,1,[' c',0]]]

现在让我们尝试一个不同的游戏数据集,类似于第一个游戏集 - 两个实例,相同的架构。

  

play = [[1,1,1,1],[1,1,1,0]]

在此数据集中,前三个属性(要拆分的属性)都相等。因此,partition_by_attr()函数HAS返回与传递的数据集相同的单个分区。 最大回归错误发生!

@njlarsson你正在做点什么!我只是不明白为什么会这样。在partition_on_attr函数将整个数据集作为一个分区返回的情况下,算法应该怎么做?

2 个答案:

答案 0 :(得分:1)

我在这段代码中看不到任何产生无限递归的东西,我认为问题出在其他地方。最有可能的是,partition_on_attr生成的分区并不严格小于其数据集参数。例如,它可能会产生一个等于参数的单个分区。

我要检查的第一件事是partition_on_attr在数据集只有一个元素时的作用。可能是它返回包含该元素的单个分区,这会产生类似于输出的内容。

答案 1 :(得分:0)

发现问题了!不幸的是,它植根于决策树和数据集的本质而不是递归。但是,由于partition_on_attr(),错过了边缘递归情况。正如@njlarsson指出的那样,在整个数据集中拆分属性一致的情况下,有时这个函数只返回一个与手数据集相同的分区。

问题是partition_on_attr()函数没有意识到拆分属性的所有可能的属性值。所以在我的游戏数据集中

  

play = [[1,1,1,1],[1,1,1,0]]

分区函数不知道前三个属性中的任何一个可能是0或1 。因此,它只返回一个与数据集相同的分区,它应该返回两个分区,一个包含整个数据集(所有实例的属性值== 1)和一个空分区保存属性值为== 0的任何实例,如果有的话。

在python字典表示法(我创建分区的方法)中,这是在不正确的分区函数中返回的内容:

  

{1:[[1,1,1,1],[1,1,1,0]]}

正确分区函数返回的内容:

  

{0:[],1:[[1,1,1,1],[1,1,1,0]]}

属性值0的空分区非常重要。现在基础案例#3启动(分区包含一个空分区),并且无限递归停止。

我们做到了!谢谢@njlarsson,你这个男人