Genuskollen

Häromdagen upptäckte jag att man kan köra Pythonskript på en webserver. På så sätt slipper användaren interagera med terminalgränssnittet. Eftersom beräkningarna körs på en fjärrserver behöver man inte ens ha Python installerat. Allt detta är ju självklarheter, men det kan ibland innebära en hel del handpåläggning innnan man får det att fungera.

Som jag tidigare bloggade om har jag börjat laborera med ett skript som jag kallar för Gendercounter. Det är detta skript som är motorn för själva beräknandet under huven på webtjänsten. Men nu kan man alltså enkelt räkna kvinnliga och manliga namn i vilken text som helst.

Resultat: Genuskollen (beta)

Men, det finns flera metodproblem som måste lösas innan detta blir någorlunda exakt. Därför tar jag gärna emot buggrapporter och observationer om felkällor så att jag kan förbättra tjänsten och skriptet framöver.

För den som vill köra detta på stora textmängder och vill göra mera avancerade saker är det bara att ladda ned källkoden.

Mäta gubbslem i texter – ett program

Citeringens praktiker är performativa, något som  Sara Ahmed reflekterar över inför sin kommande bok. Hon har valt att inte citera en enda vit man. Men hur ser det ut i vilken text som helst?

För det mesta reflekterar vi inte över vilka vi citerar eller omnämner när vi skriver. Ofta går det av bara farten och helt plötsligt sitter vi med en riktigt gubslemmig text. Med ”citering” kan man förstå även intervjupersoner och vilka vi skriver om i texterna. På så sätt kan man även inkludera journalistiska texter i analysen.

Jag skrev ett litet skript som räknar förekomsten av manliga och kvinnliga namn i en text. Programmet heter Gendercounter (ursäkta min dåliga fantasi). Programmet kollar varje ord i en text och matchar det sedan mot två listor, en med manliga namn och en med kvinnliga. Det finns förvisso ett flertal metodologiska problem med detta angrepssätt (namn är inte en hundraprocentig enkönade). Men man kan ändå använda detta som en första indikator.

Här kommer några tester jag gjorde. (uppdaterade efter att jag hittat några buggar)

Jag började med den Statliga offentliga utredningen Om Sverige i framtiden – en antologi om digitaliseringens möjligheter (ren textversion länkad). Resultat: 281 Kvinnor, 597 Män.

Sedan en lite äldre text, Den sociala och kulturella utvecklingen från Oskar I:s tid till våra dagar samt De politiska förhållandena under Karl XV:s, Oscar II:s och Gustaf V:s regering 1859-1923 Resultat: 418 Kvinnor, 1447 Män.

Till sist, Teknisk tidskrift årgång 1962 (1368 sid.) Resultat: 537 Kvinnor, 2472 Män.

Skriptet är än så länge bara ett test. Men det borde finnas intressanta saker man kan göra med denna form av kvantitativa innehållsanalys, givet att man har i bakhuvudet de metodologiska fallgroparna.

Testa gärna skriptet själva, instruktioner finns på Github-sidan. Fråga gärna i kommentarerna om något är oklart.

Förslag och kommentarer mottages gärna!

Sara Ahmed, lycka, olycka och falskt medvetande

Den 9 februari håller jag föredrag om Sara Ahmed, lycka, olycka och falskt medvetande på ABFs filosofiscen. Detta knyter an till min pågående forskning om lyckoforskning, men jag kommer även att diskutera Ahmeds kritik av ”affektfilosofin” och dess betoning på ”joy”, bland så som den kommer till uttryck hos Braidotti och Colebrook. I detta mellanrum ryms en ny form av lyckokritk som går bortom Benthams och Aristoteles välkända positioner.

Samförfattarnätverk ur Swepub?

Och nu ännu mera bibliometri. Swepubs databas innehåller via API-funktionen uppgifter om forskares institutionstillhörighet. En kollega ringde för ett par veckor sedan och påpekade att detta skulle kunna användas för att studera bland annat disciplin- och fakultetsgränser i relation till den faktiska forskningen som bedrivs.

Två personer som samförfattar en artikel kan ju sägas samarbeta. Om de gör det över disciplingränser kallar vi det för inter/tvär/disciplinär/vetenskap, beroende på perspektiv. Fördelen med den monadologiska nivån är att vi varken behöver göra avkall på den kvaliativa mikronivån eller den aggregerade makronivån. Vi kan zooma in och ut.

