Contando Palavras#

Neste capítulo iremos começar a interagir com os textos no nível da linguagem, por meio das ferramentas do Processamento de Linguagem Natural (PLN). Vamos progredir gradualmente nossa representação da linguagem a partir da morfologia, passando pela sintaxe e chegando à semântica.

Nesta etapa, faremos uso de biliotecas especializadas em PLN como o NLTK e a Spacy.

Hide code cell source
import warnings
warnings.filterwarnings('ignore')
import nltk
import spacy
import numpy as np
from collections import Counter
import pandas as pd
from sqlalchemy import create_engine
import matplotlib.pyplot as plt
%matplotlib inline

Bibliotecas de PLN requerem o carregamento de modelos de liguagem para funcionar de maneira apropriada: para este capitulo iremos carregar os modelos específicos da lingua portuguesa. Para isso precisamos executar comandos no terminal do sistema operacional:

# Descomente a linha abaixo para instalar o modelo para a língua portuguesa, caso ainda não o tenha feito.
# !python3 -m spacy download pt_core_news_sm

Para contar as palavras de um texto, é preciso primeiro separá-las uma a uma. A este processo dá-se o nome de tokenização, e é típicamente fácil de fazer mal-feito e difícil de fazer bem-feito.

Primeiramente precisaremos carregar os verbetes com a função que criamos no capítulo 1.

import glob, os
caminho = "../dhbb/text/*.text"
arquivos  = glob.glob(caminho)

def tabula_verbete(n=None):
    """
    Carrega todos os verbetes disponíveis, ou os primeiros n.
    n:  número de verbetes a tabular
    """
    if n is None:
        n = len(arquivos)
    linhas = []
    for a in arquivos[:n]:
        with open (a, 'r', encoding='utf8') as f:
            verbete = f.read()
        cabeçalho = verbete.split('---')[1]
        campos = {l.split(':')[0].strip() :l.split(':')[1].strip() for l in cabeçalho.split('\n')[:4] if l}
        campos['arquivo'] = os.path.split(a)[1]
        campos['cargos'] = np.nan if 'cargos:' not in cabeçalho else cabeçalho.split('cargos:')[1]
        campos['corpo'] = verbete.split('---')[2]
        linhas.append(campos)
    tabela = pd.DataFrame(data = linhas, columns=['arquivo','title', 'natureza', 'sexo', 'cargos', 'corpo'])
    return tabela

Lendo os verbetes do Banco de Dados#

