feat: collega il lessico semantico al filler
This commit is contained in:
249
build_lexicon.py
Normal file
249
build_lexicon.py
Normal file
@@ -0,0 +1,249 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
from build_vocabulary import (
|
||||
FILTERED_OUTPUT_PATH,
|
||||
METADATA_OUTPUT_PATH,
|
||||
build_vocabulary,
|
||||
)
|
||||
|
||||
|
||||
LEXICON_OUTPUT_PATH = Path(__file__).with_name("lexicon_it.json")
|
||||
|
||||
POS_BY_TAG = {
|
||||
"function": "PREP",
|
||||
"verb_infinitive": "VERB",
|
||||
"adverb": "ADV",
|
||||
"adjective_like": "ADJ",
|
||||
"noun_like": "NOUN",
|
||||
}
|
||||
|
||||
REGISTER_BY_QUALITY = [
|
||||
(8, "common"),
|
||||
(5, "standard"),
|
||||
(3, "formal"),
|
||||
(0, "rare"),
|
||||
]
|
||||
|
||||
TOPIC_KEYWORDS = {
|
||||
"animals": {
|
||||
"cane", "gatto", "lupo", "volpe", "orso", "pesce", "cervo", "cavallo", "capra", "pecora",
|
||||
"leone", "tigre", "zebra", "aquila", "falco", "serpente", "vipera", "gabbiano", "anatra",
|
||||
"passero", "coniglio", "castoro", "bruco", "cigno", "asino", "alpaca",
|
||||
},
|
||||
"plants": {
|
||||
"albero", "pianta", "fiore", "foglia", "radice", "seme", "bosco", "selva", "ulivo", "quercia",
|
||||
"ortica", "edera", "aloe", "tulipano", "spiga", "polline", "grano", "erba",
|
||||
},
|
||||
"nature": {
|
||||
"natura", "bosco", "selva", "montagna", "collina", "roccia", "pietra", "fiume", "lago", "mare",
|
||||
"riva", "fonte", "onda", "vento", "fuoco", "terra", "sole", "luna", "aurora", "nuvola",
|
||||
"nebbia", "deserto", "isola", "greto", "radice", "fiore", "foglia", "erba", "zolla",
|
||||
},
|
||||
"ecology": {
|
||||
"ambiente", "ecologia", "natura", "bosco", "energia", "acqua", "terra", "clima", "sorgere",
|
||||
"fonte", "solare", "verde", "ulivo", "pianta", "polline", "grano", "radice",
|
||||
},
|
||||
"geography": {
|
||||
"montagna", "collina", "isola", "deserto", "equatore", "ovest", "oriente", "riva", "mare",
|
||||
"lago", "fiume", "ponte", "confine", "quota", "pianeta", "roccia", "greto",
|
||||
},
|
||||
"weather": {
|
||||
"vento", "nebbia", "aurora", "pioggia", "sole", "nuvola", "tempesta", "brina", "sereno",
|
||||
"clima", "goccia",
|
||||
},
|
||||
"sea": {
|
||||
"mare", "onda", "vela", "barca", "porto", "pesce", "ancora", "scoglio", "riva", "veliero",
|
||||
},
|
||||
"mountain": {
|
||||
"montagna", "quota", "vetta", "roccia", "greto", "collina", "sentiero", "alpino",
|
||||
},
|
||||
"health": {
|
||||
"salute", "febbre", "medico", "cura", "respiro", "diuretico", "anemico", "vigore", "energia",
|
||||
"dente", "cuore", "corpo", "viso",
|
||||
},
|
||||
"science": {
|
||||
"atomo", "energia", "metodo", "equatore", "digitale", "misura", "tecnica", "triangolo",
|
||||
"microfibra", "microscopio", "algoritmo", "motore", "materia", "liquido",
|
||||
},
|
||||
"sport": {
|
||||
"calcio", "atleta", "sportivo", "gol", "pallone", "gara", "trionfo", "primato", "allenatore",
|
||||
"stadio", "squadra", "rete",
|
||||
},
|
||||
"history": {
|
||||
"re", "principe", "regno", "impero", "senato", "console", "legione", "vittoria", "epoca",
|
||||
"origine", "ritorno",
|
||||
},
|
||||
"school": {
|
||||
"libro", "quaderno", "lezione", "classe", "studiare", "maestro", "scuola", "esame", "penna",
|
||||
"aula", "figura", "titolo",
|
||||
},
|
||||
"cinema": {
|
||||
"film", "teatro", "attore", "scena", "dialogo", "regista", "pellicola", "voce", "visione",
|
||||
"finale", "figura",
|
||||
},
|
||||
"literature": {
|
||||
"libro", "poesia", "favola", "fiaba", "frase", "parola", "lettura", "autore", "storia",
|
||||
"leggenda", "scrivere", "titolo",
|
||||
},
|
||||
"food": {
|
||||
"pane", "cacao", "gelato", "burro", "latte", "mandorla", "nocciola", "cena", "pranzo",
|
||||
"zuppa", "zucchero", "acqua", "fiore", "frutto",
|
||||
},
|
||||
"city": {
|
||||
"porta", "strada", "piazza", "ponte", "palazzo", "cortile", "villaggio", "citta", "urbano",
|
||||
"casale", "balcone", "finestra", "stazione",
|
||||
},
|
||||
"transport": {
|
||||
"automobile", "barca", "vela", "treno", "motore", "viaggio", "ruota", "ponte", "pilota",
|
||||
"volo", "aeroporto", "vettura",
|
||||
},
|
||||
"work": {
|
||||
"lavoro", "opera", "progetto", "metodo", "tecnica", "strumento", "martello", "guida",
|
||||
"mestiere", "servire",
|
||||
},
|
||||
"home": {
|
||||
"casa", "finestra", "porta", "parete", "divano", "tavolo", "sedia", "camera", "balcone",
|
||||
"camino", "tetto", "cortile", "vasca",
|
||||
},
|
||||
}
|
||||
|
||||
TOPIC_SUFFIXES = {
|
||||
"actions": ("are", "ere", "ire"),
|
||||
"abstract": ("zione", "zioni", "ismo", "ezza", "ita", "mento", "anza", "enza"),
|
||||
"animals": ("cane", "gatto", "lupo", "pesce", "volpe", "orso"),
|
||||
"plants": ("fiore", "foglia", "seme", "radice", "erba"),
|
||||
"nature": ("mare", "lago", "bosco", "vento", "onda", "roccia"),
|
||||
"geography": ("montagna", "isola", "deserto", "confine"),
|
||||
"city": ("strada", "palazzo", "porta", "ponte"),
|
||||
}
|
||||
|
||||
|
||||
def infer_pos(tags: List[str]) -> str:
|
||||
for tag in tags:
|
||||
if tag in POS_BY_TAG:
|
||||
return POS_BY_TAG[tag]
|
||||
return "NOUN"
|
||||
|
||||
|
||||
def infer_topics(word: str, tags: List[str]) -> List[str]:
|
||||
topics = {"general"}
|
||||
|
||||
if "verb_infinitive" in tags:
|
||||
topics.add("actions")
|
||||
if any(word.endswith(suffix) for suffix in ("tore", "trice", "zione", "ismo", "ista", "mento", "anza", "enza")):
|
||||
topics.add("abstract")
|
||||
|
||||
for topic, keywords in TOPIC_KEYWORDS.items():
|
||||
if word in keywords:
|
||||
topics.add(topic)
|
||||
|
||||
for topic, suffixes in TOPIC_SUFFIXES.items():
|
||||
if any(word.endswith(suffix) for suffix in suffixes):
|
||||
topics.add(topic)
|
||||
|
||||
if "animals" in topics:
|
||||
topics.add("nature")
|
||||
if "plants" in topics:
|
||||
topics.update({"nature", "ecology"})
|
||||
if "sea" in topics or "mountain" in topics or "weather" in topics:
|
||||
topics.add("nature")
|
||||
if "geography" in topics and "nature" not in topics:
|
||||
topics.add("nature")
|
||||
|
||||
return sorted(topics)
|
||||
|
||||
|
||||
def infer_register(quality: int) -> str:
|
||||
for threshold, label in REGISTER_BY_QUALITY:
|
||||
if quality >= threshold:
|
||||
return label
|
||||
return "rare"
|
||||
|
||||
|
||||
def frequency_from_quality(quality: int, index: int, total: int) -> tuple[int, float]:
|
||||
rank = index + 1
|
||||
normalized_rank = 1.0 - (rank - 1) / max(1, total - 1)
|
||||
quality_boost = min(max(quality, 0), 10) / 20.0
|
||||
frequency_score = round(min(1.0, normalized_rank * 0.7 + quality_boost), 4)
|
||||
return rank, frequency_score
|
||||
|
||||
|
||||
def load_words() -> List[str]:
|
||||
if not FILTERED_OUTPUT_PATH.exists() or not METADATA_OUTPUT_PATH.exists():
|
||||
build_vocabulary()
|
||||
|
||||
words = [
|
||||
line.strip()
|
||||
for line in FILTERED_OUTPUT_PATH.read_text(encoding="utf-8").splitlines()
|
||||
if line.strip()
|
||||
]
|
||||
return words
|
||||
|
||||
|
||||
def load_metadata() -> Dict[str, Dict[str, object]]:
|
||||
if not METADATA_OUTPUT_PATH.exists():
|
||||
build_vocabulary()
|
||||
return json.loads(METADATA_OUTPUT_PATH.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def build_lexicon() -> Dict[str, object]:
|
||||
words = load_words()
|
||||
metadata = load_metadata()
|
||||
|
||||
entries = []
|
||||
total = len(words)
|
||||
for index, word in enumerate(words):
|
||||
meta = metadata.get(word, {})
|
||||
tags = list(meta.get("tags", []))
|
||||
quality = int(meta.get("quality", 0))
|
||||
frequency_rank, frequency_score = frequency_from_quality(quality, index, total)
|
||||
|
||||
entry = {
|
||||
"form": word,
|
||||
"normalized_form": word,
|
||||
"lemma": word,
|
||||
"pos": infer_pos(tags),
|
||||
"length": len(word),
|
||||
"frequency_rank": frequency_rank,
|
||||
"frequency_score": frequency_score,
|
||||
"difficulty_word": max(1, min(5, 6 - max(1, min(5, quality // 2 + 1)))),
|
||||
"allowed_in_crossword": True,
|
||||
"quality_score": max(0, min(10, quality)),
|
||||
"topics": infer_topics(word, tags),
|
||||
"morph_features": {},
|
||||
"register": infer_register(quality),
|
||||
"source_flags": ["from_filtered_vocabulary", "from_metadata_heuristics"],
|
||||
"crossword_flags": tags,
|
||||
"notes": "",
|
||||
}
|
||||
entries.append(entry)
|
||||
|
||||
return {
|
||||
"meta": {
|
||||
"language": "it",
|
||||
"version": 1,
|
||||
"sources": ["vocaboli_it_filtrato.txt", "vocaboli_it_metadata.json"],
|
||||
"generated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
|
||||
"entry_count": len(entries),
|
||||
},
|
||||
"entries": entries,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
lexicon = build_lexicon()
|
||||
LEXICON_OUTPUT_PATH.write_text(
|
||||
json.dumps(lexicon, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
print(f"Lessico generato: {LEXICON_OUTPUT_PATH}")
|
||||
print(f"Voci generate: {lexicon['meta']['entry_count']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user