Tupeln richtig ausgeben

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
rumoll
User
Beiträge: 12
Registriert: Sonntag 20. Juni 2021, 11:44

Hallo Zusammen,

im Zuge einer Aufgabe, bin ich dabei ein Skript zu schreiben mit dem ich eine Lemmatisierung von den Wörtern in einem Satz durchführe.
Die Sätze kommen aus Tupeln, welche sich in einer Liste befinden - zum Beispiel:

satz = [('Das ist ein super verspielter Satz', '01.01.2020'), ('Das ist ein zweiter super duschgeknallter Satz', '02.01.2020'),('Das ist ein dritter normal verrückter Satz', '03.01.2021')]

(Bitte nicht auf den Sinn der Texte achten :wink: )

Nun will ich mit meinem Code für jedes Tupel, die Adjektive und die Verben mit Ihrem Datum in einem separaten Tupel haben.

Code: Alles auswählen

liste_token_deu = []
for token in satz:
    for i in nlp(token[0]):
        if i.is_alpha:
            if not i.is_stop:
                if i.pos_ == 'VERB' or i.pos_ == 'ADJ':
                    text = (i.lemma_, token[1])
                    liste_token_deu.append(text)
print(liste_token_deu)
Das Problem dabei ist, dass wenn ich mit der zweiten For-Schleife auf das Tupel zugreife, zerschlage ich die Tupeln und bekomme eine Liste mit Tupeln zurück, bei denen nach jedem ADJ und VERB das Datum mit ausgegeben wird.
Wie zB:

[('super', '01.01.2020'), ('verspielt', '01.01.2020'), ('super', '02.01.2020'), ('duschgeknallter', '02.01.2020'), ('normal', '03.01.2021'), ('verrückt', '03.01.2021')]

Mein Ziel ist aber:
[('super', 'verspielt', '01.01.2020'), ('super', 'duschgeknallter', '02.01.2020'), ('normal', 'verrückt', '03.01.2021')]

Ich bin für jede Idee dankbar!

