I have a script which contains two classes. (I’m obviously deleting a lot of stuff that I don’t believe is relevant to the error I’m dealing with.) The eventual task is to create a decision tree, as I mentioned in this question.
Unfortunately, I’m getting an infinite loop, and I’m having difficulty identifying why. I’ve identified the line of code that’s going haywire, but I would have thought the iterator and the list I’m adding to would be different objects. Is there some side effect of list’s .append functionality that I’m not aware of? Or am I making some other blindingly obvious mistake?
class Dataset:
individuals = [] #Becomes a list of dictionaries, in which each dictionary is a row from the CSV with the headers as keys
def field_set(self): #Returns a list of the fields in individuals[] that can be used to split the data (i.e. have more than one value amongst the individuals
def classified(self, predicted_value): #Returns True if all the individuals have the same value for predicted_value
def fields_exhausted(self, predicted_value): #Returns True if all the individuals are identical except for predicted_value
def lowest_entropy_value(self, predicted_value): #Returns the field that will reduce <a href="http://en.wikipedia.org/wiki/Entropy_%28information_theory%29">entropy</a> the most
def __init__(self, individuals=[]):
and
class Node:
ds = Dataset() #The data that is associated with this Node
links = [] #List of Nodes, the offspring Nodes of this node
level = 0 #Tree depth of this Node
split_value = '' #Field used to split out this Node from the parent node
node_value = '' #Value used to split out this Node from the parent Node
def split_dataset(self, split_value): #Splits the dataset into a series of smaller datasets, each of which has a unique value for split_value. Then creates subnodes to store these datasets.
fields = [] #List of options for split_value amongst the individuals
datasets = {} #Dictionary of Datasets, each one with a value from fields[] as its key
for field in self.ds.field_set()[split_value]: #Populates the keys of fields[]
fields.append(field)
datasets[field] = Dataset()
for i in self.ds.individuals: #Adds individuals to the datasets.dataset that matches their result for split_value
datasets[i[split_value]].individuals.append(i) #<---Causes an infinite loop on the second hit
for field in fields: #Creates subnodes from each of the datasets.Dataset options
self.add_subnode(datasets[field],split_value,field)
def add_subnode(self, dataset, split_value='', node_value=''):
def __init__(self, level, dataset=Dataset()):
My initialisation code is currently:
if __name__ == '__main__':
filename = (sys.argv[1]) #Takes in a CSV file
predicted_value = "# class" #Identifies the field from the CSV file that should be predicted
base_dataset = parse_csv(filename) #Turns the CSV file into a list of lists
parsed_dataset = individual_list(base_dataset) #Turns the list of lists into a list of dictionaries
root = Node(0, Dataset(parsed_dataset)) #Creates a root node, passing it the full dataset
root.split_dataset(root.ds.lowest_entropy_value(predicted_value)) #Performs the first split, creating multiple subnodes
n = root.links[0]
n.split_dataset(n.ds.lowest_entropy_value(predicted_value)) #Attempts to split the first subnode.
Suspicious. Unless you want to have a static member list shared by all instances of
Datasetyou shouldn’t do that. If you are settingself.individuals= somethingin the__init__, then you don’t need to setindividualshere too.Still suspicious. Are you assigning the
individualsargument toself.individuals? If so, you are assigning the sameindividualslist, created at function definition time, to everyDatasetthat is created with a default argument. Add an item to oneDataset‘s list and all the others created without an explicitindividualsargument will get that item too.Similarly:
All
Nodes created without an explicitdatasetargument will receive the exact same defaultDatasetinstance.This is the mutable default argument problem and the kind of destructive-iterations it would produce would seem very likely to be causing your infinite loop.