colav / impactu

Colav Impactu Issues and Documentation
BSD 3-Clause "New" or "Revised" License
0 stars 1 forks source link

Obtener artículos de datos abiertos de Minciencias con sólo título sin autor #19

Closed restrepo closed 4 months ago

restrepo commented 6 months ago

De una muestra de 100 artículos de datos abiertos de Minciencias con sólo título sin autor se lograron obtener 41 con alta similaridad en el título (WRatio score > 94) y con al menos una afiliación de Colombia ('CO') usando el API de OpenAlex. Lo que significa que muy seguramente están dentro del conjunto de datos de OpenAlex para Colombia que estamos usando.

Muestra: https://raw.githubusercontent.com/colav/impactu/main/data/minciencias_noauthors.json

41 resultados: https://github.com/colav/impactu/blob/main/data/found.csv

Muchos de ellos con DOI.

Código:

import json
from time import sleep
import requests
from re import sub
from thefuzz import process
from bs4 import BeautifulSoup
from unidecode import unidecode # OpenAlex uses unicode characters
#https://api.openalex.org/works?filter=title.search:Baryonic+violation+of+R+parity+from+anomalous+U(1)H
#https://api.openalex.org/works/W4360796310
def flatten(xss):
    return [x for xs in xss for x in xs]

r = requests.get('https://raw.githubusercontent.com/colav/impactu/main/data/minciencias_noauthors.json')
oa_url='https://api.openalex.org/works?filter=title.search:'

j=r.json()

found = []

for i in range(len(j)):
    results = []
    titles = []
    print(str(i).zfill(3),end='\r')
    sleep(0.1)
    original_title = j[i].get('titles')[0].get('title')
    #Remove special OpenAlex API special characters
    title =sub(r'\s+','+',sub('[_\|,]','',original_title))
    oa=requests.get(oa_url+title)
    # remove words with unicode character is there is not match
    if oa.status_code == 200 and not oa.json().get('results'):
        title = '+'.join([w for w in title.split('+') if unidecode(w) == w])
        if len(title) > 20:
            oa=requests.get(oa_url+title)

    if oa.status_code == 200 and oa.json().get('results'):
        results = oa.json().get('results') 
        titles = [result.get('display_name') for result in results]
        #Check mathml in titles and UPDATE display_name value!
        for ii in range(len(titles)):
            if [tag.name for tag in BeautifulSoup(titles[ii]).find_all() if tag.name.find('math')>-1]:
                titles[ii] =sub(r'\s+',' ',
                                   sub('\n',' ',
                                       BeautifulSoup(sub(r"([a-zA-Z])<", r"\1 <",titles[ii])).text.strip()
                                      )
                                )
                results[ii]['display_name'] = titles[ii]
        # Get better match
        display_name,score = process.extractOne(original_title,titles)
        # store if score sufficiently high
        result = [d for d in results if d.get('display_name')==display_name and score > 94]
        #If not author name avalaible → check for country 'CO'
        if result:
            result = result[0]
            countries = [d.get('countries') for d  in result.get('authorships')]
            if countries:
                countries = flatten(countries)
            if 'CO' in list(set(countries)):
                result['minciencias'] = j[i]
                result['original_title'] = original_title
                result['score_thefuzz'] = score
                #append result
                found.append(result)
omazapa commented 5 months ago

implementado https://github.com/colav/Mohan/pull/2 requerido para resolver este issue

restrepo commented 5 months ago

@fedevergara : He actualizado la búsqueda de tags HTML con math en lugar de mml

>>> from bs4 import BeautifulSoup

>>> t1 = 'Baryonic violation of<mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML" display="inline"><mml:mi>R</mml:mi></mml:math>parity from anomalous<mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML" display="inline"><mml:mi>U</mml:mi><mml:mo stretchy="false">(</mml:mo><mml:mn>1</mml:mn><mml:msub><mml:mo stretchy="false">)</mml:mo><mml:mi>H</mml:mi></mml:msub></mml:math>'
>>> t2 = '''Table 6 of Measurement of underlying event characteristics using charged particles in pp collisions at <math xmlns="http://www.w3.org/1998/Math/MathML">
  <msqrt>
    <mi>s</mi>
  </msqrt>
  <mo>=</mo>
  <mn>900</mn>
  <mi>G</mi>
  <mi>e</mi>
  <mi>V</mi>
</math> and 7 TeV with the ATLAS detector
'''
>>> [tag.name for tag in BeautifulSoup(t1).find_all() if tag.name.find('math')>-1]
['mml:math', 'mml:math']

>>> [tag.name for tag in BeautifulSoup(t2).find_all() if tag.name.find('math')>-1]
['math']

Y he actualizado la conversión a texto cambiando las rupturas de línea por espacios, tal como lo hace OpenAlex para la visualación:

>>> sub('\n',' ',BeautifulSoup(sub(r"([a-zA-Z])<", r"\1 <",t2)).text.strip())
'Table 6 of Measurement of underlying event characteristics using charged particles in pp collisions at   s   = 900 G  e  V   and 7 TeV with the ATLAS detector'

image

https://api.openalex.org/works?filter=title.search:Table+6+of+Measurement+of+underlying+event+characteristics+using+charged+particles+in+pp+collisions+at+s+=+900+G+e+V+and+7+TeV+with+the+ATLAS+detector

fedevergara commented 5 months ago

Usando la misma muestra de los datos de minciencias y buscando con ElasticSearch estos productos en el último corte de Colombia en OpenAlex, (y teniendo en cuenta las actualizaciones de tags HTML y la conversion a texto). Se tuvo un total de 50 resultados.

omazapa commented 4 months ago

Hola @restrepo la búsqueda sobre todo openalex(272 millones) de los productos de minciencias encontró 75 de los 100 productos de la muestra. Acá esta el resultado https://raw.githubusercontent.com/colav-playground/impactu_data_samples/main/minsci_results_noauth.json

La idea es hacer el corte de datos de colombia incluyendo este nuevo desarrollo en el que estoy trabajando para incluir lo que más podamos de minciencias.

Para los datos que tienen autor tengo otro algoritmo que te presentaré después en la reunión.

restrepo commented 4 months ago

@omazapa He revisado y está muy bien. Para evitar falsos positivos, con títulos muy génericos, tipo: "Editorial", es bueno poner un límite mínimo para el número de caracteres en el título,

len(title) > 30

basado en el siguiente:

{
minciencias: "Remesas y organizaciones locales",
openalex: "remesas y organizaciones locales"
},
omazapa commented 4 months ago

De acuerdo, Osea que todo lo que tenga un título menor a 30 caracteres simplemente lo ignoro y no lo busco en openalex

restrepo commented 4 months ago

Exacto

omazapa commented 4 months ago

del total de 322645 se obtienen 244192 75%

cabe aclarar que acá no tengo en cuenta el autor, voy a correrlo con autor para saber la diferencia

restrepo commented 4 months ago

¿Cómo es la jerarquía de búsqueda? Yo supongo que primero se busca lo que tenga título y autor. Y para lo que no se encuentre o no se tenga autor se busca sólo por título. ¿Es correcto?

omazapa commented 4 months ago

recorremos los productos sin jerarquía, si solo tenemos el título, se corre un algoritmo, si tenemos el autor otro.

por lo menos lo tengo así para el corte de los datos.

restrepo commented 4 months ago

De acuerdo

omazapa commented 4 months ago

creo que con la misma estrategia de minciencias se podrían implementar la búsqueda de títulos y autor de otras dbs

con este pr https://github.com/colav-playground/openalex_load/pull/1 cierro el issue, ya se puede hacer el corte