如何用Python计算嵌套数组的精确率、召回率与F1值
Alright, let's break down how to calculate precision, recall, and F1-score for your entity labeling task. You've got two large nested arrays (ground truth correct and predictions predicted) where each subarray corresponds to a sequence of labels, and you need metrics across the classes PER, ORG, MISC, LOC, *, and GPE.
Core Approach
First, we need to track three key metrics for each class:
- True Positives (TP): The number of times we correctly predicted the class
- False Positives (FP): The number of times we predicted the class when it wasn't the ground truth
- False Negatives (FN): The number of times the ground truth was the class, but we predicted something else
We'll iterate through every label pair in the nested arrays, update these counters, then use them to compute precision, recall, and F1 for each class plus overall micro/macro averages.
Python Implementation
# Define the target classes CLASSES = ['PER', 'ORG', 'MISC', 'LOC', '*', 'GPE'] # Initialize counters for each class tp = {cls: 0 for cls in CLASSES} fp = {cls: 0 for cls in CLASSES} fn = {cls: 0 for cls in CLASSES} # Iterate through each sequence pair for true_seq, pred_seq in zip(correct, predicted): # Compare each label in the sequence for true_label, pred_label in zip(true_seq, pred_seq): if true_label == pred_label: # Correct prediction: increment TP for the matching class tp[true_label] += 1 else: # Wrong prediction: increment FP for the predicted class, FN for the true class fp[pred_label] += 1 fn[true_label] += 1 def calculate_metrics(tp, fp, fn): metrics = {} # Calculate class-wise metrics for cls in CLASSES: # Handle division by zero cases to avoid errors precision = tp[cls] / (tp[cls] + fp[cls]) if (tp[cls] + fp[cls]) > 0 else 0.0 recall = tp[cls] / (tp[cls] + fn[cls]) if (tp[cls] + fn[cls]) > 0 else 0.0 f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0.0 metrics[cls] = { 'precision': round(precision, 4), 'recall': round(recall, 4), 'f1': round(f1, 4) } # Calculate micro-average (treat all classes as a single group) total_tp = sum(tp.values()) total_fp = sum(fp.values()) total_fn = sum(fn.values()) micro_precision = total_tp / (total_tp + total_fp) if (total_tp + total_fp) > 0 else 0.0 micro_recall = total_tp / (total_tp + total_fn) if (total_tp + total_fn) > 0 else 0.0 micro_f1 = 2 * (micro_precision * micro_recall) / (micro_precision + micro_recall) if (micro_precision + micro_recall) > 0 else 0.0 # Calculate macro-average (average metrics across all classes) macro_precision = sum(metrics[cls]['precision'] for cls in CLASSES) / len(CLASSES) macro_recall = sum(metrics[cls]['recall'] for cls in CLASSES) / len(CLASSES) macro_f1 = sum(metrics[cls]['f1'] for cls in CLASSES) / len(CLASSES) metrics['micro-average'] = { 'precision': round(micro_precision, 4), 'recall': round(micro_recall, 4), 'f1': round(micro_f1, 4) } metrics['macro-average'] = { 'precision': round(macro_precision, 4), 'recall': round(macro_recall, 4), 'f1': round(macro_f1, 4) } return metrics # Compute and print results results = calculate_metrics(tp, fp, fn) print("Class-wise Metrics:") for cls in CLASSES: print(f"\n{cls}:") print(f" Precision: {results[cls]['precision']}") print(f" Recall: {results[cls]['recall']}") print(f" F1-Score: {results[cls]['f1']}") print("\nOverall Metrics:") print("Micro-Average (weighted by class frequency):") print(f" Precision: {results['micro-average']['precision']}") print(f" Recall: {results['micro-average']['recall']}") print(f" F1-Score: {results['micro-average']['f1']}") print("\nMacro-Average (equal weight per class):") print(f" Precision: {results['macro-average']['precision']}") print(f" Recall: {results['macro-average']['recall']}") print(f" F1-Score: {results['macro-average']['f1']}")
Key Notes
- Handling
*: If*represents "no entity" and you don't want to include it in entity-specific metrics, simply remove it from theCLASSESlist. The code will automatically adjust counters and averages. - Efficiency: This implementation uses basic Python loops, which will handle your 10K+ subarrays efficiently without needing external libraries.
- Division by Zero: We explicitly handle cases where a class has no TP/FP or TP/FN to avoid runtime errors.
内容的提问来源于stack exchange,提问作者Harsh2093
相关产品推荐
相关产品推荐

