def pitch_class(data, method, sound_ids=None): """ Compares the estimated note with the reference note expecting an exact match or an octave error (pitch class) :param data: sound data with annotations and analysis :param method: analysis algorithm name to compare with annotation :param sound_ids: ids of the sounds to include in comparison (default=None, all found in data) :return: list with 1 for sounds that match and 0 for sounds that do not match """ if sound_ids is None: sound_ids = data.keys() output = list() for sound_id in sound_ids: ground_truth_value = data[sound_id]['annotations']['midi_note'] try: estimated_value = vfkp(data[sound_id]['analysis'], method + '.midi_note', ignore_non_existing=False) except KeyError: try: estimated_value = vfkp(data[sound_id]['analysis'], method + '.note_midi', ignore_non_existing=False) except KeyError: estimated_value = None if estimated_value is not None: if (int(estimated_value) - int(ground_truth_value)) % 12 == 0: output.append(1) else: output.append(0) else: output.append(0) return output
def filter_data(data, filters=None, condition=None): """ Returns a dictionary with all keys and values in data that passes one of the given filters (OR). Filters are given as a list of tuples in the filters parameter. Each tuple contains a filter_term and filter_values. Filter terms contain a key_path that will be used to get the corresponding value from each elements' dict in data, and the operation that will be performed against the given filter values. Key path and operation are separated by '__', and filter_values contain all values required to carry out the operation. For example: >> filtered_data = filter_data(data, filters=[('analysis.RhythmExtractor2013.bpm__>=' , 120)]) Alternatively, the filter_data also accepts a function as a condition parameter. This function will be passed the key and current item being filtered as well as the rest of the data as a third argument. The function must return True or False to decide whether to add or not the item to the filtered data. >> def condition_func(key, item, data): >> return item['property1'] == item['property2'] >> filtered_data = filter_data(data, condition=condition_func) Remember that if any of the filters or the condition passes, the element is added to the output. To get AND filtering you should call this method in "cascade". :param data: data dictionary :param filters: list of tuples specifying filter_term and filter_values (optional) :param condition: function for which current item must return True in order to be returned :return: filtered dictionary """ out_data = dict() for key, item in data.items(): passes_filter = False if filters is not None: for filter_term, filter_values in filters: key_path, operation = filter_term.split('__') if operation == '>': if vfkp(item, filter_term.replace('__' + operation, '')) > filter_values: passes_filter = True elif operation == '>=': if vfkp(item, filter_term.replace('__' + operation, '')) >= filter_values: passes_filter = True elif operation == '<': if vfkp(item, filter_term.replace('__' + operation, '')) < filter_values: passes_filter = True elif operation == '<=': if vfkp(item, filter_term.replace('__' + operation, '')) <= filter_values: passes_filter = True elif operation == '==': if vfkp(item, filter_term.replace('__' + operation, '')) == filter_values: passes_filter = True else: raise Exception("Filter term could not be parsed correctly") if condition is not None: passes_filter = condition(key, item, data) if passes_filter: out_data[key] = item return out_data
def get_data(self, key_path, format=list, ignore_non_existing=True): if format == list: return vfkp(self.data.values(), key_path, ignore_non_existing=ignore_non_existing) elif format == dict: out_dict = dict() for key, item in self.data.items(): value = vfkp(item, key_path, ignore_non_existing=ignore_non_existing) if value is not None: out_dict[key] = value return out_dict else: raise Exception(u'Unknown format \'%s\'' % format)
def accuracy1e(data, method, sound_ids=None, skip_zeroed_values=False): """ Compares estimated bpm with annotated bpm of provided data. Considers as good matches values that, after rounder, are exatcly that of the ground truth. :param data: sound data with annotations and analysis :param method: analysis algorithm name to compare with annotation :param sound_ids: ids of the sounds to include in comparison (default=None, all found in data) :param skip_zeroed_values: whether to take into account sounds annotated with ground truth bpm=0.0 :return: list with 1 for sounds that match and 0 for sounds that do not match """ if sound_ids is None: sound_ids = data.keys() output = list() for sound_id in sound_ids: ground_truth_value = data[sound_id]['annotations']['bpm'] if ground_truth_value is None: continue try: estimated_value = vfkp(data[sound_id]['analysis'], method + '.bpm', ignore_non_existing=False) if skip_zeroed_values and estimated_value == 0.0: continue except KeyError: # Method produced no estimation if not skip_zeroed_values: output.append(0) continue if int(round(estimated_value)) == int(round(ground_truth_value)): output.append(1) else: output.append(0) return output
def accuracy2(data, method, sound_ids=None, tolerance=0.04, skip_zeroed_values=False, allowed_multiples=[1.0, 1.0 / 3, 0.5, 1.0, 2.0, 3.0]): """ Compares estimated bpm with annotated bpm of provided data. Considers as good matches values that are either the same or multiples as listed in 'allowed_multiples' parameter. See: Gouyon, F., Klapuri, A., andM. Alonso, S. D., Tzanetakis, G., & Uhle, C. (2006). An Experimental Comparison of Audio Tempo Induction Algorithms. IEEE Transactions on Audio, Speech and Language Processing, 14(5), 1832-1844. :param data: sound data with annotations and analysis :param method: analysis algorithm name to compare with annotation :param sound_ids: ids of the sounds to include in comparison (default=None, all found in data) :param tolerance: max % deviation of bpm to consider a good match (default=0.04) :param skip_zeroed_values: whether to take into account sounds annotated with ground truth bpm=0.0 :param allowed_multiples: multiples of the ground truth bpm value which are to be accepted as good estimates :return: list with 1 for sounds that match and 0 for sounds that do not match """ if sound_ids is None: sound_ids = data.keys() output = list() for sound_id in sound_ids: ground_truth_value = data[sound_id]['annotations']['bpm'] if ground_truth_value is None: continue try: estimated_value = vfkp(data[sound_id]['analysis'], method + '.bpm', ignore_non_existing=False) if skip_zeroed_values and estimated_value == 0.0: continue except KeyError: # Method produced no estimation if not skip_zeroed_values: output.append(0) continue threshold = tolerance * float(ground_truth_value) found_match = 0 for multiple in allowed_multiples: delta = abs(float(ground_truth_value) * multiple - estimated_value) if delta <= threshold: found_match = 1 break output.append(found_match) return output
def accuracy2(data, method, sound_ids=None, tolerance=0.04, skip_zeroed_values=False, allowed_multiples=[1.0, 1.0/3, 0.5, 1.0, 2.0, 3.0]): """ Compares estimated bpm with annotated bpm of provided data. Considers as good matches values that are either the same or multiples as listed in 'allowed_multiples' parameter. See: Gouyon, F., Klapuri, A., andM. Alonso, S. D., Tzanetakis, G., & Uhle, C. (2006). An Experimental Comparison of Audio Tempo Induction Algorithms. IEEE Transactions on Audio, Speech and Language Processing, 14(5), 1832-1844. :param data: sound data with annotations and analysis :param method: analysis algorithm name to compare with annotation :param sound_ids: ids of the sounds to include in comparison (default=None, all found in data) :param tolerance: max % deviation of bpm to consider a good match (default=0.04) :param skip_zeroed_values: whether to take into account sounds annotated with ground truth bpm=0.0 :param allowed_multiples: multiples of the ground truth bpm value which are to be accepted as good estimates :return: list with 1 for sounds that match and 0 for sounds that do not match """ if sound_ids is None: sound_ids = data.keys() output = list() for sound_id in sound_ids: ground_truth_value = data[sound_id]['annotations']['bpm'] if ground_truth_value is None: continue try: estimated_value = vfkp(data[sound_id]['analysis'], method + '.bpm', ignore_non_existing=False) if skip_zeroed_values and estimated_value == 0.0: continue except KeyError: # Method produced no estimation if not skip_zeroed_values: output.append(0) continue threshold = tolerance * float(ground_truth_value) found_match = 0 for multiple in allowed_multiples: delta = abs(float(ground_truth_value) * multiple - estimated_value) if delta <= threshold: found_match = 1 break output.append(found_match) return output