Skip to main content

Biblioteca de algoritmos de similitud para desambiguación de términos históricos

Project description

Portada S-Index

Biblioteca Python para desambiguación de nombres históricos mediante algoritmos de similitud de cadenas

PyPI version Python Version License Downloads Code style: black Maintained PRs Welcome

CaracterísticasInstalaciónUso RápidoDocumentaciónEjemplos


🎯 Descripción

Portada S-Index es una biblioteca especializada en la desambiguación automática de nombres históricos mediante el análisis de similitud con vocabularios controlados. Desarrollada para el proyecto PORTADA, utiliza múltiples algoritmos de similitud de cadenas y un sistema de consenso para clasificar nombres con diferentes niveles de confianza.

Interfaz principal: JSON - Todas las entradas y salidas se manejan exclusivamente mediante JSON para máxima interoperabilidad.

✨ Características

🔧 Algoritmos de Similitud

  • Levenshtein OCR: Distancia de Levenshtein con corrección para errores comunes de OCR
  • Levenshtein Ratio: Distancia de Levenshtein estándar normalizada
  • Jaro-Winkler: Algoritmo optimizado para nombres propios con énfasis en prefijos
  • N-gramas (2 y 3): Similitud basada en bigramas y trigramas

📊 Sistema de Clasificación

Clasificación automática en 5 niveles de confianza:

Nivel Descripción Criterio
CONSENSUADO Alta confianza 2+ algoritmos votan por la misma entidad + Levenshtein OCR incluido
CONSENSUADO_DEBIL Confianza moderada 2+ algoritmos aprueban sin criterio estricto
SOLO_1_VOTO Baja confianza Solo 1 algoritmo supera el umbral
ZONA_GRIS Ambiguo Al menos 1 algoritmo en zona gris
RECHAZADO Sin correspondencia Ningún algoritmo supera umbrales

🎛️ Configuración Flexible

  • Selección de algoritmos a utilizar
  • Umbrales de aprobación personalizables por algoritmo
  • Zonas grises ajustables para casos ambiguos
  • Normalización automática de texto (Unicode, diacríticos, minúsculas)
  • Mapeo de voces a entidades para agrupación semántica

🚀 Procesamiento Avanzado

  • Procesamiento por lotes: Múltiples operaciones en una sola llamada
  • Entrada flexible: Diccionarios Python, strings JSON o archivos JSON
  • Salida estructurada: Siempre en formato JSON con información completa
  • Reportes estadísticos: Generación automática de métricas y cobertura

📦 Instalación

Requisitos

  • Python >= 3.12
  • Sin dependencias externas (solo biblioteca estándar de Python)

Instalación desde PyPI

pip install portada-s-index

O con uv (más rápido):

uv pip install portada-s-index

Instalación desde código fuente

git clone <repository>
cd portada-s-index
pip install -e .

O con uv:

uv pip install -e .

🚀 Uso Rápido

Calcular Similitud

from portada_s_index import calculate_similarity_json
import json

# Entrada JSON
input_data = {
    "term": "alemán",
    "voices": ["aleman", "alemana", "germano", "frances"]
}

# Procesar
result_json = calculate_similarity_json(input_data)
result = json.loads(result_json)

# Resultado
print(json.dumps(result, indent=2, ensure_ascii=False))

Salida:

{
  "term": "alemán",
  "results": {
    "levenshtein_ocr": {
      "voice": "aleman",
      "similarity": 1.0,
      "approved": true
    },
    "jaro_winkler": {
      "voice": "aleman",
      "similarity": 1.0,
      "approved": true
    },
    "ngram_2": {
      "voice": "aleman",
      "similarity": 1.0,
      "approved": true
    }
  }
}

Clasificar Nombres

from portada_s_index import classify_name_json
import json

# Entrada JSON
input_data = {
    "name": ["aleman", "frances", "ingles"],
    "voices": ["aleman", "alemana", "frances", "francesa", "ingles", "inglesa"],
    "frequencies": {
        "aleman": 100,
        "frances": 80,
        "ingles": 150
    }
}

# Procesar
result_json = classify_name_json(input_data)
result = json.loads(result_json)

print(f"Nombres clasificados: {result['total_name']}")

Clasificar con Reporte

from portada_s_index import classify_name_with_report_json
import json

# Entrada JSON (mismo formato que classify_name)
input_data = {
    "name": ["aleman", "frances"],
    "voices": ["aleman", "frances"]
}

# Procesar
result_json = classify_name_with_report_json(input_data)
result = json.loads(result_json)