tabela = tabula_verbete()
eng = create_engine("sqlite:///minha_tabela.sqlite")
#dhbb = pd.read_sql_table('resultados')
biograficos = pd.read_sql_query('select * from resultados where natureza="biográfico"', con=eng)
biograficos.head()
index arquivo title natureza sexo cargos corpo
0 0 4736.text RUBIM, Floriano biográfico m \n - militar\n - dep. fed. ES 1955-1959\n - d... \n\n«Floriano Lopes Rubim» nasceu em Alegre (E...
1 1 1376.text COELHO, Inocêncio Mártires biográfico m \n - proc. ger. Rep. 1981-1985\nautor: \n - L... \n\n«Inocêncio Mártires Coelho» nasceu em Belé...
2 2 732.text BOPP, Raul biográfico m \n - diplomata\n - emb. Bras. Peru 1962-1963\n \n\n«Raul Bopp» nasceu em Santa Maria (RS) no ...
3 3 2669.text KRÜGER, Nivaldo biográfico m \n - dep. fed. PR 1979-1983\n - sen. PR 2002-... \n\n«Nivaldo Passos Krüger» nasceu em Canoinha...
4 5 3752.text NASCIMENTO, César biográfico m \n - dep. fed. SC 1973-1975\n - dep. fed. SC ... \n\n«César Correia do Nascimento» nasceu em La...

Tokenização com o NLTK#

nltk.download('punkt')
nltk.download('punkt_tab')
True
palavras = nltk.word_tokenize(biograficos.corpo[0])
palavras[:10]
['«', 'Floriano', 'Lopes', 'Rubim', '»', 'nasceu', 'em', 'Alegre', '(', 'ES']

Uma vez “tokenizado” o texto, podemos contar as palavras muito simplesmente usando a função Counterdo Python

c = Counter(palavras)
c.most_common(n=10)
[(',', 40),
 ('de', 39),
 ('.', 24),
 ('do', 18),
 ('a', 14),
 ('(', 13),
 (')', 13),
 ('em', 12),
 ('da', 12),
 ('e', 11)]

Outra maneira de fazer isso seria usando o NLTK. Com a vantagem de ainda podermos visualizar graficamente.

plt.figure(figsize=(15,5))
fd = nltk.FreqDist([p.lower() for p in palavras])
fd.plot();
_images/c330c4bdc02cc7aaa837cabff6a6d2df3263fdb453455fd7f23c653d180fb12b.png
print("Numero de tokens:", len(palavras))
print("Tamanho do Vocabulário:", len(set([p.lower() for p in palavras])))
Numero de tokens: 595
Tamanho do Vocabulário: 258

Como vemos no exemplo acima, as palavras mais frequentes não muito informativas. Muitas vezes pode ser útil removê-las da análise. Linguistas costumams chamar este tipo de palavras de stop-words. Alem disso, vamos tambem remover as pontuaçoes.

Removendo as Stopwords#

nltk.download('stopwords')
True

Vamos juntar as stopwords e a pontuação em uma única lista:

sw = nltk.corpus.stopwords.words('portuguese')
from string import punctuation
print(punctuation)
sw[:10]
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
['a',
 'à',
 'ao',
 'aos',
 'aquela',
 'aquelas',
 'aquele',
 'aqueles',
 'aquilo',
 'as']
plt.figure(figsize=(15,5))
fd = nltk.FreqDist([p for p in palavras if p not in sw+list(punctuation)])
fd.plot();
_images/db7cfc726fbce6cc7bb6c464278f811f71f6d401001078d7d540605ffb217e48.png

Analizando coleções de textos#

Acima analizamos o conteúdo de apenas um verbete, mas seria bem mais relevante podermos fazer a análise em uma coleção de verbetes. Para isso aprenderemos a criar um objeto TextCollection que como o nome diz, representa uma coleção de textos, mas que nos permite a realização de análises a nível de coleção.

tc = nltk.TextCollection([nltk.word_tokenize(c) for c in biograficos.corpo])
nltk.FreqDist(tc)
FreqDist({',': 597012, 'de': 525737, '.': 264102, 'do': 230022, 'a': 218214, 'e': 196003, 'da': 185135, 'em': 155492, 'o': 150981, ')': 89553, ...})
tc.concordance('filho', width=150)
Displaying 25 of 10304 matches:
no Lopes Rubim » nasceu em Alegre ( ES ) no dia 18 de janeiro de 1912 , filho de Francisco Lopes Rubim , funcionário público , e de Maria Sílvia Lous
ncio Mártires Coelho » nasceu em Belém no dia 14 de fevereiro de 1941 , filho de Inocêncio Machado Coelho Neto e de Celina Mártires Coelho . Graduado
« Raul Bopp » nasceu em Santa Maria ( RS ) no dia 4 de agosto de 1898 , filho de Alfredo Bopp e de Josefina Bopp . Seus avós maternos , chegados ao R
 Passos Krüger » nasceu em Canoinhas ( SC ) no dia 27 de maio de 1929 , filho de Augusto Krüger e de Araci Passos . Industrial e fazendeiro , iniciou
do Nascimento » nasceu em Laguna ( SC ) no dia 25 de dezembro de 1929 , filho de Elias Júlio Correia e de Maria Correia da Silva . Funcionário da Cai
Cola » nasceu em Conceição do Castelo ( ES ) , em 26 de julho de 1923 , filho de Pedro Cola e de Hermínia Piovesan . Filho de imigrantes italianos de
, em 26 de julho de 1923 , filho de Pedro Cola e de Hermínia Piovesan . Filho de imigrantes italianos de origem pobre , Camilo Cola cursou as quatro 
Nascimento » nasceu em Teresópolis ( RJ ) no dia 27 de agosto de 1936 , filho de Eduardo Pereira do Nascimento e de Aclimea de Oliveira Nascimento . 
 Mendes Adeodato » nasceu em Sobral ( CE ) no dia 19 de junho de 1927 , filho de João Nogueira Adeodato e de Luzia Mendes Adeodato . Seu pai foi depu
ma » nasceu no município de Tiros ( MG ) no dia 9 de setembro de 1955 , filho de José Barbosa de Lima e de Helena Maria de Lima . Professor do Centro
elho Martins » nasceu em Loreto ( MA ) no dia 11 de fevereiro de 1947 , filho de Joel Martins dos Reis e de Luzia Botelho Martins Reis . Concluiu os 
é de Almeida » nasceu em Januária ( MG ) no dia 23 de outubro de 1912 , filho do militar José Antônio de Almeida e de Rita Dias de Almeida . Cursou o
reitas Brito » nasceu em Propriá ( SE ) no dia 18 de novembro de 1942 , filho de João Fernandes de Brito e de Dalva Aires de Freitas Brito . Ingresso
omiro Meger » nasceu em Rio Azul ( PR ) no dia 8 de fevereiro de 1945 , filho de Francisco Meger e de Maria Galemba . Empresário e agropecuarista , f
erte Ramos Vieira » nasceu em Lajes ( SC ) no dia 29 de março de 1925 , filho de Álvaro Vieira e de Altina Ramos Vieira . Bacharelou-se em ciências c
e Jaguaribemirim , hoje Jaguaribe ( CE ) , no dia 15 de julho de 1881 , filho de Joaquim Antônio do Nascimento e de Clara Fernandes Távora do Nascime
nome . « Gileno De Carli » nasceu em Recife no dia 22 de maio de 1908 , filho de Carlos De Carli e de Eugênia Ribeiro De Carli . Fez os estudos primá
casado com Teresa de Petribu De Carli , com quem teve oito filhos . Seu filho João Carlos de Petribu De Carli foi deputado federal por Pernambuco ent
92 ) e « Sudene : uma memória histórica » ( 1996 ) . « Edvaldo Nogueira Filho » nasceu em Pão de Açúcar ( AL ) no dia 25 de janeiro de 1961 , filho d
a Filho » nasceu em Pão de Açúcar ( AL ) no dia 25 de janeiro de 1961 , filho de Edvaldo Bezerra Nogueira e de Maria de Lourdes Santana Nogueira . Cu
caju na legenda do PCdoB . Casou-se com Tânia Soares , com quem teve um filho . Tânia Soares foi deputada estadual pelo PCdoB . « João Wagner » elege
arneiro Campelo Júnior » nasceu em Recife no dia 3 de janeiro de 1900 , filho de Manuel Neto Campelo , professor e diretor da Faculdade de Direito de
lo Dias Novais » nasceu em Avaré ( SP ) no dia 11 de novembro de 1928 , filho de José de Araújo Novais e de Judite Dias Novais . Seu irmão , Israel D
essor especial do governo de São Paulo na gestão de Luís Antônio Fleury Filho ( 1991-1995 ) . Paulo Novais assumiu mandato na Câmara em 4 de novembro
Vilela Veloso » nasceu em Jataí ( GO ) , no dia 7 de setembro de 1975 , filho de João Bosco Veloso e Nelma Vilela Veloso . Seu tio , Maguito Vilela ,
linhas = tc.concordance_list('filho',width=150)
linhas[0]
ConcordanceLine(left=['«', 'Floriano', 'Lopes', 'Rubim', '»', 'nasceu', 'em', 'Alegre', '(', 'ES', ')', 'no', 'dia', '18', 'de', 'janeiro', 'de', '1912', ','], query='filho', right=['de', 'Francisco', 'Lopes', 'Rubim', ',', 'funcionário', 'público', ',', 'e', 'de', 'Maria', 'Sílvia', 'Lousada', 'Rubim', '.', 'Bacharel', 'em', 'ciências', 'jurídicas', 'e', 'sociais', 'pela', 'Faculdade', 'de', 'Direito', 'da', 'Universidade', 'do', 'Espírito', 'Santo', ',', 'formou-se', 'também', 'na', 'Escola', 'de'], offset=19, left_print='no Lopes Rubim » nasceu em Alegre ( ES ) no dia 18 de janeiro de 1912 ,', right_print='de Francisco Lopes Rubim , funcionário público , e de Maria Sílvia Lous', line='no Lopes Rubim » nasceu em Alegre ( ES ) no dia 18 de janeiro de 1912 , filho de Francisco Lopes Rubim , funcionário público , e de Maria Sílvia Lous')
print(*linhas[0].right)
de Francisco Lopes Rubim , funcionário público , e de Maria Sílvia Lousada Rubim . Bacharel em ciências jurídicas e sociais pela Faculdade de Direito da Universidade do Espírito Santo , formou-se também na Escola de

