Corpusanalyse UiTdatabank

We hebben de volgende bibliotheken nodig:


In [1]:
from pandas import read_excel, read_csv, DataFrame, Series, concat
from datetime import datetime
from codecs import open
from re import compile
from json import dumps
from datetime import datetime
from random import sample
from collections import Counter
from itertools import combinations

Dan lezen we lezen de gegevens in


In [2]:
df_podium = read_excel("ruwe data/podium.xlsx", sheetname='theaterdans1014')
df_bk = read_excel("ruwe data/beeldendekunsten.xlsx", sheetname='UitRapport')
df_muziek = read_excel("ruwe data/muziek.xlsx", sheetname='Int nat reg')
df_podium["Organisator"] = df_podium["Typologie organisator"]
df_podium = df_podium[df_podium["Datum"].between(datetime(2014, 1, 1), datetime(2014, 12, 31))]

Samenbrengen en voorbereiden van de data

Voor de muziekgegevens moeten we nog controleren dat concerten eventuele herhalingen hebben, door in de speelmomenten kolom na te gaan wat de speelmomenten zijn. Dit is niet nodig bij de beeldende kunsten, aangezien daar de unit of analysis de tentoonstelling is, onafhankelijk van hoelang die tentoonstelling loopt. Bij podiumkunsten zijn de speelmomenten manueel gecheckt, ocharme simon.


In [3]:
datumregex = compile(r"\d\d/\d\d/\d\d")
df_muziek_expanded = df_muziek.copy()
for row in df_muziek_expanded.iterrows():
    speelmomenten = row[1]["Speelmomenten"]
    if str(speelmomenten) != "nan":    
        for speelmoment in datumregex.findall(speelmomenten):
            speelmoment_dt = datetime(int("20" + speelmoment.split("/")[2]), 
                                      int(speelmoment.split("/")[1]), 
                                      int(speelmoment.split("/")[0]))
            if speelmoment_dt != row[1]["Datum"]:
                df_muziek_expanded = df_muziek_expanded.append(
                    Series(
                        {"Discipline": row[1]["Discipline"],
                         "Subdiscipline": row[1]["Subdiscipline"],
                         "Tekst": row[1]["Tekst"],
                         "Datum": speelmoment_dt,
                         "Gemeente": row[1]["Gemeente"]
                        }, name=speelmoment_dt.isoformat() + " " + str(row[0])
                    )
                )
df_muziek_expanded = df_muziek_expanded[df_muziek_expanded["Datum"].between(datetime(2014, 1, 1), datetime(2014, 12, 31))]

Voor de tentoonstellingen moeten we ook nog inperken op thema, en ook de permanente tentoonstellingen eruit zwieren. Bvendien lopen tentoonstellingen ook gedurende een zekere periode, dus we moeten ook controleren op tentoonstellingen die nog voor 1 januari 2014 beginnen, maar wel nog tijdens 2014 lopen. Idem voor einde van het jaar.


In [4]:
subdisciplines = ["Beeldhouwkunst", 
                  "Fotografie", 
                  "Grafiek", 
                  "Installatiekunst", 
                  "Kunst en kunsteducatie", 
                  "Meerdere kunstvormen", 
                  "Schilderkunst"]
df_bk_filtered = df_bk[df_bk["Datum tot"] != datetime(1900, 1, 1)]
df_bk_filtered = df_bk_filtered[df_bk_filtered["Subdiscipline"].isin(subdisciplines)]
df_bk_filtered = df_bk_filtered[
    (df_bk_filtered["Datum van"].between(datetime(2014, 1, 1), datetime(2014, 12, 31))) |
    (df_bk_filtered["Datum tot"].between(datetime(2014, 1, 1), datetime(2014, 12, 31)))
]
df_bk_filtered["Datum"] = df_bk_filtered["Datum van"]
df_bk_filtered = df_bk_filtered.drop(["Datum van", "Datum tot"], axis=1)

We gaan nu de de organisator omzetten naar een typering. We moeten eerst beginnen met die mapping van organisator naar typering op te bouwen op basis van de gegevens die Simon wist aan te leveren.


In [5]:
typering = read_excel("extra gegevens/typologie-organisatoren-plat.xlsx")

In [6]:
def simplify_key(k):
    return str(str(k).lower().strip().replace(" ", "").encode("ascii", "replace")).replace("?", "").replace("_", "").replace('"', '').lstrip("b'").rstrip("'").replace('"', '').replace("'", "").replace(".", "").replace(",", "")

In [7]:
def map_organisator_naar_typologie_plat(item, mapping):
    key = simplify_key(item)
    try:
        return mapping[mapping["key"] == key]["value"].values[0]
    except IndexError:
        onmapbaar.add(key)

In [8]:
onmapbaar = set()