Med Skript A (se nedan) hämtade jag alla artiklar som publicerats på Göteborgs universitet 2015-16. Sedan genererade jag en visualiserbar nätverksfil med Skript B. Därefter gjorde jag några justeringar i Gephi: Jag tog bort alla institutioner förutom de som ryms inom samhällsvetenskaplig och humanistisk fakultet. Sedan sorterade jag ut alla ensamförfattare och inkluderade bara artiklar med minst två författare.

Med lite färger och annat visuellt förtydligande får vi en bilds som vi kan ta fram i en helhet som en pdf. Här klustrar sig forskargrupperna ungefär så här:

Screenshot from 2016-01-18 22:28:33

Ett typiskt göteborgskluster: Statsvetenskap och medie- och kommunikationsvetenskap.

Eller:

Screenshot from 2016-01-18 22:30:33

Överskridande av fakultetsgränser. Bra jobbat!

Eller:

Screenshot from 2016-01-18 22:33:15

Min egen forskningsmiljö. Inte superbra. (till mitt försvar måste jag tillägga att Tillämpad IT inte är med i datamängden som jag samarbetar med).

Om någon vill laborera med visualiseringarna går Gephi-filen att ladda ned här.

Nu vet jag inte riktigt vad detta leder någonstans eller hur det kan göras mera överskådligt. Förslag?

 

// Bilagor (Python3)

Skript A

from urllib.request import urlopen

counter = 1

#while True:
while counter < 50000:
  url = 'http://libris.kb.se/xsearch?d=swepub&q=%C3%A5r%3a%282015%29&hitlist&q=l%C3%A4ros%C3%A4te%3agu&f=ext&spell=true&hist=true&n=200&start=' + str(counter)
  print ("Fetching: " + url)
  data = urlopen(url).read()
    #f not data.find(b'"identifier"') >= 0:
  #	print("No more records!")
  #	break
  with open(str(counter) + ".xml", "wb") as outputfile:
    print("Writing file...")
    outputfile.write(data)
  counter += 200

Skript B

from os import listdir
from lxml import etree as ET
from gexf import *
from itertools import combinations
#import xml.etree.ElementTree as ET #Use this if you don't have lxml installed

# Open up a gexf file
gexf = Gexf("Author-Institution network", "GU")
graph = gexf.addGraph("undirected", "static", "Swepub network")
attribute_node = graph.addNodeAttribute("University", "default_value", "string")
attribute_nodetwo = graph.addNodeAttribute("Institution", "default_value", "string")

records = 0 # Just a counter for control
therecords = [] # A list for storing many dictionaries created in the loop below

# The loop for extracting author/institution from the xml-files
for filename in listdir("GUYear2015N47761Searched20160110/"):
    with open("GUYear2015N47761Searched20160110/" + filename) as currentFile:
        tree = ET.parse(currentFile)
        root = tree.getroot()

        for child in root[0]:
            records += 1 #Add to counter above
            #print("-" * 10)
            coauthors = {}
            for c in child: #This iterates over the records
                if c.get("tag") == "100": # The 100 Value is first author
                    authorlist = []
                    for value in c:
                        if value.get("code") == "a": # a is author name
                            author = value.text
                            #print(author)
                        elif value.get("code") == "u": # u is institution
                            #print(value.text)
                            institution = value.text
                            #print(institution)
                            coauthors.update({author: institution})

                elif c.get("tag") == "700": # The 700 value is authors
                    for value in c:
                        if value.get("code") == "a":
                            author = value.text
                            #print(author)
                        elif value.get("code") == "u":
                            #print(value.text)
                            institution = value.text
                            #print(institution)
                            coauthors.update({author: institution})

            therecords.append(coauthors) # Add each dictionary to the list above

#this removes only EXACT duplicate dictionaries from therecords list
#Possible false negative: Two articles may have identical groups of authors
seen = set()
therecordsdeduplicated = []
for d in therecords:
    t = tuple(d.items())
    if t not in seen:
        seen.add(t)
        therecordsdeduplicated.append(d)