Exercício#

Utilizando a ferramenta de concordance, procure extrair a filiação dos verbetados, escrevendo uma função que percorra todas as ocorrências da palavra “filho”, separando aquelas com sentido de indicação de filiação.

def parse_progenitores(linha):
    '''
    A partir de uma linha do DHBB contendo nomes de pai e mãe, extrai os nomes e os retorna
    '''
    right = linha.right[1:]
    try:
        pos_virg = right.index(',')
    except ValueError:
        pos_virg = 100
    try:
        pos_pt = right.index('.')
    except ValueError:
        pos_pt = 100
    fim = min(pos_virg, pos_pt)
    pais = right[:fim]
    if 'e' in pais:
        pai = " ".join(pais[:pais.index('e')])
        mae = " ".join([p for p in pais[pais.index('e'):] if p not in [',', 'de', 'e']])
    else:
        pai = " ".join(pais)
        mae = "desconhecida."
    return pai, mae

def encontra_pais(tc):
    linhas = tc.concordance_list('filho',width=180, lines=50000) + tc.concordance_list('filha',width=180, lines=50000)
    print("Processando {} linhas".format(len(linhas)))
    i = 0
    for linha in linhas:
        if linha.right[0] not in ['de', 'da', 'do']:
            continue
        pai, mae = parse_progenitores(linha)
        i += 1
        if i <11:
            print("Pai:",pai, '\n', "Mãe:", mae)
    print("Encontramos progenitores em {} linhas".format(i))
