Pesquisa difusa de palavras próximas umas das outras

4

Dado um pequeno grupo de palavras (para concretude, digamos 3), mas mais geralmente n , eu quero pesquisar um arquivo por ocorrências de duas dessas palavras próximas umas das outras. De perto, digamos que as duas palavras tenham, no máximo, k caracteres separadas, onde k é alguma constante.

Justificativa: Estou procurando e-mails específicos na minha caixa de entrada ( /var/spool/mail/username ), com palavras-chave específicas. Não tenho certeza de como as palavras-chave ocorrem. No entanto, uma palavra é relativamente comum. Duas palavras juntas são menos comuns.

Um exemplo motivador específico:

"alumínio", "bagagem", "armazenamento".

Nesse caso, estou procurando e-mails sobre uma caixa de bagagem.

Uma solução em termos de n e k seria melhor.

Alguma indicação de como aplicar isso a vários arquivos seria útil.

E eu não me importo com o idioma em que a solução está.

    
por Faheem Mitha 17.12.2017 / 17:02

3 respostas

0

Comece com uma ferramenta de stemming como link então use regex link então use wc sort e unique para classificar por quão próximas as palavras estão.

pseudo-bash;

WORDS=$1
HAYSTACK=/var/mail

STEMS=$(hunspell --stem $WORDS)
REGEX=$(echo $STEMS | perl -pe 's/ /.*/g')
while read MATCH ; do
    FILE=$(echo $MATCH | cut -d : 1)
    COUNT=$(echo $MATCH | cut -d : 2 | perl -pe 's/.*('"$REGEXX"').*/$1/g' | wc -c)
    echo $COUNT\t$FILE
done < <(grep -rP "$REGEX" $HAYSTACK) | \
sort -nr

Se você quiser mais rápido, você pode usar link com regex para limitar o espaço entre as palavras

a.{1,50}b
    
por 17.12.2017 / 17:50
0

Você pode considerar:

1) glark, which has an option:
   ( expr1 --and=NUM expr2 )
   Match both of the two expressions, within NUM lines of each other.

2) bool, with expressions like:
   bool -O0 -C0 -D5 -b "two near three"

3) peg, which accepts options like:
   peg "/x/ and near(sub { /y/ or /Y/ }, 5)"

O código para o glark está no link e pode estar em alguns repositórios.

Alguns detalhes para bool e peg:

bool    print context matching a boolean expression (man)
Path    : ~/executable/bool
Version : 0.2.1
Type    : ELF 64-bit LSB executable, x86-64, version 1 (SYS ...)
Help    : probably available with -h,--help
Home    : https://www.gnu.org/software/bool/ (doc)

peg     Perl version of grep, q.v. (what)
Path    : ~/bin/peg
Version : 3.10
Length  : 4749 lines
Type    : Perl script, ASCII text executable
Shebang : #!/usr/bin/env perl
Repo    : Debian 8.9 (jessie) 
Home    : http://piumarta.com/software/peg/ (pm)
Home    : http://www.cpan.org/authors/id/A/AD/ADAVIES/peg-3.10 (doc)

Felicidades ... felicidades, drl

    
por 17.12.2017 / 20:56
0

Eu gosto da idéia do grepmail (e em nossa loja nós codificamos um utilitário chamado rapgrep, requer todos os padrões , para o caso geral).

Uma resposta mais específica em termos de distância do personagem é demonstrada com este trecho, procurando palavras: country, men, time:

# Utility functions: print-as-echo, print-line-with-visual-space.
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
pl " Input data file $FILE:"
head $FILE

pl " Results, egrep:"
egrep 'time|men|country' $FILE

pl " Results, egrep, with byte offset:"
egrep -b 'time|men|country' $FILE

pl " Results, egrep, with byte offset, matches only:"
egrep -o -b 'time|men|country' $FILE |
tee t1

pl " Looking for minimum distance between all pairs:"

awk -F":" '
  { a[$2] = $1  # Compare every item to the new item
    for ( b in a ) {
      for ( c in a ) {
      # print " Working on b = ",b," c = ",c
        if ( b != c ) {
        v0 = a[c]-a[b]
        v1 = v0 < 0 ? -v0 : v0  # convert to > 0
        v2 = (b < c) ? b " " c : c " " b  # trivial sort of names
        print v1, v2
      }
    }
    }
  }

' t1 |
datamash -t" " -s --group 2,3 min 1 

produzindo:

-----
 Input data file data1:
Now is the time
for all good men
to come to the aid
of their country.

-----
 Results, egrep:
Now is the time
for all good men
of their country.

-----
 Results, egrep, with byte offset:
0:Now is the time
16:for all good men
52:of their country.

-----
 Results, egrep, with byte offset, matches only:
11:time
29:men
61:country

-----
 Looking for minimum distance between all pairs:
country men 32
country time 50
men time 18

e com um arquivo um pouco mais complicado, com várias ocorrências de algumas palavras:

-----
 Input data file data2:
Now is the time men
for all good men
to come to the aid
of their men country.

-----
 Results, egrep:
Now is the time men
for all good men
of their men country.

-----
 Results, egrep, with byte offset:
0:Now is the time men
20:for all good men
56:of their men country.

-----
 Results, egrep, with byte offset, matches only:
11:time
16:men
33:men
65:men
69:country

-----
 Looking for minimum distance between all pairs:
country men 4
country time 58
men time 5

Isso faz uso da opção de contagem de bytes no GNU grep, o programa awk calcula todas as distâncias entre os pares de palavras, finalmente datamash classifica, agrupa e escolhe as distâncias mínimas.

Isso pode ser facilmente parametrizado para permitir palavras em uma linha de comando, bem como a distância permitida. Veja o arquivo t1 para o formulário nos dados de entrada para datamash do programa awk.

Executar em um sistema como:

OS, ker|rel, machine: Linux, 3.16.0-4-amd64, x86_64
Distribution        : Debian 8.9 (jessie) 
bash GNU bash 4.3.30
grep (GNU grep) 2.20
awk GNU Awk 4.1.1, API: 1.1 (GNU MPFR 3.1.2-p3, GNU MP 6.0.0)
datamash (GNU datamash) 1.2

Felicidades ... felicidades, drl

    
por 19.12.2017 / 19:01