#this creates a list which can be used to create edges
edges = []
coauthorcounter = 0 #just a counter
for t in therecordsdeduplicated:
    if len(t) > 1: #This removes single author articles, only more than one authors are allowed
        coauthorcounter += 1 # counts the number of articles with minimum 2 authors
        #print("--")
        #print(len(t))
        edgelist = []
        for key, value in t.items():
            coauthoredge = list(combinations(t, 2)) #This function calculates all possible relations between authors of an article.
            for c in coauthoredge:
                edgelist.append(c)
            newvalue = value.split(', ')
            n = graph.addNode(key, key)
            try:
                print(newvalue[0])
                n.addAttribute(attribute_node, newvalue[0])
            except IndexError:
                print("nothing here...")
            try:
                print(newvalue[1])
                n.addAttribute(attribute_nodetwo, newvalue[1])
            except IndexError:
                print("nothing here...")


            #graph.addNode(value, value) #This adds the Institutions as nodes
        edges.append(edgelist)
        #print(edgelist)

#this creates a list of edges then enumerates it and creates edges
authoredges = []
for e in edges:
    for ee in e:
        #print(ee)
        authoredges.append(ee)

for enumer, e in enumerate(authoredges):
    #print(enumer, e[0], e[1])
    graph.addEdge(enumer, e[0], e[1])


# Print some meta-data
print("There are " + str(records) + " records")
print("There are " + str(coauthorcounter) + " co-authored articles (more than 1 author)\n")

#write file
gexf_file = open("coauthors.gexf", "wb")
gexf.write(gexf_file)

 

Hur programmera mot Swepubs csv-filer?

Swepub har lanserat en bibliometrisida som låter användare specificera en sökning och sedan ladda ned resultaten i ett flertal öppna format (csv, tsv, xml, json). Detta är väldigt positiva nyheter för oss forskare som vill göra scientometriska analyser, och äntligen ett bibliometriskt index som inte är kontrollerat av stora företag!

Men jag funderar nu på hur jag på ett smart sätt ska kunna programmera mot den data man får ut ur Swepubsystemet. Det är nämligen så att varje artikel förekommer flera gånger. Om man till exempel sparar ned en csv-fil ser det ut så här om man öppnar den i exempelvis LibreOffice (eller Excel):

Screenshot from 2016-01-16 14:34:32

Varje artikel har ett unikt Swepubnummer, vilket gör det enkelt att isolera artikelnivån. Men sedan är varje författare angiven i en ny rad, istället för att vara inkluderade men separerade inom ett och samma värden, vilket är fallet med data från Web of Science och Scopus. Detta är lite ”jobbigt”. Men givetvis går det att ordna. Jag letar nu efter den enklaste och mest eleganta lösningen och behöver lite input.

En av de mest intressanta analyserna är samförfattarskap. Att se vilka som skriver artiklar tillsammans kan vara ett väldigt kraftfullt mått på interdisciplinaritet, internationalisering, forskningspraktiker osv. Men då måste man för varje artikel få en lista på författare (och gärna så mycket meta-data om dessa författare som möjligt, ex. institutionstillhörighet).

Csv-filen är superenkel att läsa in till Python:

import csv

thefile = open('query_result.csv', 'r')
csv = csv.reader(thefile, delimiter=',')
next(csv) #remove headers

for c in csv:
    print(c[24]) #put here what values you want, see list below.

Därefter är det bara att välja vilken meta-data man önskar. Jag begriper inte allt, men här är ett första index (om ni vet mer kommentera gärna):

 """
[0] = Swepub ID-nummer
[1] = Organisation/Lärosäte (ex. "gu")
[2] = Publikationsår
[3] = Status (ex. "PUBLISHED")
[4] = Publikationstyp
[5] = Innehållstyp
[6] = Publikationstyp (igen)
[7] = Open Access-status (ex. "gold", "green")
[8] = "_hsv1" ?
[9] = "_hsv3" ?
[10] = "_hsv5"?
[11] = Titel
[12] = Antal författare
[13] = "_numLocalCreator", ?
[14] = Författar (namn)
[15] = Användarnamn i systemet (ex. xjsode)
[16] = ORCID (se http://orcid.org)
[17] = Affiliering (ex. "gu.se")
[18] = Källa (ex. "Journal of Python Snakes")
[19] = ISSN
[20] = Förlag
[21] = URL
[22] = Fulltext (länk till URL)
[23] = ISI-nummer (Web of Science)
[24] = DOI
[25] = Scopus-nummer
[26] = Pubmed-nummer
[27] = ISBN
[28] = "_projekt", ?
[29] = "_program", ?
[30] = "_contract", ?
[31] = "_dubblettID" ?
"""