# Acceder al reporte
report = result["report"]
print(f"Cobertura: {report['coverage']['consensuado_strict']['percentage']:.2f}%")

Procesamiento por Lotes

from portada_s_index import process_batch_json
import json

# Múltiples operaciones en una llamada
input_data = {
    "operations": [
        {
            "type": "calculate_similarity",
            "data": {
                "term": "alemán",
                "voices": ["aleman", "alemana"]
            }
        },
        {
            "type": "classify_name",
            "data": {
                "name": ["frances", "ingles"],
                "voices": ["frances", "ingles"]
            }
        }
    ],
    "config": {
        "algorithms": ["levenshtein_ocr", "jaro_winkler"],
        "normalize": true
    }
}

# Procesar
result_json = process_batch_json(input_data)
result = json.loads(result_json)

print(f"Operaciones exitosas: {result['successful']}/{result['total_operations']}")

Procesar desde Archivos

from portada_s_index import classify_name_from_file

# Procesar archivo JSON y guardar resultado
result_json = classify_name_from_file(
    input_file="input.json",
    output_file="output.json"
)

Orientado a Objetos (Nueva API)

Para implementaciones más estructuradas, puedes usar las clases principales de la librería. Esto facilita la separación de responsabilidades:

from portada_s_index.core import PortAdaSIndex, EntityCitation, KnownEntity
from portada_s_index.similarity import SimilarityConfig

# 1. Configurar
config = SimilarityConfig(algorithms=["levenshtein_ratio", "jaro_winkler"])
index = PortAdaSIndex(config)
index.set_entity_type("Países")

# 2. Cargar Entidades
index.load_known_entities([
    KnownEntity(name="ALEMANIA", voices=["aleman", "alemana", "germano"]),
    KnownEntity(name="FRANCIA", voices=["frances", "francesa"]),
])

# 3. Cargar Citaciones
index.load_citations([
    EntityCitation(id="doc1", cited_name="alemán"),
    EntityCitation(id="doc2", cited_name="frances")
])

# 4. Generar Matriz y Reporte
matrix = index.generate_similarity_matrix()
report = index.get_statistical_report()

print(f"Tipo: {report['entity_type']}")
print(f"Total citaciones analizadas: {report['total_citations']}")

📖 Documentación

Estructura de Entrada JSON

Entrada Mínima

{
  "name": ["aleman", "frances"],
  "voices": ["aleman", "frances"]
}

Entrada Completa

{
  "name": ["aleman", "frances"],
  "voices": ["aleman", "alemana", "frances", "francesa"],
  "frequencies": {
    "aleman": 100,
    "frances": 80
  },
  "voice_to_entity": {
    "aleman": "ALEMANIA",
    "alemana": "ALEMANIA",
    "frances": "FRANCIA",
    "francesa": "FRANCIA"
  },
  "config": {
    "algorithms": ["levenshtein_ocr", "jaro_winkler", "ngram_2"],
    "thresholds": {
      "levenshtein_ocr": 0.75,
      "jaro_winkler": 0.85,
      "ngram_2": 0.66
    },
    "gray_zones": {
      "levenshtein_ocr": [0.71, 0.749],
      "jaro_winkler": [0.8, 0.849],
      "ngram_2": [0.63, 0.659]
    },
    "normalize": true,
    "min_votes_consensus": 2,
    "require_levenshtein_ocr": true
  }
}

API Principal

Funciones de Entrada JSON

Función Descripción
calculate_similarity_json(input_json) Calcula similitud de un término con voces
classify_name_json(input_json) Clasifica múltiples términos
classify_name_with_report_json(input_json) Clasifica términos y genera reporte resumen
process_batch_json(input_json) Procesa múltiples operaciones en lote

Funciones de Archivo JSON

Función Descripción
calculate_similarity_from_file(input_file, output_file) Procesa archivo de similitud
classify_name_from_file(input_file, output_file) Procesa archivo de clasificación
classify_name_with_report_from_file(input_file, output_file) Procesa con reporte
process_batch_from_file(input_file, output_file) Procesa lote desde archivo

Algoritmos y Umbrales

Algoritmo Identificador Umbral Default Zona Gris Default
Levenshtein OCR levenshtein_ocr 0.75 [0.71, 0.749]
Levenshtein Ratio levenshtein_ratio 0.75 [0.71, 0.749]
Jaro-Winkler jaro_winkler 0.85 [0.80, 0.849]
N-gramas 2 ngram_2 0.66 [0.63, 0.659]
N-gramas 3 ngram_3 0.60 [0.55, 0.599]

