Satz mit bestimmter Länge aus Text extrahieren

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.
AlinaPy
User
Beiträge: 9
Registriert: Sonntag 31. Juli 2022, 19:12

Guten Abend/Tag,

wie im Betreff stehend soll ich aus einem Text (falls jemand das Detail will, es handelt sich hierbei um "Alice im Wunderland") Sätze mit einer bestimmten Länge, nämlich genau 10 Wörtern (Kommas und Punkte zählen dabei nicht), entziehen. Python soll diese Sätze einzeln und zufällig ausgeben. Mir wurde als Tipp NLTK empfohlen, aber damit kenne ich mich noch nicht so gut aus und komme deswegen nicht weiter.

Das bisschen an Code was ich bisher habe lautet:

import nltk

nltk.corpus.gutenberg.fileids()
alice_sentences = nltk.corpus.gutenberg.sents('carroll-alice.txt')

Schon einmal vielen Dank falls einer weiter weiß!
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@AlinaPy: Was macht denn die erste Zeile nach dem Import? Was bewirkt die? Was passiert wenn man die weg lässt?

Was genau ist das Problem bei dem Du nicht weiter kommst? Du hast jetzt die Sätze. Davon sollen welche ausgegeben werden die bestimmte Bedingungen erfüllen. Die musst Du also ausfiltern. Dafür könntest Du beispielsweise eine Funktion schreiben, die einen Satz bekommt, und `True` zurück gibt, falls die Bedingung zutrifft, und `False` sonst. Damit lässt sich dann Code schreiben, der eine Liste erstellt, wo nur noch die Sätze drin stehen, welche die Bedingung erfüllen.

Und je nach dem was „einzeln und zufällig“ genau bedeuten soll, gibt es dann im `random`-Modul Werkzeuge um beispielsweise zufällig einen oder mehrere Sätze zu wählen, oder die Liste mit den Sätzen zu mischen, um sie dann alle nacheinander in einer zufälligen Reihenfolge ausgeben zu können.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
AlinaPy
User
Beiträge: 9
Registriert: Sonntag 31. Juli 2022, 19:12

Ich habe versucht mit deinen Denkanstößen zu arbeiten, stoße nur auf das Problem, dass ich nicht genau angeben kann, was ich nun filtern will. Bei Python gibt es ja nicht direkt "sentences" und die Sätze liegen ja nun erstmal in einer riesengroße Liste (in alice_sentences), sodass ich nicht als Bedingung schreiben kann, dass ein Satz eine Länge von 10 Wörtern haben soll.

Das mit dem random hatte ich mir schon gedacht, also bleibt mein Hauptproblem nun beim Filtern an sich.

Danke auf jeden Fall für deine Antwort, ich setze mich da weiter dran!
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@AlinaPy: Warum kannst Du die Bedingung nicht schreiben?

Programmieren bedeutet in der Regel Probleme in kleinere Teilprobleme zu zerlegen, und auch die Teilprobleme wieder in kleinere Teilprobleme zu zerlegen, solange bis die einzelnen Teilprobleme leicht mit einer Funktion in ein paar Zeilen gelöst werden können. Wenn man dann so eine Teillösung hat, und die getestet hat, kann man aus den kleinen Teillösungen grössere Teillösungen zusammensetzen. Wieder in Funktionen mit ein paar Zeilen Code. Wieder testen, und dann hat man am Ende eine Gesamtlösung.

Du könntest beispielsweise eine Funktion schreiben, die einen Eintrag aus `alice_sentences` als Argument bekommt, und die Anzahl der Wörter zurück gibt. Dazu muss unter anderem entschieden werden, ob ein Eintrag in einem Satz ein Wort ist, oder beispielsweise nur ein Komma oder ein Punkt. Dazu lohnt es sich mal die Methoden anzuschauen die Zeichenketten so bieten.

Und so ganz grundsätzlich sind Grundkenntnisse von Python und dem Umgang mit Sequenzen wie beispielsweise Listen notwendig. Falls da noch Nachholbedarf besteht, findet man in der Python-Dokumentation ein Grundlagentutorial, dass man mal durchgearbeitet haben sollte.

