03 Sentimentanalyse (Demo-Workflow)

Daidalos 2025 (https://daidalos-projekt.de)

Sentiment Analysis#

Lernziele#


  • Was ist eine Sentimentanalyse?

  • Was ist ein Sentimentlexikon und wie wird es erstellt?

  • Welche Sentimentlexika sind für die Alten Sprachen verfügbar?

  • Welche Anwendungsmöglichkeiten der Sentimentanalyse gibt es in der Klassischen Philologie?

  • Wie sieht der Workflow einer Sentimentanalyse aus?

  • Durchführung einer Sentimentanalyse anhand eines kuratierten Workflows

  • Evaluation der Ergebnisse

  • Planung der nächsten Schritte: Ein bunter Text - was jetzt?

  • Methodenreflexion

Einführung#


Was ist eine Sentimentanalyse?#

Die Sentimentanalyse ist die Untersuchung eines Textes auf die darin ausgedrückten Gefühle, Stimmungen, Meinungen oder Haltungen, um deren emotionale Ausrichtung (positiv, negativ, neutral) und spezifische Emotionen (z.B. Freude, Wut, Trauer) zu identifizieren. Der Begriff bezieht sich häufig auf quantitative Textanalysen, obwohl grundsätzlich auch qualitativ durchgeführte Studien zu Emotionen in Texten darunter fallen können. Mithilfe eines Algorithmus wird ein beliebiger Text auf Grundlage eines Sentimentlexikons (siehe unten) annotiert. Als Output erhält man den Ausgangstext mit farblichen Markierungen, wobei Wörter mit positiver Konnotation grün, solche mit negativer Konnotation rot und neutrale Wörter grau hervorgehoben werden. Wörter, deren Sentiment nicht im verwendeten Sentimentlexikon hinterlegt ist, behalten ihren ursprünglichen weißen Hintergrund.

Was ist ein Sentimentlexikon und wie wird es erstellt?#

Ein Sentimentlexikon ist eine Liste von Wörtern (i.d.R. Einzelwörter, manchmal auch Phrasen und Sätze), in der jedem Wort (oder jeder Phrase, jedem Satz) kontextunabhängig ein Polaritätswert zugeordnet wird (vgl. Sprugnoli et al. 2020, S. 3079). Die Polaritätswerte unterscheiden sich von Lexikon zu Lexikon:

  • Skala von 1 bis 5 (1 = extrem positiv, 5 = extrem negativ)

  • Skala von -1 bis +1 (+1 = extrem positiv, -1 = extrem negativ)

Bei der Erstellung eines Sentimentlexikons gibt es unterschiedliche Vorgehensweisen:

  • manuelle Sentimentannotation (“Goldstandard”) -> zeitaufwendig

  • semiautomatische Sentimentannotation: z.B. Erweiterung eines Goldstandards (zu einem “Silberstandard”) durch:

    • Induktionsmethoden: Nutzung von Derivation (z.B. dignus - indignus)

    • sprachübergreifende Projektionsmethode: Übersetzung neusprachlicher Sentimentlexika

  • vollautomatische Sentimentannotation durch einen Algorithmus

Besonderheiten der Alten Sprachen:

  • Es gibt keine Muttersprachler/innen für die manuelle Sentimentannotation oder für die Überprüfung der maschinellen Annotationen.

  • Das Textkorpus antiker Literatur wächst im Gegensatz zu den modernen Sprachen nicht mehr (“geschlossenes Korpus”). Schätzungen gehen davon aus, dass rund 150 Mio. Wörter aus der römisch-griechischen Antike überliefert sind (Sprugnoli et al. 2020, S. 3078).

Welche Sentimentlexika sind für die Alten Sprachen verfügbar?#

Latein

Für das Lateinische liegen einige Übersetzungen neusprachlicher Sentimentlexika vor:

Darüber hinaus gibt es ein genuin lateinisches Sentimentlexikon:

Altgriechisch

  • Für Altgriechisch gibt es noch kein Sentimentlexikon.

Andere Option: Anwendung eines neusprachlichen Sentimentlexikons auf eine Übersetzung des antiken Texts

Anwendungsmöglichkeiten der Sentimentanalyse in der Klassischen Philologie#

analog (in Auswahl):

  • Beyond Anger: A Study of Juvenal’s Third Book of Satires (1988)

  • Ancient Anger: Perspectives From Homer to Galen (2003)

  • Happiness and Greek Ethical Thought (2004)

  • The Emotions of the Ancient Greeks: Studies In Aristotle and Classical Literature (2006)

  • Reading Fear In Flavian Epic: Emotion, Power, and Stoicism (2022)

  • Emotion and Historiography In Polybius’ Histories (2024)

  • Memory and Emotions In Antiquity (2024)

digital:

  • Odi et Amo. Creating, Evaluating and Extending Sentiment Lexicons for Latin (2020)

  • Interpretation of Sentiment Analysis in Aeschylus’s Greek Tragedy (2020)

  • Sentiment Analysis for Latin: a Journey from Seneca to Thomas Aquinas (2021)

  • Sentiment Analysis of Homeric Text: The 1st Book of Iliad (2022)

  • Sentiment Analysis of Latin Poetry: First Experiments on the Odes of Horace (2022)

  • The Sentiment of Latin Poetry. Annotation and Automatic Analysis of the Odes of Horace (2023)

Anwendungsbeispiel#


Ein klassisch-philologisches Anwendungsbeispiel: Für die Beantwortung einer übergeordneten Forschungsfrage1 soll ermittelt werden, welche Stimmung (“positiv”, “negativ”) in zwei Textstellen vorherrscht (Suet. Iul. 24; Vell. 2,46,1-2). Die “vorherrschende Stimmung” wird hier definiert als die Summe aller positiven bzw. negativen Sentimentzuordnungen.

Vorgehen (Workflow):

  1. Zu untersuchende Texte einspeisen

  2. Data preprocessing: Entfernung aller störenden Zeichen

  3. Installation der Python-Bibliothek spaCy (Vorbereitung der Lemmatisierung)

  4. Installation des KI-Modells LatinCy (Vorbereitung der Lemmatisierung)

  5. Einbindung von spaCy und LatinCy in den Python-Code (eigentliche Lemmatisierung)

  6. Vorbereitung des Sentiment-Algorithmus

  7. Eigentliche Sentimentanalyse Sueton

  8. Eigentliche Sentimentanalyse Velleius Paterculus

Wenn alles gut geht, sollten Sie am Ende des Workflows zwei Texte mit farblichen Hervorhebungen (= den jeweiligen Sentimentannotationen) sehen, einen für Velleius Paterculus und einen für Sueton:


1 Wie lässt sich das Fehlen einer Information in einem Werk der antiken Geschichtsschreibung feststellen und deuten? Wir beginnen mit einer Case Study zur Konferenz von Luca (56 v. Chr.).

Aufgaben#


Aufgabe 1: Zu untersuchende Texte einlesen#

Bevor Sie einen Text mithilfe Ihres Computers untersuchen können, müssen Sie ihm das relevante Textkorpus zur Verfügung stellen. Um Zeit und Platz zu sparen, beschränken wir uns an dieser Stelle auf zwei kurze Textstellen aus Suetons Caesar-Biographie und Velleius Paterculus’ Historia. Prinzipiell kann in diesem Schritt jeder digital verfügbare Text eingespeist werden, unabhängig von seiner Länge.

Führen Sie den ersten Code-Schnipsel (= grau hinterlegt) aus, indem Sie ihn anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, sind die Texte eingelesen und Sie können mit dem nächsten Schritt fortfahren.

with open('data/Suet_Iul_24.txt','r',encoding='utf-8') as file:
    suet_text = file.read()
print(suet_text)
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[1], line 1
----> 1 with open('data/Suet_Iul_24.txt','r',encoding='utf-8') as file:
      2     suet_text = file.read()
      3 print(suet_text)

File /usr/local/lib/python3.10/site-packages/IPython/core/interactiveshell.py:324, in _modified_open(file, *args, **kwargs)
    317 if file in {0, 1, 2}:
    318     raise ValueError(
    319         f"IPython won't let you open fd={file} by default "
    320         "as it is likely to crash IPython. If you know what you are doing, "
    321         "you can use builtins' open."
    322     )
--> 324 return io_open(file, *args, **kwargs)

FileNotFoundError: [Errno 2] No such file or directory: 'data/Suet_Iul_24.txt'
with open('data/Vell_2_46_1-2.txt','r',encoding='utf-8') as file:
    vell_text = file.read()
print(vell_text)
Cum deinde inmanis res vix multis voluminibus explicandas C. Caesar in Gallia gereret​152 nec contentus plurimis ac felicissimis victoriis innumerabilibusque caesis et captis hostium milibus etiam in Britanniam traiecisset exercitum, alterum paene imperio nostro ac suo quaerens orbem, vetus par​153 consulum, Cn. Pompeius et M. Crassus, alterum iniere consulatum, qui neque petitus honeste ab iis neque probabiliter gestus est. 2 Caesari lege, quam Pompeius ad populum tulit, prorogatae in idem spatium temporis provinciae, Crasso bellum Parthicum iam​154 animo molienti Syria decreta. Qui vir cetera sanctissimus immunisque voluptatibus neque in pecunia neque in gloria concupiscenda aut modum norat aut capiebat terminum.

Aufgabe 2: Entfernung aller störenden Zeichen#

Satzzeichen, Zahlen und andere störende Zeichen, die dem antiken Text später hinzugefügt wurden, sind für die Sentimentanalyse irrelevant.

Entfernen Sie in Vorbereitung auf die maschinelle Analyse alle Satzzeichen im Text, indem Sie den nächsten Code-Schnipsel anklicken und dann im Menü erneut auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

suet_clean = [x if x == ' ' or x.isalpha() else '' for x in suet_text]
suet_clean = ''.join(suet_clean)
print(suet_clean)
Sed cum Lucius Domitius consulatus candidatus palam minaretur consulem se effecturum quod praetor nequisset adempturumque ei exercitus Crassum Pompeiumque in urbem provinciae suae Lucam extractos conpulit ut detrudendi Domitii causa consulatum alterum peterent perfecitque per utrumque ut in quinquennium sibi imperium prorogaretur Qua fiducia ad legiones quas a re publica acceperat alias privato sumptu addidit unam etiam ex Transalpinis conscriptam vocabulo quoque Gallico  Alauda enim appellabatur  quam disciplina cultuque Romano institutam et ornatam postea universam civitate donavit Nec deinde ulla belli occasione ne iniusti quidem ac periculosi abstinuit tam foederatis quam infestis ac feris gentibus ultro lacessitis adeo ut senatus quondam legatos ad explorandum statum Galliarum mittendos decreverit ac nonnulli dedendum eum hostibus censuerint Sed prospere decedentibus rebus et saepius et plurium quam quisquam umquam dierum supplicationes impetravit
vell_clean = [x if x == ' ' or x.isalpha() else '' for x in vell_text]
vell_clean = ''.join(vell_clean)
print(vell_clean)
Cum deinde inmanis res vix multis voluminibus explicandas C Caesar in Gallia gereret nec contentus plurimis ac felicissimis victoriis innumerabilibusque caesis et captis hostium milibus etiam in Britanniam traiecisset exercitum alterum paene imperio nostro ac suo quaerens orbem vetus par consulum Cn Pompeius et M Crassus alterum iniere consulatum qui neque petitus honeste ab iis neque probabiliter gestus est  Caesari lege quam Pompeius ad populum tulit prorogatae in idem spatium temporis provinciae Crasso bellum Parthicum iam animo molienti Syria decreta Qui vir cetera sanctissimus immunisque voluptatibus neque in pecunia neque in gloria concupiscenda aut modum norat aut capiebat terminum

Aufgabe 3: Installation der Python-Bibliothek spaCy (Vorbereitung der Lemmatisierung)#

Führen Sie alle flektierten Formen im Text auf ihre Grundform zurück, indem sie den Text von einem Algorithmus lemmatisieren lassen. Dieser Schritt ist notwendig, damit der Sentiment-Algorithmus später flektierten Wortformen ihre Grundformen im Sentimentlexikon zuordnen kann.

Installieren Sie die Python-Bibliothek spaCy mit dem Paketmanager pip, indem Sie den nächsten Code-Schnipsel anklicken und dann im Menü erneut auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

%%capture --no-stderr
!pip install spacy==3.8.3

Aufgabe 4: Installation des KI-Modells LatinCy (Vorbereitung der Lemmatisierung)#

Laden Sie LatinCy (Burns 2023), ein KI-Modell für Lemmatisierung, herunter, indem Sie den nächsten Code-Schnipsel anklicken und dann im Menü erneut auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

!pip install "la-core-web-lg @ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl"
Collecting la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl
  Downloading https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl (241.2 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 241.2/241.2 MB 34.9 MB/s eta 0:00:0000:0100:01
Collecting spacy-lookups-data@ git+https://github.com/diyclassics/spacy-lookups-data.git#egg=spacy-lookups-data (from la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl)
  Cloning https://github.com/diyclassics/spacy-lookups-data.git to /tmp/pip-install-3gzumwno/spacy-lookups-data_9807e8dd0fca4d05b20fb99c72deb23f
  Running command git clone --filter=blob:none --quiet https://github.com/diyclassics/spacy-lookups-data.git /tmp/pip-install-3gzumwno/spacy-lookups-data_9807e8dd0fca4d05b20fb99c72deb23f
  Resolved https://github.com/diyclassics/spacy-lookups-data.git to commit 5f2b7e60d3b461cd61649c0bb75f65a242b56ece
  Preparing metadata (setup.py) ... ?25ldone
?25hCollecting spacy<3.8.0,>=3.7.5 (from la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl)
  Using cached spacy-3.7.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (27 kB)
Requirement already satisfied: spacy-legacy<3.1.0,>=3.0.11 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (3.0.12)
Requirement already satisfied: spacy-loggers<2.0.0,>=1.0.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (1.0.5)
Requirement already satisfied: murmurhash<1.1.0,>=0.28.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (1.0.11)
Requirement already satisfied: cymem<2.1.0,>=2.0.2 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (2.0.10)
Requirement already satisfied: preshed<3.1.0,>=3.0.2 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (3.0.9)
Collecting thinc<8.3.0,>=8.2.2 (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl)
  Using cached thinc-8.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (15 kB)
Requirement already satisfied: wasabi<1.2.0,>=0.9.1 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (1.1.3)
Requirement already satisfied: srsly<3.0.0,>=2.4.3 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (2.5.0)
Requirement already satisfied: catalogue<2.1.0,>=2.0.6 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (2.0.10)
Requirement already satisfied: weasel<0.5.0,>=0.1.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (0.4.1)
Requirement already satisfied: typer<1.0.0,>=0.3.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (0.15.1)
Requirement already satisfied: tqdm<5.0.0,>=4.38.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (4.67.1)
Requirement already satisfied: requests<3.0.0,>=2.13.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (2.32.3)
Requirement already satisfied: pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (2.10.4)
Requirement already satisfied: jinja2 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (3.1.5)
Requirement already satisfied: setuptools in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (75.1.0)
Requirement already satisfied: packaging>=20.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (24.2)
Requirement already satisfied: langcodes<4.0.0,>=3.2.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (3.5.0)
Requirement already satisfied: numpy>=1.19.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (1.26.4)
Requirement already satisfied: language-data>=1.2 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from langcodes<4.0.0,>=3.2.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (1.3.0)
Requirement already satisfied: annotated-types>=0.6.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (0.7.0)
Requirement already satisfied: pydantic-core==2.27.2 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (2.27.2)
Requirement already satisfied: typing-extensions>=4.12.2 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from pydantic!=1.8,!=1.8.1,<3.0.0,>=1.7.4->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (4.12.2)
Requirement already satisfied: charset-normalizer<4,>=2 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from requests<3.0.0,>=2.13.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (3.4.1)
Requirement already satisfied: idna<4,>=2.5 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from requests<3.0.0,>=2.13.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from requests<3.0.0,>=2.13.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (2.3.0)
Requirement already satisfied: certifi>=2017.4.17 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from requests<3.0.0,>=2.13.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (2024.12.14)
Collecting blis<0.8.0,>=0.7.8 (from thinc<8.3.0,>=8.2.2->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl)
  Using cached blis-0.7.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (7.4 kB)
Requirement already satisfied: confection<1.0.0,>=0.0.1 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from thinc<8.3.0,>=8.2.2->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (0.1.5)
Requirement already satisfied: click>=8.0.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from typer<1.0.0,>=0.3.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (8.1.8)
Requirement already satisfied: shellingham>=1.3.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from typer<1.0.0,>=0.3.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (1.5.4)
Requirement already satisfied: rich>=10.11.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from typer<1.0.0,>=0.3.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (13.9.4)
Requirement already satisfied: cloudpathlib<1.0.0,>=0.7.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from weasel<0.5.0,>=0.1.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (0.20.0)
Requirement already satisfied: smart-open<8.0.0,>=5.2.1 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from weasel<0.5.0,>=0.1.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (7.1.0)
Requirement already satisfied: MarkupSafe>=2.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from jinja2->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (3.0.2)
Requirement already satisfied: marisa-trie>=1.1.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from language-data>=1.2->langcodes<4.0.0,>=3.2.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (1.2.1)
Requirement already satisfied: markdown-it-py>=2.2.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from rich>=10.11.0->typer<1.0.0,>=0.3.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (3.0.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from rich>=10.11.0->typer<1.0.0,>=0.3.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (2.19.1)
Requirement already satisfied: wrapt in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from smart-open<8.0.0,>=5.2.1->weasel<0.5.0,>=0.1.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (1.17.0)
Requirement already satisfied: mdurl~=0.1 in /home/konstantin/miniconda3/envs/daidalos-workshops/lib/python3.11/site-packages (from markdown-it-py>=2.2.0->rich>=10.11.0->typer<1.0.0,>=0.3.0->spacy<3.8.0,>=3.7.5->la-core-web-lg@ https://huggingface.co/DaidalosTeam/LatinCy/resolve/main/la_core_web_lg-any-py3-none-any.whl) (0.1.2)
Using cached spacy-3.7.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.6 MB)
Using cached thinc-8.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (920 kB)
Using cached blis-0.7.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (10.2 MB)
Building wheels for collected packages: spacy-lookups-data
  Building wheel for spacy-lookups-data (setudone
?25h  Created wheel for spacy-lookups-data: filename=spacy_lookups_data-1.0.5-py2.py3-none-any.whl size=100463154 sha256=04b5c3fa3956f4f4ab612c91ab06b6f11bd6f0e83b7de179c73e9acfc254d22b
  Stored in directory: /tmp/pip-ephem-wheel-cache-0yk4i12k/wheels/6b/f6/47/795643afe8bcb2d4d6fd38f86d75032831c395c11057968075
Successfully built spacy-lookups-data
Installing collected packages: spacy-lookups-data, blis, thinc, spacy, la-core-web-lg
  Attempting uninstall: blis
    Found existing installation: blis 1.1.0
    Uninstalling blis-1.1.0:
      Successfully uninstalled blis-1.1.0
  Attempting uninstall: thinc
    Found existing installation: thinc 8.3.3
    Uninstalling thinc-8.3.3:
      Successfully uninstalled thinc-8.3.3
  Attempting uninstall: spacy
    Found existing installation: spacy 3.8.3
    Uninstalling spacy-3.8.3:
      Successfully uninstalled spacy-3.8.3
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
grc-proiel-trf 3.7.5 requires spacy<3.9.0,>=3.8.2, but you have spacy 3.7.5 which is incompatible.
Successfully installed blis-0.7.11 la-core-web-lg-3.7.7 spacy-3.7.5 spacy-lookups-data-1.0.5 thinc-8.2.5

Aufgabe 5: Einbindung von spaCy und LatinCy in den Python-Code (eigentliche Lemmatisierung)#

Binden Sie spaCy und LatinCy in den Python-Code ein, um die eigentliche Lemmatisierung zu starten, indem Sie den nächsten Code-Schnipsel anklicken und dann im Menü erneut auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

import spacy
from spacy import Language
from spacy.tokens import Doc
import warnings

# Warnungen werden ausgeblendet
warnings.filterwarnings('ignore')

# Der Lemmatizer wird initialisiert 
lemmatizer = spacy.load("la_core_web_lg", exclude=["morphologizer", "tagger", "parser", "transformer"])

Der Algorithmus produziert nun einen Text, in dem jede ehemals flektierte Form in ihrer Grundform erscheint. Um Ihnen den Unterschied zwischen dem Originaltext und dem lemmatisierten Text zu verdeutlichen, werden Ihnen beide Versionen untereinander angezeigt.

Führen Sie die Code-Schnipsel aus, indem Sie sie anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

# Jedes Wort in jedem der obrigen Sätze wird auf sein Lemma zurückgeführt und dann ausgegeben
suet_lemmatized = lemmatizer(Doc(vocab=lemmatizer.vocab,words=suet_clean.split()))
suet_lemmatized_text = " ".join(token.lemma_ for token in suet_lemmatized)
print(f'Original \n {suet_text}')
print(f'Lemmatisiert: \n {suet_lemmatized_text}')
# Jedes Wort in jedem der obrigen Sätze wird auf sein Lemma zurückgeführt und dann ausgegeben
vell_lemmatized = lemmatizer(Doc(vocab=lemmatizer.vocab,words=vell_clean.split()))
vell_lemmatized_text = " ".join(token.lemma_ for token in lemmatizer(vell_lemmatized))
print(f'Original \n {vell_text}')
print(f'Lemmatisiert: \n {vell_lemmatized_text}')

Aufgabe 6: Vorbereitung des Sentiment-Algorithmus#

In diesem Schritt wird das Sentimentlexikon aus einer Textdatei importiert (LatinAffectusv4.txt), damit der Algorithmus in Aufgabe 7 und 8 darauf zugreifen kann.

Führen Sie den Code-Schnipsel aus, indem Sie ihn anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

# Import des Sentimentlexikons
import csv

sentiments: dict[str, float] = dict()
with open('data/LatinAffectusv4.txt','r',encoding='utf-8') as file:
    reader: csv.reader = csv.reader(file, delimiter="\t", quotechar='"')
    next(reader)
    for row in reader:
        lemma, _, polarity_score, _, _ = row
        sentiments[lemma] = float(polarity_score)

Für die Ermittlung der “vorherrschenden Stimmung” in den Texten müssen einige Vorbereitungen getroffen werden. In diesem Schritt wird jedem Sentimentzahlenwert (-1; -0.5; 0; +0.5; +1) ein Label zugewiesen (negativ; eher negativ; neutral; eher positiv; positiv), das später bei der Auszählung der Ergebnisse verwendet wird. Für die Berechnung des Gesamtsentiments und des durchschnittlichen Sentiments der Texte sollen nur Worte berücksichtigt werden, die mit einem Sentimentwert versehen sind. Dafür werden alle Worte ohne Sentimentwert (= Lemmata sind nicht Teil des Sentimentlexikons) ausgeschlossen.

Führen Sie den Code-Schnipsel aus, indem Sie ihn anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

# Natürlichsprachliche Definition der Sentimente 
translations: dict[float, str] = {-999: "unbekannt", -1: "negativ", -0.5: "eher negativ", 0: "neutral",
                                      0.5: "eher positiv", 1: "positiv"}

# Ausschluss von "unbekannt" aus den relevanten Sentimenten
relevant_sentiments: list[float] = [x for x in translations.keys() if x != -999]

Für die spätere Visualisierung der Ergebnisse (= Texte mit farblichen Hervorhebungen) müssen ebenfalls einige Vorbereitung getroffen werden. Die Ergebnisse werden später in das HTML-Skelett eingebunden und durch die CSS-Styles formatiert.

Führen Sie die Code-Schnipsel aus, indem Sie sie anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

# HTML Vorlage für die Visualisierung
html_template: str = """
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <title>Sentiment</title>
            {0}
        </head>
        <body>
            {1}
        </body>
    </html>
"""
# CSS Vorlage für die Visualisierung
css_style: str = """
    <style>
        div.unit {
            float: left;
            margin-bottom: 1em;
            color: black;
        }
        p.form {
            font-size: 16pt;
            margin: 0em;
            padding: 0em 0.5em;
        }
        p.lemma {
            font-size: 10pt;
            font-family: sans-serif;
            color: gray;
            margin: 0em;
            padding: 0em 1em;
        }
    </style>
"""

Die folgenden rgb-Angaben (= rot, grün und blau) bestimmen die farbliche Hervorhebung der Sentimentannotationen. Dabei wird eine Skala von 0-255 verwendet (z.B.: -1 = dunkelrot; 1 = dunkelgrün).

Führen Sie den Code-Schnipsel aus, indem Sie ihn anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

# Farbzuordnungen für die Sentimente
color_map: dict[float, str] = {
    -999: "rgb(255, 255, 255)",
    -1: "rgb(255, 0, 0)",
    -0.5: "rgb(255, 200, 200)",
    0: "rgb(230, 230, 230)",
    0.5: "rgb(200, 255, 200)",
    1: "rgb(0, 255, 0)",
}

Aufgabe 7: Sentimentausgabe Sueton#

In diesem Schritt wird für jedes Wort im lemmatisierten Text (Token) nach einem Sentimentwert im Sentimentlexikon gesucht. Wird ein Wert gefunden, wird dem Token dieser Wert zugewiesen; ansonsten erhält er den Wert -999 (unbekannt). Dann werden für den Gesamttext die Sentimente gezählt: es werden für jede Kategorie sowohl absolute Zahlen als auch Prozentangaben ausgegeben.

Führen Sie den Code-Schnipsel aus, indem Sie ihn anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

# Zählung aller Sentimentvorkommen
from collections import Counter
from spacy.tokens.token import Token
Token.set_extension("sentiment", default=-999, force=True)
for token in suet_lemmatized: 
    token._.sentiment = sentiments.get(token.lemma_, -999)
counter: Counter = Counter([x._.sentiment for x in suet_lemmatized])
keys = sorted(list(counter.keys()))
for k in keys:
    print(f'{translations[k]}:{counter[k]}')
print("Wörter insgesamt: ", sum(counter.values()))
# Ausgabe in Prozent
percentages: dict[float, float] = {i: (counter[i] / len(suet_lemmatized) * 100) for i in counter}
for k in keys:
    print(f'{translations[k]}:{percentages[k]}')

In dieser Zeile wird der Average_sentiment-Wert berechnet. Der Average_sentiment-Wert ist das durchschnittliche Sentiment des Textes.

Führen Sie den Code-Schnipsel aus, indem Sie ihn anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

# Duchschnittsscore
average_sentiment: float = sum([(counter[x] * x) for x in relevant_sentiments]) / len(suet_lemmatized)
print(f'{average_sentiment=}')

Im Baukastenprinzip wird für jedes Wort eine Einheit aus Wort, Lemma und Sentimentfarbe generiert. Die einzelnen Bausteine werden dann aneinander gereiht und im HTML-Format (“browserfähig”) ausgegeben.

Führen Sie den Code-Schnipsel aus, indem Sie ihn anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

# Vorbereiten des HTML-Strings
text_html: str = ""
for token in suet_lemmatized:
    style_html: str = f'style="background-color: {color_map[token._.sentiment]};"'
    token_html = f'<p class="form" {style_html}>{token.text}</p><p class="lemma" {style_html}>{token.lemma_}</p>'
    div_html: str = f'<div class="unit">{token_html}</div>'
    text_html += div_html
html_string: str = html_template.format(css_style, text_html)
html_string
# HTML- AUsgabe
from IPython.display import HTML
HTML(html_string)

Aufgabe 8: Sentimentausgabe Velleius Paterculus#

Vgl. Aufgabe 7.

Führen Sie die Code-Schnipsel aus, indem Sie sie anklicken und dann im Menü auf das Play-Zeichen klicken. Ein Stern in den eckigen Klammern [*] am linken Bildschirmrand weist Sie darauf hin, dass die Ausführung des Codes noch andauert. Sobald aus dem Stern eine Zahl wird, können Sie mit dem nächsten Schritt fortfahren.

# Zählung aller Sentimentvorkommen
for token in vell_lemmatized: 
    token.set_extension("sentiment", default=-999, force=True)
    token._.sentiment = sentiments.get(token.lemma_, -999)
counter: Counter = Counter([x._.sentiment for x in vell_lemmatized])
keys = sorted(list(counter.keys()))
for k in keys:
    print(f'{translations[k]}:{counter[k]}')
print("Wörter insgesamt: ", sum(counter.values()))
# Ausgabe in Prozent
percentages: dict[float, float] = {i: (counter[i] / len(vell_lemmatized) * 100) for i in counter}
for k in keys:
    print(f'{translations[k]}:{percentages[k]}')
# Duchschnittsscore
average_sentiment: float = sum([(counter[x] * x) for x in relevant_sentiments]) / len(vell_lemmatized)
print(f'{average_sentiment=}')
# Vorbereiten des HTML-Strings
text_html: str = ""
for token in vell_lemmatized:
    style_html: str = f'style="background-color: {color_map[token._.sentiment]};"'
    token_html = f'<p class="form" {style_html}>{token.text}</p><p class="lemma" {style_html}>{token.lemma_}</p>'
    div_html: str = f'<div class="unit">{token_html}</div>'
    text_html += div_html
html_string: str = html_template.format(css_style, text_html)
html_string
# HTML- AUsgabe
HTML(html_string)

Reflexion#


Ergebnis (mögliche Interpretation)#

  • Die stärkere Grünfärbung des Velleius-Textes könnte darauf hindeuten, dass Velleius Paterculus die Beziehung zwischen Caesar, Pompeius und Crassus zur Zeit der Konferenz von Luca positiver darstellt als Sueton.

Methode#

  • Vorteile:

    • replizierbare Ergebnisse (Sprugnoli 2023, S. 54)

    • Aufgestellte Hypothesen zum Sentiment eines Textes lassen sich leichter hinsichtlich ihrer Gültigkeit für ein größeres Textkorpus überprüfen, als dies bei der händischen Bearbeitung möglich wäre.

    • Ermöglicht die Überprüfung bestehender Hypothesen und gleichzeitig das Auffinden neuer Forschungsfragen (explorativer Zugang).

  • Nachteile:

    • Es ist nicht leicht ersichtlich, wie das Sentimentlexikon erstellt wurde, d.h. welche Worte enthalten sind und wie diese annotiert wurden.

    • Bei einer lexikonbasierten Sentimentanalyse wird der Kontext (z.B. Wortstellung) vernachlässigt.

    • Die Qualität der Ergebnisse der Sentimentanalyse hängt u.a. auch von der Qualität der Datenvorverarbeitung ab (z.B. Lemmatisierung).

Ausblick#

  • Die Hypothese könnte anschließend durch ein Close Reading der Textstellen überprüft werden und mit anderen Texten verglichen werden (z.B. Sentiment-Vergleich zu anderen Texten derselben Autoren: Schreibt Sueton generell negativer als Velleius Paterculus? / mit anderen Historikern: Wie stellen Cassius Dio oder andere griechische Historiker die Beziehung dar? / über Gattungen hinweg: Wie stellt Cicero die Beziehung in seinen Briefen dar?)

Vertiefung/Übungsaufgaben (optional)#


  1. Welche Wörter hat der Algorithmus Ihrer Meinung nach mit einem falschen Sentiment versehen? Gibt es nicht annotierte Wörter, für die Sie einen Sentimentwert vergeben könnten?

  2. Öffnen Sie im Data-Ordner die Datei LatinAffectusv4.txt, um das Sentimentlexikon zu öffnen. Verändern Sie die Werte einiger Wörter, die in den Beispieltexten verwendetet werden (-1 bis +1). Wiederholen Sie dann alle Schritte und beobachten Sie die Veränderungen im Ergebnis.

  3. Ändern Sie die Farbe der positiven Sentimente von grün zu hellblau.

  4. Für welche Forschungsfragen eignet sich die Methode der computergestützten Sentimentanalyse besonders? Für welche eher weniger?

  5. Wie beeinflussen Negationspartikel die Analyse (z.B. nicht gut -> positives Label für gut, obwohl das in dem Kontext nicht stimmt)?

Zum Nachlesen#


  • Agri, Dalida. Reading Fear In Flavian Epic: Emotion, Power, and Stoicism. Oxford University Press, 2022.

  • Asgari, Ehsaneddin, Fabienne Braune, Benjamin Roth, Christoph Ringlstetter, and Mohammad Mofrad. “UniSent: Universal Adaptable Sentiment Lexica for 1000+ Languages.” In Proceedings of the Twelfth Language Resources and Evaluation Conference, edited by Nicoletta Calzolari et al. European Language Resources Association, 2020. http://www.lrec-conf.org/proceedings/lrec2020/pdf/2020.lrec-1.506.pdf.

  • Baccianella, Stefano, Andrea Esuli, and Fabrizio Sebastiani. “Sentiwordnet 3.0: An Enhanced Lexical Resource for Sentiment Analysis and Opinion Mining.” In Proceedings of the Seventh International Conference on Language Resources and Evaluation, edited by Nicoletta Calzolari et al. European Language Resources Association, 2010. http://lrec-conf.org/proceedings/lrec2010/pdf/769_Paper.pdf.

  • Braund, S. H. Beyond Anger: A Study of Juvenal’s Third Book of Satires. Cambridge University Press, 1988.

  • Braund, Susanna Morton, and Glenn W. Most. Ancient Anger: Perspectives From Homer to Galen. Cambridge University Press, 2003.

  • Burns, Patrick J. 2023. “LatinCy: Synthetic Trained Pipelines for Latin NLP.” arXiv Preprint arXiv:2305.04365. https://arxiv.org/pdf/2305.04365.pdf.

  • Holowchak, M. Andrew. Happiness and Greek Ethical Thought. Thoemmes Continuum, 2004.

  • Kazantzidis, Georgios, and Dēmos G. Spatharas. Memory and Emotions In Antiquity. De Gruyter, 2024.

  • Kim, Evgeny, and Roman Klinger. “A Survey on Sentiment and Emotion Analysis for Computational Literary Studies.” In Zeitschrift für digitale Geisteswissenschaften 4, edited by Forschungsverbund Marbach Weimar Wolfenbüttel. 2018.

  • Konstan, David. The Emotions of the Ancient Greeks: Studies In Aristotle and Classical Literature. University of Toronto Press, 2006.

  • Loehr, Regina M. Emotion and Historiography In Polybius’ Histories. Routledge, Taylor & Francis Group, 2024.

  • Min, Semi, and Juyong Park. “Modeling Narrative Structure and Dynamics with Networks, Sentiment Analysis, and Topic Modeling.” In PLoS ONE 14/12, edited by Thilo Gross. 2019. https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0226025.

  • Pavlopoulos, John, Alexandros Xenos, and Davide Picca. “Sentiment Analysis of Homeric Text: The 1st Book of Iliad.” In Proceedings of the Thirteenth Language Resources and Evaluation Conference, edited by Nicoletta Calzolari. European Language Resources Association, 2022. https://aclanthology.org/2022.lrec-1.765.pdf.

  • Sprugnoli, Rachele, Marco Passarotti, Daniela Corbetta, and Andrea Peverelli. “Odi et Amo. Creating, Evaluating and Extending Sentiment Lexicons for Latin.” In Proceedings of the Twelfth Language Resources and Evaluation Conference, edited by Nicoletta Calzolari. European Language Resources Association, 2020. https://aclanthology.org/2020.lrec-1.376.pdf.

  • Sprugnoli, Rachele. “Sentiment Analysis for Latin: A Journey from Seneca to Thomas Aquinas.” Zenodo, 2021. https://doi.org/10.5281/zenodo.4575431.

  • Sprugnoli, Rachele, Francesco Mambrini, Marco Passarotti, et Giovanni Moretti. “Sentiment Analysis of Latin Poetry: First Experiments on the Odes of Horace.” In Proceedings of the Eighth Italian Conference on Computational Linguistics, edited by Elisabetta Fersini, Marco Passarotti, and Viviana Patti. Accademia University Press, 2022. https://doi.org/10.4000/books.aaccademia.10854.

  • Sprugnoli, Rachele, Francesco Mambrini, Marco Passarotti, and Giovanni Moretti. “The Sentiment of Latin Poetry. Annotation and Automatic Analysis of the Odes of Horace.” In Italian Journal of Computational Linguistics 9, no. 9–1, edited by Roberto Basili and Simonetta Montemagni. Associazione Italiana di Linguistica Computazionale, 2023. https://www.academia.edu/download/108409966/10417.pdf.

  • Yeruva, Vijaya Kumari, Mayanka ChandraShekar, Yugyung Lee, Jeff Rydberg-Cox, Virginia Blanton, and Nathan A Oyler. “Interpretation of Sentiment Analysis in Aeschylus’s Greek Tragedy.” In Proceedings of the The 4th Joint SIGHUM Workshop on Computational Linguistics for Cultural Heritage, Social Sciences, Humanities and Literature, edited by Stefania DeGaetano, Anna Kazantseva, Nils Reiter, and Stan Szpakowicz. International Committee on Computational Linguistics, 2020. https://www.aclweb.org/anthology/2020.latechclfl-1.17.

  • Yeruva, Vijaya Kumari, Mayanka Chandrashekar, Yugyung Lee, Jeff Rydberg-Cox, Virginia Blanton, and Nathan A Oyler. “Interpretation of Sentiment Analysis with Human-in-the-Loop.” IEEE International Conference on Big Data (Big Data), Atlanta, GA, USA. IEEE, 2020.