{"id":2822,"date":"2025-09-01T12:53:06","date_gmt":"2025-09-01T17:53:06","guid":{"rendered":"https:\/\/biblioteca.utc.edu.ec\/?page_id=2822"},"modified":"2025-09-01T13:40:18","modified_gmt":"2025-09-01T18:40:18","slug":"analisis-de-archivos-bib","status":"publish","type":"page","link":"https:\/\/biblioteca.utc.edu.ec\/?page_id=2822","title":{"rendered":"An\u00e1lisis de archivos .bib"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>An\u00e1lisis Bibliom\u00e9trico Web<\/title>\n    <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n    <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n    <link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\">\n    <style>\n        body {\n            font-family: 'Inter', sans-serif;\n            background-color: #f3f4f6;\n        }\n        .container {\n            max-width: 95%;\n        }\n        .file-upload-card, .dashboard-card {\n            background-color: #ffffff;\n            border-radius: 12px;\n            padding: 2rem;\n            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n            transition: all 0.3s ease;\n        }\n        .file-upload-card:hover {\n            box-shadow: 0 10px 15px rgba(0, 0, 0, 0.2);\n            transform: translateY(-5px);\n        }\n        #results-card {\n            background-color: #ffffff;\n            border-radius: 12px;\n            padding: 2rem;\n            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n            display: none;\n            transition: all 0.5s ease-in-out;\n            opacity: 0;\n            transform: translateY(20px);\n        }\n        .show-results {\n            display: block !important;\n            opacity: 1 !important;\n            transform: translateY(0) !important;\n        }\n        .list-group li:nth-child(odd) {\n            background-color: #f9fafb;\n        }\n        input[type=\"file\"] {\n            display: none;\n        }\n        .custom-file-upload {\n            border: 2px solid #3b82f6;\n            color: #3b82f6;\n            background-color: #eff6ff;\n            padding: 0.75rem 2rem;\n            border-radius: 9999px;\n            font-weight: 600;\n            cursor: pointer;\n            transition: all 0.3s ease;\n        }\n        .custom-file-upload:hover {\n            background-color: #dbeafe;\n        }\n        .section-divider {\n            border-top: 1px solid #e5e7eb;\n            margin: 2rem 0;\n        }\n        .loading-spinner {\n            border: 4px solid #f3f3f3;\n            border-top: 4px solid #3b82f6;\n            border-radius: 50%;\n            width: 40px;\n            height: 40px;\n            animation: spin 1s linear infinite;\n        }\n        @keyframes spin {\n            0% { transform: rotate(0deg); }\n            100% { transform: rotate(360deg); }\n        }\n        th, td {\n            padding: 0.75rem 1rem;\n            text-align: left;\n        }\n        th {\n            background-color: #f3f4f6;\n            font-weight: 600;\n            text-transform: uppercase;\n            font-size: 0.875rem;\n            letter-spacing: 0.05em;\n        }\n        tr:nth-child(even) {\n            background-color: #f9fafb;\n        }\n        \n        \/* Estilo para el panel de gu\u00eda *\/\n        .guide-panel {\n            background-color: #e5f2ff;\n            border-left: 5px solid #3b82f6;\n            border-radius: 12px;\n            padding: 1.5rem;\n            margin-bottom: 2rem;\n        }\n        .guide-panel summary {\n            font-size: 1.25rem;\n            font-weight: 600;\n            color: #1e40af;\n            cursor: pointer;\n            padding: 0.5rem 0;\n        }\n        .guide-panel ul {\n            list-style-type: disc;\n            margin-left: 1.5rem;\n            margin-top: 1rem;\n            color: #374151;\n            font-size: 1rem;\n        }\n        .guide-panel li {\n            margin-bottom: 0.5rem;\n        }\n        .guide-panel h4 {\n            font-weight: 700;\n            color: #1f2937;\n            margin-top: 1rem;\n        }\n        .guide-panel p {\n            margin-bottom: 0.5rem;\n        }\n        \n    <\/style>\n<\/head>\n<body class=\"bg-gray-100 flex items-center justify-center min-h-screen p-4\">\n\n    <div class=\"container mx-auto p-8 bg-gray-50 rounded-lg shadow-xl\">\n        <h1 class=\"text-4xl font-bold text-center text-gray-800 mb-6\">An\u00e1lisis de Archivos BibTeX .bib<\/h1>\n        <p class=\"text-center text-gray-600 mb-8\">Una herramienta para analizar archivos .bib directamente en tu navegador.<\/p>\n\n        <!-- Panel de Gu\u00eda de Uso -->\n        <div class=\"guide-panel\">\n            <details>\n                <summary>Gu\u00eda de Uso \u2013 An\u00e1lisis Bibliom\u00e9trico<\/summary>\n                <div>\n                    <p class=\"text-gray-600 mt-4\">Esta herramienta te permite analizar de manera visual y r\u00e1pida tus referencias bibliogr\u00e1ficas en formato BibTeX (.bib).<\/p>\n\n                    <h4>\ud83d\udd39 \u00bfQu\u00e9 necesito?<\/h4>\n                    <p>Un archivo .bib exportado desde tu base de datos acad\u00e9mica (Scopus, Web of Science, Mendeley, Zotero, etc.).<\/p>\n\n                    <h4>\ud83d\udd39 Campos requeridos en el .bib<\/h4>\n                    <p>Para que el an\u00e1lisis funcione correctamente, tu archivo debe contener al menos los siguientes campos en cada registro:<\/p>\n                    <ul class=\"list-disc pl-6 text-sm\">\n                        <li><strong>author<\/strong> &rarr; Nombre(s) de los autores.<\/li>\n                        <li><strong>title<\/strong> &rarr; T\u00edtulo de la publicaci\u00f3n.<\/li>\n                        <li><strong>abstract<\/strong> &rarr; Resumen (si est\u00e1 disponible).<\/li>\n                        <li><strong>year<\/strong> &rarr; A\u00f1o de publicaci\u00f3n.<\/li>\n                        <li><strong>type<\/strong> &rarr; Tipo de documento (article, conference, book, etc.).<\/li>\n                    <\/ul>\n                    <p class=\"text-xs text-gray-500 mt-2\">\ud83d\udca1 Si algunos campos no est\u00e1n presentes, la herramienta seguir\u00e1 funcionando, pero los resultados podr\u00edan ser incompletos (por ejemplo, sin autores o sin evoluci\u00f3n por a\u00f1o).<\/p>\n\n                    <h4>\ud83d\udd39 Pasos para usar la herramienta<\/h4>\n                    <ol class=\"list-decimal pl-6 text-sm\">\n                        <li>Haz clic en \u201cSeleccionar Archivo\u201d y carga tu archivo .bib.<\/li>\n                        <li>Pulsa \u201cAnalizar\u201d para procesar la informaci\u00f3n.<\/li>\n                        <li>Explora el panel de resultados, donde encontrar\u00e1s:<\/li>\n                        <ul class=\"list-disc pl-6\">\n                            <li>\ud83d\udcca <strong>Resumen General:<\/strong> n\u00famero total de registros y rango de a\u00f1os.<\/li>\n                            <li>\ud83d\udfe2 <strong>Distribuci\u00f3n de Documentos:<\/strong> tipos de publicaciones.<\/li>\n                            <li>\ud83d\udc65 <strong>Autores Principales:<\/strong> tabla y gr\u00e1fico de los autores m\u00e1s frecuentes.<\/li>\n                            <li>\ud83d\udcdd <strong>N-gramas M\u00e1s Comunes:<\/strong> palabras y frases frecuentes en t\u00edtulos y res\u00famenes en Ingl\u00e9s.<\/li>\n                            <li>\ud83d\udcc5 <strong>Publicaciones por A\u00f1o:<\/strong> evoluci\u00f3n temporal de la producci\u00f3n.<\/li>\n                        <\/ul>\n                    <\/ol>\n\n                    <h4>\ud83d\udd39 Personalizaci\u00f3n<\/h4>\n                    <p>Puedes ajustar la cantidad de autores o n-gramas a mostrar. Los gr\u00e1ficos y tablas se actualizan autom\u00e1ticamente al cambiar estos valores.<\/p>\n                    <p class=\"text-sm font-semibold text-gray-700 mt-4\">\ud83d\udc49 Esta herramienta es ideal para investigadores, docentes y bibliotecarios que quieran obtener una visi\u00f3n r\u00e1pida de las tendencias, autores relevantes y patrones de publicaci\u00f3n de un conjunto de referencias.<\/p>\n                <\/div>\n            <\/details>\n        <\/div>\n\n        <!-- Contenedor principal para la carga de archivos -->\n        <div id=\"file-upload-container\" class=\"file-upload-card mx-auto max-w-lg\">\n            <h2 class=\"text-2xl font-semibold text-gray-700 mb-4 text-center\">Cargar Archivo .bib<\/h2>\n            <p class=\"text-gray-500 mb-6 text-center\">Selecciona un archivo .bib para comenzar el an\u00e1lisis.<\/p>\n            <div class=\"flex flex-col items-center\">\n                <label for=\"fileInput\" class=\"custom-file-upload\">\n                    Seleccionar Archivo\n                <\/label>\n                <input type=\"file\" id=\"fileInput\" accept=\".bib\" class=\"hidden\">\n                <p id=\"fileName\" class=\"text-gray-500 mt-2 text-sm\"><\/p>\n                <button id=\"analyzeBtn\" class=\"bg-blue-600 text-white font-semibold py-2 px-6 rounded-full mt-4 hover:bg-blue-700 transition-colors duration-300 transform scale-100 disabled:bg-gray-400 disabled:cursor-not-allowed\" disabled>\n                    Analizar\n                <\/button>\n            <\/div>\n        <\/div>\n\n        <!-- Indicador de carga -->\n        <div id=\"loading\" class=\"flex justify-center items-center mt-8 hidden\">\n            <div class=\"loading-spinner\"><\/div>\n            <p class=\"ml-4 text-gray-600\">Analizando el archivo&#8230;<\/p>\n        <\/div>\n\n        <!-- Contenedor de resultados (Dashboard) -->\n        <div id=\"results-card\" class=\"mt-8\">\n            <h2 class=\"text-3xl font-bold text-gray-800 mb-6\">Panel de Resultados<\/h2>\n\n            <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n\n                <!-- Resumen de Publicaciones -->\n                <div class=\"dashboard-card col-span-1 lg:col-span-2\">\n                    <h3 class=\"text-2xl font-semibold text-gray-700 mb-4\">Resumen General<\/h3>\n                    <ul id=\"summaryList\" class=\"list-disc pl-6 text-gray-600 space-y-2\">\n                        <!-- Los resultados se inyectar\u00e1n aqu\u00ed -->\n                    <\/ul>\n                <\/div>\n\n                <!-- Gr\u00e1fico de Tipos de Documento -->\n                <div class=\"dashboard-card col-span-1\">\n                    <h3 class=\"text-xl font-semibold text-gray-700 mb-4 text-center\">Distribuci\u00f3n de Documentos<\/h3>\n                    <canvas id=\"documentTypesChart\"><\/canvas>\n                <\/div>\n            <\/div>\n\n            <div class=\"section-divider\"><\/div>\n\n            <!-- Nuevo M\u00f3dulo de An\u00e1lisis de Salud -->\n            <div class=\"grid grid-cols-1 gap-6 mb-8\">\n                <div class=\"dashboard-card\">\n                    <h3 class=\"text-2xl font-semibold text-gray-700 mb-4\">An\u00e1lisis de Salud del Dataset<\/h3>\n                    <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n                         <!-- Gr\u00e1fico de Completitud -->\n                        <div class=\"col-span-1\">\n                            <h4 class=\"text-lg font-semibold text-gray-600 mb-2\">Completitud de Campos<\/h4>\n                            <canvas id=\"healthChart\"><\/canvas>\n                        <\/div>\n\n                        <!-- Tabla de Completitud -->\n                        <div class=\"col-span-1 overflow-x-auto\">\n                             <h4 class=\"text-lg font-semibold text-gray-600 mb-2\">M\u00e9tricas de Completitud<\/h4>\n                            <div id=\"healthTableContainer\"><\/div>\n                        <\/div>\n                    <\/div>\n                    <!-- Recomendaciones -->\n                    <div class=\"mt-6 p-4 rounded-lg bg-red-50 border border-red-200\" id=\"recommendations-container\" style=\"display: none;\">\n                        <h4 class=\"text-lg font-semibold text-red-700 mb-2\">Recomendaciones y Advertencias<\/h4>\n                        <div id=\"recommendationsList\" class=\"text-red-600\"><\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n\n            <div class=\"section-divider\"><\/div>\n\n            <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n                <!-- Autores m\u00e1s frecuentes - Gr\u00e1fico -->\n                <div class=\"dashboard-card\">\n                    <div class=\"flex justify-between items-center mb-4\">\n                        <h3 class=\"text-xl font-semibold text-gray-700\">Autores Principales<\/h3>\n                        <div class=\"flex items-center space-x-2\">\n                            <label for=\"authorCount\" class=\"text-gray-600\">Cantidad:<\/label>\n                            <input type=\"number\" id=\"authorCount\" value=\"10\" min=\"1\" max=\"50\" class=\"w-16 rounded-md border border-gray-300 p-1 text-center\">\n                        <\/div>\n                    <\/div>\n                    <canvas id=\"authorsChart\"><\/canvas>\n                <\/div>\n                \n                <!-- Autores m\u00e1s frecuentes - Tabla -->\n                <div class=\"dashboard-card overflow-x-auto\">\n                    <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">Tabla de Autores<\/h3>\n                    <div id=\"authorsTableContainer\"><\/div>\n                <\/div>\n            <\/div>\n\n            <div class=\"section-divider\"><\/div>\n\n            <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n                <!-- N-gramas - Gr\u00e1fico -->\n                <div class=\"dashboard-card\">\n                    <div class=\"flex justify-between items-center mb-4\">\n                        <h3 class=\"text-xl font-semibold text-gray-700\">N-gramas M\u00e1s Comunes<\/h3>\n                        <div class=\"flex items-center space-x-2\">\n                            <label for=\"ngramCount\" class=\"text-gray-600\">Cantidad:<\/label>\n                            <input type=\"number\" id=\"ngramCount\" value=\"10\" min=\"1\" max=\"50\" class=\"w-16 rounded-md border border-gray-300 p-1 text-center\">\n                        <\/div>\n                    <\/div>\n                    <p class=\"text-gray-600 mb-2\">Se muestran los n-gramas m\u00e1s frecuentes de t\u00edtulos y abstracts.<\/p>\n                    <canvas id=\"ngramsChart\"><\/canvas>\n                <\/div>\n\n                <!-- N-gramas - Tabla -->\n                <div class=\"dashboard-card overflow-x-auto\">\n                    <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">Tabla de N-gramas<\/h3>\n                    <div id=\"ngramsTableContainer\"><\/div>\n                <\/div>\n            <\/div>\n\n            <div class=\"section-divider\"><\/div>\n            \n            <!-- Publicaciones por A\u00f1o - Gr\u00e1fico -->\n            <div class=\"dashboard-card col-span-1 md:col-span-2\">\n                <h3 class=\"text-xl font-semibold text-gray-700 mb-4 text-center\">Publicaciones por A\u00f1o<\/h3>\n                <canvas id=\"yearsChart\"><\/canvas>\n            <\/div>\n\n        <\/div>\n    <\/div>\n\n    <script>\n        document.addEventListener('DOMContentLoaded', () => {\n            const fileInput = document.getElementById('fileInput');\n            const fileNameDisplay = document.getElementById('fileName');\n            const analyzeBtn = document.getElementById('analyzeBtn');\n            const loadingIndicator = document.getElementById('loading');\n            const resultsCard = document.getElementById('results-card');\n            const authorCountInput = document.getElementById('authorCount');\n            const ngramCountInput = document.getElementById('ngramCount');\n\n            let bibRecords = [];\n            let myCharts = {};\n\n            \/\/ Actualiza el nombre del archivo seleccionado y habilita el bot\u00f3n\n            fileInput.addEventListener('change', (event) => {\n                const file = event.target.files[0];\n                if (file) {\n                    fileNameDisplay.textContent = `Archivo seleccionado: ${file.name}`;\n                    analyzeBtn.disabled = false;\n                } else {\n                    fileNameDisplay.textContent = '';\n                    analyzeBtn.disabled = true;\n                }\n            });\n\n            \/\/ L\u00f3gica de an\u00e1lisis al hacer clic en el bot\u00f3n\n            analyzeBtn.addEventListener('click', async () => {\n                const file = fileInput.files[0];\n                if (!file) {\n                    return;\n                }\n\n                \/\/ Mostrar el indicador de carga\n                loadingIndicator.classList.remove('hidden');\n                resultsCard.classList.remove('show-results');\n                resultsCard.style.display = 'none';\n\n                try {\n                    const bibtexText = await file.text();\n                    bibRecords = parseBibtex(bibtexText);\n                    \n                    if (bibRecords.length === 0) {\n                        alert('El archivo .bib no contiene registros v\u00e1lidos.');\n                        return;\n                    }\n\n                    runAnalysisAndDisplay();\n                    runHealthAnalysis();\n\n                } catch (error) {\n                    console.error('Error al procesar el archivo:', error);\n                    alert('Hubo un error al procesar el archivo. Aseg\u00farate de que es un archivo .bib v\u00e1lido.');\n                } finally {\n                    loadingIndicator.classList.add('hidden');\n                    resultsCard.classList.add('show-results');\n                }\n            });\n\n            \/\/ Re-ejecutar el an\u00e1lisis y la visualizaci\u00f3n cuando cambian los inputs\n            authorCountInput.addEventListener('change', runAnalysisAndDisplay);\n            ngramCountInput.addEventListener('change', runAnalysisAndDisplay);\n\n            function runAnalysisAndDisplay() {\n                if (bibRecords.length === 0) return;\n\n                const authorCount = parseInt(authorCountInput.value, 10) || 10;\n                const ngramCount = parseInt(ngramCountInput.value, 10) || 10;\n\n                \/\/ Realizar an\u00e1lisis\n                const summary = getPublicationSummary(bibRecords);\n                const topAuthors = getTopAuthors(bibRecords, authorCount);\n                const topNgrams = getTopNgrams(bibRecords, ngramCount);\n                const yearsData = getPublicationsByYear(bibRecords);\n                \n                \/\/ Mostrar resultados\n                displaySummary(summary);\n                displayDocumentTypesChart(summary.documentTypes);\n                displayAuthors(topAuthors);\n                displayNgrams(topNgrams);\n                displayYearsChart(yearsData);\n            }\n            \n            function runHealthAnalysis() {\n                if (bibRecords.length === 0) return;\n                \n                const totalDocs = bibRecords.length;\n                const fields = ['author', 'title', 'year', 'journal', 'abstract', 'keywords', 'doi'];\n                \n                const healthData = fields.map(field => {\n                    const docsWithField = bibRecords.filter(record => record.fields[field] && record.fields[field].trim() !== '').length;\n                    const completeness = (docsWithField \/ totalDocs) * 100;\n                    return {\n                        entry: field,\n                        completeness: completeness.toFixed(1),\n                        docs: docsWithField\n                    };\n                });\n                \n                displayHealthChart(healthData);\n                displayHealthTable(healthData);\n                showHealthRecommendations(healthData);\n            }\n            \n            function showHealthRecommendations(healthData) {\n                const recommendationsContainer = document.getElementById('recommendations-container');\n                const recommendationsList = document.getElementById('recommendationsList');\n                recommendationsList.innerHTML = '';\n                \n                let hasRecommendations = false;\n                \n                \/\/ Advertencias sobre campos cr\u00edticos (menos del 95%)\n                const criticalFields = {\n                    'author': 'Autor(es)',\n                    'title': 'T\u00edtulo',\n                    'year': 'A\u00f1o'\n                };\n                \n                for (const field in criticalFields) {\n                    const data = healthData.find(d => d.entry === field);\n                    if (data && parseFloat(data.completeness) < 95) {\n                        recommendationsList.innerHTML += `<p class=\"mb-2\"><strong>\u00a1Atenci\u00f3n!<\/strong> El campo <strong>${criticalFields[field]}<\/strong> tiene solo ${data.completeness}% de completitud. Este campo es esencial para el an\u00e1lisis.<\/p>`;\n                        hasRecommendations = true;\n                    }\n                }\n                \n                \/\/ Recomendaciones para campos con baja completitud (<80%)\n                const lowCompletenessFields = healthData.filter(d => parseFloat(d.completeness) < 80);\n                \n                if (lowCompletenessFields.length > 0) {\n                    if (hasRecommendations) {\n                        recommendationsList.innerHTML += `<div class=\"mt-4 border-t border-red-300 pt-4\"><\/div>`;\n                    }\n                    recommendationsList.innerHTML += `<p class=\"mb-2\"><strong>Recomendaciones para campos con baja completitud (<80%):<\/strong><\/p>`;\n                    lowCompletenessFields.forEach(item => {\n                        let recommendationText = `El campo '${item.entry}' tiene solo ${item.completeness}% de completitud. Considera revisar los registros para completarlos.`;\n                        if (item.entry === 'doi') {\n                            recommendationText = `El campo 'DOI' tiene solo ${item.completeness}% de completitud. Los DOI son importantes para enlaces persistentes; se recomienda completarlos.`;\n                        } else if (item.entry === 'keywords') {\n                             recommendationText = `El campo 'keywords' tiene solo ${item.completeness}% de completitud. Considera a\u00f1adir palabras clave adicionales para mejorar el an\u00e1lisis de contenido.`;\n                        }\n                        recommendationsList.innerHTML += `<p class=\"mb-1 text-sm\">- ${recommendationText}<\/p>`;\n                    });\n                    hasRecommendations = true;\n                }\n                \n                recommendationsContainer.style.display = hasRecommendations ? 'block' : 'none';\n            }\n\n\n            \/\/ Funci\u00f3n para analizar el contenido de un archivo .bib\n            function parseBibtex(text) {\n                const entries = text.split(\/@\/g).filter(e => e.trim().length > 0);\n                return entries.map(entry => {\n                    const lines = entry.trim().split('\\n');\n                    const recordType = lines[0].split('{')[0].trim();\n                    const fields = {};\n                    lines.slice(1).forEach(line => {\n                        const [key, value] = line.trim().split('=', 2);\n                        if (key && value) {\n                            let cleanValue = value.replace(\/[{}\"',]\/g, '').trim();\n                            \/\/ Manejar el caso de un campo 'year' que puede tener un punto final\n                            if (key.trim() === 'year' && cleanValue.endsWith('.')) {\n                                cleanValue = cleanValue.slice(0, -1);\n                            }\n                            fields[key.trim().toLowerCase()] = cleanValue;\n                        }\n                    });\n                    return { type: recordType, fields };\n                });\n            }\n\n            \/\/ Funci\u00f3n para obtener el resumen de publicaciones\n            function getPublicationSummary(records) {\n                const totalRecords = records.length;\n                const years = records.map(r => parseInt(r.fields.year)).filter(Boolean);\n                const minYear = years.length > 0 ? Math.min(...years) : null;\n                const maxYear = years.length > 0 ? Math.max(...years) : null;\n\n                const documentTypes = records.reduce((acc, record) => {\n                    const type = record.fields.type || 'Art\u00edculo';\n                    acc[type] = (acc[type] || 0) + 1;\n                    return acc;\n                }, {});\n\n                return { totalRecords, minYear, maxYear, documentTypes };\n            }\n\n            \/\/ Funci\u00f3n para obtener los autores m\u00e1s frecuentes\n            function getTopAuthors(records, count = 10) {\n                const authorsCount = {};\n                records.forEach(record => {\n                    if (record.fields.author) {\n                        const authors = record.fields.author.split(' and ').map(a => a.trim().replace(\/\\s+\/, ' '));\n                        authors.forEach(author => {\n                            authorsCount[author] = (authorsCount[author] || 0) + 1;\n                        });\n                    }\n                });\n                return Object.entries(authorsCount)\n                    .sort(([, a], [, b]) => b - a)\n                    .slice(0, count);\n            }\n\n            \/\/ Funci\u00f3n para obtener los n-gramas m\u00e1s comunes\n            function getTopNgrams(records, count = 10) {\n                const allText = records.map(r => (r.fields.title || '') + ' ' + (r.fields.abstract || '')).join(' ');\n                \n                const stopWords = new Set(['a', 'an', 'the', 'and', 'or', 'of', 'in', 'on', 'is', 'are', 'for', 'with', 'to', 'from', 'as', 'by', 'at', 'we', 'this', 'that', 'with', 'which', 'its', 'their', 'our', 'have', 'has', 'we', 'ourself', 'ourselves', 'you', 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', 'her', 'hers', 'herself', 'it', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that', 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don', 'should', 'now', 'd', 'll', 'm', 'o', 're', 've', 'y', 'ain', 'aren', 'couldn', 'didn', 'doesn', 'hadn', 'hasn', 'haven', 'isn', 'ma', 'mightn', 'mustn', 'needn', 'shan', 'shouldn', 'wasn', 'weren', 'won', 'wouldn', 'un', 'una', 'unas', 'unos', 'el', 'la', 'los', 'las', 'y', 'e', 'o', 'u', 'm\u00e1s', 'pero', 'si', 'porque', 'como', 'en', 'sobre', 'con', 'entre', 'sin', 'mi', 'su', 'es', 'son', 'han', 'ha', 'hab\u00eda', 'hab\u00edan', 'fue', 'fueron', 'del']);\n                const cleanedText = allText.toLowerCase().replace(\/[^a-z\u00f1\u00e1\u00e9\u00ed\u00f3\u00fa\u00fc\\s]\/g, '');\n                const words = cleanedText.split(\/\\s+\/).filter(word => !stopWords.has(word) && word.length > 2);\n\n                const wordCounts = words.reduce((acc, word) => {\n                    acc[word] = (acc[word] || 0) + 1;\n                    return acc;\n                }, {});\n\n                const ngrams = getNGrams(words, 2); \/\/ Bigramas\n                const bigramCounts = ngrams.reduce((acc, bigram) => {\n                    acc[bigram] = (acc[bigram] || 0) + 1;\n                    return acc;\n                }, {});\n\n                const combinedCounts = { ...wordCounts, ...bigramCounts };\n                \n                return Object.entries(combinedCounts)\n                    .sort(([, a], [, b]) => b - a)\n                    .slice(0, count);\n            }\n            \n            \/\/ Funci\u00f3n para obtener publicaciones por a\u00f1o\n            function getPublicationsByYear(records) {\n                const yearCounts = records.reduce((acc, record) => {\n                    const year = record.fields.year;\n                    if (year) {\n                        acc[year] = (acc[year] || 0) + 1;\n                    }\n                    return acc;\n                }, {});\n                const sortedYears = Object.keys(yearCounts).sort();\n                return sortedYears.map(year => [year, yearCounts[year]]);\n            }\n\n            \/\/ Funci\u00f3n auxiliar para generar n-gramas\n            function getNGrams(words, n) {\n                const ngrams = [];\n                for (let i = 0; i <= words.length - n; i++) {\n                    ngrams.push(words.slice(i, i + n).join(' '));\n                }\n                return ngrams;\n            }\n\n            \/\/ Funciones de visualizaci\u00f3n\n            function displaySummary(summary) {\n                const summaryList = document.getElementById('summaryList');\n                summaryList.innerHTML = `\n                    <li><strong>Total de Registros:<\/strong> ${summary.totalRecords}<\/li>\n                    <li><strong>Rango de a\u00f1os:<\/strong> ${summary.minYear || 'N\/A'} - ${summary.maxYear || 'N\/A'}<\/li>\n                `;\n            }\n\n            function displayDocumentTypesChart(documentTypes) {\n                if (myCharts.documentTypesChart) myCharts.documentTypesChart.destroy();\n                const documentTypesLabels = Object.keys(documentTypes);\n                const documentTypesData = Object.values(documentTypes);\n                const documentTypesCtx = document.getElementById('documentTypesChart').getContext('2d');\n                myCharts.documentTypesChart = new Chart(documentTypesCtx, {\n                    type: 'doughnut',\n                    data: {\n                        labels: documentTypesLabels,\n                        datasets: [{\n                            data: documentTypesData,\n                            backgroundColor: ['#3B82F6', '#60A5FA', '#93C5FD', '#BFDBFE', '#D1D5DB']\n                        }]\n                    },\n                    options: {\n                        responsive: true,\n                        plugins: {\n                            legend: {\n                                position: 'top',\n                            },\n                            title: {\n                                display: true,\n                                text: 'Distribuci\u00f3n de Tipos de Documentos'\n                            }\n                        }\n                    }\n                });\n            }\n\n            function displayAuthors(authors) {\n                if (myCharts.authorsChart) myCharts.authorsChart.destroy();\n                const authorsLabels = authors.map(item => item[0]);\n                const authorsData = authors.map(item => item[1]);\n                const authorsCtx = document.getElementById('authorsChart').getContext('2d');\n                myCharts.authorsChart = new Chart(authorsCtx, {\n                    type: 'bar',\n                    data: {\n                        labels: authorsLabels,\n                        datasets: [{\n                            label: 'N\u00famero de Publicaciones',\n                            data: authorsData,\n                            backgroundColor: '#3B82F6',\n                            borderColor: '#1D4ED8',\n                            borderWidth: 1\n                        }]\n                    },\n                    options: {\n                        responsive: true,\n                        scales: {\n                            y: {\n                                beginAtZero: true,\n                                title: {\n                                    display: true,\n                                    text: 'Publicaciones'\n                                }\n                            }\n                        },\n                        plugins: {\n                            legend: {\n                                display: false\n                            },\n                            title: {\n                                display: true,\n                                text: 'Autores Principales por Publicaciones'\n                            }\n                        }\n                    }\n                });\n                displayTable(authors, 'authorsTableContainer', ['Autor', 'Publicaciones']);\n            }\n\n            function displayNgrams(ngrams) {\n                if (myCharts.ngramsChart) myCharts.ngramsChart.destroy();\n                const ngramsLabels = ngrams.map(item => item[0]);\n                const ngramsData = ngrams.map(item => item[1]);\n                const ngramsCtx = document.getElementById('ngramsChart').getContext('2d');\n                myCharts.ngramsChart = new Chart(ngramsCtx, {\n                    type: 'bar',\n                    data: {\n                        labels: ngramsLabels,\n                        datasets: [{\n                            label: 'Frecuencia',\n                            data: ngramsData,\n                            backgroundColor: '#10B981',\n                            borderColor: '#047857',\n                            borderWidth: 1\n                        }]\n                    },\n                    options: {\n                        indexAxis: 'y',\n                        responsive: true,\n                        scales: {\n                            x: {\n                                beginAtZero: true,\n                                title: {\n                                    display: true,\n                                    text: 'Frecuencia'\n                                }\n                            }\n                        },\n                        plugins: {\n                            legend: {\n                                display: false\n                            },\n                            title: {\n                                display: true,\n                                text: 'N-gramas M\u00e1s Comunes'\n                            }\n                        }\n                    }\n                });\n                displayTable(ngrams, 'ngramsTableContainer', ['N-grama', 'Frecuencia']);\n            }\n\n            function displayYearsChart(yearsData) {\n                if (myCharts.yearsChart) myCharts.yearsChart.destroy();\n                const yearsLabels = yearsData.map(item => item[0]);\n                const yearsCounts = yearsData.map(item => item[1]);\n                const yearsCtx = document.getElementById('yearsChart').getContext('2d');\n                myCharts.yearsChart = new Chart(yearsCtx, {\n                    type: 'bar',\n                    data: {\n                        labels: yearsLabels,\n                        datasets: [{\n                            label: 'N\u00famero de Publicaciones',\n                            data: yearsCounts,\n                            backgroundColor: '#FFC107',\n                            borderColor: '#FFB300',\n                            borderWidth: 1\n                        }]\n                    },\n                    options: {\n                        responsive: true,\n                        scales: {\n                            y: {\n                                beginAtZero: true,\n                                title: {\n                                    display: true,\n                                    text: 'Publicaciones'\n                                }\n                            }\n                        },\n                        plugins: {\n                            legend: {\n                                display: false\n                            },\n                            title: {\n                                display: true,\n                                text: 'Publicaciones por A\u00f1o'\n                            }\n                        }\n                    }\n                });\n            }\n            \n            function displayHealthChart(healthData) {\n                if (myCharts.healthChart) myCharts.healthChart.destroy();\n                \n                const sortedData = [...healthData].sort((a, b) => parseFloat(a.completeness) - parseFloat(b.completeness));\n                const labels = sortedData.map(d => d.entry);\n                const data = sortedData.map(d => parseFloat(d.completeness));\n                \n                const ctx = document.getElementById('healthChart').getContext('2d');\n                myCharts.healthChart = new Chart(ctx, {\n                    type: 'bar',\n                    data: {\n                        labels: labels,\n                        datasets: [{\n                            label: 'Completitud (%)',\n                            data: data,\n                            backgroundColor: '#9ca3af',\n                            borderColor: '#6b7280',\n                            borderWidth: 1\n                        }]\n                    },\n                    options: {\n                        indexAxis: 'y',\n                        responsive: true,\n                        scales: {\n                            x: {\n                                beginAtZero: true,\n                                max: 100,\n                                title: {\n                                    display: true,\n                                    text: 'Porcentaje de Completitud'\n                                }\n                            }\n                        },\n                        plugins: {\n                            legend: {\n                                display: false\n                            },\n                            title: {\n                                display: true,\n                                text: 'Completitud de Campos'\n                            }\n                        }\n                    }\n                });\n            }\n\n            \/\/ Funci\u00f3n para crear y mostrar una tabla de datos\n            function displayTable(data, containerId, headers) {\n                const container = document.getElementById(containerId);\n                let tableHtml = `<table class=\"min-w-full divide-y divide-gray-200 rounded-md overflow-hidden\">\n                                     <thead class=\"bg-gray-100\">\n                                         <tr>\n                                             <th class=\"py-3 px-6 text-xs text-gray-500 uppercase tracking-wider\">${headers[0]}<\/th>\n                                             <th class=\"py-3 px-6 text-xs text-gray-500 uppercase tracking-wider\">${headers[1]}<\/th>\n                                         <\/tr>\n                                     <\/thead>\n                                     <tbody class=\"bg-white divide-y divide-gray-200\">`;\n                data.forEach(item => {\n                    tableHtml += `<tr>\n                                    <td class=\"py-4 px-6 whitespace-nowrap\">${item[0]}<\/td>\n                                    <td class=\"py-4 px-6 whitespace-nowrap\">${item[1]}<\/td>\n                                  <\/tr>`;\n                });\n                tableHtml += `<\/tbody><\/table>`;\n                container.innerHTML = tableHtml;\n            }\n            \n            function displayHealthTable(healthData) {\n                const container = document.getElementById('healthTableContainer');\n                let tableHtml = `<table class=\"min-w-full divide-y divide-gray-200 rounded-md overflow-hidden\">\n                                     <thead class=\"bg-gray-100\">\n                                         <tr>\n                                             <th class=\"py-3 px-6 text-xs text-gray-500 uppercase tracking-wider\">Entrada<\/th>\n                                             <th class=\"py-3 px-6 text-xs text-gray-500 uppercase tracking-wider\">Completitud (%)<\/th>\n                                             <th class=\"py-3 px-6 text-xs text-gray-500 uppercase tracking-wider\">Documentos<\/th>\n                                         <\/tr>\n                                     <\/thead>\n                                     <tbody class=\"bg-white divide-y divide-gray-200\">`;\n                healthData.forEach(item => {\n                    tableHtml += `<tr>\n                                    <td class=\"py-4 px-6 whitespace-nowrap\">${item.entry}<\/td>\n                                    <td class=\"py-4 px-6 whitespace-nowrap\">${item.completeness}<\/td>\n                                    <td class=\"py-4 px-6 whitespace-nowrap\">${item.docs}<\/td>\n                                  <\/tr>`;\n                });\n                tableHtml += `<\/tbody><\/table>`;\n                container.innerHTML = tableHtml;\n            }\n        });\n    <\/script>\n<\/body>\n<\/html>\n\n","protected":false},"excerpt":{"rendered":"<p>An\u00e1lisis Bibliom\u00e9trico Web An\u00e1lisis de Archivos BibTeX .bib Una herramienta para analizar archivos .bib directamente en tu navegador. Gu\u00eda de [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_eb_attr":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-2822","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/pages\/2822","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2822"}],"version-history":[{"count":5,"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/pages\/2822\/revisions"}],"predecessor-version":[{"id":2961,"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/pages\/2822\/revisions\/2961"}],"wp:attachment":[{"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2822"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}