anno_eval/eval/synthetic.rs
1//! Synthetic NER Test Datasets
2//!
3//! This module provides access to synthetic NER datasets for testing and evaluation.
4//! The actual data is organized in `dataset::synthetic` submodules by domain.
5//!
6//! # Research Context
7//!
8//! Synthetic data has known limitations (arXiv:2505.16814 "Does Synthetic Data Help NER"):
9//!
10//! | Issue | Mitigation |
11//! |-------|------------|
12//! | Entity type skew | Stratified sampling |
13//! | Clean annotations | Add noise injection |
14//! | Domain gap | Mix with real data |
15//! | Label shift | Track via `LabelShift` |
16//!
17//! # What This Dataset IS Good For
18//!
19//! - **Unit testing**: Does the code work at all?
20//! - **Pattern coverage**: Are regex patterns correct?
21//! - **Edge cases**: Unicode, boundaries, special chars
22//! - **Fast iteration**: Runs in <1s, no network
23//!
24//! # What This Dataset IS NOT Good For
25//!
26//! - **Zero-shot claims**: Label overlap with training ≈ 100%
27//! - **Real-world performance**: Synthetic ≠ domain-specific noise
28//! - **Model comparison**: Needs WikiGold/CoNLL/WNUT for fair eval
29//!
30//! # Usage
31//!
32//! ```rust
33//! use anno_eval::eval::synthetic::{all_datasets, datasets_by_domain, datasets_by_difficulty, Domain, Difficulty};
34//!
35//! // Get all datasets
36//! let all = all_datasets();
37//! assert!(!all.is_empty());
38//!
39//! // Filter by domain
40//! let news = datasets_by_domain(Domain::News);
41//!
42//! // Filter by difficulty
43//! let hard = datasets_by_difficulty(Difficulty::Hard);
44//! ```
45
46// Re-export types from dataset module (single source of truth)
47pub use super::dataset::{AnnotatedExample, Difficulty, Domain};
48
49// Re-export dataset functions from the modular structure
50pub use super::dataset::synthetic::{
51 // Domain-specific datasets
52 academic_dataset,
53 adversarial_dataset,
54 aerospace_dataset,
55 // Aggregate functions
56 all_datasets,
57 automotive_dataset,
58 biomedical_dataset,
59 conversational_dataset,
60 cybersecurity_dataset,
61 ecommerce_dataset,
62 energy_dataset,
63 entertainment_dataset,
64 financial_dataset,
65 food_dataset,
66 globally_diverse_dataset,
67 hard_domain_examples,
68 healthcare_dataset,
69 historical_dataset,
70 legal_dataset,
71 manufacturing_dataset,
72 multilingual_dataset,
73 news_dataset,
74 politics_dataset,
75 real_estate_dataset,
76 scientific_dataset,
77 social_media_dataset,
78 sports_dataset,
79 structured_dataset,
80 technology_dataset,
81 travel_dataset,
82 weather_dataset,
83};
84
85use std::collections::HashMap;
86
87// ============================================================================
88// Backward Compatibility Aliases
89// ============================================================================
90
91/// Alias for `news_dataset()` - CoNLL-2003 style examples.
92///
93/// This is the traditional news/formal text dataset used in CoNLL shared tasks.
94#[inline]
95pub fn conll_style_dataset() -> Vec<AnnotatedExample> {
96 news_dataset()
97}
98
99/// Filter datasets by domain.
100///
101/// # Example
102///
103/// ```rust
104/// use anno_eval::eval::synthetic::{datasets_by_domain, Domain};
105///
106/// let news = datasets_by_domain(Domain::News);
107/// for ex in &news {
108/// assert_eq!(ex.domain, Domain::News);
109/// }
110/// ```
111pub fn datasets_by_domain(domain: Domain) -> Vec<AnnotatedExample> {
112 super::dataset::synthetic::by_domain(domain)
113}
114
115/// Filter datasets by difficulty.
116///
117/// # Example
118///
119/// ```rust
120/// use anno_eval::eval::synthetic::{datasets_by_difficulty, Difficulty};
121///
122/// let hard = datasets_by_difficulty(Difficulty::Hard);
123/// for ex in &hard {
124/// assert_eq!(ex.difficulty, Difficulty::Hard);
125/// }
126/// ```
127pub fn datasets_by_difficulty(difficulty: Difficulty) -> Vec<AnnotatedExample> {
128 super::dataset::synthetic::by_difficulty(difficulty)
129}
130
131/// Statistics about the synthetic datasets.
132#[derive(Debug, Clone)]
133pub struct DatasetStats {
134 /// Total number of examples
135 pub total_examples: usize,
136 /// Total number of entities
137 pub total_entities: usize,
138 /// Examples per domain
139 pub examples_per_domain: HashMap<String, usize>,
140 /// Examples per difficulty
141 pub examples_per_difficulty: HashMap<String, usize>,
142}
143
144/// Get statistics about all synthetic datasets.
145pub fn dataset_stats() -> DatasetStats {
146 let stats = super::dataset::synthetic::stats();
147 DatasetStats {
148 total_examples: stats.total_examples,
149 total_entities: stats.total_entities,
150 examples_per_domain: stats.domains,
151 examples_per_difficulty: stats.difficulties,
152 }
153}
154
155/// Extended quality dataset with diverse entity types and contexts.
156///
157/// This dataset focuses on quality over quantity, with carefully crafted
158/// examples covering edge cases and challenging scenarios.
159pub fn extended_quality_dataset() -> Vec<AnnotatedExample> {
160 // Combine challenging examples from various sources
161 let mut all = Vec::new();
162 all.extend(hard_domain_examples());
163 all.extend(globally_diverse_dataset());
164 all.extend(adversarial_dataset());
165 all
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_all_datasets() {
174 let all = all_datasets();
175 assert!(!all.is_empty());
176 assert!(all.len() >= 100, "Expected at least 100 examples");
177 }
178
179 #[test]
180 fn test_conll_alias() {
181 let conll = conll_style_dataset();
182 let news = news_dataset();
183 assert_eq!(conll.len(), news.len());
184 }
185
186 #[test]
187 fn test_datasets_by_domain() {
188 let news = datasets_by_domain(Domain::News);
189 assert!(!news.is_empty());
190 for ex in &news {
191 assert_eq!(ex.domain, Domain::News);
192 }
193 }
194
195 #[test]
196 fn test_datasets_by_difficulty() {
197 let hard = datasets_by_difficulty(Difficulty::Hard);
198 for ex in &hard {
199 assert_eq!(ex.difficulty, Difficulty::Hard);
200 }
201 }
202
203 #[test]
204 fn test_dataset_stats() {
205 let stats = dataset_stats();
206 assert!(stats.total_examples > 0);
207 assert!(stats.total_entities > 0);
208 assert!(!stats.examples_per_domain.is_empty());
209 }
210
211 #[test]
212 fn test_extended_quality_dataset() {
213 let extended = extended_quality_dataset();
214 assert!(!extended.is_empty());
215 }
216}