I'm trying to parsing text from a file, the first character in a line will be used to decide of what type the remaining text should be.
'?' - Question, '#' - Description, '!' - Answers, and the next number will determine what is the correct answer.
I want to have an object that stores the respective data, and a class that holds references to those objects (list of objects). The amount of answers can vary, so I tried to use a list for that.
The problem is that the returned "answers" list is blank.
Where did I go wrong?
class Question:
def __init__(self, ques, desc, n_ans, corr, ans):
self.ques = ques
self.desc = desc
self.n_ans = n_ans
self.corr = corr
self.ans = ans
print(self.ans)
def get(self):
print(self.ans)
return [self.ques, self.desc, self.n_ans, self.corr, self.ans]
class Question_handler:
arr_ans = []
array = []
firstTime = True
def __init__(self):
self.num_ans = 0
self.cor_ans = 0
self.ques = None
self.desc = None
def question(self, ques): # if it isn't the first time reaching a line starts with '?'
if self.firstTime == True: # the data is made into an instance of Question
pass
else:
self.array.append(Question(self.ques, self.desc, self.num_ans, self.cor_ans, self.arr_ans)) # List of objects
self.ques = None # Reset everything
self.desc = None
self.num_ans = 0
self.cor_ans = 0
self.arr_ans.clear()
self.ques = ques
self.firstTime = False
def description(self, desc):
if self.desc == None:
self.desc = desc
else:
self.desc = self.desc + '\n' + desc
def answer(self, ans):
self.arr_ans.append(ans) # Append answers (line starts with '!') to list
self.num_ans = len(self.arr_ans)
def correct(self, num):
if num >= 1 and num <= len(self.arr_ans):
self.cor_ans = num
else:
self.cor_ans = 0
def end(self): # Reach EOF
if self.ques == None:
pass
else:
self.question(None) # To push the remaining data
def get(self, Nques): # Get info of Question
if Nques <= len(self.array) and Nques >= 1:
return self.array[Nques - 1].get()
QQ = Question_handler()
Qfile = open('C:/Users/Admin/Desktop/test/test.txt','r')
Qdata = Qfile.readlines()
for line in Qdata:
Qline = line[1:-1]
if line[0] == '?':
QQ.question(Qline)
continue
if line[0] == '#':
QQ.description(Qline)
continue
if line[0] == '!':
QQ.answer(Qline)
continue
if line[0].isnumeric():
QQ.correct(int(line[0]))
continue
else:
QQ.end() # EOF
print(QQ.get(1))
print(QQ.get(2))
print(QQ.get(3))
TXT file
?Cau 1
#Test so 1
!abc
!def
!ghj
!ikl
3
?Cau 2
#Test so 2
!100
!1000
!10000
2
?Question 3
!A
!B
!C
!D
!E
5
Output
['abc', 'def', 'ghj', 'ikl']
['100', '1000', '10000']
['A', 'B', 'C', 'D', 'E']
[]
['Cau 1', 'Test so 1', 4, 3, []]
[]
['Cau 2', 'Test so 2', 3, 2, []]
[]
['Question 3', None, 5, 5, []]
To draw a set of objects in a breadth first search manner
To get this out of the way in the beginning, I'm quite a noob at programming in general so please pardon the obviously noobish things I would've done in the following project.
The goal of this project is to get a graph structure if you give it a table of nodes, their descriptions and the nodes that it is connected to. The hurdle though is that I cannot use any external libraries to do this apart from matplotlib.
The input would look something like this:
inputs as a table
The expected output should be something along the lines of this: (note this diagram ignores the dimensions)
Expected output
The logic which I developed is:
Convert the raw list data into an class object which has all the attributes from the list.
2)Proceed to searching the nodes in a breadth first manner
3)Put the sequence of spaces that the BFS searched into a list
4)Then draw out the spaces as they appeared in the list (note the distance between them is necessary for what I need it for)
How far I got:
I figured out everything till step no 3. The problem I have is that I have to set a x and y position for each space now.
To break down what I have in mind is that :
If the first node is an entry type node then it draws it out in the middle of the maximum breadth of the graph
Count the number of external connections it has and then draw them out as (maximum breadth /number of connections , incrementing through them. So, the living room has none so it will draw it out in the middle. The living room has 5 connections ( out of which one has already been drawn i.e. the entry so we iterate through the next set of four spaces only moving in the x axis) Then we step up the y and draw again.
To execute this I intended to have a loop which goes though the ordered list of spaces and have a counter. The process:
1)if the node encountered is one of the type of entry node then we draw out that node and set the counter to number of connections.
2)if the counter is 1 then we move the Y axis up. and draw out that space
3)if the counter is > 1 then we divide the the counter value by the maximum depth value
The problem I have:
The example shown here only takes into account that we have one entry.
2)The ordering of spaces is wrong when using the counter method.
3) The counter method doesnt draw out the termination nodes properly.
And when I actually write it down in python I get this, which means I've clearly failed:
The output which I got
The script as it stands now:
Pastebin link
And at this point I'm out of ideas as how I should actually fix this problem. Any help is much appreciated :)
#imports
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from math import pi , sin
#raw data
connections = ['1', '0,2,5,7,8', '1,3', '2,4', '3', '1,6', '5', '1', '1']
indices = ['0', '1', '2', '3', '4', '5', '6', '7', '8']
spaceTags = ['entry ', 'living area', 'dining area', 'Kitchen', 'Utility',
'Bedroom1', 'Toilet attached', 'Toilet common', 'Bedroom2']
area = ['1', '40', '20', '15', '6', '20', '6', '6', '20']
minimumDimension = ['1', '5', '4', '3', '2', '4', '2', '2', '4']
#define the space class
class Space(object):
def __init__(self,index,spaceName,connections,area,minimumDimension):
self.index = index
self.spaceName = spaceName
self.connections = connections
self.area = area
self.ydim = minimumDimension
self.xdim = (self.area / self.ydim)
self.x = 0
self.y = 0
self.isExplored = False
self.polarAngle = 0
self.spaceType = 0
# 0= not assigned ; 1 = entry; 2 = intermediate; 3 = termination
def ObjectAttributes(self):
return (self.index,
self.spaceName,
self.connections,
self.area,
self.y,self.x,
self.xdim,self.ydim,
self.isExplored,
self.spaceType)
#definations
#process integers
def convert_to_int_vals (input_list):
output_list = []
i = 0
while i < len(input_list):
output_list.append(int(input_list[i]))
i += 1
return output_list
#process floats
def convert_to_float_vals (input_list):
output_list = []
i = 0
while i < len(input_list):
output_list.append(float(input_list[i]))
i += 1
return output_list
#process 2D lists for connections
def process_2d_connections (input_list):
output_list = []
for item in input_list:
if len(item) <= 1:
lst = []
lst.append(int(item))
output_list.append(lst)
else:
var = item
var = (var.split(','))
var = convert_to_int_vals(var)
output_list.append(var)
return output_list
#make data into objects i.e. spaces
def convertDataToSpaces(index,spaceTag,connections,area,minimumDimension):
print('Processing data...')
if (len(index)==len(spaceTag)==len(connections)==len(area)==len(minimumDimension)):
i = 0
output_list = []
while i < len(spaceTag):
space = Space(index[i],spaceTag[i],connections[i],area[i],minimumDimension[i])
output_list.append(space)
i += 1
print('Done.')
return output_list
else:
print('Number of lists dont match')
#find first node
def FindFirstNode(spaceList):
output = 'null'
for item in spaceList:
if item.spaceName == 'entry ' or item.spaceName =='entry':
item.spaceType = 1
output = item
if output == 'null':
print('No entry defined. Please define entry!')
return output
#Calculate hypotenuse
def calculate_hypotenuse(arg1,arg2):
val = ((arg1**2)+(arg2**2))**(0.5)
return val
#Calculate max hypotenuse
def calculate_max_hypotenuse (spaceList):
outputval = 0
for item in spaceList:
var = calculate_hypotenuse(item.xdim,item.ydim)
if var > outputval:
outputval = var
else:
outputval
return outputval
# Note this is a FIFO list
def FindAndQueueNextNode(spaceList,searchNode,queue):
searchNode.isExplored = True
if len(searchNode.connections) == 1:
if searchNode.spaceName == 'entry ' or searchNode.spaceName =='entry':
searchNode.spaceType = 1
else:
searchNode.spaceType = 3
elif len(searchNode.connections) > 1:
searchNode.spaceType = 2
else:
searchNode.spaceType = 0
for item in spaceList:
if ( item.index in searchNode.connections) and (item.isExplored == False) :
queue.append(item)
# Calculate the position based on the dimension (inputs are the object dimensions and current floating dim)
def CalculatePosition(currentx, currenty, space):
spaceXdimadjusted = (space.xdim / 2)* -1
spaceYdimadjusted = (space.ydim / 2)* -1
adjustedx = currentx + spaceXdimadjusted
adjustedy = currenty + spaceYdimadjusted
return (adjustedx,adjustedy)
#core algorithm
def coreAlgorithm(spacesList):
## variable holding max hypotenuse distance
grid_dimension = int((calculate_max_hypotenuse(spacesList))*(1.5))
print('Grid dimensions are : ' + str(grid_dimension) + (' units'))
## create empty processing variables
processingQueue = []
orderedListOfSpacesInBFS = []
maxTreeWidth = 0
## find the first space
firstSpace = FindFirstNode(spacesList)
orderedListOfSpacesInBFS.append(firstSpace)
print('The first node is : ' + str(firstSpace.spaceName) +
'; Index being : ' + str(firstSpace.index))
## queue the next space
FindAndQueueNextNode(spacesList,firstSpace,processingQueue)
##start while loop (while queue length loop > 0)
while len(processingQueue) > 0 :
if len(processingQueue) > maxTreeWidth:
maxTreeWidth = len(processingQueue)
else:
maxTreeWidth = maxTreeWidth
item = processingQueue.pop(0)
orderedListOfSpacesInBFS.append(item)
FindAndQueueNextNode(spacesList,item,processingQueue)
## second loop to loop through spaces and draw them
maxXDepthDimension = grid_dimension * maxTreeWidth
ypos = grid_dimension
counter = 0
while len(orderedListOfSpacesInBFS) > 0:
item = orderedListOfSpacesInBFS.pop(0)
if item.spaceType == 1:
xpos = maxXDepthDimension / 2
(item.x , item.y) = CalculatePosition(xpos,ypos, item)
ypos += grid_dimension
counter = len(item.connections)
elif counter == 1:
xpos = maxXDepthDimension / 2
(item.x , item.y) = CalculatePosition(xpos,ypos, item)
ypos += grid_dimension
counter = len(item.connections) - 1
elif counter > 1:
xpos = (maxXDepthDimension / counter)
(item.x, item.y) = CalculatePosition(xpos, ypos, item)
counter -= 1
#draw lines as a separete method
#core algorithm preprocess
def coreAlgorithmLoop (spaces_list):
#check object feasibility and if the algorithm can run.
print('Starting algorithm...')
startrun = False
floatingvartoggle = 1
for item in spaces_list:
if type(item) == Space:
floatingvartoggle = floatingvartoggle * 1
else:
floatingvartoggle = floatingvartoggle * 0
if floatingvartoggle == 1:
startrun = True
else:
print('Objects should be spaces.')
#start of core-algorithm.
if startrun == True:
coreAlgorithm(spaces_list)
#implementation
#pre-process data
indices = convert_to_int_vals(indices)
spaceTags = spaceTags
connections = process_2d_connections(connections)
area = convert_to_float_vals(area)
minimumDimension = convert_to_float_vals(minimumDimension)
#initialize processing
listOfSpaces = convertDataToSpaces(indices,
spaceTags,
connections,
area,
minimumDimension)
coreAlgorithmLoop(listOfSpaces)
#matplotlibtester - start
fig, ax = plt.subplots()
ax.set_xlim((0, 100))
ax.set_ylim((0, 70))
for space in listOfSpaces:
var = space.area
print(space.ObjectAttributes())
rect = patches.Rectangle((space.x,space.y),
space.xdim,space.ydim,0,
linewidth=1,
edgecolor='r',
facecolor='none')
ax.add_patch(rect)
plt.show()
#matplotlibtester - end
So this is awkward but just in-case it helps someone, The method to solve it is:
Create the attributes explored from and depth
Then in the queue keep track of how the node was explored and what it's depth is (the current searchnode is assigned to the items getting queued, the searchnode's depth is equal to the depth of the node that it was explored from +1
Using this create a layered chart to understand where each object goes, (depth is the row/ tier/ level number) and the instance that the object is in that level is the column number
This is easier to do with another class.Here is the modified version of the code:
(note it draws an asymmetric chart which is okay for my purposes and I've still not solved the issue of more than one starting points which is okay for now)
#imports
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from math import pi , sin
#raw data
connections = ['1', '0,2,5,7,8', '1,3', '2,4', '3', '1,6', '5', '1', '1']
indices = ['0', '1', '2', '3', '4', '5', '6', '7', '8']
spaceTags = ['entry ', 'living area', 'dining area', 'Kitchen', 'Utility',
'Bedroom1', 'Toilet attached', 'Toilet common', 'Bedroom2']
area = ['1', '40', '20', '15', '6', '20', '6', '6', '20']
minimumDimension = ['1', '5', '4', '3', '2', '4', '2', '2', '4']
#define the space class
class Space(object):
def __init__(self,index,spaceName,connections,area,minimumDimension):
self.index = index
self.spaceName = spaceName
self.connections = connections
self.area = area
self.ydim = minimumDimension
self.xdim = (self.area / self.ydim)
self.x = 0
self.y = 0
self.isExplored = False
self.isExploredFrom = 0
self.Depth = 0
self.spaceType = 0
# 0= not assigned ; 1 = entry; 2 = intermediate; 3 = termination
self.CentroidX = 0
self.CentroidY = 0
def ObjectAttributes(self):
return (self.index,
self.spaceName,
self.y,self.x,
self.Depth)
class DrawingStructure(object):
def __init__(self,Depth):
self.Depth = Depth
self.numberOfInstances = 0
self.count = 0
#definations
#process integers
def convert_to_int_vals (input_list):
output_list = []
i = 0
while i < len(input_list):
output_list.append(int(input_list[i]))
i += 1
return output_list
#process floats
def convert_to_float_vals (input_list):
output_list = []
i = 0
while i < len(input_list):
output_list.append(float(input_list[i]))
i += 1
return output_list
#process 2D lists for connections
def process_2d_connections (input_list):
output_list = []
for item in input_list:
if len(item) <= 1:
lst = []
lst.append(int(item))
output_list.append(lst)
else:
var = item
var = (var.split(','))
var = convert_to_int_vals(var)
output_list.append(var)
return output_list
#make data into objects i.e. spaces
def convertDataToSpaces(index,spaceTag,connections,area,minimumDimension):
print('Processing data...')
if (len(index)==len(spaceTag)==len(connections)==len(area)==len(minimumDimension)):
i = 0
output_list = []
while i < len(spaceTag):
space = Space(index[i],spaceTag[i],connections[i],area[i],minimumDimension[i])
output_list.append(space)
i += 1
print('Done.')
return output_list
else:
print('Number of lists dont match')
#find first node
def FindFirstNode(spaceList):
output = 'null'
for item in spaceList:
if item.spaceName == 'entry ' or item.spaceName =='entry':
item.spaceType = 1
output = item
if output == 'null':
print('No entry defined. Please define entry!')
return output
#Calculate hypotenuse
def calculate_hypotenuse(arg1,arg2):
val = ((arg1**2)+(arg2**2))**(0.5)
return val
#Calculate max hypotenuse
def calculate_max_hypotenuse (spaceList):
outputval = 0
for item in spaceList:
var = calculate_hypotenuse(item.xdim,item.ydim)
if var > outputval:
outputval = var
else:
outputval
return outputval
# Note this is a FIFO list
def FindAndQueueNextNode(spaceList,searchNode,queue):
searchNode.isExplored = True
if len(searchNode.connections) == 1:
if searchNode.spaceName == 'entry ' or searchNode.spaceName =='entry':
searchNode.spaceType = 1
else:
searchNode.spaceType = 3
elif len(searchNode.connections) > 1:
searchNode.spaceType = 2
else:
searchNode.spaceType = 0
for item in spaceList:
if ( item.index in searchNode.connections) and (item.isExplored == False) :
item.isExploredFrom = searchNode.index
item.Depth = searchNode.Depth + 1
queue.append(item)
# Calculate the position based on the dimension (inputs are the object dimensions and current floating dim)
def CalculatePosition(currentx, currenty, space):
spaceXdimadjusted = (space.xdim / 2)* -1
spaceYdimadjusted = (space.ydim / 2)* -1
adjustedx = currentx + spaceXdimadjusted
adjustedy = currenty + spaceYdimadjusted
return (adjustedx,adjustedy)
#def return only unique values in a list
def ReturnUniqueValues(input_list):
output_list = []
for i in input_list:
if i not in output_list:
output_list.append(i)
return output_list
#core algorithm
def coreAlgorithm(spacesList):
## variable holding max hypotenuse distance
grid_dimension = int((calculate_max_hypotenuse(spacesList))*(1.5))
print('Grid dimensions are : ' + str(grid_dimension) + (' units'))
## create empty processing variables
processingQueue = []
orderedListOfSpacesInBFS = []
maxTreeWidth = 0
## find the first space
firstSpace = FindFirstNode(spacesList)
orderedListOfSpacesInBFS.append(firstSpace)
print('The first node is : ' + str(firstSpace.spaceName) +
'; Index being : ' + str(firstSpace.index))
##initialize first space
firstSpace.Depth = 0
firstSpace.isExploredFrom = 0
FindAndQueueNextNode(spacesList,firstSpace,processingQueue)
##start while loop (while queue length loop > 0)
while len(processingQueue) > 0 :
if len(processingQueue) > maxTreeWidth:
maxTreeWidth = len(processingQueue)
else:
maxTreeWidth = maxTreeWidth
item = processingQueue.pop(0)
orderedListOfSpacesInBFS.append(item)
FindAndQueueNextNode(spacesList,item,processingQueue)
#second loop for figuring out the depth and column number to draw
DepthList = []
uniquelist = []
DrawingList = []
for item in orderedListOfSpacesInBFS:
DepthList.append(item.Depth)
uniquelist = ReturnUniqueValues(DepthList)
for item in uniquelist:
var = DrawingStructure(item)
DrawingList.append(var)
copyList = orderedListOfSpacesInBFS
while len(copyList) > 0:
space = copyList.pop(0)
for thing in DrawingList:
if int(thing.Depth) == int(space.Depth):
thing.numberOfInstances += 1
##actually setting the values to later draw
for item in spacesList:
rowNum = item.Depth
rowData = 'null'
for entry in DrawingList:
if rowNum == entry.Depth:
rowData = entry
colNum = rowData.count
rowData.count += 1
xpos = rowNum * grid_dimension
ypos = colNum * grid_dimension
item.CentroidY = xpos
item.CentroidX = ypos
(item.x, item.y) = CalculatePosition(ypos,xpos,item)
#draw lines as a separete method
def DrawConnectionLines(spacesList):
for item in spacesList:
centroid = [item]
#core algorithm preprocess
def coreAlgorithmLoop (spaces_list):
#check object feasibility and if the algorithm can run.
print('Starting algorithm...')
startrun = False
floatingvartoggle = 1
for item in spaces_list:
if type(item) == Space:
floatingvartoggle = floatingvartoggle * 1
else:
floatingvartoggle = floatingvartoggle * 0
if floatingvartoggle == 1:
startrun = True
else:
print('Objects should be spaces.')
#start of core-algorithm.
if startrun == True:
coreAlgorithm(spaces_list)
DrawConnectionLines(spaces_list)
#implementation
#pre-process data
indices = convert_to_int_vals(indices)
spaceTags = spaceTags
connections = process_2d_connections(connections)
area = convert_to_float_vals(area)
minimumDimension = convert_to_float_vals(minimumDimension)
#initialize processing
listOfSpaces = convertDataToSpaces(indices,
spaceTags,
connections,
area,
minimumDimension)
coreAlgorithmLoop(listOfSpaces)
#matplotlibtester - start
fig, ax = plt.subplots()
ax.set_xlim((-10, 60))
ax.set_ylim((0, 70))
xvals = []
yvals = []
for space in listOfSpaces:
print(space.ObjectAttributes())
rect = patches.Rectangle((space.x,space.y),
space.xdim,space.ydim,0,
linewidth=1,
edgecolor='r',
facecolor='none')
ax.add_patch(rect)
xvals.append(space.CentroidX)
yvals.append(space.CentroidY)
plt.scatter(xvals,yvals)
plt.show()
#matplotlibtester - end
I'm trying to implement my own version of a radix sort. The large section at the bottom of the program that I have commented out works as intended with no errors, however, when I place it into a method call main_sort, I get an error: 'Nontype' object is not iteratable. It's literally the same code, copy and pasted and I cannot see what I have done wrong. I have indicated the line where the error occurs, but as I said I don't know why I'm getting one.
def read_file(file):
with open(file) as f:
content = [x.strip('\n') for x in f.readlines()]
f.close()
return content
def create_bins(list):
if list == listDec:
sortBins = {item : [] for item in dictionIndexes[:10]}
return sortBins
elif list == listOct:
sortBins = {item : [] for item in dictionIndexes[:8]}
return sortBins
elif list == listHex:
sortBins = {item : [] for item in dictionIndexes}
return sortBins
def sort(list, index):
bins = create_bins(list)
for key in bins: //WHERE THE ERROR OCCURS
for num in list:
if num[index] == key:
bins[key].append(num)
return bins
def main_sort(list):
mainBin = []
index = -1
while index > -11:
sortBins = sort(list, index)
for key in sorted(sortBins):
for items in sortBins[key]:
mainBin.append(items)
if index == -10:
break
else:
list = mainBin
mainBin = []
index = index - 1
return list
# mainBin = []
dictionIndexes = "0123456789ABCDEF"
sortBins = {}
#Number lists
listDec = read_file("Number Lists/random_numbers10.txt")
listHex = read_file("Number Lists/random_numbers4.txt")
listOct = read_file("Number Lists/random_numbers3.txt")
# outputFile = open("output.txt", "w")
#The main sort part, just need to change between listHex, Oct and Dec
# index = -1
# while index > -11:
# sortBins = sort(listHex, index)
# for key in sorted(sortBins):
# for items in sortBins[key]:
# mainBin.append(items)
# if index == -10:
# break
# else:
# listHex = mainBin
# mainBin = []
# index = index - 1
# for item in mainBin:
# outputFile.write(item + "\n")
var = main_sort(listDec)
Your problem is that, inside main_sort, you do list=mainBin, so list will point to something else than, let's say, ListDec, after an iteration.
I added a few prints so you can understand where is the issue:
def create_bins(list):
if list == listDec:
print("ListDec")
sortBins = {item : [] for item in dictionIndexes[:10]}
return sortBins
elif list == listOct:
sortBins = {item : [] for item in dictionIndexes[:8]}
return sortBins
elif list == listHex:
sortBins = {item : [] for item in dictionIndexes}
return sortBins
else:
print("not in the list ...")
def sort(list, index):
print(list)
bins = create_bins(list)
print(bins)
for key in bins: #WHERE THE ERROR OCCURS
for num in list:
if num[index] == key:
bins[key].append(num)
return bins
def main_sort(list):
mainBin = []
index = -1
while index > -11:
sortBins = sort(list, index)
for key in sorted(sortBins):
for items in sortBins[key]:
mainBin.append(items)
if index == -10:
break
else:
list = mainBin
mainBin = []
index = index - 1
return list
Result:
# first iteration
['22', '12', '472', '224', '15653', '545']
ListDec
{'3': [], '4': [], '5': [], '9': [], '0': [], '2': [], '7': [], '1': [], '8': [], '6': []}
# second iteration ... List != ListDec
['22', '12', '472', '15653', '224', '545']
not in the list ...
None
Traceback (most recent call last):
File "sort.py", line 73, in <module>
var = main_sort(listDec)
File "sort.py", line 35, in main_sort
sortBins = sort(list, index)
File "sort.py", line 25, in sort
for key in bins: #WHERE THE ERROR OCCURS
TypeError: 'NoneType' object is not iterable
What to do?
I would advise reworking the create_bins function so it does not check the type of data (Dec/Hex/Oct) by testing the equality of the current list