encontra_pais(tc)
Processando 11202 linhas
Pai: Francisco Lopes Rubim 
 Mãe: desconhecida.
Pai: Inocêncio Machado Coelho Neto 
 Mãe: Celina Mártires Coelho
Pai: Alfredo Bopp 
 Mãe: Josefina Bopp
Pai: Augusto Krüger 
 Mãe: Araci Passos
Pai: Elias Júlio Correia 
 Mãe: Maria Correia da Silva
Pai: Pedro Cola 
 Mãe: Hermínia Piovesan
Pai: imigrantes italianos de origem pobre 
 Mãe: desconhecida.
Pai: Eduardo Pereira do Nascimento 
 Mãe: Aclimea Oliveira Nascimento
Pai: João Nogueira Adeodato 
 Mãe: Luzia Mendes Adeodato
Pai: José Barbosa de Lima 
 Mãe: Helena Maria Lima
Encontramos progenitores em 6572 linhas
tc.dispersion_plot(['deputado', 'senador', 'prefeito', 'governador'])
_images/b89b6b7cc20ec3decc8d3363a638e8a630425091dcb411439d22bec09ec8de13.png

Separando frases#

Além de quebrar os textos em seus tokens constituintes, podemos também dividi-los por frases, o que pode ser mais convenientes para certas análises. Para isso utilizamos a função sent_tokenize do nltk.

