Skip to main content

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}