7.3. 🚀 Analyse 2: Keyword in Context (KWIC)#

Hinweise zur Ausführung des Notebooks

Dieses Notebook kann auf unterschiedlichen Levels erarbeitet werden (siehe Abschnitt “Technische Voraussetzungen”):

  1. Book-Only Mode: Sie lesen das Notebook hier im “Jupyter Book”, ohne den Code selbst auszuführen.

  2. Cloud Mode: Klicken Sie oben rechts in der Menüleiste auf das Raketen-Symbol 🚀 und wählen Sie “Colab”, um das Notebook auszuführen.

  3. Local Mode: Klicken Sie oben rechts in der Menüleiste auf das Download-Symbol und wählen Sie “.ipynb”, um das Notebook lokal auszuführen.

7.3.1. Übersicht#

Im Folgenden werden die annotierten Dateien (CSV-Format) analysiert. Unser Ziel ist es, den annotierten Korpus zu nutzen, um KWIC-Ausgaben zu erzeugen.

Dafür werden folgende Schritte durchgeführt:

  1. Einlesen des Korpus, der Metadaten und der Grippe-Wortliste

  2. Extraktion und Darstellung der Wortkontexte durch KWIC

Informationen zum Ausführen des Notebooks – Zum Ausklappen klicken ⬇️

Voraussetzungen zur Ausführung des Jupyter Notebooks

  1. Installieren der Bibliotheken
  2. Pfad zu den Daten setzen
  3. Laden der Daten (s.u.)
Zum Testen: Ausführen der Zelle „load libraries“ und der Sektion „Einlesen der Daten“.
Alle Zellen, die mit 🚀 gekennzeichnet sind, werden nur bei der Ausführung des Notebooks in Colab / JupyterHub bzw. lokal ausgeführt.
Hide code cell content
#  🚀 Install libraries 
! pip install pandas bokeh 
Hide code cell content
import re
from pathlib import Path
import pandas as pd
from tqdm import tqdm
import requests

## for interactivity in jupyter books
from bokeh.io import output_notebook, show
from bokeh.layouts import column, layout
from bokeh.models import  ColumnDataSource, DataTable, DateFormatter, TableColumn
# Ensure Bokeh output is displayed in the notebook
output_notebook()
Loading BokehJS ...

Bevor wir Daten herunterladen, definieren wir eine kleine Hilfsfunktion download_file. Sie lädt eine Datei plattformunabhängig – also auch unter Windows – aus dem Internet in einen Zielordner herunter und ersetzt damit das Kommando wget, das nicht auf allen Systemen (z.B. Windows) nativ verfügbar ist.

# helper: download a single file (cross-platform replacement for `! wget -P`)
def download_file(url, target_dir):
    """Download the file at `url` into `target_dir`, keeping its original name."""
    target_dir = Path(target_dir)
    target_dir.mkdir(parents=True, exist_ok=True)
    target_path = target_dir / url.split("/")[-1]
    response = requests.get(url)
    response.raise_for_status()
    target_path.write_bytes(response.content)
    return target_path

7.3.2. Einlesen der Daten, Metadaten#

Einlesen des Korpus (CSV-Dateien)#

Informationen zum Ausführen des Notebooks – Zum Ausklappen klicken ⬇️ Zuerst wird der Ordner angelegt, in dem die CSV-Dateien gespeichert werden. Der Einfachheit halber wird die gleiche Datenablagestruktur wie in dem GitHub Repository, in dem die Daten gespeichert sind, vorausgesetzt.
Danach werden alle CSV-Dateien im Korpus heruntergeladen und gespeichert. Dafür sind folgende Schritte nötig:
  1. Es wird eine Liste erstellt, die die URLs zu den einzelnen CSV-Dateien beinhaltet.
  2. Alle Dateien aus der Liste werden heruntergeladen und in dem Ordner ../data/csv gespeichert.