nltk.tokenize.sent_tokenize(biograficos.corpo[0])
['\n\n«Floriano Lopes Rubim» nasceu em Alegre (ES) no dia 18 de janeiro de\n1912, filho de Francisco Lopes Rubim, funcionário público, e de Maria\nSílvia Lousada Rubim.',
 'Bacharel em ciências jurídicas e sociais pela Faculdade de Direito da\nUniversidade do Espírito Santo, formou-se também na Escola de Educação\nFísica do Rio de Janeiro, então Distrito Federal, em 1941.',
 'Nesse mesmo\nano ingressou na Força Policial do Espírito Santo, onde faria carreira.',
 'Participou da campanha militar brasileira na Itália durante a Segunda\nGuerra Mundial (1939-1945), retornando ao Brasil em 1945, ao término do\nconflito.',
 'Em 1946 assumiu a direção da Penitenciária do Estado do Espírito Santo,\nque ocupou até o ano seguinte.',
 'Em 1948 passou a ajudante-de-ordens na\nCasa Militar do governador do Espírito Santo, Carlos Lindenberg\n(1947-1951), função que exerceu até 1949.',
 'Em outubro de 1950 elegeu-se\ndeputado estadual pelo Espírito Santo, na legenda do Partido Trabalhista\nBrasileiro (PTB).',
 'Para assumir o mandato, foi reformado na Força\nPolicial do estado no posto de coronel, ocupando a sua cadeira no\nLegislativo em fevereiro de 1951.',
 'Nessa legislatura integrou as\ncomissões de Educação e Cultura e de Justiça da Assembléia capixaba.',
 'Em outubro de 1954 elegeu-se deputado federal por seu estado, agora com\no apoio da Coligação Democrática formada pelo PTB e pelos partidos\nRepublicano (PR), de Representação Popular (PRP) e Social Progressista\n(PSP).',
 'Encerrou o mandato estadual em janeiro de 1955, assumindo no mês\nseguinte sua cadeira na Câmara Federal.',
 'Ocupou-a, no entanto, apenas de\n2 a 9 de fevereiro, quando se licenciou para assumir a Secretaria de\nViação e Obras Públicas do Espírito Santo no governo de Francisco\nLacerda de Aguiar (1955-1959).',
 'Em abril reassumiu o mandato parlamentar\ne, até o fim da legislatura, foi vice-presidente da Comissão de\nSegurança Nacional da Câmara.',
 'Em maio de 1957, tornou-se vice-líder do\nPTB.',
 'Em outubro de 1958 tentou a reeleição, na mesma legenda, mas obteve\napenas uma suplência.',
 'Concluiu o mandato em janeiro de 1959.',
 'Em 1960, participou da campanha de Jânio Quadros para a presidência da\nRepública, filiando-se ao Partido Democrata Cristão (PDC).',
 'No entanto,\nno dia seguinte ao pleito realizado em outubro e vencido por Jânio,\ndesligou-se do PDC, filiando-se ao Partido Trabalhista Nacional (PTN).',
 'Em outubro de 1962 voltou a se eleger deputado federal com o apoio da\ncoligação formada pelo PTN e o Partido Social Democrático (PSD).',
 'Assumiu\no mandato em fevereiro de 1963, tornando-se vice-líder do partido na\nCâmara em junho do ano seguinte.',
 'Com a edição do Ato Institucional nº 2 (AI-2), em 27 de outubro de 1965,\nextinguindo os partidos políticos, e com a posterior implantação do\nbipartidarismo, filiou-se à Aliança Renovadora Nacional (Arena), partido\nde apoio ao regime militar instalado no país após a deposição do\npresidente João Goulart em 31 de março de 1964.',
 'Em novembro de 1966 reelegeu-se deputado federal pelo Espírito Santo, já\nna legenda da Arena, exercendo o mandato de fevereiro de 1967 a janeiro\nde 1971.',
 'Fundou a Imexferral Ltda., empresa de sua propriedade.',
 'Faleceu no dia 4 de maio de 1997.']