📚 Documentación Completa

💡 Ejemplos

La carpeta examples/ contiene ejemplos completos de uso (no incluida en el repositorio):

  • json_usage.py: Ejemplos con JSON en memoria
  • json_file_processing.py: Procesamiento de archivos JSON
  • input_*.json: Archivos de ejemplo de entrada
  • quick_test.py: Tests rápidos sin instalación

Ejecutar Ejemplos

# Desde el directorio del proyecto
python3 examples/json_usage.py
python3 examples/json_file_processing.py

🧪 Testing

Tests Unitarios

# Tests básicos
python3 tests/test_basic.py

Test Externo (Simulación Real)

# Test que simula uso real desde fuera del proyecto
python3 test_portada_external.py

Resultado esperado:

✓ TEST 1: Similitud simple
✓ TEST 2: Clasificación de términos
✓ TEST 3: Clasificación con reporte
✓ TEST 4: Configuración personalizada
✓ TEST 5: Procesamiento por lotes
✓ TEST 6: Mapeo de voces a entidades
✓ TEST 7: Entrada como string JSON

✓ TODOS LOS TESTS EXTERNOS PASARON CORRECTAMENTE

Test con Datos Reales

# Test con datos reales del proyecto portada_s_index
python3 test_real_data.py

Este test:

  1. Convierte CSVs de términos históricos a JSON
  2. Carga listas de voces normalizadas
  3. Procesa 100 términos reales con los algoritmos
  4. Genera reportes estadísticos detallados

Resultados con datos reales (100 términos, 110,924 ocurrencias):

Clasificación Términos Ocurrencias Porcentaje
CONSENSUADO 92 110,567 99.68%
SOLO_1_VOTO 7 322 0.29%
RECHAZADO 1 35 0.03%

Cobertura consensuada: 99.68%

Top entidades identificadas:

  • INGLATERRA: 34,976 ocurrencias
  • NACIONAL_AR: 14,921 ocurrencias
  • FRANCIA: 9,982 ocurrencias
  • ITALIA: 9,283 ocurrencias
  • ALEMANIA: 6,421 ocurrencias

🔧 Configuración Avanzada

Personalizar Algoritmos

{
  "config": {
    "algorithms": ["levenshtein_ocr", "jaro_winkler"],
    "thresholds": {
      "levenshtein_ocr": 0.8,
      "jaro_winkler": 0.9
    }
  }
}

Ajustar Criterios de Consenso

{
  "config": {
    "min_votes_consensus": 3,
    "require_levenshtein_ocr": false
  }
}

Desactivar Normalización

{
  "config": {
    "normalize": false
  }
}

🎯 Casos de Uso

Desambiguación de Banderas de Barcos

input_data = {
    "name": ["aleman", "alemana", "germano"],
    "voices": ["aleman", "alemana", "germano"],
    "voice_to_entity": {
        "aleman": "ALEMANIA",
        "alemana": "ALEMANIA",
        "germano": "ALEMANIA"
    }
}

Normalización de Puertos

input_data = {
    "name": ["barcelona", "barzelona", "barcino"],
    "voices": ["barcelona"],
    "voice_to_entity": {
        "barcelona": "BARCELONA"
    }
}

Clasificación de Tipos de Embarcación

input_data = {
    "name": ["bergantin", "bergantín", "bergatin"],
    "voices": ["bergantin"],
    "frequencies": {
        "bergantin": 150,
        "bergantín": 80,
        "bergatin": 20
    }
}

📊 Formato de Salida

Resultado de Similitud

{
  "term": "alemán",
  "results": {
    "levenshtein_ocr": {
      "term": "alemán",
      "voice": "aleman",
      "algorithm": "levenshtein_ocr",
      "similarity": 1.0,
      "approved": true,
      "in_gray_zone": false
    }
  }
}

Clasificación de Nombres

{
  "total_name": 2,
  "classifications": [
    {
      "term": "aleman",
      "frequency": 100,
      "results": {...},
      "votes_approval": 3,
      "entity_consensus": "ALEMANIA",
      "voice_consensus": "aleman",
      "votes_entity": 3,
      "levenshtein_ocr_in_consensus": true,
      "classification": "CONSENSUADO"
    }
  ]
}

Reporte Resumen

{
  "report": {
    "total_name": 100,
    "total_occurrences": 5000,
    "by_level": {
      "CONSENSUADO": {
        "count": 75,
        "occurrences": 4500,
        "percentage": 90.0
      }
    },
    "coverage": {
      "consensuado_strict": {
        "occurrences": 4500,
        "percentage": 90.0
      }
    }
  },
  "classifications": [...]
}