So richtig riesig ist die Liste mit den Sätzen IMHO nicht. 1.703 Elemente sind nicht wirklich viel. Da in den Worten weder Leerzeichen noch Zeilenendezeichen vorkommen, und es sich um einen reinen ASCII-Text handelt, kann man die Worte durch Leerzeichen trennen und die Sätze mit einem Zeilenendezeichen abschliessen, und auf einer 5¼" Diskettenseite für den Commodore 64 speichern. 🤓
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
AlinaPy
User
Beiträge: 9
Registriert: Sonntag 31. Juli 2022, 19:12

Ich bin ehrlich, deine Antworten und Tipps bringen mich nur dazu, mehr und mehr zu googlen, nachzulesen und tausend Lösungswege zu versuchen, die alle nicht klappen. So wirklich weiter komme ich durch deine Vorschläge nicht und deine Art so leicht von oben herab zu reden hilft mir dabei nicht.

Diese Sätze besitzen Anführungszeichen, Kommas, Ausrufezeichen und andere Satzzeichen, bei denen ich sehr wohl weiß, dass ich dafür nochmal eine spezielle Funktion braucht, die diese nicht mitzählt. Aber egal ob über regex, mit dem gutenberg corpus oder ganz komplizierten Kombination aus sum(), split() und wer weiß sonst noch was, keines ist 100% das, was ich brauche.

Dieses Forum war meine letzte Hoffnung nach stundenlangen, tagelangem überlegen, ausprobieren und googlen...
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dieses Forum ist die letzte Hoffnung von hunderten Schuelern, Studenten und anderen Leuten, die irgendwelche Hausaufgaben bekommen, und denken, dass man ihnen die hier prompt & ohne Eigenleistung hinstellt. Und wir haben uns hier entschlossen, dass nicht zu tun. Wir glauben an Hilfe zur Selbsthilfe, nicht an vorkauen. Das kannst du doof finden, aber so ist es nunmal. Ich bin sicher dein Dozent kann das Problem auch selbst loesen, aber aus irgendeinem Grund glaubt der oder die ja auch daran, dass *du* es probieren sollst. Wir auch.

Wenn du konkretere Hilfe willst, dann zeig konkreteren Code. Du erzaehlst, was du alles ausprobiert hast, in aehnlich vielen Worten wie __blackjack__ - aber ich sehe davon nix. Was konkret hast du probiert? Wie sieht ein Satz aus, der gueltig ist, und wie ein Versuch, ein Praedikat zu schreiben, dass den identifiziert? Was liefert das stattdessen? Dann kann man dazu auch Stellung beziehen, und darauf hinweisen, was genau da nicht passt, wo Denkfehler sind, etc.
AlinaPy
User
Beiträge: 9
Registriert: Sonntag 31. Juli 2022, 19:12

Ich habe keinen Code kommentiert, da ich genau weiß und ja gemerkt habe, dass er nicht mal ansatzweise funktioniert und damit 100% falsch ist. Diese Frage betrifft vielleicht 5 % von meinen eigentlichen Aufgaben, ist aber der einzige Teil bei dem ich eben nicht weiter weiß.

Ich habe mit gutenberg.words und gutenberg.sents die Anzahl der Sätze und Wörter des Textes bekommen, habe versucht herauszufinden, wie man Wörter in Sätzen zählt, ohne das Python Kommas und ähnliches mitzählt (bin dabei dann auf regex, u.a. gestoßen, deren Beispiel Code aber immer ein selbstgeschriebener String war und keine Liste mit mehreren Listen).

Meine einzige Überlegung, von der ich bis jetzt glaube, dass sie funktionieren könnte, ist, dass über eine for-Schleife alle knapp 1700 Sätze des Textes (man könnte da dann alice_sentences[n] und n = 1 bis 1700 benutzen, dass eben jeder Satz da einmal durchläuft) eben nach der Wörter Anzahl filtern lässt. Das Problem ist nun wie gesagt das Filtern, oder eher gesagt, wie Python meine einzelnen Sätze/Listen nun zählen kann.

Ich erwarte keine Komplettlösung, was ich auch nie geschrieben habe, sondern hatte auf Empfehlungen von bestimmten Funktionen gehofft.