Sollten die Dateien schon an einem anderen Ort vorhanden sein, können die Dateipfade zu den Ordnern angepasst werden.
Hide code cell content
# 🚀 Create data directory path
corpus_dir = Path("../data/csv")
if not corpus_dir.exists():
    corpus_dir.mkdir(parents=True)
Hide code cell content
# 🚀 Create download list 
github_api_txt_dir_path = "https://api.github.com/repos/quadriga-dk/Text-Fallstudie-1/contents/data/csv"
txt_dir_info = requests.get(github_api_txt_dir_path).json()
url_list = [entry["download_url"] for entry in txt_dir_info]
Hide code cell content
# ⚠️ Only execute, if you haven't downloaded the files yet!
# 🚀 Download all csv files – this step will take a while (ca. 7 minutes)
for url in tqdm(url_list, desc="Downloading csv files"):
    download_file(url, "../data/csv")

Setzen des Pfads:

# set the path to csv files to be processed
csv_dir = Path(r"../data/csv")
# Create dictionary to save the corpus data (filenames and tables)
corpus_annotations = {}

# Iterate over csv files 
for file in csv_dir.iterdir():
    # check if the entry is a file, not a directory
    if file.is_file():
        # check if the file has the correct suffix csv
        if file.suffix == '.csv':
            # read the csv table to a data frame
            data = pd.read_csv(file) 
            # save the data frame to the dictionary, key=filename (without suffix), value=dataframe
            corpus_annotations[file.name] = data

Einlesen der Metadaten#

Informationen zum Ausführen des Notebooks – Zum Ausklappen klicken ⬇️ Zuerst wird der Ordner angelegt, in dem die Metadaten-Datei gespeichert wird. Wieder wird die gleiche Datenablagestruktur wie in dem GitHub Repository vorausgesetzt.
Der Text wird aus GitHub heruntergeladen und in dem Ordner ../data/metadata/ abgespeichert.
Der Pfad kann in der Variable metadata_path angepasst werden. Die einzulesende Datei muss die Endung `.csv` haben.
Hide code cell content
# 🚀 Create metadata directory path
metadata_dir = Path("../data/metadata")
if not metadata_dir.exists():
    metadata_dir.mkdir()
Hide code cell content
# 🚀 Load the metadata file from GitHub 
download_file("https://raw.githubusercontent.com/quadriga-dk/Text-Fallstudie-1/refs/heads/main/data/metadata/QUADRIGA_FS-Text-01_Data01_Corpus-Table.csv", "../data/metadata")
# set path to metadata file
metadata_path = '../data/metadata/QUADRIGA_FS-Text-01_Data01_Corpus-Table.csv'

# read metadata file to pandas dataframe and set index
corpus_metadata = pd.read_csv(metadata_path, sep=';')
corpus_metadata = corpus_metadata.set_index('DC.identifier')

Kombinieren von tokenisierten Texten und deren Metadaten für KWIC-Suche#

def get_date_metadata(txtname, corpus_metadata):  
    date = corpus_metadata.loc[txtname, 'DC.date']
    date = str(date)
    year = date[:4]
    month = date[:7]
    day = date
    return year, month, day 
for filename, annotated_text in corpus_annotations.items():
    txtname = filename.replace('.csv', '')
    if txtname in corpus_metadata.index:
        year, month, day = get_date_metadata(txtname, corpus_metadata)
        annotated_text['month'] = month
        annotated_text['filename'] = filename
search_df = pd.concat(corpus_annotations.values())
search_df = search_df.reset_index()
search_df["Token"] = search_df["Token"].astype(str)
print(f'The KWIC-search will be over a corpus of {search_df.shape[0]} word occurences')
The KWIC-search will be over a corpus of 33185650 word occurences

Einlesen der Wortliste (Semantisches Feld „Grippe“)#

Hide code cell content
# 🚀 Create word list directory path
wordlist_dir = Path("../data/wordlist")
if not wordlist_dir.exists():
    wordlist_dir.mkdir()
Hide code cell content
# 🚀 Load the wordlist file from GitHub 
download_file("https://raw.githubusercontent.com/quadriga-dk/Text-Fallstudie-1/refs/heads/main/data/wordlist/grippe.txt", "../data/wordlist")
path_to_wordlist = Path("../data/wordlist/grippe.txt")
word_list = path_to_wordlist.read_text(encoding="utf-8").split("\n")

Wie sieht die Wortliste aus?

word_list
['Influenza',
 'Grippe',
 'Grippeepidemie',
 'Grippewelle',
 'Grippekranke',
 'Grippepandemie',
 'Lungenentzündung',
 'Krankheitswelle',
 'Seuchenzug',
 'Krankheitsausbruch',
 'Fieberanfall',
 'Schüttelfrost',
 'Atemnot',
 'Körpererschöpfung',
 'Genesungszeit',
 'Ansteckungsgefahr',
 'Seuchenschutz',
 'Desinfektionsmittel',
 'Schutzmaske',
 'Krankenstation',
 'Isolationsstation',
 'Sanitätsdienst',
 'Krankheitsverlauf',
 'Todesopfer',
 'Krankheitssymptom',
 'Erkrankungsfall',
 'Lungeninfektion',
 '']

7.3.3. Extraktion und Darstellung der Wortkontexte durch KWIC#

Hide code cell content
class ContextViewer:
    
    def __init__(self, search_df):
        self.full_df = search_df
        print(f'Searching in a corpus of {self.full_df.shape[0]} word occurences')
        
    def show_kwic_output(self, search_terms, n_words):
        contexts_df = self.get_context_words(search_terms, n_words)
        
        # Convert DataFrame to ColumnDataSource
        source = ColumnDataSource(contexts_df)
                
        # Create Table Columns
        columns = [TableColumn(field=col, title=col) for col in contexts_df.columns]

        # Create DataTable
        data_table = DataTable(source=source, columns=columns)

        # Display DataTable
        output_notebook()  # Use this to render in Jupyter Notebook
        show(layout([data_table]))

    
    def get_context_words(self, search_terms, n_words):
        #search_terms = input('Insert a word to search, split by comma if more than one: ')
        if len(search_terms) == 0:
            search_terms = word_list
        if isinstance(search_terms, str):
            search_terms = search_terms.split(',')
        search_terms = [x.strip() for x in search_terms]
        indices = self.full_df.query(f'Lemma.isin({search_terms})').index
        #print(indices)
        left_contexts = []
        this_words = []
        right_contexts = []
        months = []
        for indice in indices:
            left = self.full_df.iloc[indice-n_words:indice-1, ]["Token"]
            leftс = left[~left.str.contains('\n')]
            right = self.full_df.iloc[indice+1:indice+n_words, ]["Token"]
            rightс = right[~right.str.contains('\n')]
            left_contexts.append(' '.join(leftс))
            right_contexts.append(' '.join(rightс))
            this_words.append(self.full_df.iloc[indice, ]["Token"])
            months.append(self.full_df.iloc[indice, ]["month"])
        newdf = pd.DataFrame()
        newdf['left_context'] = left_contexts
        newdf['word'] = this_words
        newdf['right_context'] = right_contexts
        newdf['month'] = months
        return newdf
        
kwic = ContextViewer(search_df)
Searching in a corpus of 33185650 word occurences
kwic.show_kwic_output(word_list, n_words=5)
Loading BokehJS ...

Worteingabe für die Suche und KWIC-Output (für Cloud Mode und Local Mode)#

Hide code cell content
text_input = input("Geben Sie die zu suchenden Wörter ein und trennen Sie sie durch Kommas, wenn es mehrere sind:")
# Convert the input to a list by splitting the input by comma
search_terms = [x.strip() for x in text_input.split(',')]
Hide code cell content
kwic.show_kwic_output(search_terms, n_words=5)