Hvordan taler man om hvor man er og skal hen

Den her bliver på dansk. For jeg vil skrive lidt om hvordan vi taler om hvor vi er. På dansk. Det er jeg blevet opfordret til af chefen. Så det må jeg hellere gøre.

Der var ingen tvivl. Det hedder oppe på Farma. Og nede på Nørre Alle. Sådan var det i hvert fald da jeg begyndte at arbejde på Farma. Jeg syntes det var meget enkelt. Farma ligger nord for Nørre Alle. Når man kigger på et kort, vender nord opad. Hvis lokation A ligger nord for lokation B, ligger A oppe relativt til B. Og B ligger nede. Det er i fuld overenstemmelse med hvad DRs sprogeksperter mente i 1994. Og hvad man finder på sproget.dk, drevet af Dansk Sprognævn og Det Danske Sprog- og Litteraturselskab. Og dog gav det problemer…

Nogen mente nemlig at Nørre Alle var et vigtigere sted end Farma. Og derfor var det upassende, at jeg talte om “oppe på Farma” og “nede på Nørre Alle”. Det havde vist noget at gøre med hvor de selv sad. Personligt mente jeg at geografien måtte gøre udslaget. Og skulle vi endelig tale om hvilken adresse der var vigtigst – og det kunne vi godt – så gav det samme resultat: “Oppe på Farma”.

Egentlig er det meget enkelt. Reglen for geografiske navne er, at vi siger op(pe) om det der ligger nord for os. Ned(e) om det der ligger mod syd. Og over/ovre, om steder der ligger på samme breddegrad som os selv. Når et sted ligger både nord og østligt for os, så er det i almindelighed oppe der vinder. Som i “oppe på Færøerne”.

Og hvorfor er det så vigtigt? Det er det, fordi op er godt, og ned er dårligt. Åbenbart. Op ad bakke synes jeg nu ikke er så godt. Når prisen på noget går ned er det som regel ikke dårligt, i hvert fald ikke for forbrugerne. Hvis vi bevæger os ind i fantasiens verden, er det selvfølgelig godt hvis ens løn går op, men det kan vi ikke huske hvornår sidst er sket, så det er irrelevant. Med andre ord, om op og ned er godt eller skidt er ikke helt entydigt. Altså bortset fra at det åbenbart betød noget at Farma lå oppe i forhold til Nørre Alle.

Well. Universitetsbiblioteket har mange adresser. Og jeg har siddet på en del af dem. Og hvordan taler vi om dem? Da jeg sad på Diamanten var der ingen tvivl. Alle andre steder var ude. Nogen var måske oppe, andre var vel egentlig nede. Men det ord brugte jeg ikke. Jeg skulle ud på Nørre Alle. Bagefter skulle jeg måske op på Farma – fordi jeg tog fra Nørre Alle til Farma. Men derefter skulle jeg ind på Diamanten igen. Rent geografisk giver det mening. Diamanten ligger i centrum af København. Ikke helt i den gamle middelalderby, men der er ingen tvivl om at der er steder der ligger inden for voldene. Og steder der ligger uden for. Og Diamanten ligger så afgjort indenfor. Sådan da. De andre steder ligger i hvert fald udenfor. Næsten. Så i København, og med et centrum der ligger hvor det nu ligger, var der ikke så meget at rafle om.

Men jeg har nu en fornemmelse af, at Diamanten kunne have ligget en del andre steder. Hvad nu hvis den faktisk havde ligget på Universitetsparken. Og det Farmaceutiske Bibliotek havde ligget inde på Slotsholmen?. Havde det så heddet ude på Diamanten? Jeg tror det, men jeg er ikke sikker. Min usikkerhed skyldes, at Diamanten er magtcentret i biblioteket. Det er der den høje chef sidder. Det er galaksens centrum. Jeg vil ikke helt udelukke at den betydning Den Sorte Diamant har gør at den ville kunne overrule geografien.

Og det er netop det forhold, der gjorde at der blev reageret på at jeg talte om “oppe på Farma”. Det blev forstået som “Farma er vigtigere end det der ligger syd for Farma”. Det er jo objektivt korrekt at det er tilfældet. Men det er altså også en simpel geografisk konstatering. Der var så nogen der havde den fejlagtige opfattelse, at Nørre Alle var så meget vigtigere end Farma, at det kunne overrule geografien.

Der er ingen tvivl om at der ligger en masse magtrelationer i brugen af disse relationelle ord. Jeg tror for eksempel ikke at det er et tilfælde at nord vender opad på kortene. Havde de nordeuropæiske lande ligget syd for ækvator, tror jeg nok vi hurtigt havde fået ændret enten sproget, eller konventionen for hvad der vender op og ned på et landkort.

Men alt det ændrer ikke på at det hedder oppe på Farma. Fordi Farma ligger længst mod nord. Og hvis der er mere brok, vil jeg bare gøre opmærksom på, at Nørre Alle 49 ligger 13,00 meter over havets overflade. Og Universitetsparken 4 ligger 15 centimeter højere.