Ich stöbere schon seit Tagen auf deutsch und auf englisch rum, habe auch hier im Forum mit mehreren verschieden Suchbegriffen erst einmal geschaut, ob andere ähnliche Probleme hatten/Hinweise suchten. Und genau aufgrund solcher Kommentare hatte ich erst gar nicht vor, hier meine Frage zu stellen...
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die magische Funktion zum zaehlen von Woertern in einer Liste heisst "len".

Code: Alles auswählen

for satz in saetze:
    if len(satz) == 10: # zu simpel, aber fuer den Anfang 
        print(satz)
Viel Erfolg, die Lust, mir diese bestaendigen Vorwuerfe, wie boese wir doch sind, anzuhoeren, ist mir vergangen.
AlinaPy
User
Beiträge: 9
Registriert: Sonntag 31. Juli 2022, 19:12

[Update für mich, falls ich diesen Code nochmal komplett umschmeiße, dieser dann aber doch besser funktioniert.]


import re
import nltk

nltk.corpus.gutenberg.fileids()
alice_sentences = nltk.corpus.gutenberg.sents('carroll-alice.txt')
list_10 = [ ]



#### Errechung der Sätze insgesamt: num_sents = len(alice_sentences) ---->>> insgesamt 1703 Sätze

#### Errechnung der Wörteranzahl eines Satzes: num_words = len(re.findall(r '\w+' , alice_sentences[n]))) ---->>> bei n kann dann Satz 1-1703 bzw Position 0-1702 der Liste rein


#### ab hier hängt es, müsste nun das alles Filtern

for i in alice_sentences
num_words = len(re.findall(r '\w+', alice_sentences[n])))
if num_words == 10
list_10.append(alice_sentences[n])

---> da gibt´s eine Fehlermeldung. Tüftel an dieser Stelle weiter.
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und oh Wunder oh Wunder, wenn man konkreten Code sieht, kann man konkret benennen, was falsch ist.

for i in alice_sentences

enumeriert dir in i bereits den Satz als Liste von Woertern. i ist damit ein schlechter Name, weil es einen Index suggeriert, obwohl es eine Liste mit Woertern ist. Also sollte i stattdessen - wie in meinem Beispiel - "satz" sein. Ohne Anfuehrungszeichen natuerlich.

Dein re.findall ist Quatsch, denn es arbeitet auf einem String. Hast du aber nicht. Du hast eine Liste mit Strings, alle Woerter + Sattzeichen in einem Satz. Kein Grund, da mit regulaeren Ausdruecken nochmal drin rumzufuhrwerken, das *ist* ja schon getrennt. Es reicht, die Laenge direkt mit len(satz) zu bestimmen. Und in deinem Ausdruck kommt ein n vor, das es nicht gibt. Das sollte wohl mal ein i sein, und dann ist es immer noch falsch, denn i ist ja - wie schon erwaehnt - keine Zahl.

Zu guter Letzt: list_10.append(...) kracht natuerlich auch, weil es kein n gibt. Aber es gibt ja satz, wenn man es richtig macht.
AlinaPy
User
Beiträge: 9
Registriert: Sonntag 31. Juli 2022, 19:12

Da mir die Aufgabe gerade über meinem Kopf zusammen kracht, beschränke ich meine Frage nur auf einen Teil:
len() will ich alleine ohne re.findall nicht nutzen, da ich Sätze mit 10 Wörtern brauche und da die Satzzeichen und Kommas nicht reinzählen dürfen. Len() zählt das ja alles mit und verfälscht somit meine Satzauswahl.

Da ich ja alle Sätze durchforsten muss und nach ihrer Wortanzahl überprüfe fand ich die for Schleife am elegantesten, aber da funktioniert halt einfach nichts. n kam daher, da ich dadurch bei der for Schleife bestimmen wollte, welcher Satz/Liste gerade bei len(re.findall) angeschaut wird, aber da taucht natürlich ein Fehler auf.
Benutzeravatar
sparrow
User
Beiträge: 4184
Registriert: Freitag 17. April 2009, 10:28

Wenn du einen Satz nach Leerzeichen trennst, wo spielen denn da die Satzzeichen eine gewichtige Rolle?
AlinaPy
User
Beiträge: 9
Registriert: Sonntag 31. Juli 2022, 19:12

