Suchen der am häufigsten verwendeten Nonstop-Wörter in einer Spalte


7

Einfach ausgedrückt, ich habe eine Postgres-Spalte mit gewöhnlicher Prosa und möchte die xam häufigsten verwendeten Wörter ("Wort" ist eine Gruppe von Zeichen, die durch ein Leerzeichen begrenzt sind, aber kein Stoppwort) über alle Zeilen hinweg bestimmen .

Ich habe zwei Lösungen gefunden, die fast ins Schwarze getroffen haben:

SELECT *                                       
FROM   ts_stat($$SELECT to_tsvector('english', title) FROM item$$) 
ORDER  BY ndoc DESC
LIMIT  50;

Dies ist großartig, außer dass es Wortstämme zurückgibt.

SELECT   UNNEST(string_to_array(title, ' ')) AS word, COUNT(*) AS ct
FROM     item 
GROUP    BY 1 
ORDER    BY 2 DESC
LIMIT    50;

Dieser gibt die vollständigen Wörter zurück, enthält jedoch Stoppwörter.

Der Einfachheit halber: Stoppwörter sollen auf gefunden werden TABLE stop_words (lowercase_stopword text PRIMARY KEY).

Kann mir jemand über die Leitung helfen?

Antworten:


4

Ihre erste Anfrage war ziemlich nah. Erstellen Sie eine Textsuchkonfiguration mit einem einfachen Wörterbuch, das dies nicht tut, um unerwünschte Stemming zu entfernen.

Ich schlage vor, ein separates Schema für Textsuchobjekte zu verwenden, aber das ist völlig optional:

CREATE SCHEMA ts;
GRANT USAGE ON SCHEMA ts TO public;
COMMENT ON SCHEMA ts IS 'text search objects';

CREATE TEXT SEARCH DICTIONARY ts.english_simple_dict (
    TEMPLATE = pg_catalog.simple
  , STOPWORDS = english
);

CREATE TEXT SEARCH CONFIGURATION ts.english_simple (COPY = simple);
ALTER  TEXT SEARCH CONFIGURATION ts.english_simple
   ALTER MAPPING FOR asciiword WITH ts.english_simple_dict;  -- 1, 'Word, all ASCII'

Dann funktioniert Ihre Abfrage und auch sehr schnell:

SELECT *                                       
FROM   ts_stat($$SELECT to_tsvector('ts.english_simple', title) FROM item$$) 
ORDER  BY ndoc DESC
LIMIT  50;

dbfiddle hier

Dies funktioniert mit Kleinbuchstaben ohne Stemming und bricht nicht für Nicht-ASCII-Buchstaben.

Hintergrund

Lesen Sie das Kapitel Einfaches Wörterbuch im Handbuch.

Die genaue Definition eines "Wortes" ist eine schwierige Angelegenheit. Der Standard-Parser für die Textsuche (derzeit der einzige) identifiziert 23 verschiedene Arten von Token. Sehen:

SELECT * FROM ts_token_type('default');

Integrierte Textsuchkonfigurationen ordnen die meisten dieser (integrierten) Wörterbüchern zu. Zuordnungen für die englishKonfiguration:

SELECT tt.*, m.mapdict::regdictionary AS dictionary
FROM   pg_ts_config_map m
LEFT   JOIN   ts_token_type(3722) tt ON tt.tokid = m.maptokentype
WHERE  mapcfg = 'english'::regconfig  --  'ts.english_simple'::regconfig
ORDER  BY tt.tokid;

Die Demo oben erstellt eine neue Konfiguration auf der Basis simpleConfig, und da alle englischen Stop - Wörter des Typs ‚asciiword‘ sind, brauchen wir nur diese Art abzubilden Stoppwörter zu entfernen, nicht ergeben oder irgendetwas anderes.


1

Dies würde Ihnen Ihre erwartete Ausgabe geben:

-- Some example data
WITH titles(title) AS
(
   VALUES 
      ('This is a title'), 
      ('This is another title'), 
      ('This is finally a third title'), 
      ('and I don''t like Mondays')
)

-- List here all the words that you consider 'stop words'
-- in lowercase
, stop_words(word) AS
(

    VALUES ('the'), ('a'), ('and')
)

-- Make list of (lowercased) found words
, found_lower_words AS
(
SELECT 
    lower(unnest(string_to_array(title, ' '))) AS word
FROM
    titles
)

-- And now anti-join with the stop_words, group and count
SELECT
    word, count(*) AS word_count
FROM
    found_lower_words
    LEFT JOIN stop_words USING(word)
WHERE
    stop_words.word is NULL
GROUP BY
    word
ORDER BY
    word_count DESC, word ASC
LIMIT
    50 ;

Die Ergebnisse wären:

  |---------+---|
  |   is    | 3 |
  |---------+---|
  |  this   | 3 |
  |---------+---|
  |  title  | 3 |
  |---------+---|
  | another | 1 |
  |---------+---|
  |  don't  | 1 |
  |---------+---|
  | finally | 1 |
  |---------+---|
  |    i    | 1 |
  |---------+---|
  |  like   | 1 |
  |---------+---|
  | mondays | 1 |
  |---------+---|
  |  third  | 1 |
  |---------+---|

1

Erwins Antwort ist besser

Im Anschluss an die Probendaten aus @joanolo oben , wollte ich so beantworten , die haben nicht Sie benötigen , um explizit Stoppwörter Liste. Dies erfordert Annahmen über das Wörterbuch.

WITH titles(title) AS
(
   VALUES 
      ('This is a title'), 
      ('This is another title'), 
      ('This is finally a third title'), 
      ('and I don''t like Mondays')
)
SELECT token, count(*)
FROM titles
CROSS JOIN LATERAL ts_debug(title)
WHERE alias = 'asciiword'
        AND array_length(lexemes,1) <> 0
GROUP BY token;

  token  | count 
---------+-------
 title   |     3
 another |     1
 like    |     1
 third   |     1
 Mondays |     1
 finally |     1
(6 rows)

Wir verwenden array_length, um einen hier beschriebenen internen Fehler zu umgehen


ts_debug()ist sehr teuer, da es viel Arbeit erledigt, die wir für die Abfrage nicht benötigen. Außerdem würde dies für Nicht-ASCII-Zeichen fehlschlagen. Und wir wollen wahrscheinlich auch nicht, dass zwischen Groß- und Kleinschreibung unterschieden wird. Eigentlich brauchen wir nur eine Textsuchkonfiguration, die nicht stammt. Ich habe eine andere Lösung hinzugefügt.
Erwin Brandstetter
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.