Visualisering af diverse data

Nørd-nørd-nørd. Og den mere detaljerede beskrivelse er, af hensyn til min faglige selvpromovering, publiceret andetsteds. Så her napper vi bare en gauge på noget data der ligger i et Google Spreadsheet. Mest for lissom at have det liggende så jeg kan finde koden igen.




Harvesting and visualizing data

OK. The datalab should know how to harvest data. And we should know how to visualize them. Otherwise – how will we be able to help our users?

Libraries should have HUGE amounts of data. Unfortunately, we have not been able to make much headway making them publicly accessible. What to do? Get some data from another source. Twitter appears to be a very important way to communicate. I do. Why not harvest the number of tweets I tweet, and visualize them?

The correct way to do this, would be to harvest the data from Twitters API. That requires a developer account, which requires a regular account that has been verified by SMS. My phone company does not support this feature. However – the data is freely available on the web. Just go to my twitterprofile, and take a look.

And how do I get this data? Enter Python. Use the urllib2 library to harvest the site:

import urllib2 
page = urllib2.urlopen('http://www.twitter.com/chrbknudsen')

Then convert it to something that we can work with:

from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup(page.read())

page.read() returns the content of the site we read. BeautifulSoup converts that content into an object with a lot of nifty methods. Take a look:

tweets = soup.find('li', {'class' : 'ProfileNav-item ProfileNav-item--tweets is-active'}).find('span', {'class' : 'ProfileNav-value'})

BeautifulSoup lets us find content that is enclosed by specific tags. The number of tweets is in a LI-tag, with the class “ProfileNav-item ProfileNav-item–tweets is-active”. But there is more under that tag than just the number of tweets. Therefore we use the method “find” twice, now with the tagt SPAN, and the class “ProfileNav-value”.

Now we have the number of tweets (the moment I reach a thousand tweets, we will have to do something – twitter shows two thousand tweets as 2,000. And if I have even more, they might show 14563 tweets as 14.6td. Simple conversions to remove punctuation and changes 14,6td to 14600. We loose some precision, but nevermind).

OK. Now we have a Python script, that can access a twitter-profile, and harvest the number of tweets. On a Raspberry Pi (or other *nix-boxes) we can run a cron-job to harvest once every hour. Note that this method of harvesting is not exactly approved by Twitter.

We need to save the data. Enter Google Spreadsheets. I wont go into details about how to aquire the json-file that is used to authorize your use of Google Spreadsheets. Maybe I’ll get back to it. But we need some libraries:

import json
import gspread
import ssl
from oauth2client.client import SignedJwtAssertionCredentials
Those are used by this code:
json_key = json.load(open('client_secret_2.json'))
scope = ['https://spreadsheets.google.com/feeds']

ssl._create_default_https_context = ssl._create_unverified_context

credentials = SignedJwtAssertionCredentials(json_key['client_email'], json_key['private_key'].encode(), scope)

gc = gspread.authorize(credentials)

wks = gc.open("twitterdata").sheet1

Note that you will need to provide a full path to the json-file in order to run the cron-job. That fact gave me an ulcer yesterday…

We have connected to Google Spreadsheets through the Google API, and we have opened the spreadsheet “twitterdata”, and the first worksheet. We now have an object “wks”, that we can manipulate.

We use this method:

wks.append_row(rowToAdd)

That appends a row with some data to the active sheet. The data is contained in a list, “rowToAdd”. Now we just have to make sure that there is some data there. Before calling rowToAdd, we populate the variable:

rowToAdd = [time.time()]
rowToAdd.append(tweets)

Yeah, we need to import time as well.

That was it. Not exactly a working example, but a pretty accurate description of my thought processes writing it.

So far so good. Theres a cronjob running on an RPi back home, it harvest the number of tweets once every hour, and writes it to a google spreadsheet.

Now we need to visualize it.

Enter Highcharts. It’s free, its written in javascript and it can be embedded.

<div id="chart_container">
</div>
<script src="//code.highcharts.com/adapters/standalone-framework.js"></script>
<script src="//code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/data.js"></script>

<script>
new Highcharts.Chart({
 "chart":{
 "backgroundColor":"#fefefe",
 "renderTo":"chart_container",
 "type":"spline"
 },
 "title":{
 "text":"Title"
 },
 "colors":["#476974","#3ca1c1","#4ccbf4","#96dff6","#c9e8f6"],
 "legend":{
 "enabled":true,
 "margin":30
 },
 data: { googleSpreadsheetKey: '[enter spreadsheetkey here]', 
 googleSpreadsheetWorksheet: 2, 
 },
 });
</script>

That was it. This is the result:

Some work still needs to be done. The UNIX-time is not formatted as dates – it needs to be converted to javascript-time. This is not actually live data. And theres a bunch of other details I would like to tinker with. But I’m still rather satisfied.