Die Trennung ist hierbei nicht wichtig. In den Sätzen/Listen tauchen Satzzeichen wie eben Kommas, Apostrophen, Ausrufezeichen und anderes vor (halt auch als Strings wie die normalen Wörter). Diese dürfen nicht mitgezählt werden, was die Funktion len() leider macht.
nezzcarth
User
Beiträge: 1633
Registriert: Samstag 16. April 2011, 12:47

AlinaPy hat geschrieben: Montag 1. August 2022, 15:04 Diese Sätze besitzen Anführungszeichen, Kommas, Ausrufezeichen und andere Satzzeichen, bei denen ich sehr wohl weiß, dass ich dafür nochmal eine spezielle Funktion braucht, die diese nicht mitzählt. Aber egal ob über regex, mit dem gutenberg corpus oder ganz komplizierten Kombination aus sum(), split() und wer weiß sonst noch was, keines ist 100% das, was ich brauche.
Dafür verwendet man einen Tokenizer, der die einzelnen Bestandteile des Textes voneinander trennt und oft auch klassifiziert. NLTK hat eine Reihe verschiedener solcher Tokenizer, die jeweils verschiedene Konzepte umsetzen, bereits dabei: https://www.nltk.org/api/nltk.tokenize.html. Die Methode "sents", die du verwendest, ist ein Shortcut, mit dem die Tokenisierung bereits für dich mit einem vorausgewählten Tokenizer durchgeführt wird. Ein Nachteil der NLTK-Tokenizer gegenüber anderen ist, dass du nur die Tokens jedoch nicht die Art des Tokens zurückgeliefert bekommst. Diese musst du selbst bestimmen. Neben RegEx, die du ja schon angesprochen hast, geht das auch mit einigen der eingebauten String-Funktionen von Python, etwa 'isalpha()'. Zudem sind List Comprehensions hier sehr hilfreich: https://docs.python.org/3/tutorial/data ... rehensions
Hier mal ein Anfang mit NLTK:

Code: Alles auswählen

In [1]: import nltk
s
In [2]: sentences = nltk.corpus.gutenberg.sents('carroll-alice.txt')

In [3]: for sentence in sentences:
   ...:     words = [token for token in sentence if token.isalpha()]
   ...:     print(words, len(words))
   ...:     _ = input()
   ...: 
['Alice', 's', 'Adventures', 'in', 'Wonderland', 'by', 'Lewis', 'Carroll'] 8

['CHAPTER', 'I'] 2

['Down', 'the', 'Rabbit', 'Hole'] 4
Hier sieht man auch schon gleich einen Sonderfall, den man bedenken muss: Der Genitiv wird als eigenes Token gewertet (sowie der "verschluckte" Apostroph). Falls das nicht gewünscht ist, muss du die Tokenisierung selbst durchführen, mit angepassten Regeln. Dazu kannst du dir den Text mit "raw" zurückgeben lassen und mit einem geeigneten Tokenizer selbst zerlegen.

Hier ein äquivalenter Ansatz mit einer anderen Bibliothek:

Code: Alles auswählen


In [1]: from somajo import SoMaJo

In [2]: tokenizer = SoMaJo("en_PTB")

In [3]: sentences = tokenizer.tokenize_text_file('./nltk_data/corpora/gutenberg/carroll-alice.txt', paragraph_separator="empty_lines")

In [4]: for sentence in sentences:
   ...:     words = [token for token in sentence if token.token_class == "regular"]
   ...:     print(' '.join(word.text for word in words), len(words))
   ...:     _ = input()
   ...: 
Alice 's Adventures in Wonderland by Lewis Carroll 8

CHAPTER Down the Rabbit Hole 5

Alice was beginning to get very tired of sitting by her sister on the bank and of having nothing to do once or twice she had peeped into the book her sister was reading but it had no pictures or conversations in it and what is the use of a book thought Alice 'without pictures or conversation ' 58
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

