Skip to content

Commit b04309b

Browse files
committed
feat: add analysis scripts for benchmark evaluations and improve table detection logic
1 parent c2dfde6 commit b04309b

5 files changed

Lines changed: 601 additions & 56 deletions

File tree

analyze_benchmark.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python3
2+
import json
3+
from pathlib import Path
4+
5+
# Load evaluations
6+
hybrid_eval = json.loads(Path('benchmark/prediction/edgeparse_hybrid/evaluation.json').read_text())
7+
baseline_eval = json.loads(Path('benchmark/prediction/edgeparse/evaluation.json').read_text())
8+
9+
# Compare documents with tables
10+
docs_with_tables = []
11+
for doc_data in hybrid_eval['documents']:
12+
doc_id = doc_data['document_id']
13+
hybrid_teds = doc_data['scores'].get('teds')
14+
baseline_teds = None
15+
for baseline_doc in baseline_eval['documents']:
16+
if baseline_doc['document_id'] == doc_id:
17+
baseline_teds = baseline_doc['scores'].get('teds')
18+
break
19+
20+
if hybrid_teds is not None:
21+
improvement = hybrid_teds - baseline_teds if baseline_teds is not None else 0
22+
docs_with_tables.append({
23+
'doc_id': doc_id,
24+
'hybrid_teds': hybrid_teds,
25+
'baseline_teds': baseline_teds,
26+
'improvement': improvement,
27+
'overall': doc_data['scores'].get('overall'),
28+
})
29+
30+
# Sort by improvement descending
31+
docs_with_tables.sort(key=lambda x: x['improvement'], reverse=True)
32+
33+
print('Top 10 documents where hybrid helped:')
34+
for i, d in enumerate(docs_with_tables[:10], 1):
35+
print(f"{i}. {d['doc_id']}: baseline={d['baseline_teds']:.4f} -> hybrid={d['hybrid_teds']:.4f} ({d['improvement']:+.4f})")
36+
37+
print('\nBottom 10 documents where hybrid hurt:')
38+
for i, d in enumerate(docs_with_tables[-10:], 1):
39+
print(f"{i}. {d['doc_id']}: baseline={d['baseline_teds']:.4f} -> hybrid={d['hybrid_teds']:.4f} ({d['improvement']:+.4f})")
40+
41+
print(f'\nTotal docs with tables: {len(docs_with_tables)}')
42+
print(f'Docs where hybrid improved: {sum(1 for d in docs_with_tables if d["improvement"] > 0.01)}')
43+
print(f'Docs where hybrid hurt: {sum(1 for d in docs_with_tables if d["improvement"] < -0.01)}')
44+
print(f'\nAverage improvement: {sum(d["improvement"] for d in docs_with_tables) / len(docs_with_tables):.4f}')

analyze_worst.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python3
2+
import json
3+
all_docs = json.loads(open('benchmark/prediction/edgeparse_hybrid/evaluation.json').read())['documents']
4+
worst = sorted(all_docs, key=lambda x: x['scores'].get('overall', 0))[:10]
5+
print('Worst performing documents (hybrid mode):')
6+
for d in worst:
7+
print(f"{d['document_id']}: overall={d['scores']['overall']:.4f}, teds={d['scores'].get('teds', 'N/A')}, mhs={d['scores'].get('mhs', 'N/A')}")
8+
9+
print('\n\nDocuments with low TEDS scores (table issues):')
10+
teds_docs = [(d['document_id'], d['scores'].get('teds', None)) for d in all_docs if d['scores'].get('teds') is not None and d['scores'].get('teds', 1) < 0.3]
11+
teds_docs.sort(key=lambda x: x[1])
12+
for doc_id, teds in teds_docs[:10]:
13+
print(f"{doc_id}: TEDS={teds:.4f}")

crates/edgeparse-core/src/output/markdown.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,9 @@ fn render_layout_single_caption_chart_document_cached(
14451445
if doc.number_of_pages != 1 {
14461446
return None;
14471447
}
1448+
if document_has_populated_table(doc) {
1449+
return None;
1450+
}
14481451

14491452
let caption_indices = doc
14501453
.kids
@@ -1555,6 +1558,22 @@ fn render_layout_single_caption_chart_document_cached(
15551558
Some(output.trim_end().to_string() + "\n")
15561559
}
15571560

1561+
fn document_has_populated_table(doc: &PdfDocument) -> bool {
1562+
doc.kids.iter().any(|element| {
1563+
table_border_from_element(element).is_some_and(|table| {
1564+
table.num_rows >= 2
1565+
&& table.num_columns >= 2
1566+
&& table.rows.iter().any(|row| {
1567+
row.cells
1568+
.iter()
1569+
.filter(|cell| !cell_text_content(cell).trim().is_empty())
1570+
.count()
1571+
>= 2
1572+
})
1573+
})
1574+
})
1575+
}
1576+
15581577
fn looks_like_chart_noise_element(_element: &ContentElement, text: &str) -> bool {
15591578
if text.is_empty() {
15601579
return false;
@@ -13174,6 +13193,32 @@ mod tests {
1317413193
assert!(md.contains("| kaolinite | 10 |"));
1317513194
}
1317613195

13196+
#[test]
13197+
fn test_single_caption_chart_renderer_skips_documents_with_populated_tables() {
13198+
let mut doc = PdfDocument::new("table-with-caption.pdf".to_string());
13199+
doc.number_of_pages = 1;
13200+
for idx in 0..10 {
13201+
let bottom = 720.0 - idx as f64 * 18.0;
13202+
doc.kids.push(make_paragraph(
13203+
"Explanatory body text that should remain outside the chart-only renderer.",
13204+
bottom,
13205+
bottom + 10.0,
13206+
));
13207+
}
13208+
doc.kids.push(make_paragraph(
13209+
"Figure 7.2: Kinematic Viscosity of Water at Atmospheric Pressure.",
13210+
150.0,
13211+
162.0,
13212+
));
13213+
doc.kids.push(make_two_column_table(&[
13214+
("Temperature", "Viscosity"),
13215+
("20", "1.004"),
13216+
("25", "0.893"),
13217+
]));
13218+
13219+
assert!(render_layout_single_caption_chart_document(&doc).is_none());
13220+
}
13221+
1317713222
#[test]
1317813223
fn test_blank_right_column_table_is_not_misrendered_as_toc() {
1317913224
let mut doc = PdfDocument::new("flocculation-table.pdf".to_string());

0 commit comments

Comments
 (0)