Busca textual#

Realizar uma busca textual em uma coleção de textos é muito mais eficiente do que simplesmente realizar operações sobre coleções de strings. Neste capítulo iremos examinar como “indexar” o DHBB, usando um simples indexador/buscador chamado whoosh.

from whoosh import index 
import os
from whoosh.fields import *
from IPython.core.display import display_html, HTML
schema = Schema(title=TEXT(stored=True), sexo=ID(stored=True), caminho=ID(stored=True), corpo=TEXT(stored=True))
if not os.path.exists('indexdir'):
    os.mkdir('indexdir')
    ix = index.create_in("indexdir", schema)
else:
    # ix = index.open_dir('indexdir') # se quiser adicionar novos documentos ao índice
    ix = index.create_in("indexdir", schema) # se Quiser começar do zero
writer = ix.writer()
for v in biograficos.itertuples():
    if pd.isnull(v.sexo):
        writer.add_document(title=v.title,sexo='na', caminho=v.arquivo,corpo=v.corpo)
    else:
        writer.add_document(title=v.title,sexo=v.sexo, caminho=v.arquivo,corpo=v.corpo)
writer.commit()
from whoosh.qparser import QueryParser
from whoosh import qparser
def busca(consulta):
    qp = QueryParser("corpo", ix.schema)
    qp.add_plugin(qparser.EveryPlugin())
    query = qp.parse(consulta)
    
    with ix.searcher() as searcher:
        results = [(dict(hit),hit.highlights("corpo")) for hit in searcher.search(query, limit=10)]
    return results
    
resultados = busca('assassin*')
print('Resultados:', len(resultados))
for res in resultados[:10]:
    print(res[0]['title'])
    display_html(HTML(res[1]))
   # encontra_pais(nltk.Text(nltk.tokenize.wordpunct_tokenize(res[0]['corpo'])))
Resultados: 10
PASCOAL, Hildebrando
prática de tortura e assassinatos e de conexão...também foram assassinadas, o que levou...foi indiciado por assassinato, envolvimento
RIPPOL, Valdemar
janeiro de 1934 foi assassinado a machadadas...o acusado do assassinato, Pedro Borges...Cunha; na segunda, o assassino seria um oficial
CUNHA, Francisco Flores da
de cumplicidade no assassinato do jornalista...delas, Rippoll fora assassinado pelo uruguaio...versão dizia que o assassino seria um oficial
MENDES, Chico
Wilson Pinheiro, assassinado com um tiro...Chico Mendes foi assassinado em sua residência...Um mês depois do assassinato foi realizado
CUNHA NETO, Augusto
mês, Cunha Neto foi assassinado com nove tiros...Na ocasião, o assassino declarou à polícia
VAZ, Rubens
major Rubens Vaz foi assassinado por tiros que...descoberta de que os assassinos eram pessoas
FURTADO, Heitor Alencar
Alencar Furtado foi assassinado por um policial...norte do Paraná. Seu assassinato representou
VANNUCHI, Paulo
uma lista geral dos assassinados desde 1964...das torturas e assassinatos praticados
CUNHA, Ceci
da Previdência. Foi assassinada em Maceió no...a apurar o assassinato, o próprio Talvane
BARROS, Manuel Gomes de
carros roubados e assassinavam por encomenda...sido o mandante do assassinato do bancário