🎖️ Resultados con Datos Reales

Pruebas realizadas con datos históricos reales del proyecto PORTADA:

Dataset de Prueba

  • Fuente: Banderas de barcos históricos
  • Nombres procesados: 100
  • Ocurrencias totales: 110,924
  • Voces de referencia: 324 (134 entidades)

Resultados de Clasificación

Nivel Nombres Ocurrencias Cobertura
CONSENSUADO 92 110,567 99.68%
SOLO_1_VOTO 7 322 0.29%
RECHAZADO 1 35 0.03%

Ejemplos de Clasificación Exitosa

Nombres con alta frecuencia correctamente identificados:

Nombre Original Frecuencia Entidad Identificada Confianza
ingles 28,247 INGLATERRA CONSENSUADO
nacional 14,921 NACIONAL_AR CONSENSUADO
frances 6,933 FRANCIA CONSENSUADO
inglesa 6,153 INGLATERRA CONSENSUADO
aleman 5,154 ALEMANIA CONSENSUADO
italiano 4,924 ITALIA CONSENSUADO

Top 10 Entidades Identificadas

  1. INGLATERRA: 34,976 ocurrencias
  2. NACIONAL_AR: 14,921 ocurrencias
  3. FRANCIA: 9,982 ocurrencias
  4. ITALIA: 9,283 ocurrencias
  5. ALEMANIA: 6,421 ocurrencias
  6. URUGUAY: 5,987 ocurrencias
  7. ESPAÑA: 5,711 ocurrencias
  8. ESTADOS UNIDOS: 4,272 ocurrencias
  9. NORUEGA: 3,717 ocurrencias
  10. ARGENTINA: 3,241 ocurrencias

Análisis de Rendimiento

  • Precisión: 99.68% de las ocurrencias clasificadas con alta confianza
  • Cobertura: 92% de nombres únicos consensuados
  • Algoritmos: 91 nombres con 3 votos de aprobación
  • Tiempo de procesamiento: < 1 segundo para 100 nombres

Conclusiones

✅ Los algoritmos demuestran alta precisión en la identificación de nombres históricos
✅ El sistema de consenso funciona efectivamente para reducir falsos positivos
✅ La normalización de texto maneja correctamente variaciones ortográficas
✅ El mapeo a entidades permite agrupación semántica efectiva

🤝 Contribuir

Las contribuciones son bienvenidas. Por favor:

  1. Fork el proyecto
  2. Crea una rama para tu feature (git checkout -b feature/AmazingFeature)
  3. Commit tus cambios (git commit -m 'Add some AmazingFeature')
  4. Push a la rama (git push origin feature/AmazingFeature)
  5. Abre un Pull Request

📝 Licencia

[Especificar licencia]

👥 Autores

Proyecto PORTADA - Desambiguación de términos históricos marítimos

🙏 Agradecimientos

  • Proyecto PORTADA
  • Comunidad de investigación histórica marítima

📧 Contacto

Para preguntas, sugerencias o reportar problemas, por favor abre un issue en el repositorio.


Hecho con ❤️ para el proyecto PORTADA

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

portada_s_index-0.1.2.tar.gz (46.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

portada_s_index-0.1.2-py3-none-any.whl (32.1 kB view details)

Uploaded Python 3

File details

Details for the file portada_s_index-0.1.2.tar.gz.

File metadata

  • Download URL: portada_s_index-0.1.2.tar.gz
  • Upload date:
  • Size: 46.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.5 {"installer":{"name":"uv","version":"0.10.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for portada_s_index-0.1.2.tar.gz
Algorithm Hash digest
SHA256 c4b79573877f50306db22bd686b35e98c32522aad0e80da04cca0d51072f4ea2
MD5 da93a36fa0040f446dc5c34fbc6d71e2
BLAKE2b-256 14f7319442865f9cfccfa289f5f867285fe4d1f7e8283a0377ad52dc53dad933

See more details on using hashes here.

File details

Details for the file portada_s_index-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: portada_s_index-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 32.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.5 {"installer":{"name":"uv","version":"0.10.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for portada_s_index-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 cabd18bb38b235cd7769f4eacfc0d6913852c7c959e088730e1d3bcc20121845
MD5 72ae1085f21d092dea4c7a948213c362
BLAKE2b-256 dcd5df0eb19458afcead508ed3b7b9882a34935d568a1f029c0d31839b6c18f0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page