我的一个递归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包含一个属性索引列表,我将"使用"一个接一个地从列表中一次选择一个而不替换。
递归函数的三个基本情况如下:
数据集(行列表)中的每一行(属性列表)在class_index索引的行中共享一个公共值
参数attr_list为空。这应该是最活跃的基本情况,最常发生。
数据被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函数将整个数据集作为一个分区返回的情况下,算法应该怎么做?
答案 0 :(得分:1)
我在这段代码中看不到任何产生无限递归的东西,我认为问题出在其他地方。最有可能的是,partition_on_attr
生成的分区并不严格小于其数据集参数。例如,它可能会产生一个等于参数的单个分区。
我要检查的第一件事是partition_on_attr
在数据集只有一个元素时的作用。可能是它返回包含该元素的单个分区,这会产生类似于输出的内容。
答案 1 :(得分:0)
问题是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,你这个男人