Eftersom Swepub ID är unikt, är det ju enkelt att isolera artiklar var för sig. Men jag skulle vilja skriva en funktion som gjorde ungefär så här:

För varje unikt Swepub-ID, gå igenom alla författare och bygg om [14] till en lista (eller kanske en lista av dictionaries/hashtabeller) som inkluderar författarnamn + ORCID + Affiliering.

På så sätt kan man sedan enkelt ta varje artikel och genast skapa ett co-authornätverk. Här finns massor av intressanta analyser att göra. Hur mycket tvärvetenskap finns på svenska universitet? Hur internationella är forskare egentligen? Vilka institutioner samarbetar ofta?

Men allt hänger på att man hittar en smidig lösning på att varje artikel förekommer flera gånger och att metadata kan sammafogas på ett smart sätt.

Någon som har en intressant och elegant strategi för att bygga om datamängden så här? Kanske är det enklare med json?

Länk till datamängden jag jobbar mot (1.7 Mb, zip)

Uppdatering: Mattias Östmar har löst problemet så här. (se även kommentarerna nedan).

Vad är medborgarforskning?

Igår publicerades min och Dick Kasperowskis studie What is Citizen Science – A Scientometric Meta-analysis i open-accesstidskriften PLOS ONE. I ett pressmeddelande på svenska förklarar vi att:

Medborgarforskning, eller ”citizen science”, har på allvar slagit igenom inom flera vetenskapliga discipliner. Fenomenet finns både inom natur- och samhällsvetenskaperna, visar den hittills största systematiska analysen av medborgarforskning, som publiceras i tidskriften PLOS ONE.

− Vi ser att forskare inom naturvetenskaperna framför allt har samlat in och klassificerat data med hjälp av intresserade volontärer. Inom samhällsvetenskaperna har fokus legat på att bjuda in berörda delar av allmänheten för att få kunskap om hur forskningen påverkar deras liv. Det kan till exempel handla om miljöproblem och risker, säger Christopher Kullenberg, vetenskapsteoretiker.

Det som är speciellt roligt med PLOS ONE är, förutom att den är helt öppen för vem som helst att läsa, är att all källdata också går att ladda ned. Detta gör inte bara att man kan replikera och verifiera/falsifiera våra resultat, utan detta är även värdefullt för den som vill göra andra typer av analyser med materialet än vad vi har gjort. Dessutom kan man med öppen data bygga vidare från resultat till resultat på ett intressant sätt. Humaniora har nog en del att lära av naturvetenskaperna på detta område.

Dessutom är det fritt att använda alla våra visualiseringar och figurer, så länge man refererar korrekt!

\\

För den datorintresserade:

PLOS ONE har en policy att all data ska vara helt och hållet replikerbar, figur för figur. Eftersom Excel är ett svåranvänt program för att göra saker med data fick jag göra en liten bisarr lösning. I S1 Appendix finns ett litet Pythonskript som emulerar en Topic-sökning i Web of Science. Detta används för att räkna hur många artiklar ett citizen science-projekt har producerat, men man måste ta ut och återimportera data ur Excel/LibreOffice för att kunna återskapa detta resultat. Det är lite otydligt när det bara är inklistrat i ett kalkylark, men så här ser det ut:

#Use with Python 3 or greater
import re

#Export the raw data from the Excel sheet as a comma separated file.
with open('NamesSearch633recs20151016.csv','r') as csv:
    next(csv) #skip the first line containing only headers
    WoSdata = [line.strip().split('","') for line in csv] # Change the delimiter according to your desired output. Could be \t or simply one comma.

#Save the project names from the Excel file as a plain text file delimited with a newline (\n)
with open('projectnames.txt', 'r') as thefile:
    projectnames = [] #build a list to iterate the search below
    for line in thefile:
        projectnames.append(line.rstrip().split("\n"))

for n in projectnames: #Iterate over each project name to build a search string
    searchword = n[0] #This accesses the project name
    counter = 0 #reset the counter to count occurences.
    for W in WoSdata:
        TSsearch = W[4] + W[8] + W[10] #This emulates the WoS Topic Search, accessing Title, Keywords, and Abstract (but leaves out keyword plus as found in W[9])
        match = re.findall(searchword, TSsearch, flags=re.IGNORECASE) #Ignorecase is needed (as this is default in WoS TS)
        if match:
            counter +=1

    print(searchword + "\t" + str(counter))