df_bk_filtered["typering"] = df_bk_filtered["Organisator"].apply(map_organisator_naar_typologie_plat, args=(typering,))
df_muziek_expanded["typering"] = df_muziek_expanded["Organisator"].apply(map_organisator_naar_typologie_plat, args=(typering,))
df_podium["typering"] = df_podium["Organisator"].apply(map_organisator_naar_typologie_plat, args=(typering,))

Nu voegen we alles mooi samen, selecteren enkel de juiste kolommen, en gooien ook duplicaten op basis van datum, gemeente en tekst eruit. Bovendien hebben we de kolom met speelmomenten ook niet meer nodig.


In [9]:
df_muziek_expanded_clean = df_muziek_expanded.drop("Speelmomenten", axis=1)
df_podium_clean = df_podium.drop("Typologie organisator", axis=1)
df = concat([df_podium_clean, df_bk_filtered, df_muziek_expanded_clean])
df.drop_duplicates(subset=["Datum", "Titel", "Discipline", "Subdiscipline", "Gemeente", "Tekst"], inplace=True)
df.drop(["Titel", "Organisator"], axis=1, inplace=True)

We kunnen kort inspecteren hoe deze data eruitzien.


In [10]:
df.head()


Out[10]:
Datum Discipline Gemeente Subdiscipline Tekst typering
747 2014-04-02 00:00:00 podium Brussel Dans Shen Yun brengt 5.000 jaar Chinese beschaving ... Rest
748 2014-04-03 00:00:00 podium Brussel Dans Shen Yun brengt 5.000 jaar Chinese beschaving ... Rest
749 2014-04-04 00:00:00 podium Brussel Dans Shen Yun brengt 5.000 jaar Chinese beschaving ... Rest
750 2014-04-05 00:00:00 podium Brussel Dans Shen Yun brengt 5.000 jaar Chinese beschaving ... Rest
752 2014-04-06 00:00:00 podium Brussel Dans Shen Yun brengt 5.000 jaar Chinese beschaving ... Rest

We zien dat elke lijn een event beschrijving bevat, de plaats waar een event plaatsvindt, en ook de datum. Merk op dat events die op meerdere dagen plaatsvinden een aparte lijn krijgen. We zullen hiermee rekening houden in de interpretatie van de resultaten.

Telling per discipline van het aantal unieke events:


In [11]:
df["Discipline"].value_counts()


Out[11]:
Concert            20500
podium             19415
Tentoonstelling     3770
Name: Discipline, dtype: int64

En hoeveel daarvan hebben geen beschrijving?


In [12]:
print("podium", len(df[(df["Tekst"].str.strip() == "") & (df["Discipline"] == "podium")]["Tekst"]),
      "concert", len(df[(df["Tekst"].str.strip() == "") & (df["Discipline"] == "Concert")]["Tekst"]),
      "beeldend", len(df[(df["Tekst"].str.strip() == "") & (df["Discipline"] == "Tentoonstelling")]["Tekst"]))


podium 1521 concert 1844 beeldend 51

Zo, we kunnen deze dataset nu mooi uitschrijven naar een Excel bestand, zodat Simon nog enkele laatste correcties en aanvullingen kan doorvoeren.


In [13]:
def encode(item):
    return item.replace("\x08", "")

df["Tekst"] = df["Tekst"].apply(encode)

In [14]:
df.to_excel("samengevoegde data/df.xlsx")

Landsvermeldingen

Na de manuele correctie van Simon kunnen we nu aan de slag met een propere dataset, die we dan hier nu ook inlezen.


In [15]:
df = read_excel("samengevoegde data/df.xlsx")

We kunnen voor de volledigheid eventjes een overzicht maken van (organisatoren, discipline).


In [16]:
df.groupby(["typering", "Discipline"]).size()


Out[16]:
typering                       Discipline     
Andere overheden               Concert            1585
                               Tentoonstelling      48
                               podium             2183
Lokaal (cultuur)beleid         Concert            3847
                               Tentoonstelling     809
                               podium             6241
Onderwijs                      Concert             378
                               Tentoonstelling     103
                               podium              327
Privaat                        Concert            3120
                               Tentoonstelling     481
                               podium             2847
Rest                           Concert             922
                               Tentoonstelling      72
                               podium              297
Sociaal-cultureel en amateur   Concert            2653
                               Tentoonstelling     258
                               podium             4188
Vlaams gesubsidieerde kunsten  Concert            2759
                               Tentoonstelling     314
                               podium             2563
dtype: int64

Voor onze analyse hebben we ook nood aan een lijst van namen van landen, coordinaten voor de plaatsnamen, en ook een manueel gemaakte mapping om de plaatsnamen in de UiTdatabank gegevens te normaliseren.


In [17]:
typering = read_csv("extra gegevens/mapping_udb-gemeente_fusie-gemeente.csv", delimiter=';')
coord = read_csv("extra gegevens/coordinaten.csv", delimiter=';')
landen = read_excel("extra gegevens/landen.xlsx", sheetname="uitgebreide lijst 2014")

