Diagramas de puntos

Python
code
Visualization
Author

Marco Aguirre

Published

September 8, 2025

Los diagramas de puntos son diagramas de dispersión con un eje categórico y un eje continuo. Permiten mostrar cambios entre dos o más puntos en el tiempo o entre dos o más condiciones. En comparación con un gráfico de barras , los diagramas de puntos son menos confusos y facilitan la comparación entre condiciones.

Dataset: Base de Artículos Publicados 2015–2023

Fuente:
Secretaría de Educación Superior, Ciencia, Tecnología e Innovación (SENESCYT)

Descripción:
Este dataset contiene la base estadística de artículos científicos publicados por las universidades y escuelas politécnicas de Ecuador en revistas indexadas durante el periodo 2015 – 2023.

import pandas as pd
df = pd.read_excel("Base_estadistica_articulos_UEP_15_23.xlsx", skiprows=12)
print(df.head())
    AÑO      TIPO            NOMBRE UNIVERSIDAD TIPO FINANCIAMIENTO  \
0  2015  ARTICULO  ESCUELA POLITECNICA NACIONAL             PUBLICA   
1  2015  ARTICULO  ESCUELA POLITECNICA NACIONAL             PUBLICA   
2  2015  ARTICULO  ESCUELA POLITECNICA NACIONAL             PUBLICA   
3  2015  ARTICULO  ESCUELA POLITECNICA NACIONAL             PUBLICA   
4  2015  ARTICULO  ESCUELA POLITECNICA NACIONAL             PUBLICA   

  PROVINCIA UNIVERSIDAD BASE DATOS INDEXADA               NOMBRE REVISTA  \
0             PICHINCHA              SCOPUS        ASTROPHYSICAL JOURNAL   
1             PICHINCHA      WEB_OF_SCIENCE      MAGNETOHYDRODYNAMICS 51   
2             PICHINCHA              SCOPUS   REVISTA MEXICANA DE FISICA   
3             PICHINCHA         LATIN_INDEX  REVISTA MEXICANA DE FÍSICA    
4             PICHINCHA              SCOPUS                   AUTOMATICA   

                                     NOMBRE ARTICULO  \
0  KINETIC ALFVÉN WAVE GENERATION BY LARGE-SCALE ...   
1  AMTEC CLUSTERS FOR POWER GENERATION IN A CONCE...   
2  MÉTODO DE LENTE TÉRMICA RESUELTA EN FRECUENCIA...   
3  SINGLE BEAM THERMAL DIFFUSIVITY MEASUREMENTS I...   
4  GRAPH-THEORETIC ANALYSIS OF NETWORK INPUT-OUTP...   

                                     CAMPO AMPLIO  \
0  CIENCIAS NATURALES, MATEMÁTICAS Y ESTADÍSTICAS   
1  CIENCIAS NATURALES, MATEMÁTICAS Y ESTADÍSTICAS   
2  CIENCIAS NATURALES, MATEMÁTICAS Y ESTADÍSTICAS   
3  CIENCIAS NATURALES, MATEMÁTICAS Y ESTADÍSTICAS   
4            INGENIERÍA, INDUSTRÍA Y CONSTRUCCIÓN   

                  CAMPO ESPECIFICO                       CAMPO DETALLADO  