#To re-import to excel, pipe the output to a new text file,
#which will in turn be a tab-separated file that can nicely
# be pasted into a spreadsheet and then sorted

Detta hade man säkert kunnat göra inuti Excel, men det känns icke-transparent på något sätt. För att emulera en Topic-sökning i Web of Science så söker man efter ett ord i Titel, Abstract eller Keywords. Nuförtiden har WoS lagt till Keyword Plus (maskininlärda nyckelord), vilket är väldigt oberäkneligt, eftersom man då måste veta exakt hur Keyword Plus-algoritmen i sin tur är tränad. Så lösningen ovan tar bort det problemet. Detta är en slags funktionalitet som jag tänkte skriva rent som en enskild funktion, men även lägga in i Citepy.

Citepy

När man analyserar bibliometrisk data behöver man ofta mjukvara för att göra den överskådlig. Ett program som tidigare varit populärt är Histcite. Men detta program fungerar bara i (gamla versioner av) Windows, och dessutom verkar det som att det mer eller mindre verkar ha förfallit som abandonware. Den slutna koden gör även att det inte går att förbättra programmet.

Så jag tog mig an uppgiften att försöka skriva ett program i Python med motsvarande funktionalitet. Men även så att funktionaliteten kan byggas ut efter behov. Man kan säga att jag kommit ungefär halvvägs. Detta halvfärdiga arbete har jag nu publicerat på Github under namnet Citepy.

Jag har även lagt upp en live-demo av ett litet dataset (10 artiklar). Den som har använt Histcite tidigare känner igen sig. Men med Python är det ju väldigt enkelt att utöka funktionaliteten. Så jag har bland annat gjort så att det automatiskt skapas nätverksfiler som kan öppnas i Gephi och lagt till en enkel visualisering av artiklar/år.

Målet är att fortsätta utveckla programmet tills det helt ersätter funktionaliteten i Histcite och överskrider den med funktioner som är användbara för forskare.

Så som programmet är i nuläget har användarvänlighet nedprioriterats till förmån för att man ska kunna forma det efter sina egna behov. Kanske framöver går det att bygga ett enklare gränssnitt och en Windows-executable.

Programmet är licensierat med MIT-licensen, så man får göra vad man vill med det ungefär.

Om någon hittar något användbart, hojta gärna till i kommentarerna. Även förslag till vilka funktioner som kan vara användbara tas emot varmt.

 

Hämta och behandla bibliometrisk data från SwePub

SwePub är en databas som svenska universitet och högskolor rapporterar in vetenskapliga publikationer i. Den administreras av Kungliga biblioteket och det som är intressant med denna databas är att den innehåller många fler typer av texter än de stora vetenskapliga databaserna (Scopus, Web of Science etc.). Här förekommer till exempel populärvetenskap och rapporter som inte skulle kvalificera sig för de stora databaserna.

En annan aspekt är hur denna databas kan användas för att administrera tilldelning av forskningsmedel. Men det kan vi lämna åt sidan.

En tredje aspekt, som är väldigt intressant, är att SwePub har ett API utan några begränsningar! Jämfört med de kommersiella databaserna är detta fantastiskt bra datakommunalism som bör uppmuntras!

Med lite programmering kan man enkelt hämta och behandla både stora och precisa datamängder. Här följer några snuttar Python(3)-kod som visar vad som kan göras på en eftermiddag.

Uppdatering: Det går att skippa detta steg. Fick precis reda på att man kan få datan på ett mycket enklare sätt via SwePubs (ännu beta-) funktion för uttag av data. Steget nedan innebär alltså att gå över ån efter vatten 😀

Uppdatering 2: Det verkar ändå som att man måste använda denna metod för att komma åt institutionstillhörighet. Se denna post + kommentarer.

För att hämta data anger man bara en söksträng så returnerar API:t datamängden i ett urval av format (grundläge är XML och 10 artiklar åt gången). Så här sökte jag alla de 91622 artiklar som svarade mot en sökning på lärosäte Göteborgs universitet. Först själva URLen:

http://libris.kb.se/xsearch?d=swepub&hitlist&q=lärosäte%3gu&f=ext&spell=true&hist=true&n=200&p=1

Det viktiga här är dels databasen d=swepub samt själva söksträngen q=lärosäte%3gu. Se även hur jag får den att returnera 200 artiklar. Om man vill kan man lägga till &format=json för att få tillbaka json-data. Men då kommer man dels att stöta på några datafel som beror på att SwePub verkar tillåta en del tecken som är förbjudna i json. Dessutom innehåller inte json alla datafält.

