Learn to code-Code to learn

¿ Investigación fiable?

Recientemente ha resurgido la discusión que ha suscitado el hecho de que los investigadores paguen a revistas para agilizar la publicación de sus resultados.

En general, esta discusión se ha basado en datos agregados. El objetivo de los códigos que presentamos en este y el siguiente tutorial/análisis es poder obtener información directamente de la web, i.e., web scrapping, para realizar estudios más micro.

Introducción: Pagar para ganar tiempo

Hace pocos días el New York Times publicaba un artículo donde reflexionaba sobre la fiabilidad del proceso de aprobación de medicamentos por parte de la Agencia Federal del Medicamento (F.D.A. -en inglés).

El artículo sostiene que tres cuartas partes del presupuesto de la F.D.A. se financia con aportaciones de la industria farmacéutica, a quien la F.D.A. supuestamente tiene que controlar. O sea, el regulado financia la actividad de quien tiene que regularlo.

La interrogante que introduce la reflexión del artículo es sobre el impacto que ha tenido este tipo de financiación en el comportamiento regulatorio de la F.D.A. con respecto a la industria farmaceutica: “favored industry through decreasing regulatory standards, shortening approval times and increasing industry involvement in F.D.A. decision making.”   En otras palabras, en contraprestación por el pago de la industria, la F.D.A. -presuntamente- ha disminuido estandares regulatorios, acortado los períodos de aceptación de medicamentos, etc..

Por tanto, se podría inferir que la industria farmacéutica presumiblemente paga para que se flexibilicen las normas regulatorias y se acepten los medicamentos en el menor tiempo posible.

La pregunta natural que surge es ¿qué tan fiable es un medicamento si se paga para que sea aprobado en un tiempo mínimo?

Podríamos hacer un planteamiento más general: ¿qué tan fiable es el resultado de una investigación científica cuyos autores pagan para que se publique en el menor tiempo posible?

Al igual que los medicamentos - cuya fiabilidad debería ser testeada por un organismo independiente- las publicaciones científicas deben ser evaluadas por expertos independientes para corroborar sus resultados, lo que necesariamente lleva su tiempo.

El no respetar estos procesos -por ejemplo, pagando para que se publique los resultados en el menor tiempo posible- podría ocasionar que pseudo-investigación sea identificada como investigación. Valga como ejemplo este artículo al que hace referencia el periódico El País y que presuntamente costó al autor alrededor de 2.000 euros para publicarse en menos de 20 días (desde que llegó a la revista y hasta que se aceptó publicarlo).

En este post analizaremos el comportamiento de los investigadores españoles con respecto a las revistas donde el autor debe pagar para agilizar la publicación. Dividimos este análisis en dos partes: en este post presentamos un ranking de universidades en función del número de publicaciones en revistas de pago. En el siguiente post nos concentraremos en la revista Sustaintability para analizar los tiempos de publicación, las citas y el número de coautores.

España: las publicaciones de pago

Según la Agencia Nacional de Evaluación de la Calidad y Acreditación (ANECA), investigadores españoles han gastado más de 50 millones de euros de fondos públicos entre 2017 y 2019 para que diversas revistas publiquen sus resultados en el menor tiempo posible. Según revela el trabajo de Delgado y Martín (2022), pagar para publicar se ha convertido en el principal medio de difusión de resultados en muchas universidades españolas (e.g., ver Tabla 4a en el citado artículo).

Hay quienes argumentan que uno de los principales motivos por el que se paga para publicar es debido a que en España el acceso a plazas de profesorado o a premios -sexenios- depende del número de publicaciones más que de su calidad. En otras palabras, cuantas más publicaciones un investigador acredite en su curriculum vitae más probabilidades de éxito en su vida académica y en la percepción que la sociedad civil tiene de su calidad como investigador.

Las editoriales se dieron cuenta de esta necesidad y fueron rápidas en captar el nicho de mercado creado por los incentivos de la ANECA, entre otras. O sea, cobrar para que un resultado científico se publique rápido -e.g., un mes- se ha convertido en un negocio muy lucrativo,


Segun parece, la ANECA ha intentado revisar los criterios de concesión de premios en base a publicaciones científicas de pago pero ha tenido que recular. El informe de la ANECA ha sido fuertemente criticado, entre otros, por quienes se benefician de este tipo de publicaciones: ¿qué va a decir el responsable de una empresa editorial que ingresó más de 250 millones de euros con este modelo de negocio?

Ranking de Universidades en publicaciones de pago

El acceso a la información para evaluar los argumentos esgrimidos a favor o en contra de estas publicaciones es limitado, e.g., cómo saber la duración entre que es recibido y aceptado un artículo (que es distinto al tiempo a ser publicado), o cuantos coautores existen en los artículos, etc..