Laten we even in detail deze tabellen bekijken. De landen:


In [18]:
landen.head()


Out[18]:
Continent (staatkundig) Land Soort mention Mention
0 Azië Afghanistan korte landnaam Afghanistan
1 Azië Afghanistan inwoner Afghaan
2 Azië Afghanistan inwoner Afghanen
3 Azië Afghanistan adjectief Afghaans
4 Azië Afghanistan adjectief Afghaanse

Voor elk land weten we in welk (staatkundig) continent het ligt, en we hebben in de kolom 'Mention' verschillende manieren waarop dat land kan voorkomen in de tekst.

Bij typering zien we het volgende:


In [19]:
typering.tail()


Out[19]:
Gemeente Origineel Postcode Fusiegemeente Stedelijkheid fusiegemeenten Gemeente cluster Province (English) Country (English) Provincie Stedelijkheid
932 Kleine-Brogel (Peer) NaN Peer Niet-stedelijk Peer Limburg Belgium Limburg Niet-stedelijk
933 Meilegem (Zwalm) NaN Zwalm Niet-stedelijk Zwalm East Flanders Belgium Oost-Vlaanderen Niet-stedelijk
934 Beek (Bree) NaN Bree Niet-stedelijk Bree Limburg Belgium Limburg Niet-stedelijk
935 Oetingen (Gooik) NaN Gooik Niet-stedelijk Gooik Flemish Brabant Belgium Vlaams-Brabant Niet-stedelijk
936 Vaalbeek (Oud-Heverlee) NaN Oud-Heverlee Niet-stedelijk Oud-Heverlee Flemish Brabant Belgium Vlaams-Brabant Niet-stedelijk

De kolom Gemeente Origineel is de naam van de gemeente in de uitdatabank gegevens, en we kunnen de naam in de kolom Fusiegemeente en Province (English) gebruiken om een genormaliseerd zicht te krijgen.

Tot slot hebben we nog de coordinaten:


In [20]:
coord.head()


Out[20]:
Fusiegemeente adress latitude longitude
0 Aalst Aalst, Belgium 50,9378101 4,0409517
1 Aalter Aalter, Belgium 51,087349 3,448371
2 Aarschot Aarschot, Belgium 50,9859959 4,8365218
3 Aartselaar Aartselaar, Belgium 51,1340539 4,3844742
4 Affligem Affligem, Belgium 50,9084 4,11281

Hiermee kunnen we voor iedere Fusiegemeente (zie vorige tabel) de latitude en longitude ophalen.

Nu gaan we voor ieder event in de UiTdatabankgegevens na welk land er vermeld wordt in de beschrijving van dat event. We houden ook al onmiddellijk bij wat de genormaliseerde naam is van de gemeente en de coordinaten van het centrum. Bovendien tellen we binnen het land ook nog de verschillende disciplines en subdisciplines.


In [62]:
niet_vlaams = ["Jodoigne", "Tournai", "Escanaffles", "Houffalize", "Haulchin", 
               "Braine l'Alleud", "Tourinnes-la-Grosse", "Liège", "Marchienne-au-Pont", 
               "Eupen", "Lessines", "Charleroi"]
count = 1
kwic = []
aantal_treffers = 0
for row in df.iterrows():
    if count % 5000 == 0:
        print(count, "of", len(df.index))
    count += 1
    tekst = row[1]["Tekst"]
    gemeente = row[1]["Gemeente"]
    organisatie = row[1]["typering"]
    if str(gemeente) != "nan" and str(gemeente) not in niet_vlaams:
        for land in set(landen["Land"].values):
            regex = compile(r"\b(" + r"|".join(landen[landen["Land"] == land]["Mention"]) + r")\b")
            matches = regex.finditer(str(tekst))
            for match in matches:
                    aantal_treffers += 1
                    typeringlijn = typering[typering["Gemeente Origineel"] == gemeente]
                    fusiegemeente = typeringlijn["Fusiegemeente"].values[0]
                    provincie = typeringlijn["Province (English)"].values[0]
                    continent = landen[landen["Land"] == land]["Continent (staatkundig)"].values[0]
                    discipline = row[1]["Discipline"]
                    subdiscipline = row[1]["Subdiscipline"]
                    uid = count
                    kwic_lijn = [uid, 
                                 tekst[:match.start()], 
                                 tekst[match.start():match.end()], 
                                 tekst[match.end():], 
                                 gemeente,
                                 land, 
                                 discipline, 
                                 subdiscipline]
                    kwic.append(kwic_lijn)


5000 of 43685
10000 of 43685
15000 of 43685
20000 of 43685
25000 of 43685
30000 of 43685
35000 of 43685
40000 of 43685

In [63]:
DataFrame(kwic, columns=["uid", "left context", "keyword", "right context", "gemeente", "land", "discipline", "subdiscipline"]).to_excel("kwic.xlsx")