Men ett enkelt anrop ger bara 200 artiklar åt gången. Men eftersom API:t är obegränsat är det bara att fråga om och om igen. Med lite hjälp från @skagedal skapades följande skript:

from urllib.request import urlopen

counter = 1

while True:
  url = 'http://libris.kb.se/xsearch?d=swepub&hitlist&q=l%C3%A4ros%C3%A4te%3agu&f=ext&spell=true&hist=true&n=200&start=' + str(counter)
  print ("Fetching: " + url)
  data = urlopen(url).read()
  if not data.find(b'"identifier"') >= 0:
    print("No more records!")
    break
  with open(str(counter) + ".json", "wb") as outputfile:
    print("Writing file...")
    outputfile.write(data)
  counter += 200

Vad som händer här är att skriptet skriver en fil för varje omgång om 200 artiklar. Tio minuter och 240 megabytes senare har man fått hem alla nittio tusen referenser. Att göra motsvarande i de begränsade kommersiella databaserna är en plåga!

Därefter ska xml-datan läsas in. Jag blir lätt förvirrad av xml, men jag fick hjälp av @jimmycallin att reda ut alla turer man måste ta för att komma åt (i det här fallet) Titel, Författare och Institution. Så här ser ett utsnitt av datan ut:

<datafield tag="245" ind1="1" ind2="0">
 <subfield code="a">Variability in quality of life 13 years after traumatic brain injury in childhood</subfield>
</datafield>

<datafield tag="700" ind1="1" ind2=" ">
 <subfield code="a">Emanuelson, Ingrid,</subfield>
 <subfield code="d">1955-,</subfield>
 <subfield code="u">Göteborgs universitet, Institutionen för kliniska vetenskaper, sektionen för kvinnors och barns hälsa, Avdelningen för pediatrik, University of Gothenburg, Institute of Clinical Sciences, Section for the Health of Women and Children, Department of Pediatrics</subfield>
 <subfield code="4">aut</subfield>
 <subfield code="0">(SwePub:chalmers.se)xeming</subfield>
</datafield>

<datafield tag="700" ind1="1" ind2=" ">
 <subfield code="a">Charlotte Smedler, Ann</subfield><subfield code="4">aut</subfield>
 <subfield code="0">(SwePub:chalmers.se)227391</subfield>

</datafield>
<datafield tag="700" ind1="1" ind2=" ">
 <subfield code="a">Smedler, Ann-Charlotte,</subfield>
 <subfield code="d">1948-,</subfield>
 <subfield code="u">Stockholms universitet, Psykologiska institutionen</subfield>
 <subfield code="4">aut</subfield>
 <subfield code="0">(SwePub:su)acsr</subfield>
</datafield>

Det som jag vill hämta här är dels titeln och sedan författare och deras institutionstillhörighet. Detta för att kunna studera samförfattarskap över institutionsgränser (ett tänkbart mått på tvärdisciplinaritet).

Så här kan man komma åt den informationen (och printa den):

from os import listdir
from lxml import etree as ET
#import xml.etree.ElementTree as ET #Use this if you don't have lxml installed

for filename in listdir("GU20151228N91622/"):
    with open("GU20151228N91622/" + filename) as currentFile:
        tree = ET.parse(currentFile)
        root = tree.getroot()

        for child in root[0]:
            for c in child:
                if c.get("tag") == "245":
                    for value in c:
                        if value.get("code") == "a":
                            print("-" * 50)
                            print(value.text)
                elif c.get("tag") == "700":
                    for value in c:
                        if value.get("code") == "a":
                            print(value.text)
                        elif value.get("code") == "u":
                            print(value.text)

Observera att jag i skriptet ovan har lagt alla filer i katalogen GU20151228/. Ur detta får vi sedan följande output:

Diagnosis and treatment of premenstrual dysphoria.
Andersch, Björn,
Göteborgs universitet, Institutionen för kvinnors och barns hälsa, Avdelningen för obstetrik och gynekologi, University of Gothenburg, Institute for the Health of Women and Children, Dept of Obstetrics and Gynaecology
Ho, Hoi-Por,
Göteborgs universitet, Institutionen för fysiologi och farmakologi, Avdelningen för farmakologi, University of Gothenburg, Institute of Physiology and Pharmacology, Dept of Pharmacology
Landén, Mikael,
Göteborgs universitet, Institutionen för klinisk neurovetenskap, Sektionen för psykiatri, University of Gothenburg, Institute of Clinical Neurosciences, Section of Psychiatry

