A partir de um índice de termos/documentos só é possível efetuar consultas de ocorrência de termos e filtros com operadores AND
, OR
e NOT
.
Entretanto, o que é preciso para executar uma consulta por presidente do Brasil?
A forma mais simples, é converter essa consulta em presidente AND do AND Brasil
(o do pode ser removido se quiser remover stop words).
O detalhe é que essa consulta vai retornar qualquer documento que contenha presidente e Brasil, mas que não não fale necessariamento do presidente do Brasil.
Um exemplo seria: O presidente do conselho está trabalhando para aumentar os empregos no Brasil.
Esse documento é retornado pela consulta presidente AND do AND Brasil
, mas não tem nada a ver com presidente do Brasil.
Portanto, é necessário melhorar a estrutura de índice para obter melhores resultados pelas consultas.
Antes de prosseguir, é importante notar que esse tipo de consulta normalmente é feito com "
.
Portanto, pesquisar por presidente do Brasil é o equivalente à presidente AND do AND Brasil
.
Para efetuar a consulta com a phrase query é necessário adicionar "
, assim “presidente do Brasil” faz a consulta pela frase.
Claro que é possível adicionar algum mecanismo que interpréte presidente do Brasil como uma phrase query, sem a necessidade de pedir ao usuário que coloque "
.
índice bi-word Link para o cabeçalho
Uma forma de resolver a consulta por frases é criar um índice de k-palavras. Sendo 2-palavras um número bom, visto que $k \gt 2$ começa a requisitar mais espaço de armazenamento para todas as combinações de três ou mais palavras. Portanto, dados os documentos de exemplo usado no índice invertido, o índice bi-word (2-palavras) seria.
Documentos:
- Uma casa à venda em Blumenau:
[casa, venda, Blumenau]
:[(casa, venda), (venda, Blumenau)]
- Vendo terreno em Gaspar:
[venda, terreno, Gaspar]
:[(venda, terreno), (terreno, Gaspar)]
- Alugo apartamento em Indaial:
[alugo, apartamento, Indaial]
:[(alugo, apartamento), (apartamento, Indaial)]
Observação: As stop words foram removidas para simplificar o exemplo.
# índice bi-word
I = {
'__alldocs__': [1, 2, 3],
('alugo', 'apartamento'): [3],
('apartamento', 'Indaial'): [3],
('casa', 'venda'): [1],
('terreno', 'Gaspar'): [2],
('venda', 'Blumenau'): [1],
('venda', 'terreno'): [2],
}
Como é possível notar no exemplo acima, a diferença fica que as chaves do dicionário são tuplas (poderia ser uma string concatenada também) de palavras em sequência.
É necessário atentar na hora de efetuar a consulta.
Nesse momento, uma consulta terreno em Gaspar não se torna terreno AND Gaspar
, mas sim terreno Gaspar
.
Voltando ao exemplo da consulta presidente do Brasil.
Com a condição de um índice de duas palavras, a consulta seria presidente do AND do Brasil
.
Note que a consulta foi separada em um AND
com as sequências de k-palavras possíveis.
Um exemplo de como montar um índice bi-word em python
.
import nltk
from nltk.corpus import brown
from itertools import chain
import string
from collections import defaultdict
nltk.download('brown')
def filtrar_pontuacao(doc):
return (p for p in doc if p not in string.punctuation)
DOCUMENTOS = brown.paras()
DOCUMENTOS = [list(chain.from_iterable(p)) for p in DOCUMENTOS]
documentos = (filtrar_pontuacao(p) for p in DOCUMENTOS)
documentos = [list(p) for p in documentos]
bi_word_inverted_index = defaultdict(list)
for (doc_id, doc) in enumerate(documentos):
# termo especial para queries com NOT
bi_word_inverted_index['__alldocs__'].append(doc_id)
for (t1, t2) in zip(doc[:-1], doc[1:]):
bi_word_inverted_index[(t1, t2)].append(doc_id)
Agora é possível comparar uma consulta nesse índice bi-word, contra o índice de termos. O teste será de uma consulta para Texas House of Representatives.
No índice de termos
print('texas AND house AND representatives')
texas = inverted_index['texas']
house = inverted_index['house']
representatives = inverted_index['representatives']
doc_ids = set(texas) & set(house) & set(representatives)
for doc_id in doc_ids:
print('> ', ' '.join(DOCUMENTOS[doc_id]))
Com resultados:
Under Formby’s plan , an appointee would be selected by a board composed of the governor , lieutenant governor , speaker of the House , attorney general and chief justice of the Texas Supreme Court . Austin , Texas – State representatives decided Thursday against taking a poll on what kind of taxes Texans would prefer to pay .
Rep. James Cotten of Weatherford insisted that a water development bill passed by the Texas House of Representatives was an effort by big cities like Dallas and Fort Worth to cover up places like Paradise , a Wise County hamlet of 250 people .
Note que o primeiro resultado contém todos os termos, mas não na sequência desejada. Já o segundo documento é um resultado esperado para a consulta.
A mesma consulta no índice bi-word
print('texas house AND house of AND of representatives')
texas_house = bi_word_inverted_index[('Texas', 'House')]
house_of = inverted_index[('House', 'of')]
of_representatives = inverted_index[('of', 'Representatives')]
doc_ids = set(texas_house) & set(house_of) & set(of_representatives)
for doc_id in doc_ids:
print('> ', ' '.join(DOCUMENTOS[doc_id]))
Com resultado:
Rep. James Cotten of Weatherford insisted that a water development bill passed by the Texas House of Representatives was an effort by big cities like Dallas and Fort Worth to cover up places like Paradise , a Wise County hamlet of 250 people .
Nesse caso, o único documento retornado foi o esperado. Obviamente esses foram exemplos simples e a ideia é combinar o índice de termos com um índice bi-word para ter um conjunto melhor de resultados.
Entretanto, ainda existe um problema no índice bi-word.
Nesse exemplo, em inglês, tirado de Introduction to Information Retrieval, fica claro.
Indexar renegotiation of the constitution seria: [(renegotiation, of), (of, the), (the, constitution)]
.
Note que esses índices de duas palavras não agregam muito e, provavelmente, retornariam resultado irrelevantes em uma consulta.
A solução é pensar em aumentar de 2-palavras para 3-palavras ou 4-palavras.
O problema é o tamanho do índice, que começa a ficar muito grande, considerando as várias combinações de 3, ou 4, palavras.
Outra possibilidade é considerar um pouco da estrutura do idioma.
Por exemplo, of the
funcionam como palavras auxiliares, stop words até certo ponto, e poderiam ser agregadas ao montar o índice.
Para isso, é necessário uma lista de stop words ou usar técnicas de Processamento de Linguagem Natural para fazer o Part-of-Speech tagging, que indica a função (verbo, substantivo, artigo, …) de cada palavra em uma frase.
Uma vez que se sabe a função das palavras é possível montar o índice ignorando palavras de estrutura (artigos, preposições, …) que aparecem entre substantivos/verbos.
Portanto, dado que renegotiation of the constitution seria anotado como N X X N (N: substantivo, X: palavra auxiliar).
Uma estrutura de índice poderia ser montada com NX*N
, ou seja, um substantivo (N
) seguido por zero ou mais palavras auxiliares (X*
) seguido por mais um substantivo (N
).
Essa alternativa para considerar os casos especiais onde é necessário indexar mais que duas palavras em sequência é chamada de phrase index, visto que frases completas podem ser indexadas e não apenas termos ou sequências fixas de k-termos.
O índice bi-word é uma forma para resolver o problema de consultas por frases. Mas tem a limitação de espaço, visto que indexar sequências muito grandes pode ser proibitivo (em espaço) e também resultar em uma consulta demorada.