LastBoyScout: Top10 der Wörter eines String

Hallo,

Mit der Funktion str_word_count() ist es ja möglich die Anzahl der Wörter (inkl. Satzzeichen) eines String zu Zählen.

 $str = "Wo kämen wir hin? wenn jeder sagen würde wo kämen wir hin! aber keiner ginge um zu schauen, wo wir hinkommen, wenn wir hin gingen!";  
  
echo str_word_count($str); //30

Aber könnte man mit PHP auch die Wiederholungen innerhalb eines String ermitteln? um quasi eine Top10 (oder besser TopX) der am häufigsten vorkommenden Wörter zu erhalten?

Array  
(  
    [wir] => 4  
    [hin] => 3  
    [kämen] => 2  
    [wo] => 2  
    //usw.  
)

Eine Suchfunktion kommt hier ja nicht in Frage, da ein Suchbergriff ja vorher nicht definiert werden kann.

  1. str_word_count() ist nett, aber völlig unbrauchbar, wenn es um das zählen von Wörtern in Sprachen mit "komplexen Zeichensätzen" geht. Es ist etwas schwierig in "charlist" mehrere 10.000 Zeichen aufzunehmen ;)

    Da ist es wesentlich hilfreicher, einen String per PCRE mit mittels Unicode Properties zu zerlegen.

    /\p{L}+/u ist als Suchmuster z.B. geeignet, ohne Unicode-Support ist /\w+/ eine option, die afaik auch mehr abdeckt als ein "nacktes" str_word_count().

    Aber könnte man mit PHP auch die Wiederholungen innerhalb eines String ermitteln? um quasi eine Top10 (oder besser TopX) der am häufigsten vorkommenden Wörter zu erhalten?

    Ja.

    Eine Suchfunktion kommt hier ja nicht in Frage, da ein Suchbergriff ja vorher nicht definiert werden kann.

    array_count_values() ist die von dir gesuchte Version, die kurz Beschreibung in der Liste der Array-Funktionen sagt "Zählt die Werte eines Arrays", da hättest du beim durchsehen dieser auch dahinterkommen können.

    1. array_count_values() ist die von dir gesuchte Version, die kurz Beschreibung in der Liste der Array-Funktionen sagt "Zählt die Werte eines Arrays", da hättest du beim durchsehen dieser auch dahinterkommen können.

      array_count_values(); hab ich mir auch schon angeschaut, aber es bezieht sich eben auf ein Array und nicht auf einen String!

      Natürlich könnte man den String anhand der Leerzeichen auch in ein Array überführen, aber ob dies für einen String mit ca. 30.000 Wörtern eine sinnvolle Übung ist!?

      Eventuell habe ich es mit meinem kurzen Beispiel nicht richtig rüber gebracht (Sorry), aber es geht hier eben um umfangreiche Texte...

      1. Hallo,

        array_count_values(); hab ich mir auch schon angeschaut, aber es bezieht sich eben auf ein Array und nicht auf einen String!

        ja und?

        Natürlich könnte man den String anhand der Leerzeichen auch in ein Array überführen, aber ob dies für einen String mit ca. 30.000 Wörtern eine sinnvolle Übung ist!?

        Nicht nur sinnvoll, sondern IMHO erforderlich. Denn wenn du die Wörter im String nach Häufigkeit beurteilen willst, wirst du ohne eine Wortliste (->Array!) vermutlich nicht auskommen.
        Ob du dieses Array nun mit explode(), mit der von suit vorgeschlagenen RegEx-Methode oder mit einem eigenen Algorithmus erzeugst, ist eine andere Frage.

        Eventuell habe ich es mit meinem kurzen Beispiel nicht richtig rüber gebracht (Sorry), aber es geht hier eben um umfangreiche Texte...

        Das war schon klar, denn die "top ten" aus einem einzelnen Satz zu bestimmen, wäre nicht so fürchterlich sinnvoll. ;-)

        Ciao,
         Martin

        --
        Wenn man keine Ahnung hat - einfach mal Fresse halten.
          (Dieter Nuhr, deutscher Kabarettist)
      2. array_count_values(); hab ich mir auch schon angeschaut, aber es bezieht sich eben auf ein Array und nicht auf einen String!

        Ja und str_word_count() liefert richtig angewandt ein Array - aber eben sehr eingeschränkt, darum mein hinweis, dass es bessere Methoden gibt, um "Wörter" in einem Text in ein Array zu überführen.

        Natürlich könnte man den String anhand der Leerzeichen auch in ein Array überführen, aber ob dies für einen String mit ca. 30.000 Wörtern eine sinnvolle Übung ist!?

        Ein Leerzeichen ist keineswegs ein sinnvoller Wortbegrenzer.

        Was
        ist mit solchen
        Texten?

        da hättest du

        "Was\nist" als "Wort ermittelt.

        Eventuell habe ich es mit meinem kurzen Beispiel nicht richtig rüber gebracht (Sorry), aber es geht hier eben um umfangreiche Texte...

        Du hast du - nur du hast meine antwort nicht verstanden und wahrscheinlich auch die Doku von str_word_count() garnicht gelesen.

        1. Du hast du - nur du hast meine antwort nicht verstanden und wahrscheinlich auch die Doku von str_word_count() garnicht gelesen.

          Du du hast recht, habs aus dem Gedächtniss geholt und ganz vergessen, das man damit auch zwei verschiedene Array erzeugen kann.

          Angesichts der Textmenge ist es wohl aber trotzdem eher ungeeignet oder?

          1. Angesichts der Textmenge ist es wohl aber trotzdem eher ungeeignet oder?

            Das ist sicher nicht das Problem - die mangelnde Eignung stellt eher die Art des erkennens eines Wortes dar.

  2. Du willst vermutlich aus einem Fließtext eine Tagwolke erstellen oder Keywords automatisch ermitteln.

    Ich hab' bereits etwas entsprechendes geschrieben - in den letzten Monate ist die Sache gewachsen.

    Allein für die Generierung der x häufigsten bzw. wichtigsten Wörter in einem Fließtext sind bereits 7 KiB an PHP (etwa 200 Zeilen) entstanden (natürlich mit Kommentaren). Die komplette Klasse mit allen notigen Files hat etwa 25 KiB - wenn dein Vorhaben also ähnlich flexibel sein soll, helfen dir möglicherweise folgende Überlegungen weiter. Wenn man damit erstmal beginnt, stößt man eben unweigerlich auf sehr viele Dinge die beachtet werden müssen:

    • HTML entfernen - mit strip_tags() geht das nicht, das muss schlauer gemacht werden - je nach Anforderung müssen z.B. bestimmte Attribute (title) behalten werden und in die Wortliste aufgenommen werden.

    • Stopwords müssen möglicherweise entfernt werden, allerdings muss dabei beachtet werden, ob es sich tatsächlich um ein Stopword der definierten Sprache handelt oder nicht. "die" ist in deutscher Sprache in Stopword, <span lang="en">die</span> in einem deutschsprachigen Text allerdings nicht.

    • Möglicherweise müssen bestimmte Dinge gewichtet werden - ein "<p><strong>foo</strong></p>" muss bei gleicher Häufigkeit weiter vorne in der Liste stehen als ein "<p>bar</p>"

    • zusammengehörige Dinge wie ein Datum muss ggf. als Wort betrachtet werden (in verschiedenen Formaten, jeweils wieder sprachabhängig).

    1. Du willst vermutlich aus einem Fließtext eine Tagwolke erstellen oder Keywords automatisch ermitteln.

      Es geht darum, das ich aus einem reinen Fließtext (ohne HTML) Schlüsselwörter ermitteln will, mit deren Hilfe ich diesen anschließend unterteilen und die erzeugten Abschnitte in eine Datenbank überführen könnte.

      Aber ich merke schon, das dies mit PHP nicht so leicht und auf die schnelle umsetzbar ist wie gehofft, so das ich es wohl anders lösen muss.

      1. Aber ich merke schon, das dies mit PHP nicht so leicht und auf die schnelle umsetzbar ist wie gehofft,

        Das Problem ist immer dasselbe - es ist ein logisches Problem, kein technisches. Die Lösung mit PHP selbst ist trivial.

        Eine dreckige Variante umfasst 4 Zeilen:

        1. Zeile: String in Wörter zerlegen (per str_word_count(), preg_match_all(), explode() oder wie auch immer)
        2. Zeile: Wörter zählen mit array_count_values()
        3. Zeile: das Array mit arsort() sortieren
        4. Ziele: mit array_splice() die gewünschte Menge abschneiden.

        Besonders Zeile 1 lässt sich aber auf ungeahnte Größen aufblasen, wenn es darum geht, zu bestimmen, was ein Wort ist - aber egal in welcher Sprache du das machst, es wird ein logisches Problem bleiben - kein technisches.

        so das ich es wohl anders lösen muss.

        Wie du meinst.

        1. Das Problem ist immer dasselbe - es ist ein logisches Problem, kein technisches. Die Lösung mit PHP selbst ist trivial.

          Da gebe ich dir schon recht, es ist eher ein logisches Problem und die Umsetzung würde nicht an PHP scheitern, sondern eher an besagtem Fließtext, welcher ein komplexeres Script erfordert und dadurch die Relation zwischen Aufwand und Nutzen zu weit auseinander geht

          Wie du meinst.

          Ja, meine ich, zumal ich jetzt gemerkt habe, da es in besagtem Text auch noch verschiedene Schlüsselworte und Schreibweisen für ein und den selben Abschnittstyp gibt.

          1. Hallo,

            Da gebe ich dir schon recht, es ist eher ein logisches Problem und die Umsetzung würde nicht an PHP scheitern, sondern eher an besagtem Fließtext, welcher ein komplexeres Script erfordert und dadurch die Relation zwischen Aufwand und Nutzen zu weit auseinander geht

            Naja, "komplex" ist relativ.
            PHP kenne ich zu wenig, aber wenn globales Suchen & Ersetzen möglich ist mit Ermittlung der Anzahl Ersetzungen, dann ist ein entsprechender Algorithmus ganz einfach:

            1. Nimm das Erste Wort im Text.
            2. Ersetze es global durch nichts, d.h. durch einen Leerstring
            3. Schreibe das Wort und die Anzahl der Erstzungen in die Datenbank.
              =>zurück zu Punkt 1) bis der Text leer ist.

            Da der Text dabei immer kürzer wird, geht es mit jedem Durchlauf schneller. Ein Array oder sowas ist dazu nicht nötig.

            Gruß, Don P

          2. Hallo,

            Naja, "komplex" ist relativ.
            PHP kenne ich zu wenig, aber wenn globales Suchen & Ersetzen möglich ist mit Ermittlung der Anzahl Ersetzungen, dann ist ein entsprechender Algorithmus ganz einfach:

            1. Nimm das Erste Wort im Text.
            2. Ersetze es global durch nichts, d.h. durch einen Leerstring
            3. Schreibe das Wort und die Anzahl der Erstzungen in die Datenbank.
              =>zurück zu Punkt 1) bis der Text leer ist.

            Da der Text dabei immer kürzer wird, geht es mit jedem Durchlauf schneller. Ein Array oder sowas ist dazu nicht nötig.

            zumal ich jetzt gemerkt habe, da es in besagtem Text auch noch verschiedene Schlüsselworte und Schreibweisen für ein und den selben Abschnittstyp gibt.

            Dann wird es natürlich komplexer. Evtl. müssen vorher ein paar Vorbereitungen getroffen werden, oder auch nachher. Man kann ja die Liste gefundener Wörter auch nachträglich noch aufräumen.

            Gruß, Don P

            1. Hallo Don,

              Naja, "komplex" ist relativ.
              PHP kenne ich zu wenig, aber wenn globales Suchen & Ersetzen möglich ist mit Ermittlung der Anzahl Ersetzungen, dann ist ein entsprechender Algorithmus ganz einfach:

              1. Nimm das Erste Wort im Text.

              wie erkennst Du ein Wort?

              1. Ersetze es global durch nichts, d.h. durch einen Leerstring
              2. Schreibe das Wort und die Anzahl der Erstzungen in die Datenbank.

              Du hast das Trimmen vergesen :-)

              =>zurück zu Punkt 1) bis der Text leer ist.

              effektiv entspricht Dein Algorithmus folgenden zwei PHP-Anweisungen:

              $myWords = [link:http://www.php.net/manual/de/function.preg-split.php@title=preg_split]( <muster zum Erkennen von Wörtern>, $input);  
              $highscore = array_count_values($myWords);
              

              siehe auch suits "dreckige" Variante (preg_split ist in "wie auch immer" enthalten).

              suit hat bereits das Problem der Worttrennung hingewiesen. Darüber hinaus gibt es noch das Problem der unerwünschten Wörter wie "der, die, das, ..." - die Stopword-Liste -  das Problem der Groß- und Kleinschreibung und natürlich die Kombination von beidem und zu allem Überfluss gegebenenfalls noch die Notwendigkeit, unterschiedliche Sprachen unterschiedlich behandeln zu wollen - siehe suits Anmerkungen und die Anmerkungen von suit. :-)

              Ich stelle fest: suit hat dieses Problem intensiv überdacht ...

              Freundliche Grüße

              Vinzenz

              1. Hallo,

                1. Nimm das Erste Wort im Text.

                wie erkennst Du ein Wort?

                Per RegEx.

                1. Ersetze es global durch nichts, d.h. durch einen Leerstring
                2. Schreibe das Wort und die Anzahl der Erstzungen in die Datenbank.

                Du hast das Trimmen vergesen :-)

                Nö, RegEx. Whitespace und sonstiger Schrott darf stehen bleiben :)

                effektiv entspricht Dein Algorithmus folgenden zwei PHP-Anweisungen:

                $myWords = [link:http://www.php.net/manual/de/function.preg-split.php@title=preg_split]( <muster zum Erkennen von Wörtern>, $input);

                $highscore = array_count_values($myWords);

                  
                Mag sein, wie gesagt kenne ich PHP zu wenig bzw. gar nicht. In Perl z.B. braucht man dazu kein Array array\_count\_sonstwas (wenn ich jetzt nicht irre), sondern die ersetzte Anzahl steht in einer Variablen.  
                  
                
                > [suit](https://forum.selfhtml.org/?t=197076&m=1321317) hat bereits [das Problem der Worttrennung](https://forum.selfhtml.org/?t=197076&m=1321330) hingewiesen. Darüber hinaus gibt es noch das Problem der unerwünschten Wörter wie "der, die, das, ..." - die Stopword-Liste -  das Problem der Groß- und Kleinschreibung und natürlich die Kombination von beidem und zu allem Überfluss gegebenenfalls noch die Notwendigkeit, unterschiedliche Sprachen unterschiedlich behandeln zu wollen - siehe [suits Anmerkungen](https://forum.selfhtml.org/?t=197076&m=1321333) und [die Anmerkungen von suit](https://forum.selfhtml.org/?t=197076&m=1321385). :-)  
                  
                Ja, von suit, aber vom OP war davon wenig bis gar nichts zu hören. Man weiß ja gar nicht, wie der Text aufgebaut ist, ob es überhaupt Trennungen, Fremdsprachen usw. gibt... und dass es unbedingt PHP sein \*muss\*, ist auch nicht garz klar. Wenn suit sich Szenarien frei ausdenkt, naja. Da würde mir auch noch einiges einfallen, um die Sache zu verkomplizieren ;)  
                  
                
                > Ich stelle fest: suit hat dieses Problem intensiv überdacht ...  
                  
                Davon gehe ich aus.  
                  
                Gruß, Don P  
                
                
                1. wie erkennst Du ein Wort?

                  Per RegEx.

                  Die entscheidende Frage ist "Wie?" - auch dafür habe ich noch keine zufriedenstellende Lösung gefunden (mögliche Lösungen, die ich bereits im Einsatz habe oder zumindst getestet, nannte ich bereits) - wenn du mir eine nennen kannst, vorzuweise mit PCRE, bin ich dir sehr dankbar.

                  Mag sein, wie gesagt kenne ich PHP zu wenig bzw. gar nicht. In Perl z.B. braucht man dazu kein Array array_count_sonstwas (wenn ich jetzt nicht irre), sondern die ersetzte Anzahl steht in einer Variablen.

                  Das tut wenig zur Sache - dieses Teil-Problem (die Wörter zählen) ist der triviale Teil, der knifflige ist: wie erkennst du ein Wort? Einfaches Beispiel: Ist "Tag-und-Nacht-Gleiche" ein Wort oder drei (wenn man und als Stopword abzieht)?

                  Ja, von suit, aber vom OP war davon wenig bis gar nichts zu hören.

                  Meine Kommentare waren eigennützig, weil ich die Thematik selbst interessiert (und ich Infos dazu gebrauchen kann) dass der OP längst aus dem Thread ausgestiegen ist, ist mir einerlei :)

                  Wenn suit sich Szenarien frei ausdenkt, naja.

                  Nein, die genannten Dinge sind nicht frei ausgedacht sondern sind praxisnah und auch weitestgehen so im Einsatz. Einzig die sache mit "<span lang="en">die</span>" ist aktuell nicht enthalten und wird ignoriert - für das entfernen der Stopwords wird in diesem Fall eine zusätzliche, generische Stopwordsliste verwandt, die Lehnwörter, verbreitete Anglizismen usw. beinhaltet - die false-positives hier sind Streuverlust und nicht so tragisch.

                  Da würde mir auch noch einiges einfallen, um die Sache zu verkomplizieren ;)

                  Ich freue mich über Kommentare hierzu, ich hab' selbst sicher auch nicht alles bedacht - was ich aktuell auch nicht behandle sind Zahlwörter und Zahlen ansich - deren Existenz nehme ich billigend in Kauf.

                  Ich stelle fest: suit hat dieses Problem intensiv überdacht ...

                  Davon gehe ich aus.

                  Das hatte den Grund, dass ich es selbst praktisch benötigt habe und mir nicht nur langweilig war :)

                  Eine der (einfachen) Implementierungen findet sich übrigens in der autometa-Extension für TYPO3 - der relevante Code ist in pi1/class.tx_autometa_pi1.php zu finden.

        2. P.S. ich meine auch keine andere Programmiersprache, sondern eine andere Technik, z.b. Copy & Paste (solang es nur ein Einzelfall bleibt) ;-)

      • Stopwords müssen möglicherweise entfernt werden, allerdings muss dabei beachtet werden, ob es sich tatsächlich um ein Stopword der definierten Sprache handelt oder nicht. "die" ist in deutscher Sprache in Stopword, <span lang="en">die</span> in einem deutschsprachigen Text allerdings nicht.

      Wie hast du das Problem gelösst? Hast du eine "Liste" mit Wörtern pro Sprache angelegt? Dürften nicht wenige Worte sein in Deutsch, in Englisch vermutlich ein paar weniger.

      1. Hast du eine "Liste" mit Wörtern pro Sprache angelegt?

        Ja - und es kommen ständig neue dazu.

        Dürften nicht wenige Worte sein in Deutsch, in Englisch vermutlich ein paar weniger.

        Das Ganze ist sicher noch weit von der "Marktreife" entfernt - aktuell hat das deutschsprachige Stopwords-File 190 Einträge - die Liste ist selbst zusammengetragen, aber sicher weit unter dem, was notwenig wäre - dennoch hat sie teilweise bedeutend mehr Einträge als diverse Stopwordslisten, die man im Internet so findet.

        Dennoch ist das ein fast unlösbares Problem, auch wenn man mit "vollständigen" stopwords-Listen arbeitet.

        1: "Ich soll das nicht tun."
        2: "Das Soll ist erreicht."
        3: "Soll ich das tun?"

        Erste Möglichkeit: Stopword ist "soll" und wird case-insenstive entfernt, damit wäre 2 ein false positive.

        Zweite Möglichkeit: stopword ist "soll" und case-senstive, damit würde #3 bleiben - ebenfalls ein false positive.

        Man müsste extrem viele Regeln einbauen, die das alles beachten - und besonders bei der deutschen Sprachen mit all den Sonderregelungen (z.B. bei Flexionen oder Kasus) ist das eine "Lebensaufgabe" ;)

  3. Hello,

    Aber könnte man mit PHP auch die Wiederholungen innerhalb eines String ermitteln? um quasi eine Top10 (oder besser TopX) der am häufigsten vorkommenden Wörter zu erhalten?

    Da habe ich mal 'was gebastelt.
    Ist schon lange her, darum schau bitte genau hin, ob noch irgendwelche bösen Sachen drinstecken. Ich habe es jetzt nicht kontrolliert:

    http://selfhtml.bitworks.de/wordcount.php

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de