AlinaPy hat geschrieben: Montag 1. August 2022, 18:06 Die Trennung ist hierbei nicht wichtig. In den Sätzen/Listen tauchen Satzzeichen wie eben Kommas, Apostrophen, Ausrufezeichen und anderes vor (halt auch als Strings wie die normalen Wörter). Diese dürfen nicht mitgezählt werden, was die Funktion len() leider macht.
Es wäre in wichtiger erster Schritt, es trotzdem so zu implementieren, denn dann steht das Gerüst drumrum. Und wenn das steht, baut man eine Funktion gueltiger_satz, die eine Liste aus deinem Corpus bekommt, und bestimmt, ob der gilt. Und in dieser Funktion bereinigt man dann die Liste von Allen nicht-wort-Bestandteilen, und zählt den Rest. Ersetzt das len damit, und die Aufgabe ist gelöst.
AlinaPy
User
Beiträge: 9
Registriert: Sonntag 31. Juli 2022, 19:12

@nezzcarth

Irgendwie wird mir meine erste Antwort nicht angezeigt, also schreibe ich vorsichtshalber nochmal:

Danke für deine Hilfe, endlich funktioniert hier was bei mir!

Ich habe den ersten Code mal ausprobiert und meiner Aufgabe entsprechend erweitert:

for sentence in alice_sentences:
words = [token for token in sentences if token.isalpha()]
if len(words) == 10:
list_10.append(words) #### ich hatte mir eine leere Liste angelegt, um die Sätze dadrin zu speichern

Das hat zum Glück geklappt! Nur ist das Fehlen von Apostrophen, und damit auch Kommas und anderen Satzzeichen, was du schon angesprochen hattest, für das weiterführen der Aufgabe schwierig. Denn obwohl nur die richtigen Wörter gezählt werden dürfen, brauche ich die Sätze mit ihren Satzzeichen.

Ein riesen Dankeschön! Auch mit dem kleinen Mankel kann ich damit endlich erstmal weitermachen.
Benutzeravatar
sparrow
User
Beiträge: 4184
Registriert: Freitag 17. April 2009, 10:28

@AlinaPy: Also entweder stehe ich völlig auf dem Schlauch, oder wir haben unterschiedliche Vorstellungen davon, was Leerzeichen tun.

Wie viele Worte hat deiner Meinung nach der Satz: „Der Mann in Schwarz floh durch die Wüste, und der Revolvermann folgte ihm.“ ?
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du den ganzen satz brauchst, dann pack den doch in deine Liste. Statt der gefilterten reinen wort Liste.
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sparrow: Es gibt keine Leerzeichen in den Daten. Dein Satz würde da so stehen: ``["Der", "Mann", "in", "Schwarz", "floh", "durch", "die", "Wüste", ",", "und", "der", "Revolvermann", "folgte", "ihm", "."]``. Das zu lösende Problem ist eine Funktion welche die Worte zählt, also nicht einfach `len()`, sondern in diesem Beispielsatz ohne das Komma und den Punkt. Und um Alice-Text kommen dann noch ein paar andere Satz und Sonderzeichen vor. Bindestriche, Semikolons, Anführungsstriche, Klammern, und so, die auch nicht gezählt werden sollten. nezzcarth hat da ja schon das `isalpha()` zum Filtern gezeigt auf das ich hinaus wollte als ich empfahl die Methoden zu studieren die Zeichenketten so bieten.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
sparrow
User
Beiträge: 4184
Registriert: Freitag 17. April 2009, 10:28

@__blackjack__: Das steht aber nur so ind en Daten, weil bereits etwas mit den Daten gemacht wurde. Die Origianldatei ist - wenn ich den Dateinamen richtig interpretire, ein reines Textdokument. Da stellt sich die Frage, ob man da wirklich eine komplexe Bibliothek drauf werden muss um die Anzahl der Wörter in den Sätzen zu ermitteln. Wenn natürlich nachträglich Dinge gemacht werden sollen ist das möglicherweise etwas anderes - aber für das reine zählen von Wörtern in enem Satz wäre mir persönlich das zu viel Kanonen auf Spatzen.

Kompliziert wird es bei Wörtlicher Rede mit eingeschobenem Begleitsatz oder Haupt- und Nebensätze bzw. ählichen Konstrukten. Da muss man dann vorweg einmal definieren, was eigentlich ein "Satz" ist.
Antworten