Sentimentanalyse mit Senti Ws

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Vesemir
User
Beiträge: 1
Registriert: Montag 6. April 2026, 16:39

Hallo :) ich möchte Reden mit einer Sentimentanalyse auswerten (SentiWS - https://www.kaggle.com/datasets/sibeliu ... ositiv.csv). Ich habe schon Code und möchte gerne wissen, ob und wie ich den reduzieren kann, da ich das Gefühl habe, dass der Code viel zu umständlich ist. Ich möchte als Ergebnis haben, ob das Sentiment je Rede eines Sprechers negativ (negative Zahl), positiv (positive Zahl)oder neutral (0) ist (Werte werden in Zahlen angegeben). Das habe ich bereits:

files = [ Rede.txt]
data = []

for file in files:

with open(file, encoding="utf-8") as f:

text = f.read()

year, speaker = file.replace(".txt","").split("_", 1)

data.append({

"Jahr": int(year),

"Sprecher": speaker,

"Text": text

})

Neujahrsansprachen = pd.DataFrame(data)

Neujahrsansprachen.head()

import pandas as pd

import re

import nltk

from nltk.corpus import stopwords

# Stopwörter laden

nltk.download("stopwords")

german_stop_words = set(stopwords.words("german"))

# SentiWS laden

def load_sentiws_simple(path):

df = pd.read_csv(path, sep=None, engine="python", header=0)

senti = {}

for _, row in df.iterrows():

word = str(row[1]).lower()

score = float(row[2])

senti[word] = score

return senti

senti_neg = load_sentiws_simple("SentiWS_ML_negativ.csv")

senti_pos = load_sentiws_simple("SentiWS_ML_positiv.csv")

senti_dict = {**senti_neg, **senti_pos}

print("Wörter im Lexikon:", len(senti_dict))

# ---------------------------------------------------------

# Tokenisierung

# ---------------------------------------------------------

def tokenize(text):

words = re.findall(r"[a-zA-ZäöüÄÖÜß]+", text.lower())

return [w for w in words if w not in german_stop_words]

Neujahrsansprachen["Tokens"] = Neujahrsansprachen["Text"].apply(tokenize)

# Sentiment pro Rede

def sentiws_score(tokens):

return sum(senti_dict.get(w, 0) for w in tokens)

Neujahrsansprachen["SentiWS_Score"] = Neujahrsansprachen["Tokens"].apply(sentiws_score)

# Sentiment pro Sprecher

sentiment_by_speaker = (

Neujahrsansprachen

.groupby("Sprecher")["SentiWS_Score"]

.mean()

.sort_values(ascending=False)

)

sentiment_by_speaker
Sirius3
User
Beiträge: 18399
Registriert: Sonntag 21. Oktober 2012, 17:20

Code postet man hier in code-Tags </>. So wie jetzt ist der Code unlesbar.

Importe stehen immer am Anfang der Datei, dann folgen Konstanten und Funktionsdefinitionen.
Das Hauptprogramm steht auch in einer Funktion, die üblicherweise `main` genannt wird.
Variablennamen werden komplett klein geschrieben.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1339
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Ohne NLTK

Code: Alles auswählen

#!/usr/bin/env python3

import csv
import pickle
import sys

from functools import cache
from pathlib import Path


@cache
def load_word_lists(pos_list_file, neg_list_file):
    file_cache = Path.home().joinpath(".cache", "positive_negative_word_list.pickle")

    if file_cache.exists():
        with file_cache.open("rb") as fd:
            return pickle.load(fd)

    results = []
    for file in (pos_list_file, neg_list_file):
        with open(file, encoding="utf8", newline="") as fd:
            reader = csv.reader(fd)
            data = {}

            for _, word, value in reader:
                try:
                    value = float(value)
                except ValueError:
                    continue
                else:
                    data[word.lower()] = value

            results.append(data)

    with file_cache.open("wb") as fd:
        pickle.dump(results, fd)

    return results


def check(text, positive_words, negative_words):
    result = 0.0

    for word in text.split():
        result += positive_words.get(word.lower(), 0)
        result += negative_words.get(word.lower(), 0)

    return result


if __name__ == "__main__":
    pos_word_list = Path.home().joinpath("Downloads/SentiWS_ML_positiv.csv")
    neg_word_list = Path.home().joinpath("Downloads/SentiWS_ML_negativ.csv")
    pos, neg = load_word_lists(pos_word_list, neg_word_list)
    text = sys.stdin.read()
    result = check(text, pos, neg)
    print(result)

Kann man dann so ausführen:

echo "Guten Tag" | python check.py
0.3716

Ggf. kann man das Ergebnis verbessern, wenn man str.casefold verwendet. Wörter, die nicht im positive- oder negative-dict vorkommen, liefern eine 0 (get Methode).

NLTK kann einem helfen Wörter aus dem Text zu filtern, was aber nicht notwendig ist, da nicht vorhandene Wörter als 0 zählen.


Anstatt die Textdatei mit einer Pipe zu übergeben, könnte man sie auch in Python direkt laden.
Der Datei-Cache ist bei so wenig Daten nicht notwendig. Habs trotzdem mal in die Funktion eingebaut, um das Prinzip zu zeigen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 18399
Registriert: Sonntag 21. Oktober 2012, 17:20

@DeaD_EyE: der halbe Code beschäftigt sich nur mit Caching, was wie Du selbst sagst, nicht notwendig ist. Zudem muß man dann wissen, dass man die Cache-Daten löschen muß, wenn man andere Scores benutzen möchte.
Eine Funktion sollte eine Sache machen, `load_word_lists` liest dagegen zwei Dateien.
Fehler einfach zu ignorieren ist selten eine gute Idee. Statt den ValueError zu ignorieren mußt Du nur die Header-Zeile explizit einlesen.
`text.split()` hat das Problem, dass alle Wörter am Ende von Sätzen, Aufzählungen, etc. bei der Analyse ignoriert werden.
Das Hauptprogramm steht üblicherweise in einer Funktion `main`.
Um zu vermeiden, dass bei vielen Dateien die Listen immer wieder gelesen werden, könnte man ja erlauben, viele Dateinamen per Argument zu übergeben.

Code: Alles auswählen

#!/usr/bin/env python3
import csv
import re
import sys

POSITIVE_WORD_SCORE_FILE = "Downloads/SentiWS_ML_positiv.csv"
NEGATIVE_WORD_SCORE_FILE = "Downloads/SentiWS_ML_negativ.csv"

def load_word_list(filename):
    with open(filename, encoding="utf8", newline="") as file:
        reader = csv.reader(file)
        _header = next(reader)
        return {
            word: float(score)
            for _, word, score in reader
        }


def calulate_score(text, positive_word_scores, negative_word_scores):
    return sum(
        positive_word_scores.get(word, 0)
        + negative_word_scores.get(word, 0)
        for word in re.findall(r"\w+", text)
    )


def main():
    positive_word_scores = load_word_list(POSITIVE_WORD_SCORE_FILE)
    negative_word_scores = load_word_list(NEGATIVE_WORD_SCORE_FILE)
    for filename in sys.args[1:]:
        with open(filename, encoding="utf8") as file:
            text = file.read()
        print(calulate_score(text, positive_word_scores, negative_word_scores), filename)


if __name__ == "__main__":
    main()
Benutzeravatar
DeaD_EyE
User
Beiträge: 1339
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Sirius3 hat geschrieben: Mittwoch 8. April 2026, 10:22 @DeaD_EyE: der halbe Code beschäftigt sich nur mit Caching, was wie Du selbst sagst, nicht notwendig ist. Zudem muß man dann wissen, dass man die Cache-Daten löschen muß, wenn man andere Scores benutzen möchte.
Stimmt, der Cache würde nie überschrieben werden. Es wird auch nicht geprüft, ob der Cache älter ist, als eine der beiden Eingabedateien.
Eine Funktion sollte eine Sache machen, `load_word_lists` liest dagegen zwei Dateien.
Genau genommen macht die Funktion mehr. Cache lesen, wenn vorhanden, falls nicht vorhanden, Dateien lesen und Cache schreiben.

Fehler einfach zu ignorieren ist selten eine gute Idee. Statt den ValueError zu ignorieren mußt Du nur die Header-Zeile explizit einlesen.
Ich war zu faul mir den Inhalt der Datei anzusehen.
`text.split()` hat das Problem, dass alle Wörter am Ende von Sätzen, Aufzählungen, etc. bei der Analyse ignoriert werden.

Code: Alles auswählen

re.sub(r"\W", "", "§$%&ÜberMirkowelle$%&$/&%/%")
'ÜberMirkowelle'

Btw. wenn ich morgens auf einen Beitrag antworte, ist das meist vor der Arbeit. Heute hatte ich nur ein paar Minuten.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten