def tonal_space_distance(sem1, sem2): """ Computes the edit distance between the tonal space paths of two logical forms, using a suitable scoring. This uses a cost of 2 for deletions and insertions and scores 1 for a substitution that gets either the step or function right, but not both. The result is then divided by 2 (meaning that effectively all costs are 1 and a 0.5 cost is given to half-right substitutions). The alignment is not between the tonal space points themselves, but between the vectors between each pair of points (that is the points relative to the previous point). This means that a path won't be penalised if part of it is translated by a constant vector, except at the point where it goes wrong. """ from jazzparser.utils.distance import levenshtein_distance # Get a list of (coord,fun) pairs for the logical forms seq1 = _lf_to_coord_funs(sem1) seq2 = _lf_to_coord_funs(sem2) # Produce a version of the paths made up of steps and functions, # rather than points and functions steps1 = _steps_list(seq1) steps2 = _steps_list(seq2) edit_dist = levenshtein_distance(steps1, steps2, delins_cost=2, subst_cost_fun=_subst_cost) return float(edit_dist) / 2.0
def tonal_space_distance(sem1, sem2): """ Computes the edit distance between the tonal space paths of two logical forms, using a suitable scoring. This uses a cost of 2 for deletions and insertions and scores 1 for a substitution that gets either the step or function right, but not both. The result is then divided by 2 (meaning that effectively all costs are 1 and a 0.5 cost is given to half-right substitutions). The alignment is not between the tonal space points themselves, but between the vectors between each pair of points (that is the points relative to the previous point). This means that a path won't be penalised if part of it is translated by a constant vector, except at the point where it goes wrong. """ from jazzparser.utils.distance import levenshtein_distance # Get a list of (coord,fun) pairs for the logical forms seq1 = _lf_to_coord_funs(sem1) seq2 = _lf_to_coord_funs(sem2) # Produce a version of the paths made up of steps and functions, # rather than points and functions steps1 = _steps_list(seq1) steps2 = _steps_list(seq2) edit_dist = levenshtein_distance( steps1, steps2, delins_cost=2, subst_cost_fun=_subst_cost) return float(edit_dist) / 2.0
def chord_sequence_match_score(seq1, seq2): """ Computes the edit distance between two chord sequences to score the extent of the matching between the two sequences between 0 and 1. Each sequence should be a list of L{jazzparser.data.db_mirrors.Chord}s. The metric is key-independent. The distance will be tried for every transposition of one of the sequences and the closest match will be used. """ costs = [] for transpose in range(12): # Give a half-point to alignments of roots without labels or vice versa def _subst_cost(crd1, crd2): cost = 0 if (crd1.root + transpose) % 12 == crd2.root: cost += -1 if crd1.type == crd2.type: cost += -1 return cost # Try aligning the two sequences align_cost = levenshtein_distance(seq1, seq2, delins_cost=0, subst_cost_fun=_subst_cost) costs.append((transpose, align_cost)) transposition, align_cost = min(costs, key=lambda x: x[1]) align_score = float(-align_cost) / 2 # Compute f-score of optimal alignment precision = align_score / len(seq1) recall = align_score / len(seq2) f_score = 2.0 * precision * recall / (precision + recall) return f_score, transposition
def chord_sequence_match_score(seq1, seq2): """ Computes the edit distance between two chord sequences to score the extent of the matching between the two sequences between 0 and 1. Each sequence should be a list of L{jazzparser.data.db_mirrors.Chord}s. The metric is key-independent. The distance will be tried for every transposition of one of the sequences and the closest match will be used. """ costs = [] for transpose in range(12): # Give a half-point to alignments of roots without labels or vice versa def _subst_cost(crd1, crd2): cost = 0 if (crd1.root+transpose) % 12 == crd2.root: cost += -1 if crd1.type == crd2.type: cost += -1 return cost # Try aligning the two sequences align_cost = levenshtein_distance(seq1, seq2, delins_cost=0, subst_cost_fun=_subst_cost) costs.append((transpose,align_cost)) transposition, align_cost = min(costs, key=lambda x:x[1]) align_score = float(-align_cost) / 2 # Compute f-score of optimal alignment precision = align_score / len(seq1) recall = align_score / len(seq2) f_score = 2.0 * precision * recall / (precision+recall) return f_score,transposition