Aquí presentamos datos sobre el número de publicaciones por universidad en la editorial MDPI. En los últimos años, la editorial MDPI ha estado en el centro de la polémica por el impactante crecimiento en el número de publicaciones anuales, más de 230 mil artículos publicados en 2021, evaluadas en un período muy corto de tiempo para el standard de una publicación científica, alrededor de un mes, y cobrandole al autor por ello, una media de 1300 euros por artículo publicado.

La información para elaborar este ranking se ha extraído directamente de la página web de la editorial MDPI utilizando el código que se adjunta más abajo. El problema más importante en la extracción de estos datos está en la afiliación de los investigadores a las universidades, ya que utilizan distintos nombres, e.g., Universidad de Vigo, Universidade de Vigo, University of Vigo, Vigo University, etc.. Por ello, los datos que aquí se presentan son la cota inferior del número de artículos donde al menos un autor pertenece a la universidad en cuestión.

El ranking de universidades se construyó en base al número de artículos per cápita, i.e., por PDI. En otras palabras, se ha dividido el número de artículos publicados por el número de profesores adscritos a dicha universidad.

Por último, el gasto se obtiene multiplicando el número de artículos por el coste medio publicar en la editorial MDPI. Este gasto esta sobreetimado dado que un mismo artículo tiene varios coautores.

De las más de 80 universidades en España, aquí se presentan las primeras 20 del ranking.


Como comentamos en los cursos, los datos son una herramienta para la toma de decisiones. Aquí hemos visto cómo es posible extraer datos de la web y resumir la información en un ranking en forma de tabla -se podría haber presentado un gráfico de barras, por ejemplo-.

En el próximo post presentaremos un código que permite analizar los tiempos de evaluación centrándonos en la revista Sustainability.

Abajo adjuntamos el código que ha permitido scrapear los datos para constuir el ranking presentado arriba.


Código Python

Presentamos aquí el código que se utilizó para extraer esta información:

Importamos los paquetes que utilizaremos para extraer datos de una web estática.

# Importamos los paquetes que utilizaremos

from bs4 import BeautifulSoup
import requests
import re
from collections import defaultdict
import pandas as pd


# Traducir al ingles los nombres de las universidades
from deep_translator import GoogleTranslator
traductor = GoogleTranslator(source="es", target="en")

Buscamos el total de univerisdades en España y corregimos algunos nombres

web = "https://www.educacion.gob.es/ruct/consultauniversidades?actual=universidades"
source = requests.get(web).text
soup = BeautifulSoup(source, "lxml")

lista_universidades = soup.find_all("select", id="codigoUniversidad")[0].text.split(
    "\n"
)

# Corregimos la lista para que no haya problemas de universidades

lista_universidades = [u.strip() for u in lista_universidades]
lista_universidades = [u.replace(" (Estudi General)", "") for u in lista_universidades]
lista_universidades = [u.replace(" (UFP-C)", "") for u in lista_universidades]
lista_universidades = [u.replace(" UDIT)", "") for u in lista_universidades]

Ahora utilizamos esta lista de universidades para buscara los datos en la web de MDPI


# Itero sobre el total de centros y me quedo solo con las universidades

for u in lista_universidades:

    if "Univ" in u:

        dict_universidades[u] = [u]
        dict_universidades[u].append(traductor.translate(u))

        if len(u.split()) > 3:

            u_c = u.replace("Universidad del", " ").strip()
            u_c = u_c.replace("Universidad de", " ").strip()
            u_c = u_c.replace("Universidad a", " ").strip()
            u_c = u_c.replace("Universidad", " ").strip()
            u_c = u_c.replace("Universitat", " ").strip()

            # Elimino nombres que se asemejen a Provincias o CCAA
            if u_c not in [
                "A Coruña",
                "Castilla-La Mancha",
                "La Laguna",
                "La Rioja",
                "Las Palmas de Gran Canaria",
            ]:
                dict_universidades[u].append(u_c)

        # Agrego Universidade a las Gallegas

        ude = [
            "Universidad de A Coruña",
            "Universidad de Vigo",
            "Universidad de Santiago de Compostela",
        ]

        if u in ude:
            dict_universidades[u].append(u.replace("Universidad", "Universidade"))


# Creo un diccionario para guardar las universidades y el númeor de articulos

dic_numero = {"universidad": [], "numero_art": []}

for u in lista_universidades:

    if "Univ" in u:
        lista_u = [f"(@(affiliations){i})" for i in dict_universidades[u]]
        s = lista_u[0]
        i = 0

        while i < len(lista_u) - 1:

            s = s + "|" + lista_u[i + 1]
            i = i + 1
            print(s)

        web = f"https://www.mdpi.com/search?advanced={s}"

        source = requests.get(web).text
        soup = BeautifulSoup(source, "lxml")

        try:
            n_art = soup.find(
                "div", class_="content__container content__container--overflow-initial",
            ).text.split("\n")[2]
        except:
            print(f"Error:{u}")
            continue

        patern = r".*\(([0-9,]*)\)"
        n_arts = re.search(patern, n_art).groups()[0].replace(",", "")

        dic_numero["universidad"].append(u)
        dic_numero["numero_art"].append(int(n_arts))

        print(f"Universidad: {u} y artículos {n_arts}")

    else:
        continue