Artikeln ovan är alltså resultatet av tre författare på tre olika institutioner. Intressant. Detta kan givetvis mätas och visualiseras. Men det blir en annan lat eftermiddags hackande.

 

 

 

 

Skrapa blogspot-bloggar

Det är oklart varför, men jag satt och funderade på om det fanns något spännande man kunde göra med bloggtexter ur ett medborgarprogrammeringsperspektiv. Men innan jag hann tänka så mycket på det dök ett praktiskt problem upp. Hur ladda ned allt som skrivits på en blogg?

Jag skrev således ett litet skrapverktyg för Blogspot-bloggar som jag döpte till det fantasifulla namnet blogspotscraper. Skraparen fungerar så att den börjar på det senaste inlägget, sedan arbetar sig skriptet bakåt i tiden rekursivt. För varje blogpost sparar den sedan en html-fil, rensad från de värsta kodsnuttarna så att man nästan bara får texten.

Man borde kunna använda samma approach för WordPressbloggar eftersom även de har en funktion för att läsa ”nästa (äldre) inlägg”.

I allt detta glömde jag bort vad jag nu ska göra med tusentals nedsparade blogginlägg. Kanske träna upp en chatterbot.

Slå ihop datamängder från Web of Science och Scopus

Jag är inne i en programmeringstunnel. Häromveckan fick jag en peer-reviewkommentar av en anonym kollega som menade att jag borde kombinera data från Web of Science och Scopus i min artikel för att få ett bredare urval från de båda databaserna, jämfört med bara en av dem.

Problemet är att dessa två (konkurrerande) arkiv inte returnerar meta-data om artiklar i samma format. Från Scopus kan man exportera till csv-filer och från Web of Science liknande tsv-filer. Men det stora problemet ligger i dels att de båda databaserna inte delar något unikt sätt att identifiera enskilda artiklar (som är kompatibelt med varandra).

Det mest intuitiva, att använda DOI-systemet, är problematiskt. Dels eftersom det är ett väldigt dåligt system att programmera mot eftersom det inte följer standarder och dels eftersom det innehåller bisarra teckenkombinationer, och att det vid utelämnande av något tecken helt plötsligt kan börja returnera hundratusentals träffar (eftersom det sorterar till tidskrift efter enskild artikel).

Så mitt problem blev: hur slå samman två datamängder samtidigt som man tar bort överlappande duplikater?

Jag skrev då en liten algoritm som försöker använda artikeltitlar som en unik identifikator för en artikel. Risken finns givetvis att två olika artiklar har samma titel, men jag tänker att den borde vara ganska liten när man arbetar med dataset under hundratusen i allafall. Resultatet blev Pythonskriptet WoScop. Ur beskrivningen:

To find duplicates, this script removes identical titles. Due to formatting inconsistencies in the Scopus and Web of Science databases, a few steps are taken to simplify the titles. The procedure works like this:

  1. Titles are converted to lower case letters only (Python’s lower() method).
  2. Only the first seven words of the title are included for further processing (to avoid long titles in multiple languages).
  3. A regular expression – [^A-Za-z0-9]+ – is applied to only allow letters and numbers (removing all special characters and spaces).
  4. The result is a string like this: aphotogrammetricapproachforassessingpositionalaccuracy, which is used as an unique identifier to avoid adding the same article twice to the final merging of the datasets.

Detta verkade fungera ganska bra på de dataset jag prövade med (se Githubsidan för länkar). Ett fåtal falska positiver (dvs. duplikater) kom med i den sammanslagna datamängden eftersom ovanstående algoritm inte (ännu) kan hantera stavfel i titlarna. Men rena stavfel är ganska ovanliga, det som skapar inkonsistens är i nio fall av tio avvikande anvädning av specialtecknen , . : ; ' " ? !, som det reguljära uttrycket ovan filtrerar bort.

Kommentera gärna om ni har förslag på förbättringar eller ser begränsningar som jag inte uppmärksammat. När man använder programmering för forskning är det viktiga inte att det fungerar så himla bra, utan att det går att beskriva hur det inte fungerar/dess begränsningar.