From the following script, the "similarity" function should be callable to compare two sets of lists and return a certain similarity score. The elements of the lists represent intervals, so [12,16] represents 12, 13, 14, 15 and 16. From the sets, the lists are compared one by one. So the first lists from both sets are compared to each other only, the second lists to each other only, and so on. The sets are to be given in text files, so the input to the "similarity" function would be similar to the following image:
I am wondering how my code could be improved, with regard to runtime, memory usage and readability. I will explain the used functions in greater detail so you know what their general purpose is, though I hope the comments in the code itself are clear enough also.
The "ls" function takes two lists as input and computes a metric based on how many intervals from the first list overlap with at least one interval of the second list. It also checks whether the input is correct.
As this function is sensitive to the order in which the lists are given, it is done for both possible orders. Then the "sym_ls" function is used to take the average of the two outputs.
Then the "ss" function applies the above functions to all the lists of the sets, so the total similarity score between the sets is calculated (an average of the scores of similarity between the individual lists).
Finally, the "similarity" function opens the text files containing the data, cleans out the data so it can be used as input to the "ss" function and then applies this "ss" function, after which the final similarity score is saved to a new text file.
Please let me know what parts of the code could be written more concisely, efficiently and whether the comments are clear or not, and of course how to improve. Eventually the code should be executed by importing the "similarity" function from the .py file, and calling it with correct input arguments.
# import necessary modules
import ast
# define functions needed for similarity computation
def ls(list1, list2):
'''
This function determines the similarity between the two given lists,
list1 and list2, using overlapping intervals.
The input (list1 and list2), should be lists containing lists of 2 integers,
of which the second integer is greater than the first.
'''
# overlap variable is set to 0 for every new computation
overlap = 0
# loop through the intervals to identify the number of intervals in list1
# that have an overlap with at least one interval in list2
for inter1 in list1:
for inter2 in list2:
# check whether intervals are correclty syntaxed
if len(inter1) != 2 or len(inter2) != 2:
print("Error ls: intervals are expected to be represented by two integers, not more or less.")
break
elif inter1[0] >= inter1[1] or inter2[0] >= inter2[1]:
print("Error ls: the first integer representing an interval should be lesser than the second.")
break
else:
# determine whether there is overlap
if inter1[0] >= inter2[0] and inter1[0] <= inter2[1]:
overlap += 1
break
elif inter1[1] >= inter2[0] and inter1[1] <= inter2[1]:
overlap += 1
break
elif inter1[0] < inter2[0] and inter1[1] > inter2[1]:
overlap += 1
break
# determine the maximum of the lengths of list1 and list2
maxlen = max(len(list1), len(list2))
# compute the similarity (ls)
ls = overlap / maxlen
return ls
def sym_ls(ls1, ls2):
'''
This function computes the symmetric version of the similarity between two
lists. It does so by taking the average of the two similarities.
The input (ls1 and ls2), should be real numbers in [0,1].
'''
# check whether input is within the expected range ([0,1])
if (ls1 < 0 or ls1 > 1) or (ls2 < 0 or ls2 > 1):
print("Error sym_ls: similarity metrics as input should be a value in [0,1], check the input.")
else:
# calculate the average similarity
symmetric_ls = (ls1 + ls2) / 2
return symmetric_ls
def ss(set1, set2):
'''
This function computes the similarity between two sets of lists of intervals.
It does so by applying the ls functions on the different set elements. Then,
using the sym_ls function, the symmetric similarities are computed. Finally,
the global set similarity is calculated and returned as S.
The input (set1 and set2) should be lists with lists as elements. These list-
elements have intervals as elements. Both sets should have an equal amount of
elements.
'''
# initialize variable to sum up all symmetric similarity values
symmetric_similarity_total = 0
# for every ith element of both sets, apply the ls function to them
# and add the results to the similarity lists
for i in range(len(set1)):
symmetric_similarity_total += sym_ls(ls(set1[i], set2[i]), ls(set2[i], set1[i]))
# compute set similarity metric, S
S = symmetric_similarity_total / len(set1)
return S
def similarity(set_1, set_2, outfile):
# open the textfiles and read in its contents
set1_data = open(set_1, "r")
set2_data = open(set_2, "r")
S1 = set1_data.readlines()
S2 = set2_data.readlines()
set1_data.close()
set2_data.close()
# loop through the set elements to clean up the data
for i in range(len(S1)):
# check for equal sizes of sets
if S1[i][-1] == "\n" and S2[i][-1] != "\n":
print("Error get_sets: Set1 seems to contain more elements than Set2.")
break
elif S1[i][-1] != "\n" and S2[i][-1] == "\n":
print("Error get_sets: Set2 seems to contain more elements than Set1.")
break
else:
# clean the data and convert it to the right type
S1[i] = list(ast.literal_eval(S1[i].replace("\n", "")))
S2[i] = list(ast.literal_eval(S2[i].replace("\n", "")))
S = round(ss(S1, S2), 2)
# write the similarity metric S to a textfile
output_file = open(outfile, "x")
output_file.write("The similarity metric S is: {}".format(S))
output_file.close()
ls()
is missing the case forinter2[0] < inter1[0] and inter1[1] < inter2[1]
. Maybe that is why the code is "sensitive" to the order in which the lists are given? The code doesn't seem ready for review. \$\endgroup\$