df_articulos = pd.DataFrame(dic_numero).sort_values(by=["numero_art"], ascending=False)

Obtengo los precios de los artículos, calculo el precio medio y el gasto por artículo:

# Precios

web = f"https://www.mdpi.com/apc"
print(web)

source = requests.get(web).text
soup = BeautifulSoup(source, "lxml")



precios = []
for pr in soup.find_all("td", align="center"):
    try:
        precios.append(int(pr.text.split(" ")[0]))
    except:
        continue

media_precios = sum(precios) / len(precios)

# Fijo tipo de cambio: habría que automatizarlo
tipo_de_cambio = 1.03

df_articulos["gasto"] = (df_articulos["numero_art"] * media_precios * tipo_de_cambio).round(0)

Utilizo información del número de PDI por universidad y los combino con los datos que tengo

# Import
import fuzzywuzzy
from fuzzywuzzy import fuzz
from fuzzywuzzy import process

# Lectura del fichero xlsx con número de pdi

pdi = pd.read_csv("pdi_universidades.csv", sep=";", encoding="latin-1", header=None)
pdi.columns = ["universidad", "pdi"]

df_articulos_pdi = df_articulos.copy()
df_articulos_pdi.set_index("universidad", inplace=True)
df_articulos_pdi["pdi"] = 0
pdi_in = pdi.copy()
pdi_in.set_index("universidad", inplace=True)

# Asignamos el pdi a la universidad
for u in df_articulos["universidad"]:

    u_r = u.replace("Universidad", " ")
    u_r = u_r.replace("Universitat", " ")
    u_r = u_r.replace(" (Estudi General)", " ")

    for pdi_u in pdi["universidad"]:
        pdi_u = pdi_u.replace(" (Estudi General)", " ")
        pdi_u = pdi_u.replace(" (UFP-C)", " ")

        ratio = fuzz.ratio(u_r.lower(), pdi_u.lower())
        if ratio > 80:
            print(f"Similarity score entre U {u} y pdi {pdi_u}: {ratio}")
            try:
                df_articulos_pdi.loc[u, "pdi"] = pdi_in.loc[pdi_u, "pdi"]
            except:
                pass

        if ratio > 70:
            print(f"Similarity score entre U {u} y pdi {pdi_u}: {ratio}")
            try:
                if df_articulos_pdi.loc[u, "pdi"] == 0:
                    df_articulos_pdi.loc[u, "pdi"] = pdi_in.loc[pdi_u, "pdi"]
            except:
                continue

# Hay universidades donde el matching no funciona bien:

df_articulos_pdi.loc["Universitat de València", "pdi"] = 4615
df_articulos_pdi.loc["Universidad de Vigo", "pdi"] = 1672
df_articulos_pdi.loc["Universidad de León", "pdi"] = 1002
df_articulos_pdi.loc["Universitat de les Illes Balears", "pdi"] = 1700
df_articulos_pdi.loc["Universidad de Cádiz", "pdi"] = 1754
df_articulos_pdi.loc["Universidad de Jaén", "pdi"] = 1010
df_articulos_pdi.loc["IE Universidad", "pdi"] = 848
df_articulos_pdi.loc["Universidad Fernando Pessoa-Canarias", "pdi"] = 99
df_articulos_pdi.loc["", "pdi"] = 848


# Articulo por pdi

df_articulos_pdi["articulo_pdi"] = (
    df_articulos_pdi["numero_art"] / df_articulos_pdi["pdi"]
)
df_articulos_pdi = df_articulos_pdi.sort_values(by=["articulo_pdi"], ascending=False)

df_articulos_pdi = df_articulos_pdi.loc[df_articulos_pdi["pdi"] != 0, :]

df_articulos_pdi["ranking"] = df_articulos_pdi.reset_index().index.astype(int) + 1

df_articulos_pdi.columns = [
    "Articulos MDPI",
    "Gasto",
    "PDI",
    "Articulos/PDI",
    "Ranking",
]

Cambiamos formato para extraer la tabla en html

# Cambiamos formato para hacer tabla

tabla_html = df_articulos_pdi.loc[df_articulos_pdi.Ranking < 21, :].copy()
tabla_html.loc[:, "Articulos MDPI"] = (
    tabla_html["Articulos MDPI"].astype(int).map("{:,d}".format)
)
tabla_html.loc[:, "Gasto"] = tabla_html["Gasto"].astype(int).map("{:,d}".format)
tabla_html.loc[:, "PDI"] = tabla_html["PDI"].astype(int).map("{:,d}".format)
tabla_html.loc[:, "Articulos/PDI"] = tabla_html["Articulos/PDI"].map("{:.3f}".format)


table_html = tabla_html.to_html()

# write html to file
text_file = open("table_1.html", "w")
text_file.write(table_html)
text_file.close()