mlebench/competitions/freesound-audio-tagging-2019/grade.py (37 lines of code) (raw):

from pathlib import Path import numpy as np import pandas as pd import sklearn.metrics from mlebench.grade_helpers import InvalidSubmissionError from .vocabulary import CLASS_NAMES # Using the metric implementation given to participants. Source: # https://colab.research.google.com/drive/1AgPdhSp7ttY18O3fEoHOQKlt_3HJDLi8 def calculate_overall_lwlrap_sklearn(truth: np.ndarray, scores: np.ndarray) -> float: """Calculate the overall lwlrap using sklearn.metrics.lrap.""" # sklearn doesn't correctly apply weighting to samples with no labels, so just skip them. sample_weight = np.sum(truth > 0, axis=1) nonzero_weight_sample_indices = np.flatnonzero(sample_weight > 0) overall_lwlrap = sklearn.metrics.label_ranking_average_precision_score( truth[nonzero_weight_sample_indices, :] > 0, scores[nonzero_weight_sample_indices, :], sample_weight=sample_weight[nonzero_weight_sample_indices], ) return overall_lwlrap def prepare_for_metric(submission: pd.DataFrame, answers: pd.DataFrame) -> tuple: """ `submission` and `answers` are both DataFrames with N rows and M + 1 columns. The first column is "fname" and the remaining M columns are the predictions for each class. The values are floats between 0 and 1. For `answers`, the values are binary. """ if len(submission) != len(answers): raise InvalidSubmissionError("Submission and answers must have the same length") if "fname" != submission.columns[0]: raise InvalidSubmissionError("Submission must have an 'fname' column") if not all(submission.columns[1:] == CLASS_NAMES): raise InvalidSubmissionError("Submission classes do not match expected classes") assert answers.columns[0] == "fname", "Answers must have an 'fname' column" assert all(answers.columns[1:] == CLASS_NAMES), "Answers classes do not match expected classes" submission = submission.sort_values("fname").reset_index(drop=True) answers = answers.sort_values("fname").reset_index(drop=True) if (submission["fname"].values != answers["fname"].values).any(): raise InvalidSubmissionError("Submission and answers must have the same ids") # Convert to numbers try: submission.iloc[:, 1:] = submission.iloc[:, 1:].apply(pd.to_numeric) except (ValueError, TypeError) as e: raise InvalidSubmissionError(f"Error converting submission values to float: {str(e)}") answers.iloc[:, 1:] = answers.iloc[:, 1:].apply(pd.to_numeric) # Return as numpy arrays return answers.iloc[:, 1:].values, submission.iloc[:, 1:].values def grade(submission: pd.DataFrame, answers: pd.DataFrame) -> float: sub, ans = prepare_for_metric(submission, answers) return calculate_overall_lwlrap_sklearn(sub, ans)