Vielen Dank für Eure Hilfe,
Russ
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Deine Variablennamen sind sehr schlecht. `satz` ist kein Satz, sondern eine Liste von Sätzen mit Datum. `token´ ist dann auch kein Token, sondern ein satz und ein datum.
`i` könnte man dann als Token bezeichnen.
Deine Zieldatenstruktur ist schlecht, weil Du eine Liste mit Adjektiven und ein Datum zusammen in ein Tuple packst, statt die Liste als Liste zu lassen.

Mit guten Namen, wird gleich viel deutlicher, wie der Code eigentlich aussehen müßte:

Code: Alles auswählen

tokens_mit_datum = []
for satz, datum in saetze_mit_datum:
    tokens = []
    for token in nlp(satz):
        if token.is_alpha and not token.is_stop:
            if token.pos_ in ['VERB', 'ADJ']:
                tokens.append(token)
    tokens_mit_datum.append((tokens, datum))
print(tokens_mit_datum)
rumoll
User
Beiträge: 12
Registriert: Sonntag 20. Juni 2021, 11:44

Vielen Dank für Deine Antwort Sirius3 und für die Anpassung des Skripts.
Ich habe tatsächlich sehr wenig Erfahrung mit Python, was Du bestimmt gemerkt hast.
Aber nun weiss ich mehr, wie meinen Code besser schreiben kann :)
rumoll
User
Beiträge: 12
Registriert: Sonntag 20. Juni 2021, 11:44

Hallo Sirius3,
hallo an alle,

ich würde gerne aus meiner Liste alle VERB, ADJ, Emoticons und Emojis extrahieren in einer separaten Liste speichern.
Hierzu habe ich folgenden Code

Code: Alles auswählen


satz = [('Das ist :-) ein super verspielter Satz :-D 🤔', '01.01.2020'), ('Das ist ein zweiter super duschgeknallter Satz 🙈', '02.01.2020'),('Das ist ein dritter normal :-( verrückter Satz 😊', '03.01.2021')]

def lemmas_mit_datum(quelle):
    tokens_mit_datum = []
    for text, datum in quelle:
        tokens = []
        emoticons = re.findall(r'[>]?[;:xB<][\']?[-]?[()DPO3S][)]?', text)  
        for words in text:
            for emojis in words:
                if emojis in emoji.UNICODE_EMOJI_ENGLISH:
                    continue
        for token in nlp_de(text):
            if token.is_alpha and not token.is_stop:
                if token.pos_ in ['VERB', 'ADJ']:
                    tokens.append(token.lemma_)
        if tokens == []:
            pass 
        else:
            tokens_mit_datum.append((tokens, emojis, emoticons, datum))
    return(tokens_mit_datum)
Der Code funktioniert, allerdings sieht das Ergebnis nicht unbedingt "gut" aus.
Da ich die emoticons in die liste token_mit_datum hinzufüge, gibt das Befehl dort wo es keine Emoticons findet, eine leere Liste raus.
wie:
[(['super', 'verspielt'], '🤔', [':-)', ':-D'], '01.01.2020'),
(['super', 'duschgeknallter'], '🙈', [], '02.01.2020'),
(['normal', 'verrückt'], '😊', [':-('], '03.01.2021')]

Letztes würde ich gerne umgehen wollen in dem ich Python sage, dort wo im Text keine Emoticons sind mache bitte nichts.
Ich habe an ein weiteres if-Statement gedacht, wie zB if emotcions: pass, damit komme ich aber auch nicht weiter.
Habt ihr eine Idee wie ich den Code optimieren kann?

Bin für jeden Vorschlag dankbar!
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Letztes würde ich gerne umgehen wollen in dem ich Python sage, dort wo im Text keine Emoticons sind mache bitte nichts.
Würde ich grundsätzlich eher nicht machen, weil deine generierte Datenstruktur dann uneinheitlich ist, sprich du hast Tupel unterschiedlicher Länge, was für die Weiter-verarbeitung ungünstig sein kann. IMHO besser wäre es, wenn du beim iterieren über das Tupel in der Weiterverarbeitung auf eine leere Liste prüfst und dann halt nichts machst.

Gruß, noisefloor
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@rumoll, ich schließe mich noisefloor an, aber wenn du unbedingt willst kannst du es so machen:

Code: Alles auswählen

if tokens and not emoticons:
    tokens_mit_datum.append((tokens, emojis, datum))
elif emoticons and not tokens:
    tokens_mit_datum.append((emojis, emoticons, datum))
else:
    tokens_mit_datum.append((tokens, emojis, emoticons, datum))
... oder jede beliebige Variante davon.

Übrigens, dieser Teil der Funktion macht nichts, kann also weg.

Code: Alles auswählen

for words in text:
    for emojis in words:
        if emojis in emoji.UNICODE_EMOJI_ENGLISH:
            continue
Diesen Teil kann man als list comprehension umschreiben:

Code: Alles auswählen

for token in nlp_de(text):
    if token.is_alpha and not token.is_stop:
        if token.pos_ in ['VERB', 'ADJ']:
            tokens.append(token.lemma_)
zu dem:

Code: Alles auswählen

tokens = [
    token.lemma_ for token in nlp_de(text)
    if token.is_alpha and not token.is_stop and token.pos_ in ['VERB', 'ADJ']
]
Alles ungetestet und ohne Gewähr
rumoll
User
Beiträge: 12
Registriert: Sonntag 20. Juni 2021, 11:44

Hallo und vielen Dank für Eure Kommentare.
@noisefloor, da ist ist schon was dran, denn das könnte tatsächlich zu einem späteren Problem führen.

@rogerb deine Vorschläge haben alle funktioniert :-)
Ich wusste nicht, dass man sich die Emojis auch ohne den Code ausgeben lassen kann.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Bei so vielen Elementen könnte man auch schon über ein `namedtuple` nachdenken.

Bei der Schleife für `emojis` sind die Namen wieder falsch und dadruch fällt vielleicht weniger auf wie furchtbar unsinnig der Code dort ist.

`words` steht nicht für Worte sondern für *ein* *Zeichen*, beziehungsweise einen Unicode-Codepoint.

Womit man dann in der innderen Schleife gleich sieht wie unsinnig das ist ``for emojis in character:`` zu schreiben. In *einem* Zeichen, kann es nicht *meherere* Emojis geben. Überhaupt eine Schleife über *ein* *Zeichen* macht überhaupt gar keinen Sinn.

Code: Alles auswählen

        for words in text:
            for emojis in words:
                if emojis in emoji.UNICODE_EMOJI_ENGLISH:
                    continue

        # `words` ist `character` =>

        for character in text:
            for emojis in character:
                if emojis in emoji.UNICODE_EMOJI_ENGLISH:
                    continue
        
        # innere Schleife ist sinnfrei =>
        
        for emojis in text:
            if emojis in emoji.UNICODE_EMOJI_ENGLISH:
                continue
Dann macht das ``if`` keinerlei Sinn, weil der nächste Schleifendurchlauf auch völlig ohne die Bedingung ausgeführt wird:

Code: Alles auswählen

        for emojis in text:
            pass
Und das ist einfach nur:

Code: Alles auswählen

        emojis = text[-1]
Also grundsätzlich einfach nur das letzte Zeichen vom `text`. Hier fällt dann noch ein Fehler auf: Wenn `text` eine leere Zeichenkette ist, dann kommt es hier zu einem `IndexError`, während es im Originalcode entweder einen `NameError` oder einen falschen Wert vom letzten Text gegeben hätte.

Was in beiden Fälllen falsch läuft, sind Emojis die aus mehr als einem Codepoint bestehen.

Code: Alles auswählen

TokensMitDatum = namedtuple("TokensMitDatum", "tokens emojis emoticons datum")


def lemmas_mit_datum(quelle):
    tokens_mit_datum = []
    for text, datum in quelle:
        emoticons = re.findall(r"\>?[;:xB<]'?-?[()DPO3S]\)?", text)
        emojis = text[-1]  # FIXME
        tokens = [
            token.lemma_
            for token in nlp_de(text)
            if token.is_alpha
            and not token.is_stop
            and token.pos_ in ["VERB", "ADJ"]
        ]
        if tokens:
            tokens_mit_datum.append(
                TokensMitDatum(tokens, emojis, emoticons, datum)
            )

    return tokens_mit_datum
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
rumoll
User
Beiträge: 12
Registriert: Sonntag 20. Juni 2021, 11:44

Hallo _blackjack_
vielen Dank für Deine Korrekturen und Erläuterungen.

Im Zuge etwaigen Anpassungen habe ich festgestellt, dass bevor ich mir die Lemmas ausgeben lasse, sollte ich überprüfen, wie die Dependencies zwischen den Wörtern sind.
Die Dependencies lasse ich mir mit spacy wie folgt ausgeben:

Code: Alles auswählen

if token.is_alpha and token.dep_ in ['ROOT'] or token.lemma_ == 'sein':
                tokens.append((token.text,[child for child in token.children]))
Ziel hierfür ist herauszufinden, ob ein Adjektiv positiv oder negativ gemeint ist - wie "glücklich" und respektive "nicht glücklich".
Ich habe viele Möglichkeiten ausprobiert, um die Verneinung mit einem Adjektiv zu verknüpfen und dieses dann als ein "token" ausgeben zu lassen, jedoch ist mir das nicht gelungen.
Demnach habe ich mein Skript so angepasst, dass ich mir die Dependencies von dem "ROOT" und dem Verb "sein" ausgeben lasse. Im Späteren verlauf, werde ich dann die Adjektiven herausfiltern lemmatisieren.

Nun habe ich den Code wie folgt angepasst:

Code: Alles auswählen

from collections import namedtuple
TokensMitDatum = namedtuple("TokensMitDatum", "tokens, emojis, emoticons, datum")

tokens_mit_datum = []
def lemmas_mit_datum(quelle):
    for text, datum in quelle:
        emoticons = re.findall(r"\>?[;:xB<]'?-?[()DPO3S]\)?", text)
        emojis = text[-1] 
        tokens = []
        for token in nlp_de(text):
            if token.is_alpha and token.dep_ in ['ROOT'] or token.lemma_ == 'sein':
                tokens.append((token.text,[child for child in token.children]))
       tokens_mit_datum.append(TweetsMitKoordinaten(tokens, season, emojis, emoticons, koordinaten))
    return tokens_mit_datum
Ich glaube, dass es mit Sicherheit einen eleganteren Weg gibt, jedoch funktioniert erstmal der Code so wie er soll (hoffe ich jedenfalls).

Nun würde ich den Part:

Code: Alles auswählen

tokens = []
for token in nlp_de(text):
	if token.is_alpha and token.dep_ in ['ROOT'] or token.lemma_ == 'sein':
                tokens.append((token.text,[child for child in token.children]))
als Comprehension List ausgeben.

Ich habe es versucht:

Code: Alles auswählen

        
tokens = [[token.text for token in nlp_de(text)
	if token.is_alpha and token.dep_ in ['ROOT'] and token.lemma_ == 'sein' 
                  != [token.text, [child for child in token.children]]
                 ]]
jedoch mit wenig Erfolg. Was mache ich falsch?
rumoll
User
Beiträge: 12
Registriert: Sonntag 20. Juni 2021, 11:44

Hallo Zusammen,

in Bezug auf das folgende Tupel habe ich eine Frage

Code: Alles auswählen

satz = [('Das ist :-) ein super verspielter Satz :-D 🤔', '01.01.2020'), ('Das ist ein zweiter super duschgeknallter Satz 🙈', '01.01.2020'), ('Das ist ein zweiter super duschgeknallter Satz 🙈', '02.01.2020'),('Das ist ein dritter normal :-( verrückter Satz 😊', '03.01.2021')]
Ich versuche mit der Schleife bzw. If-Abfrage

Code: Alles auswählen

cleanTupel = []

for text, date in satz:
    if text not in cleanTupel:
        cleanTupel.append((text, date))
den Satz oder den Eintrag, der doppelt vorkommt rauszunehmen und die übrigen Einträge in ein neues Tupel hinzufügen.
Wenn ich das neue neue Tupel nur mit dem Text, also cleanTupel.append(text) ausgeben lasse, dann habe ich drei Einträge (was ich erwarten würde), allerdings brauche ich auch noch das Datum, also cleanTupel.append((text, date)) dann habe ich aber alle Einträge wieder drin.
Demnach wie kann ich den Code so optimieren, so dass ich alle Sätze die doppelt vorkommen rausnehmen lassen kann und dem neuen cleanTupel bestehend aus Text und Datum habe?

Bin für jeden Vorschlag dankbar!
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du hast kein Tupel. Sondern eine Liste. Tupel kennen kein append. Und wenn du nur mit einem text nach Einträgen in deiner Liste sucht, die aber Tupel enthält, dann wird das nichts. Denn “Text” ist immer ungleich (“Text”, irgendwas).
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Da bietet sich ja ein Wörterbuch an:

Code: Alles auswählen

cleaned_text = {}
for text, date in satz:
    if text not in cleaned_text:
        cleaned_text[text] = date
narpfel
User
Beiträge: 643
Registriert: Freitag 20. Oktober 2017, 16:10

Und damit man sich das nicht immer wieder selbst schreiben muss, gibt es das schon fertig als `dict.setdefault`:

Code: Alles auswählen

cleaned_text = {}
for text, date in satz:
    cleaned_text.setdefault(text, date)
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das ist eventuell auch schon zu kompliziert, man könnte auch einfach nur schreiben:

Code: Alles auswählen

cleaned_text = {}
for text, date in satz:
    cleaned_text[text] = date

# oder dann auch gleich nur:

cleaned_text = dict(satz)
Falls es wichtig ist, dass das letzte gefundene Datum genommen wird, kann man da einfach noch ein `reversed()` mit rein werfen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten