├── Aelius ├── AeliusLite.py ├── AnotaCorpus.py ├── Avalia.py ├── CalculaEstatisticasLexicais.py ├── Chunking.py ├── Extras.py ├── MXPOST.py ├── ProcessaNomesProprios.py ├── SimplificaEtiquetas.py ├── Toqueniza.py ├── __init__.py └── expandecontracoes.py ├── Chunking.py ├── INSTALL.pdf ├── INSTALL.txt ├── LICENSE.txt ├── NOTICE.txt ├── README ├── __init__.py └── aelius_data ├── .DS_Store ├── AeliusBRUBT.pkl ├── AeliusHunPos ├── AeliusRUBT.pkl ├── LICENSE.txt ├── README.pdf ├── README.txt ├── exemplo.nltk.gold.txt ├── exemplo.txt └── freq_tycho_a.pkl /Aelius/ AeliusLite.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: AeliusLite.py $ 13 | 14 | """TODO. 15 | """ 16 | 17 | import os 18 | from Extras import carrega 19 | from AnotaCorpus import toqueniza_contracoes,anota_sentencas,anota_texto 20 | from Toqueniza import TOK_PORT, TOK_PORT_LX 21 | 22 | EXEMPLO=carrega("exemplo.txt") 23 | 24 | def AnotaTextoBRUBT(caminho_do_arquivo=EXEMPLO): 25 | if os.path.isabs(caminho_do_arquivo): 26 | raiz,nome=os.path.split(caminho_do_arquivo) 27 | 28 | else: 29 | raiz,nome=os.path.split(os.path.join(os.getcwd(),caminho_do_arquivo)) 30 | anota_texto(nome,raiz=raiz) 31 | 32 | # acrescentar outras funções desse tipo, como 33 | # AnotaTextoHunPos(caminho_do_arquivo), 34 | # AnotaTextoLXTagger(caminho_do_arquivo) etc. 35 | 36 | -------------------------------------------------------------------------------- /Aelius/AnotaCorpus.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: AnotaCorpus.py $ 13 | 14 | """Anota sentenças enquanto listas de tokens unicode ou arquivos de texto em utf-8, utilizando os toquenizadores e etiquetadores morfossintáticos especificados. Para anotar um texto de um arquivo, consultar documentação da função AnotaCorpus.anota_texto. Exemplo de toquenização e etiquetagem de uma sentença: 15 | 16 | >>> from Aelius.Extras import carrega 17 | >>> from Aelius import Toqueniza, AnotaCorpus 18 | >>> h=carrega('AeliusHunPos') 19 | >>> b=carrega('AeliusBRUBT.pkl') 20 | >>> lx=carrega('lxtagger') 21 | >>> tokens1=Toqueniza.TOK_PORT.tokenize(AnotaCorpus.EXEMPLO) 22 | >>> tokens2=Toqueniza.TOK_PORT_LX.tokenize(AnotaCorpus.EXEMPLO) 23 | >>> AnotaCorpus.anota_sentencas([tokens1],b) 24 | [[('Os', 'D-P'), ('candidatos', 'N-P'), ('classific\xc3\xa1veis', 'ADJ-G-P'), ('dos', 'P+D-P'), ('cursos', 'N-P'), ('de', 'P'), ('Sistemas', 'NPR-P'), ('de', 'P'), ('Informa\xc3\xa7\xc3\xa3o', 'NPR'), ('poder\xc3\xa3o', 'VB-R'), ('ocupar', 'VB'), ('as', 'D-F-P'), ('vagas', 'ADJ-F-P'), ('remanescentes', 'ADJ-G-P'), ('do', 'P+D'), ('Curso', 'NPR'), ('de', 'P'), ('Engenharia', 'NPR'), ('de', 'P'), ('Software', 'NPR'), ('.', '.')]] 25 | >>> AnotaCorpus.anota_sentencas([tokens1],h,'hunpos') 26 | [[('Os', 'D-P'), ('candidatos', 'N-P'), ('classific\xc3\xa1veis', 'ADJ-G-P'), ('dos', 'P+D-P'), ('cursos', 'N-P'), ('de', 'P'), ('Sistemas', 'NPR'), ('de', 'P'), ('Informa\xc3\xa7\xc3\xa3o', 'NPR'), ('poder\xc3\xa3o', 'VB-R'), ('ocupar', 'VB'), ('as', 'D-F-P'), ('vagas', 'ADJ-F-P'), ('remanescentes', 'N-P'), ('do', 'P+D'), ('Curso', 'NPR'), ('de', 'P'), ('Engenharia', 'NPR'), ('de', 'P'), ('Software', 'NPR'), ('.', '.')]] 27 | >>> AnotaCorpus.anota_sentencas([tokens2],lx,'mxpost') 28 | [[('Os', 'DA'), ('candidatos', 'CN'), ('classific\xc3\xa1veis', 'ADJ'), ('de', 'PREP'), ('os', 'DA'), ('cursos', 'CN'), ('de', 'PREP'), ('Sistemas', 'PNM'), ('de', 'PREP'), ('Informa\xc3\xa7\xc3\xa3o', 'PNM'), ('poder\xc3\xa3o', 'V'), ('ocupar', 'INF'), ('as', 'DA'), ('vagas', 'CN'), ('remanescentes', 'ADJ'), ('de', 'PREP'), ('o', 'DA'), ('Curso', 'PNM'), ('de', 'PREP'), ('Engenharia', 'PNM'), ('de', 'PREP'), ('Software', 'PNM'), ('.', 'PNT')]] 29 | 30 | """ 31 | import os,sys,nltk 32 | from cPickle import load 33 | from Aelius.Extras import carrega 34 | from ProcessaNomesProprios import * 35 | from ExpandeContracoes import expande_contracoes 36 | from Toqueniza import PUNKT,TOK_PORT 37 | from MXPOST import MXPOSTTagger 38 | 39 | EXEMPLO="Os candidatos classificáveis dos cursos de Sistemas de Informação poderão ocupar as vagas remanescentes do Curso de Engenharia de Software.".decode("utf-8") 40 | # Extraído da seguinte fonte: 41 | 42 | # UFC convoca os classificáveis do Vestibular 2010. Disponível em: 43 | # 45 | # Acesso em: 17/05/2011. 46 | 47 | # A seguinte variável permite expandir contrações para obter maior 48 | # acurácia com o LXTagger: 49 | EXPANDE_CONTRACOES=True 50 | 51 | TAGGER=carrega("AeliusBRUBT.pkl") 52 | 53 | # A seguinte variável global permite definir um 54 | # infixo para arquivos anotados; caso essa variável 55 | # permaneça com cadeia vazia como valor, o infixo é dado 56 | # pela arquitetura do etiquetador: 57 | INFIXO="" 58 | 59 | USUARIO= os.path.expanduser("~") 60 | 61 | HUNPOS=carrega("AeliusHunPos") 62 | 63 | DESTINO="." 64 | 65 | 66 | # O AeliusRUBT, usado como parte do procedimento 67 | # de expansão de contrações, é mais rápido que o AeliusBRUBT, 68 | # embora menos preciso: 69 | TAGGER2=carrega("AeliusRUBT.pkl") 70 | 71 | def toqueniza_contracoes(sentencas): 72 | """Esta função primeiro anota as sentenças com o TAGGER2, para depois utilizar seu output para separar as contrações. 73 | 74 | >>> tokens1=AnotaCorpus.TOK_PORT.tokenize(AnotaCorpus.EXEMPLO) 75 | >>> tokens1 76 | [u'Os', u'candidatos', u'classific\xe1veis', u'dos', u'cursos', u'de', u'Sistemas', u'de', u'Informa\xe7\xe3o', u'poder\xe3o', u'ocupar', u'as', u'vagas', u'remanescentes', u'do', u'Curso', u'de', u'Engenharia', u'de', u'Software', u'.'] 77 | >>> AnotaCorpus.toqueniza_contracoes([tokens1]) 78 | [[u'Os', u'candidatos', u'classific\xe1veis', u'de', u'os', u'cursos', u'de', u'Sistemas', u'de', u'Informa\xe7\xe3o', u'poder\xe3o', u'ocupar', u'as', u'vagas', u'remanescentes', u'de', u'o', u'Curso', u'de', u'Engenharia', u'de', u'Software', u'.']] 79 | >>> 80 | """ 81 | sents=anota_sentencas(sentencas,TAGGER2) 82 | sents=decodifica_sentencas_anotadas(sents) 83 | sents=[expande_contracoes(sent) for sent in sents] 84 | return [nltk.tag.untag(sent) for sent in sents] 85 | 86 | # as duas funções seguintes representam uma modularização do programa 87 | # e devem substituir partes de funções deste módulo e do módulo Avalia 88 | def codifica_sentencas(sentencas): 89 | """Toma unicode como input e retorna string. 90 | """ 91 | lista_sentencas=[] 92 | for sent in sentencas: 93 | cols=[] 94 | for w in sent: 95 | cols.append(w.encode("utf-8")) 96 | lista_sentencas.append(cols) 97 | return lista_sentencas 98 | 99 | def decodifica_sentencas(sentencas): 100 | """Toma string como input e retorna unicode. 101 | """ 102 | lista_sentencas=[] 103 | for sent in sentencas: 104 | cols=[] 105 | for w in sent: 106 | cols.append(w.decode("utf-8")) 107 | lista_sentencas.append(cols) 108 | return lista_sentencas 109 | 110 | def codifica_sentencas_anotadas(sentencas_anotadas): 111 | lista_codificada=[] 112 | for sent in sentencas_anotadas: 113 | cols=[] 114 | for w,t in sent: 115 | #print w,t # teste 116 | cols.append((w.encode("utf-8"),t.encode("utf-8"))) 117 | lista_codificada.append(cols) 118 | return lista_codificada 119 | 120 | def decodifica_sentencas_anotadas(sentencas_anotadas): 121 | lista_codificada=[] 122 | for sent in sentencas_anotadas: 123 | cols=[] 124 | for w,t in sent: 125 | cols.append((w.decode("utf-8"),t.decode("utf-8"))) 126 | lista_codificada.append(cols) 127 | return lista_codificada 128 | 129 | def extrai_corpus(arquivos,raiz=".", 130 | toquenizador_sentencial= PUNKT, 131 | #toquenizador_sentencial= REGEXP, 132 | toquenizador_vocabular=TOK_PORT, 133 | codificacao="utf-8"): 134 | 135 | """Retorna um corpus no formato do NLTK a partir de um arquivo de texto puro sem anotações. O texto é segmentado em palavras com base na expressão regular dada como argumento de nltk.RegexpTokenizer(), que retorna toquenizador armazenado na variável global em TOK_PORT. A segmentação em sentenças ocorre a cada quebra de linha. Em textos extraídos da WWW geralmente não há coincidência entre quebra de linha e final de sentença, o que torna necessária a definição de um outro toquenizador, o que é feito na função main, onde se utiliza o nltk.data.load('tokenizers/punkt/portuguese.pickle'). Assume-se que a codificação do texto-fonte é utf-8.""" 136 | 137 | return nltk.corpus.PlaintextCorpusReader(raiz, 138 | arquivos, 139 | sent_tokenizer=toquenizador_sentencial, 140 | word_tokenizer=toquenizador_vocabular, 141 | encoding=codificacao) 142 | 143 | def abre_etiquetador(modelo,arquitetura="nltk"): 144 | """Retorna etiquetador a partir do modelo e arquitetura especificados. O parâmetro 'arquitetura' tem como default 'nltk' e pode assumir também um dos seguintes valores: 'hunpos', 'stanford' ou 'mxpost'. Nesse último caso, é construída e retornada instância de etiquetador de um desses tipos, invocando o construtor das classes HunposTagger, StanfordTagger e MXPOSTTagger, respectivamente. A codificação dos modelos deve ser utf-8. Caso a arquitetura seja nltk, assume-se que se trata de instância de etiquetador do NLTK armazenada em formato binário (extensão do arquivo '.pkl' ou '.pickle', por exemplo), sendo retornada instância de etiquetador do NLTK armazenada em formato binário.""" 145 | if arquitetura == "nltk": 146 | f=open(modelo,"rb") 147 | etiquetador=load(f) 148 | f.close() 149 | return etiquetador 150 | if arquitetura =="hunpos": 151 | return nltk.tag.HunposTagger(modelo,encoding="utf-8") 152 | if arquitetura =="stanford": 153 | return nltk.tag.StanfordTagger(modelo,encoding="utf-8") 154 | if arquitetura =="mxpost": 155 | return MXPOSTTagger(modelo,encoding="utf-8") 156 | 157 | 158 | def anota_sentencas(sents,modelo,arquitetura="nltk"): 159 | '''A partir de uma lista de sentenças (cada sentença consituindo, por sua vez, uma lista de palavras), retorna uma lista de sentenças anotadas pelo etiquetador especificado nos parâmetros modelo e arquitetura (ver documentação da função abre_etiquetador). Cada sentença anotada é uma lista de duplas (w,t), onde w é uma palavra e t, uma etiqueta.''' 160 | 161 | # As cadeias da lista de entrada são primeiro 162 | # codificadas em utf-8, antes de serem etiquetadas. Isso se mostrou necessário 163 | # para que o anotador baseado em expressões regulares pudesse reconhecer 164 | # afixos com caracteres com código fora do intervalo definido por range(128), 165 | # não suportados pelo codec 'ascii'. 166 | 167 | # Simplicar, no futuro, esta função, pois, no caso da etiquetagem 168 | # pelas arquiteruras HunPos, StanfordTagger e MXPOST, as sentenças 169 | # precisam ser decodificadas e, depois, recodificadas. No caso 170 | # dessas arquiteturas, não haveria nem codificação nem decodificação; 171 | # a função tomaria unicode como entrada e retornaria unicode. Para 172 | # tanto, o módulo ProcessaNomesProprios também operaria em unicode. 173 | # A versão ProcessaNomesProprios02.py já opera com unicode. 174 | 175 | sents=codifica_sentencas(sents) 176 | 177 | #minusculiza palavras exceto as que ocorrem com maiúscula em posição não inicial 178 | sents=minusculiza_nao_nomes_proprios(sents) 179 | etiquetador=abre_etiquetador(modelo,arquitetura) 180 | if arquitetura in ["hunpos","stanford","mxpost"]: 181 | sents=decodifica_sentencas(sents) 182 | if EXPANDE_CONTRACOES and arquitetura == "mxpost": # para funcionar com o LX-Tagger 183 | sents=toqueniza_contracoes(sents) 184 | etiquetadas=etiquetador.batch_tag(sents) 185 | #print etiquetadas[0][0] 186 | #print type(etiquetadas[0][0][0]) 187 | if isinstance(etiquetadas[0][0][0],unicode): 188 | etiquetadas=codifica_sentencas_anotadas(etiquetadas) 189 | maiusculiza_inicio(etiquetadas) 190 | return etiquetadas 191 | 192 | def anota_paragrafos(paras,modelo,arquitetura): 193 | paragrafos=[] 194 | for paragrafo in paras: 195 | paragrafos.append(anota_sentencas( 196 | paragrafo,modelo,arquitetura)) 197 | return paragrafos 198 | 199 | # esta função foi substituída por várias funções de 200 | # escrita de corpus em arquivo (ver infra) 201 | def escreve_corpus(lista_de_sentencas,nome): 202 | """A partir de lista de sentenças anotadas, onde cada sentença é uma lista de pares ordenados do tipo de ('palavra','N'), escreve corpus em arquivo de texto 'nome' em que os tokens são etiquetados da forma canônica palavra/N.""" 203 | #f=open(os.path.join(USUARIO,nome),"w") 204 | f=open(nome,"w") # salva por defeito no diretório de trabalho 205 | c=1 # inicialização de um contador de palavras 206 | for sentenca in lista_de_sentencas: 207 | for palavra in sentenca: 208 | f.write("%s/%s<%d> " % (palavra[0], 209 | palavra[1], c ) ) 210 | c+=1 211 | f.write("\n") 212 | f.write("\n====================\n") # recurso para fase de teste 213 | f.close() 214 | 215 | def maiusculiza_inicio(lista_de_sentencas): 216 | '''Maiusculiza as palavras minúsculas no início de sentença. 217 | ''' 218 | for indice in range(len(lista_de_sentencas)): 219 | palavras,etiquetas= separa_palavras_de_etiquetas(lista_de_sentencas[indice]) 220 | palavras=maiusculiza_inicio_de_sentenca(palavras) 221 | lista_de_sentencas[indice]=zip(palavras,etiquetas) 222 | 223 | def formata_paragrafos(paras): 224 | '''Maiusculiza o início de cada sentença do parágrafo. 225 | ''' 226 | paragrafos=[] 227 | for p in paras: 228 | maiusculiza_inicio(p) 229 | 230 | 231 | def escreve_formato_nltk(paras,nome,desenvolvimento=False): 232 | separa_linhas="\n" 233 | separa_paragrafos="\n\n" 234 | c=1 # inicialização de um contador de palavras 235 | contador="" 236 | 237 | # recurso para fase de teste 238 | if desenvolvimento: 239 | separa_linhas="\n---------------\n" 240 | separa_paragrafos="\n====================\n" 241 | 242 | f=open(os.path.join(DESTINO,nome),"w") 243 | 244 | for p in paras: 245 | for sentenca in p: 246 | for palavra in sentenca: 247 | f.write("%s/%s%s " % (palavra[0],palavra[1], contador ) ) 248 | if desenvolvimento: 249 | contador="<%s>" % str(c) 250 | c+=1 251 | f.write(separa_linhas) 252 | f.write(separa_paragrafos) 253 | f.close() 254 | 255 | def escreve_formato_xml(paras,nome,capitulo="1"): 256 | # inicialização de contadores para palavras, sentenças e parágrafos 257 | c,s,p=1,1,1 258 | f=open(os.path.join(DESTINO,nome),"w") 259 | f.write('
' % capitulo) 260 | for para in paras: 261 | f.write('

' % p) 262 | for sentenca in para: 263 | f.write('' %s) 264 | for palavra in sentenca: 265 | f.write('%s ' % (c, palavra[1],palavra[0] ) ) 266 | c+=1 267 | f.write('') 268 | s+=1 269 | f.write('

') 270 | p+=1 271 | f.write('
') 272 | f.close() 273 | 274 | def main(modelo="AeliusBRUBT.pkl", 275 | arquitetura="nltk", 276 | toquenizador=TOK_PORT, 277 | textos_fonte=r"corpus\.txt", 278 | diretorio=".", 279 | caminho_de_destino=".", 280 | arquivo_de_destino="corpus.pos.txt"): 281 | 282 | '''Compila e anota um corpus.''' 283 | 284 | caminho=os.path.join(USUARIO,diretorio) 285 | corpus_nao_anotado=extrai_corpus(textos_fonte, 286 | caminho, 287 | toquenizador_sentencial=PUNKT, 288 | toquenizador_vocabular=toquenizador 289 | ) 290 | 291 | sentencas_nao_anotadas=list(corpus_nao_anotado.sents()) 292 | 293 | sentencas_anotadas=anota_sentencas(sentencas_nao_anotadas,modelo,arquitetura) 294 | 295 | escreve_corpus(sentencas_anotadas,os.path.join(caminho_de_destino,arquivo_de_destino)) 296 | 297 | def anota_texto(arquivo, 298 | modelo=TAGGER, 299 | arquitetura="nltk", 300 | toquenizador=TOK_PORT, 301 | raiz=".", 302 | codificacao="utf-8", 303 | formato=None): 304 | """Anota arquivo e salva o resultado, no atual diretório de trabalho, 305 | em arquivo com extensão .INFIXO.txt (onde INFIXO é o valor dessa variável 306 | global, se definida como cadeia não vazia, ou, caso contrário, o valor do 307 | parâmetro arquitetura). É possível especificar um dos três formatos da 308 | anotação: nltk (default), aelius e xml. 309 | Exemplo de anotação de um texto com o etiquetador default AeliusBRUBT.pkl: 310 | 311 | >>> import os 312 | >>> os.getcwd() 313 | '/Users/leonel' 314 | >>> os.chdir("analises") 315 | >>> os.getcwd() 316 | '/Users/leonel/analises' 317 | >>> from Aelius import AnotaCorpus 318 | >>> from Aelius.Extras import carrega 319 | >>> raiz,nome=os.path.split(carrega("exemplo.txt")) 320 | >>> AnotaCorpus.anota_texto(nome,raiz=raiz) 321 | Arquivo anotado: 322 | /Users/leonel/analises/exemplo.nltk.txt 323 | >>> AnotaCorpus.anota_texto(nome,raiz=raiz,formato="xml") 324 | Arquivo anotado: 325 | /Users/leonel/analises/exemplo.nltk.xml 326 | >>> 327 | 328 | """ 329 | 330 | nome_output="" 331 | corpus=extrai_corpus(arquivo, 332 | raiz=raiz, 333 | toquenizador_vocabular=toquenizador, 334 | codificacao=codificacao) 335 | paragrafos=corpus.paras() 336 | anotados=anota_paragrafos(paragrafos,modelo,arquitetura) 337 | if INFIXO: 338 | infixo=INFIXO 339 | else: 340 | infixo=arquitetura 341 | #formata_paragrafos(anotados) # linha a ser eliminada? 30.03.2011 342 | lista=arquivo.split(".txt") 343 | base_nome_input=lista[0] 344 | if formato: 345 | if formato == "aelius": 346 | nome_output="%s.pos.%s.txt" % (base_nome_input,formato) 347 | nome_output=escreve_formato_nltk(anotados,nome_output,desenvolvimento=True) 348 | elif formato == "xml": 349 | nome_output="%s.%s.%s" % (base_nome_input,infixo,formato) 350 | escreve_formato_xml(anotados,nome_output) 351 | else: 352 | nome_output="%s.%s.txt" % (base_nome_input,infixo) 353 | escreve_formato_nltk(anotados,nome_output) 354 | else: 355 | nome_output="%s.%s.txt" % (base_nome_input,infixo) 356 | escreve_formato_nltk(anotados,nome_output) 357 | print "Arquivo anotado:\n%s" % os.path.join(os.getcwd(),nome_output) 358 | 359 | 360 | # a função main pode ser executada a partir da shell do sistema 361 | # operacional 362 | if __name__ == '__main__': 363 | if len(sys.argv) == 1: # sem argumentos executa-se main com os valores default 364 | main() 365 | else: # os parâmetros da função podem ser especificados na linha de comando 366 | main(arquivo_do_etiquetador=sys.argv[1], 367 | textos_fonte=r"%s" % sys.argv[2], 368 | diretorio=sys.argv[3], 369 | caminho_de_destino=sys.argv[4], 370 | arquivo_de_destino=sys.argv[5]) 371 | 372 | def teste(s): 373 | lista = TOK_PORT.tokenize(s) 374 | for e in lista: 375 | print e 376 | return lista 377 | -------------------------------------------------------------------------------- /Aelius/Avalia.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: Avalia.py $ 13 | """Este módulo contém funções úteis para avaliação de etiquetadores morfossintáticos. 14 | """ 15 | import os 16 | import nltk,AnotaCorpus 17 | from cPickle import dump 18 | from ProcessaNomesProprios import minusculiza_nao_nomes_proprios 19 | VERBOSE=False 20 | LISTA_DE_ERROS=[] 21 | USUARIO=os.path.expanduser("~") 22 | 23 | def avalia(output,gold,tagged_words): 24 | """Output é a lista de sentenças anotadas automaticamente, 25 | gold é a lista de sentenças anotadas por humano; tagged_words é a 26 | quantidade de tokens anotados. Se o valor da variável global VERBOSE 27 | for True, os erros são armazenados na variável global LISTA_DE_ERROS. 28 | """ 29 | erros=0 30 | del LISTA_DE_ERROS[:] 31 | for m in range(len(output)): 32 | for n in range(len(output[m])): 33 | out=output[m][n] 34 | g=gold[m][n] 35 | if out[1] != g[1]: 36 | erros+=1 37 | # esta variável global, cujo valor é um booliano, determina o armazenamento ou não# dos erros 38 | if VERBOSE: 39 | LISTA_DE_ERROS.append((out[0],out[1],g[0],g[1])) 40 | # os erros só poderão ser exibidos se tiverem sido armazenados nesta lista 41 | print "Total de erros: %d\nTotal de palavras:%d\nAcurácia:%f" % (erros,tagged_words,100-100.0/tagged_words*erros) 42 | 43 | def exibe_erros(maximo=None): 44 | """Exibe os erros armazenados na variável global LISTA_DE_ERROS. 45 | """ 46 | if LISTA_DE_ERROS: 47 | i=0 48 | print "Anotação automática\tAnotação humana\n" 49 | if maximo: 50 | limite=maximo 51 | else: 52 | limite=len(LISTA_DE_ERROS) 53 | while i < limite: 54 | w1,t1,w2,t2=LISTA_DE_ERROS[i] 55 | print "%s/%s\t%s/%s" % (w1,t1,w2,t2) 56 | i+=1 57 | else: 58 | print "Lista de erros vazia." 59 | 60 | def grava_erros(arquivo): 61 | """Grava em arquivo os erros armazenados na variável global LISTA_DE_ERROS. 62 | """ 63 | if LISTA_DE_ERROS: 64 | f=open(arquivo,"w") 65 | for w1,t1,w2,t2 in LISTA_DE_ERROS: 66 | f.write("%s\t%s\t%s\t%s\n" % (w1,t1,w2,t2)) 67 | f.close() 68 | else: 69 | print "Lista de erros vazia." 70 | 71 | def testa_etiquetador(nome,arquitetura,raiz=".",ouro="luzia_gold\.cap5\.txt"): 72 | etiquetador=AnotaCorpus.abre_etiquetador(nome,arquitetura) 73 | corpus=nltk.corpus.TaggedCorpusReader(raiz,ouro, 74 | sent_tokenizer=nltk.RegexpTokenizer(r"\n",gaps=True), 75 | encoding="utf-8") 76 | # sents são listas de tokens em unicode 77 | sents=list(corpus.sents()) 78 | # a seguinte operação transforma unicode em string 79 | lista_sents=AnotaCorpus.codifica_sentencas(sents) 80 | # o output da minusculização são listas de strings 81 | lista_sents2=minusculiza_nao_nomes_proprios(lista_sents) 82 | # mudar o seguinte levando em conta parâmetro arquitetura 83 | if arquitetura=="hunpos": 84 | # O HunposTagger exige input em unicode; por isso, neste comando, 85 | # transformamos unicode em string 86 | output1=etiquetador.batch_tag(AnotaCorpus.decodifica_sentencas(lista_sents2)) 87 | # transformação do output do HunposTagger de unicode para string 88 | output=AnotaCorpus.codifica_sentencas_anotadas(output1) 89 | else: 90 | output=etiquetador.batch_tag(lista_sents2) 91 | tagged_words=len(corpus.tagged_words()) 92 | tagged_sents=list(corpus.tagged_sents()) 93 | # transformação do padrão-ouro de unicode para string 94 | lista_gold=AnotaCorpus.codifica_sentencas_anotadas(tagged_sents) 95 | avalia(output,lista_gold,tagged_words) 96 | 97 | def imprime(lista_de_sentencas,arquivo): 98 | f=open(arquivo,"w") 99 | for m in range(len(lista_de_sentencas)): 100 | for n in range(len(lista_de_sentencas[m])): 101 | p=lista_de_sentencas[m][n] 102 | palavra=p[0].encode("utf-8") 103 | etiqueta=p[1].encode("utf-8") 104 | f.write( "%s/%s " % (palavra,etiqueta) ) 105 | f.write("\n") 106 | f.close() 107 | 108 | def tempo(): 109 | t1=time.time() 110 | avalia.testa_etiquetador("etiquetador213_100_01.pkl","amostra",".*_pos.*gold.txt") 111 | t2=time.time() 112 | print "Tempo de avaliação RUBT: %f" % (t2-t1) 113 | t1=time.time() 114 | avalia.testa_etiquetador("braupt213_100_01_100.pkl","amostra",".*_pos.*gold.txt") 115 | t2=time.time() 116 | print "Tempo de avaliação BRUBT: %f" % (t2-t1) 117 | 118 | def main(): 119 | testa_etiquetador("/Users/shane/aelius/etiquetador143_100.pkl", 120 | "/Users/shane/aelius/senhora", 121 | r"senhora_aa\.pos\.txt") 122 | -------------------------------------------------------------------------------- /Aelius/CalculaEstatisticasLexicais.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: CalculaEstatisticasLexicais.py $ 13 | 14 | """Este módulo contém funções para análise automática de textos. 15 | Certifique-se de que os arquivo a serem processados estão em utf-8.""" 16 | 17 | import nltk, pickle, os 18 | from nltk.probability import FreqDist 19 | 20 | MODELO=os.path.join(os.path.expanduser("~"),"aelius_data/freq_tycho_a.pkl") 21 | 22 | def FreqCorpusRef(arquivo=MODELO): 23 | """Extrai de arquivo a distribuição de freqüências de um corpus de 24 | referência 25 | """ 26 | f=open(arquivo,"rb") 27 | return pickle.load(f) 28 | 29 | def CriaLeitorDeCorpus(diretorio=".",arquivo="conquista_div_01.txt", codificacao="utf-8"): 30 | return nltk.corpus.PlaintextCorpusReader(diretorio,arquivo,encoding=codificacao) 31 | 32 | def ExtraiAlfabeticas(palavras): 33 | 'A partir de uma lista de token, extrai as palavras alfabéticas e as minusculiza' 34 | # minusculize p para p na lista de palavras se p é alfabético 35 | return [p.lower() for p in palavras if p.isalpha()] 36 | 37 | def CalculaDiversidadeLexical(freq): 38 | '''Esta função calcula a diversidade lexical de um texto a partir de 39 | uma distribuição de frequências do NLTK.''' 40 | print "Calculando diversidade lexical..." 41 | return 100*float(freq.B())/freq.N() 42 | 43 | def ExibeListaDeFrequencias(freq, maximo=30): 44 | """A partir de instância da classe FreqDist do NLTK, esta função constrói 45 | uma lista dos itens mais freqüentes, com as respectivas quantidades de ocorrências 46 | e freqüencias. 47 | """ 48 | i=0 49 | for k in freq: 50 | if i < maximo: 51 | print "%d\t%s\t%d\t%.4f" % (i+1,k, freq[k],100*freq.freq(k)) 52 | else: 53 | break 54 | i+=1 55 | 56 | def ComparaListasDeFrequencias(freq,freq_ref=FreqCorpusRef(),maximo=30,dif_min=0.0015): 57 | """Compara as distribuições de freqüências de um corpus de estudo 58 | e de um corpus de referência. 59 | """ 60 | r=[] # resultados 61 | i=0 62 | print "nr.\tpalavra\t%\tdiferença %" 63 | for f in freq: 64 | if i > maximo-1: 65 | break 66 | if (freq.freq(f) - freq_ref.freq(f)) > dif_min: 67 | r.append((freq.freq(f) - freq_ref.freq(f),f,freq.freq(f)) ) 68 | #print i 69 | print "%d\t%s\t\t%.3f\t%.6f" % (i+1,f,freq.freq(f) ,freq.freq(f) - freq_ref.freq(f)) 70 | i+=1 71 | 72 | r.sort(reverse=True) 73 | print "\nResultados ordenados decrescentemente pelas diferenças de freqüência\n" 74 | for e in r: 75 | print "%.6f\t\t%s\t\t%.3f" % e 76 | 77 | def DefinePontuacao(incluir=[],excluir=[]): 78 | """Esta função permite incluir ou excluir elementos da lista 79 | pré-definida de sinais de pontuação de Python. 80 | """ 81 | from string import punctuation as punct 82 | punct=[p for p in punct] 83 | if incluir: 84 | punct.extend(incluir) 85 | if excluir: 86 | punct=[p for p in punct if p not in excluir] 87 | return punct 88 | 89 | def SeparaPontuacao(texto, pontuacao=DefinePontuacao(excluir=["-"])): 90 | """Esta função separa as palavras dos sinais de pontuação, contidos 91 | por defeito na lista retornada pela função DefinePontuacao(). 92 | """ 93 | s="" 94 | for c in linha: 95 | if c in pontuacao: 96 | s="%s %s" % (s,c) 97 | else: 98 | s="%s%s" % (s,c) 99 | return s 100 | 101 | def ProcessaArquivo(f): 102 | """Calcula estatísticas do arquivo dado.""" 103 | print "Processando arquivo %s..." % f 104 | corpus=CriaLeitorDeCorpus(arquivo=f) 105 | tokens=corpus.words() 106 | print "Quantidade de tokens: %d." % len(tokens) 107 | alfabeticas=ExtraiAlfabeticas(tokens) 108 | print "Quantidade de tokens alfabéticos: %d." % len(alfabeticas) 109 | freq=FreqDist(alfabeticas) 110 | print "Diversidade lexical: %.2f%%" % CalculaDiversidadeLexical(freq) 111 | print "Quantidade de hapaxes: %d.\n\n\n" % len(freq.hapaxes()) 112 | 113 | 114 | def ProcessaArquivos(*lista_de_arquivos): 115 | for f in lista_de_arquivos: 116 | if os.path.isfile(f): 117 | ProcessaArquivo(f) 118 | else: 119 | print 'Arquivo "%s" inexistente em %s\n\n' % (f,os.getcwd()) 120 | 121 | 122 | -------------------------------------------------------------------------------- /Aelius/Chunking.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: Chunking.py $ 13 | 14 | """Chunker simples para o português baseado no tagset do LX-Tagger 15 | http://lxcenter.di.fc.ul.pt/tools/pt/conteudo/LXTagger.html""" 16 | 17 | import nltk 18 | from os.path import join,splitext 19 | 20 | RAIZ="analises" 21 | TEXTO="ufc_07042011.pdt.mxpost.txt" 22 | 23 | CHUNKER= nltk.chunk.RegexpParser(r''' 24 | NP: 25 | {?(???)*} 26 | {?????????***(||)**} 27 | ''') 28 | 29 | def CriaChunkedCorpus(raiz,texto,chunker=CHUNKER,destino=None,formato="trees"): 30 | nome_do_destino="" 31 | if destino: 32 | nome_do_destino=destino 33 | else: 34 | nome_do_destino=join(raiz,"%s.%s.txt" % (splitext(texto)[0],formato)) 35 | f=open(nome_do_destino,"w") 36 | c=nltk.corpus.TaggedCorpusReader(raiz,texto,encoding="utf-8") 37 | sents=c.tagged_sents() 38 | chunked_sents=chunker.batch_parse(sents) 39 | if formato=="IOB": 40 | for s in chunked_sents: 41 | s=nltk.chunk.tree2conllstr(chunker.parse(s)) 42 | f.write("%s\n\n" % (s.encode("utf-8"))) 43 | else: 44 | for s in chunked_sents: 45 | f.write("%s\n\n" % (s.pprint().encode("utf-8"))) 46 | f.close() 47 | 48 | def IOB2trees(arquivo): 49 | """A partir de um arquivo de sentenças cujos chunks do tipo NP estão anotados no formato IOB, retorna uma lista de árvores do tipo nltk.Tree. 50 | """ 51 | linhas=open(arquivo).read().strip().split("\n\n") 52 | return [nltk.chunk.conllstr2tree(c) for c in linhas] 53 | 54 | def avalia(arquivo,chunker=CHUNKER): 55 | """Avalia um chunker com base num arquivo no formato IOB corrigido por humano. 56 | """ 57 | trees=IOB2trees(arquivo) 58 | print chunker.evaluate(trees) 59 | 60 | def main(): 61 | CriaChunkedCorpus(RAIZ,TEXTO,CHUNKER,destino=None,formato="trees") 62 | CriaChunkedCorpus(RAIZ,TEXTO,CHUNKER,destino=None,formato="IOB") 63 | -------------------------------------------------------------------------------- /Aelius/Extras.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 3 | # 4 | # Copyright (C) 2010-2011 Leonel F. de Alencar 5 | # Author: Leonel F. de Alencar 6 | # 7 | # URL: 8 | # For license information, see LICENSE.TXT 9 | # 10 | # $Id: Extras.py $ 11 | 12 | """Contém algumas funções utilizadas por diversos outros módulos, 13 | como a função 'carrega', que facilita o uso de modelos,corpora e 14 | outros recursos. 15 | Esses recursos devem estar armazenados ou no diretório 16 | $HOME/aelius_data ou no diretório especificado na variável de ambiente 17 | AELIUS_DATA.""" 18 | 19 | import os 20 | 21 | USUARIO=os.path.expanduser("~") # extração do valor de $HOME 22 | CAMINHO=os.path.join(USUARIO,"aelius_data") 23 | 24 | # AELIUS_DATA é o valor da variável de ambiente $AELIUS_DATA ou o valor 25 | # da variável da variável global CAMINHO 26 | AELIUS_DATA=os.environ.get("AELIUS_DATA",CAMINHO) 27 | 28 | 29 | def carrega(caminho): 30 | """Assume-se que 'caminho' é definido relativamente à variável AELIUS_DATA. 31 | """ 32 | p=os.path.join(AELIUS_DATA,caminho) 33 | if os.path.exists(p): 34 | return p 35 | else: 36 | print "Arquivo ou diretório inexistente: %s" % p 37 | 38 | def escreve_corpus(sentencas_anotadas,nome_do_arquivo): 39 | """Escreve em arquivo sentenças no formato unicode, anotadas, dadas como listas de pares (w,t), onde w é uma palavra e t, uma etiqueta. 40 | """ 41 | sep1="\t" # separador entre palavra e etiqueta 42 | sep2="\n" # separador de pares de palavra e etiqueta 43 | sep3= "\n" # separador de sentenças 44 | arquivo=open(nome_do_arquivo,"w") 45 | for s in tagged_sents: 46 | for w,t in s: 47 | arquivo.write("%s%s%s%s" % (w.encode("utf-8"),sep1,t.encode("utf-8"),sep2)) 48 | arquivo.write(sep3) 49 | arquivo.close() 50 | -------------------------------------------------------------------------------- /Aelius/MXPOST.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 3 | # 4 | # Interface to Adwait Ratnaparkhi's MXPOST POS-tagger 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # URL: 9 | # For license information, see LICENSE.TXT 10 | # 11 | # $Id: mxpost.py $ 12 | 13 | # Code and documentation mostly adapted from the following open source, 14 | # licensed under the Apache License, Version 2.0: 15 | # Natural Language Toolkit: Interface to the Stanford POS-tagger 16 | # 17 | # Copyright (C) 2001-2011 NLTK Project 18 | # Author: Nitin Madnani 19 | # URL: 20 | # 21 | # $Id: stanford.py $ 22 | 23 | """ 24 | A module for interfacing with Adwait Ratnaparkhi's MXPOST POS-tagger. 25 | """ 26 | 27 | import os 28 | from subprocess import PIPE 29 | import tempfile 30 | import nltk 31 | #from api import * 32 | 33 | _mxpost_url = 'http://sites.google.com/site/adwaitratnaparkhi/publications' 34 | 35 | class MXPOSTTagger(nltk.TaggerI): 36 | """ 37 | A class for pos tagging with MXPOST Tagger. The input is the paths to: 38 | - a model trained on training data 39 | - (optionally) the path to the mxpost tagger jar file. If not specified here, 40 | then this jar file must be specified in the CLASSPATH envinroment variable. 41 | - (optionally) the encoding of the training data (default: ASCII) 42 | 43 | Example using the LX-Tagger Portuguese model from http://lxcenter.di.fc.ul.pt/tools/en/LXTaggerEN.html, described in the following paper: 44 | 45 | Branco, António and João Silva, 2004, Evaluating Solutions for the Rapid Development of State-of-the-Art POS Taggers for Portuguese. In Maria Teresa Lino, Maria Francisca Xavier, Fátima Ferreira, Rute Costa and Raquel Silva (eds.), Proceedings of the 4th International Conference on Language Resources and Evaluation (LREC2004), Paris, ELRA, ISBN 2-9517408-1-6, pp.507-510. 46 | 47 | >>> mx = MXPOSTTagger('UTF-8_Model_Cintil_Written') 48 | >>> mx.tag('Será que está funcionando ?'.decode('utf-8').split()) 49 | [(u'Ser\xe1', u'V'), (u'que', u'CJ'), (u'est\xe1', u'V'), (u'funcionando', u'GER'), (u'?', u'PNT')] 50 | """ 51 | def __init__(self, path_to_model, path_to_jar=None, encoding=None, verbose=False): 52 | 53 | self._mxpost_jar = nltk.internals.find_jar( 54 | 'mxpost.jar', path_to_jar, 55 | searchpath=(), url=_mxpost_url, 56 | verbose=verbose) 57 | 58 | if not os.path.isdir(path_to_model): 59 | raise IOError("MXPOST tagger model file not found: %s" % path_to_model) 60 | self._mxpost_model = path_to_model 61 | self._encoding = encoding 62 | 63 | def tag(self, tokens): 64 | return self.batch_tag([tokens])[0] 65 | 66 | def batch_tag(self, sentences): 67 | encoding = self._encoding 68 | nltk.internals.config_java(options='-mx30m', verbose=False) 69 | 70 | # Create a temporary input file 71 | _input_fh, _input_file_path = tempfile.mkstemp(text=True) 72 | 73 | # Build the java command to run the tagger 74 | _mxpos_cmd = ['tagger.TestTagger', \ 75 | self._mxpost_model] 76 | 77 | # Write the actual sentences to the temporary input file 78 | _input_fh = os.fdopen(_input_fh, 'w') 79 | _input = '\n'.join((' '.join(x) for x in sentences)) 80 | if isinstance(_input, unicode) and encoding: 81 | #_input = _input.encode("utf-8") 82 | _input = _input.encode(encoding) 83 | _input_fh.write(_input) 84 | _input_fh.close() 85 | 86 | # Run the tagger and get the output 87 | _input_fh=open(_input_file_path,"r") 88 | mxpos_output, _stderr = nltk.internals.java(_mxpos_cmd, 89 | classpath=self._mxpost_jar, 90 | stdin= _input_fh,stdout=PIPE, stderr=PIPE) 91 | _input_fh.close() 92 | if encoding: 93 | mxpos_output = mxpos_output.decode(encoding) 94 | 95 | # Delete the temporary file 96 | os.unlink(_input_file_path) 97 | 98 | # Output the tagged sentences 99 | tagged_sentences = [] 100 | for tagged_sentence in mxpos_output.strip().split("\n"): 101 | sentence = [tuple(tagged_word.strip().split("_")) 102 | for tagged_word in tagged_sentence.strip().split()] 103 | tagged_sentences.append(sentence) 104 | return tagged_sentences 105 | 106 | -------------------------------------------------------------------------------- /Aelius/ProcessaNomesProprios.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: ProcessaNomesProprios.py $ 13 | 14 | #from cPickle import dump 11042011 15 | import string, nltk 16 | 17 | exemplos=["– Luzia pediu a Deus e a Ávila para que lhe ajudassem a sair de Sobral .", 18 | "Deus ajudou Luzia .", 19 | "... Sobral era uma cidade intelectual .", 20 | "... Cidade intelectual , Sobral tinha muitos poetas .", 21 | "Município intelectual , Sobral tinha muitos poetas ", 22 | "Fortaleza era uma cidade provinciana .", # também é minusculizada, embora seja nome próprio 23 | "Ávila ajudou Luzia .", 24 | "... Cansada , Luzia logo dormiu .", 25 | "Ávida por sossego , Luzia deixou a cidade .", 26 | "Ótimo !", 27 | "Bom", 28 | "... – Bom", 29 | "?", 30 | "! ? ? –", 31 | ""] 32 | 33 | # sinais de pontuação que podem 34 | # ocorrer no início das sentenças; 35 | # pressupõe-se que no texto ocorre apenas 36 | # Unicode 2013 EN DASH e reticências como sinais 37 | # de pontuação além dos definidos em string.punctuation 38 | pontuacao="–...%s" % string.punctuation 39 | 40 | 41 | 42 | # palavras com inicial maiúscula em 43 | # posição não inicial 44 | dicionario=nltk.defaultdict(int) 45 | 46 | def extrai_palavra_inicial(sentenca): 47 | '''Extrai índice da palavra que se encontra no início de uma sentença 48 | dada, sob a forma de uma lista de tokens, com cada sinal de pontuação, tal como 49 | definido na variável global pontuacao,representando um token. 50 | Caso iniciada por sinal de pontuação, esse é ignorado. 51 | ''' 52 | comprimento=len(sentenca) 53 | if comprimento < 1: 54 | return -1 55 | 56 | 57 | else: 58 | inicio=0 59 | indice=0 60 | condicao=True 61 | 62 | while condicao and comprimento > indice: 63 | # print sentenca 64 | # print len(sentenca), indice 65 | if sentenca[indice] in pontuacao: # pressupõe entrada toquenizada 66 | inicio+=1 67 | else: 68 | condicao=False 69 | return inicio 70 | indice+=1 71 | return -1 72 | 73 | def armazena_palavras_maiusculas(sentenca): 74 | inicio=extrai_palavra_inicial(sentenca) 75 | if inicio >= 0: 76 | for palavra in sentenca[inicio+1:]: 77 | p=palavra.decode("utf-8") 78 | if len(p) > 0 and p[0].isupper(): #aqui 14092010 79 | dicionario[palavra]+=1 80 | #f=open("dicionario_maiusculas.pkl","wb") # isso é mesmo necessário? 11042011: não! 81 | #dump(dicionario,f,-1) 82 | #f.close() 83 | 84 | def minusculiza_nao_nomes_proprios(lista_de_sentencas): 85 | for sent in lista_de_sentencas: 86 | if len(sent) > 0: 87 | armazena_palavras_maiusculas(sent) 88 | for sent in lista_de_sentencas: 89 | if len(sent) > 0: 90 | inicio=extrai_palavra_inicial(sent) 91 | if inicio >=0 and dicionario[sent[inicio]] == 0: 92 | # print "Início da sentença:%s" % sent[inicio] 93 | palavra=sent[inicio] 94 | palavra=palavra.decode("utf-8").lower() 95 | sent[inicio]= palavra.encode("utf-8") 96 | 97 | # print dicionario 98 | return lista_de_sentencas 99 | 100 | def maiusculiza_inicio_de_sentenca(sentenca): 101 | inicio=extrai_palavra_inicial(sentenca) 102 | if inicio >= 0: 103 | palavra=sentenca[inicio] 104 | # print palavra 105 | palavra=palavra.decode("utf-8") 106 | palavra="%s%s" % (palavra[0].upper(),palavra[1:]) 107 | sentenca[inicio]=palavra.encode("utf-8") 108 | # print "Início da sentença:%s" % sentenca[inicio] 109 | return sentenca # como o argumento da função é uma lista, esta é 110 | # alterada pela função, tornando-se desnecessário retorná-la; talvez fosse 111 | # mais elegante criar uma nova lista 112 | 113 | def separa_palavras_de_etiquetas(lista_de_tuplas): 114 | lista_de_palavras=[w for w,t in lista_de_tuplas] 115 | lista_de_etiquetas=[t for w,t in lista_de_tuplas] 116 | return lista_de_palavras,lista_de_etiquetas 117 | 118 | def pprint(lista_de_sentencas): 119 | for sent in lista_de_sentencas: 120 | for p in sent: 121 | print p, 122 | print 123 | 124 | def main(): 125 | lista_de_sentencas=[] 126 | for sent in exemplos: 127 | lista_de_sentencas.append(sent.split()) 128 | for sent in lista_de_sentencas: 129 | armazena_palavras_maiusculas(sent) 130 | #print dicionario 131 | lista_de_sentencas=minusculiza_nao_nomes_proprios(lista_de_sentencas) 132 | pprint(lista_de_sentencas) 133 | for i in range(len(lista_de_sentencas)): 134 | lista_de_sentencas[i]=maiusculiza_inicio_de_sentenca(lista_de_sentencas[i]) 135 | pprint(lista_de_sentencas) 136 | 137 | if __name__ == '__main__': 138 | main() 139 | -------------------------------------------------------------------------------- /Aelius/SimplificaEtiquetas.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: SimplificaEtiquetas.py $ 13 | 14 | """Este módulo contém funções para simplificar as etiquetas de um corpus anotado ou transformar um conjunto de etiquetas em outro.""" 15 | 16 | from Aelius import carrega 17 | 18 | ARQUIVO=carrega("tag_mapping.txt") 19 | 20 | def constroiDicionarioDeArquivo(arquivo=ARQUIVO): 21 | """Esta função transforma arquivo cujas linhas são pares do tipo 22 | 23 | etiqueta1 etiqueta 24 | 25 | em um dicionário em que o valor de dic[etiqueta1] é etiqueta2. 26 | """ 27 | dic={} 28 | f=open(arquivo,"rU") 29 | for linha in f: 30 | chave,valor=linha.strip().split() 31 | dic[chave]=valor 32 | return dic 33 | 34 | def LXTagger2CHPTB(etiqueta): 35 | """Esta função converte uma etiqueta do LXTagger em uma etiqueta do CHPTB ou no estilo deste corpus, conforme um dicionário que mapeia um conjunto de etiquetas em outro. Caso uma etiqueta não esteja incluída como chave no dicionário, a função retorna simplesmente retorna a etiqueta dada como entrada. 36 | """ 37 | dicionario=constroiDicionarioDeArquivo() 38 | t=dicionario.get(etiqueta,0) 39 | if t: 40 | return t 41 | else: 42 | return etiqueta 43 | -------------------------------------------------------------------------------- /Aelius/Toqueniza.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: Toqueniza.py $ 13 | 14 | """Este módulo oferece diversos recursos para a toquenizaçao de cadeias 15 | em unicode, representando sentenças de línguas como a portuguesa. 16 | Por toquenização de uma cadeia entende-se a transformação da cadeia 17 | em uma lista de cadeias representando palavras, expressões numéricas 18 | etc. da língua.""" # esta documentação necessita de atualização 19 | 20 | import string, nltk 21 | 22 | # Para facilitar a testagem do módulo, definimos a variável global SENT: 23 | SENT="""O Prof. Leonel (UFC) chamou a atenção para o fato de que a toquenização é um processo não trivial, 24 | devido, sobretudo, às abreviaturas.""" 25 | # Decodificação da cadeia unicode utilizando a codificação utf-8: 26 | SENT=SENT.decode("utf-8") 27 | 28 | # Outros exemplos para testar toquenização: 29 | TEXTO="""O Dr. José P. Fernandes disse-lhe que a pistola .45 custa R$ 3,5 mil, 35.08% de Cz$ 3.800,98, às 18h30min da segunda-feira (22/10/2010). 30 | No passado. 31 | Dir-se-ia que ele deu com os burros n'água...""".decode("utf-8") 32 | SENTENCAS=TEXTO.split("\n") 33 | 34 | SINAIS=string.punctuation 35 | 36 | # Esta lista contém apenas algumas das abreviaturas mais comuns, 37 | # não sendo, portanto, exaustiva. Deve ser complementada consultando-se 38 | # dicionários e pesquisando-se em corpora. 39 | ABREVIATURAS="Prof Profa Sr Sra Dr Dra".split() 40 | 41 | 42 | # Hífen, apóstrofo e ponto como parte de palavra; 43 | # esta expressão deve ser usada para toquenizar textos 44 | # a serem anotados por etiquetadores como o LX-Tagger; 45 | # o ponto final delimitador de sentença, contudo, não é separado: 46 | EX='''(?x)[A-Za-z]+[.$]| # palavras terminando em ponto ou cifrão 47 | [)(!?"]| # sinais de pontuação 48 | \.\d+| # expressões numéricas como .45 49 | \d+[.,]\d+[.,]\d+| # números como 3.800,98 50 | \d+[.,]\d+| # números como 3,5 e 3.5 51 | \w+([-\']\w+|\w+)* | # palavras com ou sem hífen ou apóstrofo 52 | \.{2,} | # dois ou mais pontos (reticências) 53 | [^\w\s]+ # não palavras e não espaços''' 54 | 55 | # Toqueniza sem separar hifens, apóstrofos e pontos, 56 | # conforme descrição acima. 57 | _TOK_PORT_LX=nltk.RegexpTokenizer(EX) 58 | 59 | def SeparaPontoFinal(s): 60 | """Toqueniza reticências e pontos finais delimitadores 61 | de sentenças, mas não pontos que constituem parte de palavras. 62 | """ 63 | s=s.rstrip() 64 | if s.endswith("..."): 65 | return "%s ..." % s[:-3] 66 | elif s.endswith("."): 67 | return "%s ." % s[:-1] 68 | else: 69 | return s 70 | 71 | class ToquenizadorBifasico(): 72 | """Esta classe oferece método tokenize que toqueniza em duas 73 | fases. Por exemplo: 74 | 75 | >>> from Aelius.Toqueniza import TOK_PORT_LX 76 | >>> from Aelius.Toqueniza import SENTENCAS 77 | >>> for s in SENTENCAS: 78 | for t in TOK_PORT_LX.tokenize(s): 79 | print t 80 | print 81 | 82 | 83 | O 84 | Dr. 85 | José 86 | P. 87 | Fernandes 88 | disse-lhe 89 | que 90 | a 91 | pistola 92 | .45 93 | custa 94 | R$ 95 | 3,5 96 | mil 97 | , 98 | 35.08 99 | % 100 | de 101 | Cz$ 102 | 3.800,98 103 | , 104 | às 105 | 18h30min 106 | da 107 | segunda-feira 108 | ( 109 | 22 110 | / 111 | 10 112 | / 113 | 2010 114 | ) 115 | . 116 | 117 | No 118 | passado 119 | . 120 | 121 | Dir-se-ia 122 | que 123 | ele 124 | deu 125 | com 126 | os 127 | burros 128 | n'água 129 | ... 130 | 131 | >>> 132 | Nesse exemplo, primeiro, os pontos são tratados como parte de palavras. 133 | Depois, separam-se os pontos finais delimitadores de sentenças. 134 | Uma instância desta classe pode ser atribuída ao argumento-chave 135 | word_tokenizer de leitores de corpus do NLTK, como o 136 | nltk.corpus.PlaintextCorpusReader. 137 | """ 138 | 139 | def __init__(self, Tokenizer_Function,NLTK_Tokenizer): 140 | self._tokenizer1 = Tokenizer_Function 141 | self._tokenizer2 = NLTK_Tokenizer 142 | 143 | def tokenize(self,sent): 144 | return self._tokenizer2.tokenize(self._tokenizer1(sent)) 145 | 146 | # Toquenizador para o LX-Tagger 147 | TOK_PORT_LX=ToquenizadorBifasico(SeparaPontoFinal,_TOK_PORT_LX) 148 | 149 | def ExtraiToquenizadorPUNKT(): 150 | """Verifica se o toquenizador PUNKT está instalado. Em caso negativo, descarrega-o da Internet. Retorna esse toquenizador. 151 | """ 152 | try: 153 | nltk.data.find('tokenizers/punkt/portuguese.pickle') 154 | except: 155 | nltk.download("punkt") 156 | 157 | finally: 158 | return nltk.data.load('tokenizers/punkt/portuguese.pickle') 159 | 160 | # Toquenizador sentencial próprio para textos em formato "bruto", 161 | # em que há quebras de linha dentro das sentenças: 162 | PUNKT= ExtraiToquenizadorPUNKT() 163 | # Toquenizador sentencial para textos pré-processados 164 | # em que cada sentença está numa linha própria: 165 | # REGEXP=nltk.RegexpTokenizer(r"\n",gaps=True) 166 | 167 | # Expressão regular que define a noção default de token vocabular 168 | # em português do Aelius, segundo a qual hífen, apóstrofos e pontos 169 | # constituem tokens separados: 170 | EXP_REG_AELIUS='''(?x)[A-Za-z]+[.$]| # palavras terminando em ponto ou cifrão 171 | [)(!?"]| # sinais de pontuação 172 | \.\d+| # expressões numéricas como .45 173 | \d+[.,]\d+[.,]\d+| # números como 3.800,98 174 | \d+[.,]\d+| # números como 3,5 e 3.5 175 | \w+| # tokens alfanuméricos 176 | \.{2,} | # dois ou mais pontos (reticências) 177 | [^\w\s]+ # não palavras e não espaços''' 178 | 179 | # Toquenizador default, separa hifens e apóstrofos 180 | TOK_ER = nltk.RegexpTokenizer(EXP_REG_AELIUS) 181 | 182 | TOK_PORT=ToquenizadorBifasico(SeparaPontoFinal,TOK_ER) 183 | 184 | def toquenizaSentenca(sentenca): 185 | """Esta função transforma unicode em uma lista de tokens unicode.""" 186 | t="" # inicializamos uma cadeia vazia 187 | for c in sentenca: 188 | # a cada volta do laço, t é atualizada 189 | if c in SINAIS: # se c é sinal de pontuação, um espaço é inserido de ambos os lados, o resultado sendo concatenado a t 190 | t="%s %s " % (t,c) 191 | else: # se c não é sinal de pontuação, c é simplesmente concatenado a t 192 | t="%s%s" % (t,c) 193 | return t.split() 194 | 195 | def processaAbreviaturas(tokens): 196 | """Esta função corrige, em uma lista de tokens, os casos em que o ponto foi indevidamente separado das abreviaturas listadas na variável global ABREVIATURAS.""" 197 | i=0 198 | while i < len(tokens)-1: 199 | for a in ABREVIATURAS: 200 | # minusculizamos antes de realizar a comparação entre as duas instâncias de unicode, de modo a dar conta de variaçãoes na grafia como "Dr." e "dr." 201 | if tokens[i].lower() ==a.lower() and tokens[i+1] == ".": 202 | # transformamos casos como "Prof" em "Prof." 203 | tokens[i]="%s." % tokens[i] 204 | # excluímos o ponto 205 | tokens.pop(i+1) 206 | i+=1 207 | 208 | def ToquenizaPontuacao(sentenca=SENT): 209 | """A partir de cadeia dada, retorna outra cadeia em que os sinais de 210 | pontuação da variável global SINAIS estão separados das palavras, 211 | exceto no caso dos itens listados na variável global ABREVIATURAS.""" 212 | tokens=toquenizaSentenca(sentenca) 213 | processaAbreviaturas(tokens) 214 | tokens=[t.encode("utf-8") for t in tokens] 215 | return " ".join(tokens) 216 | -------------------------------------------------------------------------------- /Aelius/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 3 | # 4 | # Copyright (C) 2010-2011 Leonel F. de Alencar 5 | # Author: Leonel F. de Alencar 6 | # 7 | # URL: 8 | # For license information, see LICENSE.TXT 9 | 10 | """TODO. 11 | """ 12 | 13 | # As variáveis abaixo iniciadas por __ são baseadas nas variáveis 14 | # análogas do NLTK: 15 | 16 | # Copyright (C) 2001-2011 NLTK Project 17 | # Authors: Steven Bird 18 | # Edward Loper 19 | # URL: 20 | 21 | __versao__ = "0.9.4 dec-23-2011" 22 | __version__ = __versao__ 23 | __copyright__ = """Copyright (C) 2010-2011 Leonel F. de Alencar 24 | 25 | Distributed and Licensed under the Apache License, Version 2.0, 26 | which is included by reference. 27 | """ 28 | __descricao__="""O Aelius integra um conjunto de módulos implementados em Python, com base no NLTK, bem como dados linguísticos para treino e avaliação de etiquetadores morfossintáticos e anotação de corpora nessa variedade. 29 | """ 30 | __description__="""Aelius is a suite of Python, NLTK-based modules and language data for training and evaluating POS-taggers for Brazilian Portuguese and annotating corpora in this language variety. 31 | """ 32 | __licenca__ = "Apache License, Version 2.0" 33 | __license__ = __licenca__ 34 | __mantenedor__ = "Leonel F. de Alencar" 35 | __maintainer__ = __mantenedor__ 36 | __email_do_mantenedor__ = "leonel.de.alencar@ufc.br" 37 | __email__ = __email_do_mantenedor__ 38 | __autor__ = __mantenedor__ 39 | __author__ = __autor__ 40 | __email_do_autor__ = __email_do_mantenedor__ 41 | 42 | __all__=['ProcessaNomesProprios', 43 | 'AnotaCorpus', 44 | 'Avalia', 45 | 'MXPOST', 46 | 'CalculaEstatisticasLexicais', 47 | 'ExpandeContracoes', 48 | 'Toqueniza', 49 | 'SimplificaEtiquetas', 50 | 'Chunking', 51 | 'Extras'] 52 | 53 | -------------------------------------------------------------------------------- /Aelius/expandecontracoes.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: ExpandeContracoes.py $ 13 | 14 | def clitico(forma): 15 | """Testa se uma etiqueta é um clítico (CL ou SE na anotação do CHPTB) ou uma combinação de clíticos. 16 | """ 17 | return forma=="CL" or forma=="SE" or forma.startswith("CL+") or forma.startswith("SE+") 18 | 19 | def ProcessaFormasHifenizadas(t): 20 | """Toma como entrada uma sentença anotada conforme o modelo do Aelius, que separa as formas ligadas por hífen, concatenando o hífen a cada forma pronominal enclítica ou mesoclítica bem como juntando os elementos de compostos como 'terça-feira' e 'pé-de-meia'. 21 | """ 22 | i=1 23 | c=len(t) 24 | while(i>> from Aelius import ExpandeContracoes, AnotaCorpus 78 | >>> sent=AnotaCorpus.EXEMPLO 79 | >>> print sent 80 | Os candidatos classificáveis dos cursos de Sistemas de Informação poderão ocupar as vagas remanescentes do Curso de Engenharia de Software. 81 | >>> tokens1=AnotaCorpus.TOK_PORT.tokenize(sent) 82 | >>> anotados=AnotaCorpus.anota_sentencas([tokens1],AnotaCorpus.TAGGER) 83 | >>> anotados 84 | [[('Os', 'D-P'), ('candidatos', 'N-P'), ('classific\xc3\xa1veis', 'ADJ-G-P'), ('dos', 'P+D-P'), ('cursos', 'N-P'), ('de', 'P'), ('Sistemas', 'NPR-P'), ('de', 'P'), ('Informa\xc3\xa7\xc3\xa3o', 'NPR'), ('poder\xc3\xa3o', 'VB-R'), ('ocupar', 'VB'), ('as', 'D-F-P'), ('vagas', 'ADJ-F-P'), ('remanescentes', 'ADJ-G-P'), ('do', 'P+D'), ('Curso', 'NPR'), ('de', 'P'), ('Engenharia', 'NPR'), ('de', 'P'), ('Software', 'NPR'), ('.', '.')]] 85 | >>> ExpandeContracoes.expande_contracoes(anotados[0]) 86 | [('Os', 'D-P'), ('candidatos', 'N-P'), ('classific\xc3\xa1veis', 'ADJ-G-P'), ('de', 'P'), ('os', 'D-P'), ('cursos', 'N-P'), ('de', 'P'), ('Sistemas', 'NPR-P'), ('de', 'P'), ('Informa\xc3\xa7\xc3\xa3o', 'NPR'), ('poder\xc3\xa3o', 'VB-R'), ('ocupar', 'VB'), ('as', 'D-F-P'), ('vagas', 'ADJ-F-P'), ('remanescentes', 'ADJ-G-P'), ('de', 'P'), ('o', 'D'), ('Curso', 'NPR'), ('de', 'P'), ('Engenharia', 'NPR'), ('de', 'P'), ('Software', 'NPR'), ('.', '.')] 87 | """ 88 | lista=[] 89 | for w,t in sentenca: 90 | # é preciso assegurar que a etiqueta não seja apenas "+", atribuída, no sistema do Aelius, ao hífen 91 | if len(t) > 2 and "+" in t: 92 | t1,t2=t.split("+") 93 | w1,w2=expande(w) 94 | lista.extend([(w1,t1),(w2,t2)]) 95 | else: 96 | if not w=="-": # exclusão de hífen como token 97 | lista.append((w,t)) 98 | return lista 99 | -------------------------------------------------------------------------------- /Chunking.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2.6 2 | # -*- coding: utf-8 -*- 3 | 4 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 5 | # 6 | # Copyright (C) 2010-2011 Leonel F. de Alencar 7 | # Author: Leonel F. de Alencar 8 | # 9 | # URL: 10 | # For license information, see LICENSE.TXT 11 | # 12 | # $Id: Chunking.py $ 13 | 14 | """Chunker simples para o português baseado no tagset do LX-Tagger 15 | http://lxcenter.di.fc.ul.pt/tools/pt/conteudo/LXTagger.html""" 16 | 17 | import nltk 18 | from os.path import join,splitext 19 | 20 | RAIZ="analises" 21 | TEXTO="ufc_07042011.pdt.mxpost.txt" 22 | 23 | CHUNKER= nltk.chunk.RegexpParser(r''' 24 | NP: 25 | {?(???)*} 26 | {?????????***(||)**} 27 | ''') 28 | 29 | def CriaChunkedCorpus(raiz,texto,chunker=CHUNKER,destino=None,formato="trees"): 30 | nome_do_destino="" 31 | if destino: 32 | nome_do_destino=destino 33 | else: 34 | nome_do_destino=join(raiz,"%s.%s.txt" % (splitext(texto)[0],formato)) 35 | f=open(nome_do_destino,"w") 36 | c=nltk.corpus.TaggedCorpusReader(raiz,texto,encoding="utf-8") 37 | sents=c.tagged_sents() 38 | chunked_sents=chunker.batch_parse(sents) 39 | if formato=="IOB": 40 | for s in chunked_sents: 41 | s=nltk.chunk.tree2conllstr(chunker.parse(s)) 42 | f.write("%s\n\n" % (s.encode("utf-8"))) 43 | else: 44 | for s in chunked_sents: 45 | f.write("%s\n\n" % (s.pprint().encode("utf-8"))) 46 | f.close() 47 | 48 | def IOB2trees(arquivo): 49 | """A partir de um arquivo de sentenças cujos chunks do tipo NP estão anotados no formato IOB, retorna uma lista de árvores do tipo nltk.Tree. 50 | """ 51 | linhas=open(arquivo).read().strip().split("\n\n") 52 | return [nltk.chunk.conllstr2tree(c) for c in linhas] 53 | 54 | def avalia(arquivo,chunker=CHUNKER): 55 | """Avalia um chunker com base num arquivo no formato IOB corrigido por humano. 56 | """ 57 | trees=IOB2trees(arquivo) 58 | print chunker.evaluate(trees) 59 | 60 | def main(): 61 | CriaChunkedCorpus(RAIZ,TEXTO,CHUNKER,destino=None,formato="trees") 62 | CriaChunkedCorpus(RAIZ,TEXTO,CHUNKER,destino=None,formato="IOB") 63 | -------------------------------------------------------------------------------- /INSTALL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompLin/Aelius/78e25d53c5bc429651b2d32b49b0dfa78b3402a9/INSTALL.pdf -------------------------------------------------------------------------------- /INSTALL.txt: -------------------------------------------------------------------------------- 1 | Instruções para instalação do Aelius 2 | 3 | 4 | =========================================== 5 | Pré-requisitos 6 | =========================================== 7 | 8 | 1) Python (versão 2.6 ou superior, mas não a versão 3.0), NLTK (versão 9 | 2.0.1 ou superior) e todas as bibliotecas exigidas pelo 10 | NLTK. Consultar: 11 | 12 | http://www.nltk.org/ 13 | 14 | Caso não saiba qual é a sua versão do NLTK, execute: 15 | 16 | >>> import nltk 17 | >>> nltk.__version__ 18 | 19 | 2) Toquenizador Punkt do NLTK, que pode ser instalado por meio do NLTK Downloader: 20 | 21 | >>> import nltk 22 | >>> nltk.download("punkt") 23 | 24 | 3) Etiquetador Hunpos 25 | 26 | http://code.google.com/p/hunpos/ 27 | 28 | 4) Etiquetador LX-Tagger e todas as bibliotecas exigidas por ele. Consultar: 29 | 30 | http://lxcenter.di.fc.ul.pt/tools/pt/conteudo/LXTagger.html 31 | 32 | 33 | =========================================== 34 | Passos para a instalação 35 | =========================================== 36 | 37 | Nas instruções abaixo, assumimos que você baixou a versão 23-12-2011 do 38 | Aelius e vai instalar o programa no Linux ou Mac OS X. 39 | Para outras versões do Aelius ou para instalação em outros sistemas operacionais, faça as adaptações necessárias. 40 | 41 | 1. Descompacte o arquivo Aelius-23-12-2011.zip 42 | 2. No diretório $HOME, crie a pasta Applications, se ela ainda não existir 43 | 3. Copie a pasta Aelius para o diretório $HOME/Applications; verifique isso na 44 | shell: 45 | 46 | ls $HOME/Applications/Aelius 47 | 48 | 4. Copie a pasta aelius_data para o diretório $HOME; verifique isso na 49 | shell: 50 | 51 | ls $HOME/aelius_data 52 | 53 | 5. O diretório $HOME/aelius_data deverá conter os seguintes arquivos: 54 | 55 | 56 | AeliusBRUBT.pkl LICENSE.txt exemplo.txt 57 | AeliusHunPos README.pdf freq_tycho_a.pkl 58 | AeliusRUBT.pkl README.txt 59 | 60 | 61 | 62 | O uso dos arquivos AeliusBRUBT.pkl, AeliusRUBT.pkl e AeliusHunPos 63 | (etiquetadores treinados em versão modificada do Corpus Histórico do Português Tycho Brahe, 64 | doravante CHPTB) e 65 | freq_tycho_a.pkl (lista de palavras mais freqüentes de amostra do CHPTB) 66 | está sujeito às condições estabelecidas no arquivo README.txt ou README.pdf 67 | que os acompanha na pasta aelius_data. Informações de copyright encontram-se também nessa pasta. 68 | 69 | Visite as seguintes páginas na WWW para obter informações sobre o Corpus Histórico do Português Tycho Brahe: 70 | 71 | Homepage: 72 | http://www.tycho.iel.unicamp.br/~tycho/corpus/ 73 | Licença de uso do corpus: 74 | http://www.tycho.iel.unicamp.br/~tycho/corpus/termos.html 75 | 76 | 6. Além desses arquivos, é necessário criar, no diretório 77 | $HOME/aelius_data, um link simbólico para o modelo do português MXPOST 78 | do LX-Tagger, disponível em: 79 | 80 | 81 | http://lxcenter.di.fc.ul.pt/tools/pt/conteudo/LXTagger.html 82 | 83 | Siga as instruções de instalação do LX-Tagger fornecidas no respectivo 84 | site ou na pasta do etiquetador descarregada da Internet. Você precisa 85 | do Java 5 (JDK1.5) ou superior, que já é pré-instalado nas máquinas com sistema operacional MAC OS X, mas que pode não constar de sua instalação do Linux. Verifique se Java está instalado no Terminal: 86 | 87 | 88 | java -version 89 | 90 | 91 | Além disso, é necessária a instalação do aplicativo JAVA MXPOST, desenvolvido por 92 | Adwait Ratnaparkhi, disponível em: 93 | 94 | ftp://ftp.cis.upenn.edu/pub/adwait/jmx/jmx.tar.gz 95 | 96 | ou a partir de link em 97 | 98 | http://sites.google.com/site/adwaitratnaparkhi/publications 99 | 100 | Siga as instruções fornecidas pelo autor do MXPOST. Você precisa 101 | incluir as seguintes linhas no seu arquivo de configuração 102 | $HOME/.profile ou $HOME/.bash_profile (conforme a sua máquina): 103 | 104 | CLASSPATH="$HOME/jmx/mxpost.jar" 105 | export CLASSPATH 106 | PATH="${PATH}:$HOME/jmx" 107 | export PATH 108 | 109 | Supondo que você descompactou o arquivo do LX-Tagger POSTagger.zip no 110 | diretório $HOME, resguardadas alterações na distribuição do LX-Tagger, proceda da seguinte 111 | forma para criar link simbólico para o modelo do português desse 112 | etiquetador: 113 | 114 | cd $HOME/aelius_data 115 | ln -s $HOME/POSTagger/Tagger/MX-PoST/UTF-8_Model_Cintil_Written lxtagger 116 | 117 | Verifique se o link está correto: 118 | 119 | ls $HOME/aelius_data/lxtagger 120 | 121 | Quanto ao Hunpos, para que funcione com o NLTK, é preciso colocar os dois arquivos binários 122 | 123 | hunpos-tag 124 | hunpos-train 125 | 126 | numa das pasta pré-definidas pela interface do NLTK para o Hunpos, por exemplo: 127 | 128 | $HOME/Applications/bin 129 | 130 | Inclua as seguintes linhas no seu arquivo de configuração 131 | $HOME/.profile ou $HOME/.bash_profile (conforme a sua máquina): 132 | 133 | PATH="${PATH}:$HOME/Applications/bin" 134 | export PATH 135 | 136 | Consultar módulo hunpos.py do pacote "tag" do NLTK para outros lugares onde instalar HunPos. 137 | 138 | 7. Inclua $HOME/Applications na lista de caminhos da variável de ambiente PYTHONPATH no seu arquivo de configuração (.profile ou .bash_profile): 139 | 140 | PYTHONPATH="${PYTHONPATH}:$HOME/Applications" 141 | export PYTHONPATH 142 | 143 | 8. Abra o IDLE a partir do Terminal e verifique se o Aelius aparece em File/Path Browser, na pasta $HOME/Applications 144 | 145 | 9. Execute 146 | 147 | >>> import Aelius 148 | >>> Aelius.__versao__ '0.9.4 December-23-2011' 149 | 150 | 10. Para obter ajuda, execute 151 | 152 | >>> help(Aelius) 153 | 154 | 11. Faça o mesmo para os módulos individuais do pacote ou para funções individuais desses módulos 155 | 156 | 12. Sugerimos que crie em $HOME uma pasta analises, onde deverá armazenar os arquivos a serem analisados. Verifique: 157 | 158 | ls $HOME/analises 159 | 160 | Inicie o IDLE a partir do terminal: 161 | 162 | cd $HOME/analises 163 | idle 164 | 165 | Com isso, a pasta $HOME/analises será o diretório de trabalho de Python, no qual o Aelius salva os arquivos que anota. 166 | 167 | 13. Caso tenha dúvida, registre-se no SourceForge e coloque a sua pergunta na seção Forum/Open Discussion do Aelius 168 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010-2011 Leonel F. de Alencar 2 | 3 | Licensed under the Apache License, Version 2.0 (the 'License'); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an 'AS IS' BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | This file is based on NTLK's LICENSE.txt 16 | http://code.google.com/p/nltk/source/browse/trunk/nltk/LICENSE.txt 17 | 18 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool http://sourceforge.net/projects/aelius/ 2 | 3 | Copyright (C) 2010-2011 Leonel F. de Alencar 4 | 5 | Alencar, Leonel Figueiredo de. Aelius: uma ferramenta para anotação automática de corpora usando o NLTK. ELC 2010, The 9th Brazilian Corpus Linguistics Meeting, Porto Alegre, Brazil, Rio Grande do Sul Catholic University (PUCRS), October 8 and 9, 2010. 6 | http://corpuslg.org/gelc/elc2010.php?paged=2 7 | http://corpuslg.org/gelc/media/blogs/elc2010/slides/Figueiredo_de_Alencar.pdf 8 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool http://sourceforge.net/projects/aelius/ 2 | 3 | Author: Leonel F. de Alencar 4 | 5 | Copyright (C) 2010-2011 Leonel F. de Alencar 6 | 7 | For license information, see LICENSE.txt 8 | 9 | Aelius is a suite of Python, NLTK-based modules and language data for training and evaluating POS-taggers for Brazilian Portuguese and annotating corpora in this language variety. 10 | 11 | 12 | Documentation: help(function_name) 13 | 14 | 15 | Mailing Lists: see Aelius homepage 16 | 17 | 18 | Contributing: If you would like to contribute to Aelius, 19 | please see Aelius homepage 20 | 21 | Donating: Have you found the toolkit helpful? Please support Aelius development 22 | by donating to the project via PayPal, using the link on the Aelius homepage. 23 | 24 | Redistributing: Aelius source code is distributed under the Apache 2.0 License. 25 | Aelius documentation is distributed under the Creative Commons 26 | Attribution-Noncommercial-No Derivative Works 3.0 United States license. 27 | Aelius corpora and language models are provided under the terms 28 | given in the README and/or LICENSE file 29 | for these corpora and language models; all are redistributable, and available for non-commercial use. 30 | Aelius may be freely redistributed, subject to the provisions of these licenses. 31 | 32 | Citing: If you publish work that uses Aelius, please cite the following: 33 | 34 | Alencar, Leonel Figueiredo de. Aelius: uma ferramenta para anotação automática de corpora usando o NLTK. ELC 2010, The 9th Brazilian Corpus Linguistics Meeting, Porto Alegre, Brazil, Rio Grande do Sul Catholic University (PUCRS), October 8 and 9, 2010. 35 | http://corpuslg.org/gelc/elc2010.php?paged=2 36 | http://corpuslg.org/gelc/media/blogs/elc2010/slides/Figueiredo_de_Alencar.pdf 37 | 38 | Acknowledgments: my Computational Linguistics students at the Universidade Federal do Ceará; Marcel Caraciolo for some code improvement suggestions and feedback on the Aelius documentation. 39 | 40 | This file is based on NTLK's README.txt 41 | http://code.google.com/p/nltk/source/browse/trunk/nltk/README.txt 42 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool 3 | # 4 | # Copyright (C) 2010-2011 Leonel F. de Alencar 5 | # Author: Leonel F. de Alencar 6 | # 7 | # URL: 8 | # For license information, see LICENSE.TXT 9 | 10 | """Os arquivos de texto e os modelos de que o Aelius necessita devem estar armazenados ou no diretório $HOME/aelius_data ou no diretório especificado na variável de ambiente AELIUS_DATA. 11 | """ 12 | import os 13 | 14 | USUARIO=os.path.expanduser("~") # extração do valor de $HOME 15 | CAMINHO=os.path.join(USUARIO,"aelius_data") 16 | AELIUS_DATA=os.environ.get("AELIUS_DATA",CAMINHO) 17 | 18 | # as variáveis abaixo iniciadas por __ são baseadas nas variáveis 19 | # análogas do NLTK: 20 | 21 | # Copyright (C) 2001-2011 NLTK Project 22 | # Authors: Steven Bird 23 | # Edward Loper 24 | # URL: 25 | 26 | __versao__ = "0.9.3 sept-01-2011" 27 | 28 | __copyright__ = """Copyright (C) 2010-2011 Leonel F. de Alencar 29 | 30 | Distributed and Licensed under the Apache License, Version 2.0, 31 | which is included by reference. 32 | """ 33 | __licenca__ = "Apache License, Version 2.0" 34 | 35 | __mantenedor__ = "Leonel F. de Alencar" 36 | __email_do_mantenedor__ = "leonel.de.alencar@ufc.br" 37 | __autor__ = __mantenedor__ 38 | __email_do_autor__ = __email_do_mantenedor__ 39 | 40 | __all__=['ProcessaNomesProprios','AnotaCorpus','Avalia','MXPOST','CalculaEstatisticasLexicais','ExpandeContracoes', 'Toqueniza','SimplificaEtiquetas','Chunking'] 41 | 42 | def carrega(caminho): 43 | """Assume-se que 'caminho' é definido relativamente à variável AELIUS_DATA. 44 | """ 45 | return os.path.join(AELIUS_DATA,caminho) 46 | -------------------------------------------------------------------------------- /aelius_data/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompLin/Aelius/78e25d53c5bc429651b2d32b49b0dfa78b3402a9/aelius_data/.DS_Store -------------------------------------------------------------------------------- /aelius_data/AeliusBRUBT.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompLin/Aelius/78e25d53c5bc429651b2d32b49b0dfa78b3402a9/aelius_data/AeliusBRUBT.pkl -------------------------------------------------------------------------------- /aelius_data/AeliusHunPos: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompLin/Aelius/78e25d53c5bc429651b2d32b49b0dfa78b3402a9/aelius_data/AeliusHunPos -------------------------------------------------------------------------------- /aelius_data/AeliusRUBT.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompLin/Aelius/78e25d53c5bc429651b2d32b49b0dfa78b3402a9/aelius_data/AeliusRUBT.pkl -------------------------------------------------------------------------------- /aelius_data/LICENSE.txt: -------------------------------------------------------------------------------- 1 | http://opencontent.org/opl.shtml 2 | 3 | OpenContent License (OPL) 4 | Version 1.0, July 14, 1998. 5 | 6 | This document outlines the principles underlying the OpenContent (OC) movement and may be redistributed provided it remains unaltered. For legal purposes, this document is the license under which OpenContent is made available for use. 7 | 8 | The original version of this document may be found at http://opencontent.org/opl.shtml 9 | 10 | LICENSE 11 | 12 | Terms and Conditions for Copying, Distributing, and Modifying 13 | 14 | Items other than copying, distributing, and modifying the Content with which this license was distributed (such as using, etc.) are outside the scope of this license. 15 | 16 | 1. You may copy and distribute exact replicas of the OpenContent (OC) as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the OC a copy of this License along with the OC. You may at your option charge a fee for the media and/or handling involved in creating a unique copy of the OC for use offline, you may at your option offer instructional support for the OC in exchange for a fee, or you may at your option offer warranty in exchange for a fee. You may not charge a fee for the OC itself. You may not charge a fee for the sole service of providing access to and/or use of the OC via a network (e.g. the Internet), whether it be via the world wide web, FTP, or any other method. 17 | 18 | 2. You may modify your copy or copies of the OpenContent or any portion of it, thus forming works based on the Content, and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: 19 | 20 | a) You must cause the modified content to carry prominent notices stating that you changed it, the exact nature and content of the changes, and the date of any change. 21 | 22 | b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the OC or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License, unless otherwise permitted under applicable Fair Use law. 23 | 24 | These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the OC, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the OC, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Exceptions are made to this requirement to release modified works free of charge under this license only in compliance with Fair Use law where applicable. 25 | 26 | 3. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to copy, distribute or modify the OC. These actions are prohibited by law if you do not accept this License. Therefore, by distributing or translating the OC, or by deriving works herefrom, you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or translating the OC. 27 | 28 | NO WARRANTY 29 | 30 | 4. BECAUSE THE OPENCONTENT (OC) IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE OC, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE OC "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK OF USE OF THE OC IS WITH YOU. SHOULD THE OC PROVE FAULTY, INACCURATE, OR OTHERWISE UNACCEPTABLE YOU ASSUME THE COST OF ALL NECESSARY REPAIR OR CORRECTION. 31 | 32 | 5. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MIRROR AND/OR REDISTRIBUTE THE OC AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE OC, EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 33 | 34 | 35 | -------------------------------------------------------------------------------- /aelius_data/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompLin/Aelius/78e25d53c5bc429651b2d32b49b0dfa78b3402a9/aelius_data/README.pdf -------------------------------------------------------------------------------- /aelius_data/README.txt: -------------------------------------------------------------------------------- 1 | LICENSE INFORMATION ON THE FILES IN THE aelius_data FOLDER OF THE AELIUS DISTRIBUTION 2 | 3 | The data in this folder is distributed solely for non-commercial, non-profit educational and research use under the terms of the Open Content License [http://opencontent.org/opl.shtml]. 4 | 5 | The files AeliusBRUBT.pkl, AeliusRUBT.pkl and AeliusHunPos contain statistical models of Portuguese for POS-tagging using NLTK or Aelius. These models were trained on a prunned, partly retagged and partly retokenized version of the Tycho Brahe Parsed Corpus of Historical Portuguese (TBCHP) [http://www.tycho.iel.unicamp.br/~tycho/corpus/en/index.html]. 6 | 7 | The file freq_tycho_a.pkl is a NLTK word frequency distribution of a sample of this corpus containing about 200000 alphabetical tokens. 8 | 9 | Thus, all this material is a derivative work of the Tycho Brahe Parsed Corpus of Historical Portuguese (TBCHP), whose copyrights are held by the respective original authors. Any use of this material must be compliant with the TBCHP's license. For more information, see the English version of the corpus license page [http://www.tycho.iel.unicamp.br/~tycho/corpus/en/termos.html] or the Portuguese version [http://www.tycho.iel.unicamp.br/~tycho/corpus/termos.html]. 10 | 11 | The file example.txt contains an excerpt with the first two paragraphs from the novel Luzia-Homem, published 1903 by the Brazilian writer Domingos Olímpio (born in Sobral, Ceará, in 1850, and died in Rio de Janeiro in 1906), extracted from Wikisource [http://pt.wikisource.org/wiki/Luzia-Homem/I]. The file exemplo.nltk.gold.txt is a manually revised version of the POS-tagged output from the AeliusBRUBT tagger. Both untagged and tagged versions of this excerpt were revised by Leonel F. de Alencar and constitute part of the CORPTEXLIT Project [http://www.leonel.profusehost.net/corptext.html]. 12 | 13 | Publishing results based on any of these data requires that the author be acknowledged. To do so, please refer to the file's name (e.g. AeliusHunPos) and the package of which it is part, citing thusly: 14 | 15 | Alencar, Leonel F. de. Aelius Brazilian Portuguese POS-Tagger and Corpus Annotation Tool. 2011. http://aelius.sourceforge.net/ 16 | 17 | Since any work using the mentioned models is indirectly based on the Tycho Brahe Parsed Corpus of Historical Portuguese, you should also, according to its above-mentioned license, acknowledge the TBCHP, its creators and/or distributors and cite this corpus accordingly. 18 | -------------------------------------------------------------------------------- /aelius_data/exemplo.nltk.gold.txt: -------------------------------------------------------------------------------- 1 | O/D excerto/N abaixo/ADV contém/VB-P os/D-P dois/NUM primeiros/ADJ-P parágrafos/N-P do/P+D romance/N "/QT Luzia/NPR -/+ Homem/NPR "/QT (/( Rio/NPR de/P Janeiro/NPR ,/, 1903/NUM )/( ,/, de/P Domingos/NPR Olímpio/NPR ,/, nascido/VB-AN em/P Sobral/NPR ,/, Ceará/NPR ,/, em/P 18/NUM de/P setembro/NPR de/P 1850/NUM e/CONJ falecido/VB-AN no/P+D Rio/NPR de/P Janeiro/NPR em/P 6/NUM de/P outubro/NPR de/P 1906/NUM ./. 2 | O/D texto/N ,/, extraído/VB-AN do/P+D site/N Wikisource/NPR ,/, foi/SR-D revisado/VB-AN por/P Leonel/NPR F./NPR de/P Alencar/NPR ./. 3 | 4 | 5 | O/D morro/N do/P+D Curral/NPR do/P+D Açougue/NPR emergia/VB-D em/P suave/ADJ-G declive/N da/P+D-F campina/N ondulada/VB-AN-F ./. 6 | Escorchado/VB-AN ,/, indigente/ADJ-G de/P arvoredo/N ,/, o/D cômoro/N enegrecido/VB-AN pelo/P+D sangue/N de/P reses/N-P sem/P conto/N ,/, deixara/VB-RA de/P ser/SR o/D sítio/N sinistro/ADJ do/P+D matadouro/N e/CONJ a/D-F pousada/N predileta/ADJ-F de/P bandos/N-P de/P urubutingas/N-P e/CONJ camirangas/N-P vorazes/ADJ-G-P ./. 7 | 8 | 9 | Bateram/VB-D -/+ se/SE os/D-P vastos/ADJ-P currais/N-P ,/, de/P grossos/ADJ-P esteios/N-P de/P aroeira/N ,/, fincados/VB-AN-P a/P pique/N ,/, rijos/ADJ-P como/CONJS barras/N-P de/P ferro/N ,/, currais/N-P seculares/ADJ-G-P ,/, obra/N ciclópica/ADJ-F ,/, da/P+D-F qual/WPRO restava/VB-D apenas/ADV ,/, como/CONJS lúgubre/ADJ-G vestígio/N ,/, o/D moirão/N ligeiramente/ADV inclinado/VB-AN ,/, adelgaçado/VB-AN no/P+D centro/N ,/, polido/VB-AN pelo/P+D contínuo/ADJ atrito/N das/P+D-F-P cordas/N-P de/P laçar/VB as/D-F-P vítimas/N-P ,/, que/WPRO a/P ele/PRO eram/SR-D arrastadas/VB-AN-F-P aos/P+D-P empuxões/N-P ,/, bufando/VB-G ,/, resistindo/VB-G ,/, ou/CONJ entregando/VB-G ,/, resignadas/VB-AN-F-P e/CONJ mansas/ADJ-F-P ,/, o/D pescoço/N à/P+D-F faca/N do/P+D magarefe/N ./. 10 | Ali/ADV ,/, no/P+D sítio/N de/P morte/N ,/, fervilhavam/VB-D ,/, então/ADV ,/, em/P ruidosa/ADJ-F diligência/N ,/, legiões/N-P de/P operários/N-P construindo/VB-G a/D-F penitenciária/N de/P Sobral/NPR ./. 11 | 12 | -------------------------------------------------------------------------------- /aelius_data/exemplo.txt: -------------------------------------------------------------------------------- 1 | O excerto abaixo contém os dois primeiros parágrafos do romance "Luzia-Homem" (Rio de Janeiro, 1903), de Domingos Olímpio, nascido em Sobral, Ceará, em 18 de setembro de 1850 e falecido no Rio de Janeiro em 6 de outubro de 1906. O texto, extraído do site Wikisource, foi revisado por Leonel F. de Alencar. 2 | 3 | O morro do Curral do Açougue emergia em suave declive da campina ondulada. Escorchado, indigente de arvoredo, o cômoro enegrecido pelo sangue de reses sem conto, deixara de ser o sítio sinistro do matadouro e a pousada predileta de bandos de urubutingas e camirangas vorazes. 4 | 5 | Bateram-se os vastos currais, de grossos esteios de aroeira, fincados a pique, rijos como barras de ferro, currais seculares, obra ciclópica, da qual restava apenas, como lúgubre vestígio, o moirão ligeiramente inclinado, adelgaçado no centro, polido pelo contínuo atrito das cordas de laçar as vítimas, que a ele eram arrastadas aos empuxões, bufando, resistindo, ou entregando, resignadas e mansas, o pescoço à faca do magarefe. Ali, no sítio de morte, fervilhavam, então, em ruidosa diligência, legiões de operários construindo a penitenciária de Sobral. 6 | 7 | 8 | -------------------------------------------------------------------------------- /aelius_data/freq_tycho_a.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompLin/Aelius/78e25d53c5bc429651b2d32b49b0dfa78b3402a9/aelius_data/freq_tycho_a.pkl --------------------------------------------------------------------------------