0                 CIENCIAS FÍSICAS                                FÍSICA  
1                 CIENCIAS FÍSICAS                                FÍSICA  
2                 CIENCIAS FÍSICAS                                FÍSICA  
3                 CIENCIAS FÍSICAS                                FÍSICA  
4  INGENIERÍA Y PROFESIONES AFINES  ELECTRÓNICA, AUTOMATIZACIÓN Y SONIDO  
print(df.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 87073 entries, 0 to 87072
Data columns (total 11 columns):
 #   Column                 Non-Null Count  Dtype 
---  ------                 --------------  ----- 
 0   AÑO                    87073 non-null  int64 
 1   TIPO                   87073 non-null  object
 2   NOMBRE UNIVERSIDAD     87073 non-null  object
 3   TIPO FINANCIAMIENTO    87073 non-null  object
 4   PROVINCIA UNIVERSIDAD  87073 non-null  object
 5   BASE DATOS INDEXADA    87073 non-null  object
 6   NOMBRE REVISTA         87073 non-null  object
 7   NOMBRE ARTICULO        87073 non-null  object
 8   CAMPO AMPLIO           87073 non-null  object
 9   CAMPO ESPECIFICO       87073 non-null  object
 10  CAMPO DETALLADO        87073 non-null  object
dtypes: int64(1), object(10)
memory usage: 7.3+ MB
None

Iteración 1

import pandas as pd
import plotly.express as px

import plotly.io as pio
pio.renderers.default = "notebook"
# Agrupar por año y contar artículos
df_year = df.groupby("AÑO").size().reset_index(name="NUM_ARTICULOS")

# Gráfico de puntos
fig = px.scatter(
    df_year,
    x="AÑO",
    y="NUM_ARTICULOS",
    size="NUM_ARTICULOS",
    text="NUM_ARTICULOS",  # mostrar el número encima de cada punto
    title="Número de artículos por Año"
)

# Ajustar layout
fig.update_traces(textposition="top center")
fig.update_layout(
    xaxis_title="Año",
    yaxis_title="Número de artículos",
    plot_bgcolor="white"
)

fig.show()

Eliminar los ejes Redundancia de Información (eje y)

import pandas as pd
import plotly.express as px
import plotly.io as pio

pio.renderers.default = "notebook"

# Agrupar por año y contar artículos
df_year = df.groupby("AÑO").size().reset_index(name="NUM_ARTICULOS")

# Formatear los números en notación abreviada (ejemplo: 12000 -> 12k)
df_year["LABEL"] = df_year["NUM_ARTICULOS"].apply(lambda x: f"{x/1000:.0f}k" if x >= 1000 else str(x))

# Gráfico de puntos
fig = px.scatter(
    df_year,
    x="AÑO",
    y="NUM_ARTICULOS",
    size="NUM_ARTICULOS",
    text="LABEL",  # mostramos los valores abreviados
    title="Número de artículos por Año"
)

# Ajustar layout: ocultar eje Y
fig.update_traces(textposition="top center")
fig.update_layout(
    xaxis_title="Año",
    yaxis=dict(showticklabels=False, showline=False, showgrid=False, zeroline=False),  # ocultar eje Y
    plot_bgcolor="white"
)

fig.show()

Iteración 3 Remarco información (2020)

import numpy as np
import pandas as pd
import plotly.express as px
import plotly.io as pio

pio.renderers.default = "notebook"

# Agrupar por año y contar artículos
df_year = df.groupby("AÑO").size().reset_index(name="NUM_ARTICULOS")

# Etiqueta abreviada (12k, 800, etc.)
def abrevia(n):
    return f"{n/1_000:.0f}k" if n >= 1000 else str(n)
df_year["LABEL"] = df_year["NUM_ARTICULOS"].map(abrevia)

# Gráfico base (un solo trazo)
fig = px.scatter(
    df_year,
    x="AÑO",
    y="NUM_ARTICULOS",
    size="NUM_ARTICULOS",
    text="LABEL",
    title="Número de artículos por Año"
)

# Colorear/contornear por punto (rojo solo en 2020)
x_vals = df_year["AÑO"].to_list()
is_2020 = [x == 2020 for x in x_vals]

# Colores de marcador por punto
colors = ["red" if f else "#7f7f7f" for f in is_2020]
# Borde rojo solo en 2020
line_widths = [2 if f else 0 for f in is_2020]

fig.data[0].marker.color = colors
fig.data[0].marker.line.color = "red"
fig.data[0].marker.line.width = line_widths

# Mostrar todos los ticks del eje X
fig.update_xaxes(
    tickmode="array",
    tickvals=sorted(df_year["AÑO"].unique()),
    ticktext=[str(y) for y in sorted(df_year["AÑO"].unique())]
)

# Ocultar eje Y y posicionar texto
fig.update_traces(textposition="top center")
fig.update_layout(
    xaxis_title="Año",
    yaxis=dict(showticklabels=False, showline=False, showgrid=False, zeroline=False),
    plot_bgcolor="white",
    margin=dict(l=10, r=10, t=60, b=40)
)

# (Opcional) línea vertical sutil en 2020 para remarcar
fig.add_vline(x=2020, line_width=1, line_dash="dot", line_color="red")

fig.show()

Cambiar titulo eliminar axis (Nombre)

import numpy as np
import pandas as pd
import plotly.express as px
import plotly.io as pio

pio.renderers.default = "notebook"

# Agrupar por año y contar artículos
df_year = df.groupby("AÑO").size().reset_index(name="NUM_ARTICULOS")

# Etiqueta abreviada (12k, 800, etc.)
def abrevia(n):
    return f"{n/1_000:.0f}k" if n >= 1000 else str(n)
df_year["LABEL"] = df_year["NUM_ARTICULOS"].map(abrevia)

# Gráfico base
fig = px.scatter(
    df_year,
    x="AÑO",
    y="NUM_ARTICULOS",
    size="NUM_ARTICULOS",
    text="LABEL",
    title='<span style="color:#43B099;">Tendencias del número de artículos académicos en el Ecuador por año,<br></span>'
'<span style="color:red;">siendo el año 2020 un punto de partida</span>'
)

# Colorear/contornear por punto (rojo solo en 2020)
x_vals = df_year["AÑO"].to_list()
is_2020 = [x == 2020 for x in x_vals]

colors = ["red" if f else "#43B099" for f in is_2020]
line_widths = [2 if f else 0 for f in is_2020]

fig.data[0].marker.color = colors
fig.data[0].marker.line.color = "red"
fig.data[0].marker.line.width = line_widths

# Mostrar todos los ticks del eje X
fig.update_xaxes(
    tickmode="array",
    tickvals=sorted(df_year["AÑO"].unique()),
    ticktext=[str(y) for y in sorted(df_year["AÑO"].unique())]
)

# Ocultar eje Y y títulos
fig.update_traces(textposition="top center")
fig.update_layout(
    xaxis_title=None,  # quitar título eje X
    yaxis_title=None,  # quitar título eje Y
    yaxis=dict(showticklabels=False, showline=False, showgrid=False, zeroline=False),
    plot_bgcolor="white",
    margin=dict(l=10, r=10, t=60, b=40)
)

# Línea vertical opcional en 2020
fig.add_vline(x=2020, line_width=1, line_dash="dot", line_color="red")

fig.show()