A.2 Implementação do algoritmo LAID
A.2.1 Construir a matriz disjunta: laid_a.py
1 #!/usr/bin/python3 2 # coding: utf-8 3
4 ##################################################### 5 # # 6 # Script para construir a matriz disjunta # 7 # # 8 ##################################################### 9 10 import h5py 11 import numpy as np 12 import sys
13 import datetime, time 14 import math
15
16 # Função que determina e formata o tempo decorrido entre dois instantes
17 # Parâmetros: xti - tempo inicial 18 # xtf - tempo final
19 # Retorno: xitf - intervalo de tempo formatado
20 def fintervalotempo(xti, xtf):
21 xit = abs(xtf - xti) # Intervalo de tempo decorrido 22 # Determinar as horas, minutos e segundos
23 xhoras = int(xit/3600)
24 xminutos = int(xit/60)-xhoras*60
25 xsegundos = xit-xminutos*60-xhoras*3600
26 # Formatar o intervalo de tempo em horas:minutos:segundos
27 if xit < 60:
28 xitf = '{0:.1f} segundos'.format(xsegundos)
29 elif xit < 3600:
30 xitf = '{0} minutos e {1:.1f} segundos'.format(xminutos, xsegundos)
31 else:
32 xitf = '{0} horas, {1} minutos e {2:.1f} segundos'.format(xhoras, xminutos, xsegundos) 33 # Retornar o intervalo de tempo formatado 34 return xitf;
35
36 # ********** Início de execução do script ********** 37
38 # Testar o número de argumentos de chamada do script
39 if len(sys.argv) != 2:
40 raise SystemExit('O script "'+sys.argv[0]+'" tem um
argumento:\n\tnome_da_bd\n') 41
42 # Indicar a hora de início da execução
43 print('Hora inicial: '+datetime.datetime.now().strftime('%H:%M:%S')) 44 xti = time.time() # guardar a hora de início da execução do script 45
47 xnomef = 'bd'+sys.argv[1]+'.h5' # construir o nome do ficheiro da BD 48 xfich = h5py.File(xnomef, 'r+')
49
50 # Inicializar variáveis
51 xll = '\033[K' # sequência de escape ANSI para limpar a linha do cursor no terminal
52 xdt = np.dtype(np.int8) # definir a precisão dos inteiros a usar 53 xds_d = xfich['dados'] # dataset com os dados
54 xds_ma = xfich['ma'] # dataset com a matriz A 55 xna = xds_d.attrs['xna'] # número de atributos
56 xnj = xds_d.attrs['xnj'] # número máximo de atributos jnsq 57 xnac = xds_d.attrs['xnac'] # número de atributos classe 58 xno = xds_d.attrs['xno'] # número de observações
59 xnc = xds_d.attrs['xnc'] # número de valores diferentes para cada atributo classe
60 xntc = xna+xnj+xnac # número total de colunas
61 xsalto = int(10000000/xno) # número de colunas a tratar de cada vez,
formando um bloco de colunas
62 xini = 0 # coluna inicial do bloco de colunas
63 xordem = np.zeros((xno), dtype=int) # guarda a ordem de ordenação das observações, observações com atributos iguais têm a mesma ordem
64 xpos = np.arange((xno), dtype=int) # guarda a posição das observações no dataset
65 xfim = int(xntc/xsalto)+1 # número de blocos de colunas a tratar
66 xf = 1000 # frequência de impressão da progressão das iterações
67
68 # Obter a sequência das observações ordenadas pelos atributos
69 print('{0:10d}/{1} ... A ordenar observações ... '.format(0, xfim),
end='\r')
70 for i in range(xfim):
71 # Redefinir xsalto caso o último bloco ultrapasse o nº total de colunas
72 if xini+xsalto > xntc: 73 xsalto = xntc - xini 74 # Ler os dado do bloco
75 xleitura = xds_d[:,xini:xini+xsalto]
76 xleitura = xleitura[xpos] # ordenar as observações pela ordem definida na iteração anterior, ou seja, do bloco anterior
77 # Juntar os arrays xordem, xleitura, xpos numa única matriz
78 xmatriz = np.zeros((xno,xsalto+2), dtype=int)
79 xmatriz[:,0] = xordem[:]
80 xmatriz[:,1:xsalto+1] = xleitura[:,:]
81 xmatriz[:,xsalto+1] = xpos[:]
82 xi = np.lexsort(np.fliplr(xmatriz).T) # determina os índices de ordem das observações (começa por inverter a ordem das colunas, faz a transposta e determina o índice de ordem das colunas)
83 xmatriz = xmatriz[xi] # ordena as linhas da matriz segundo os índices de ordem
84 xordem[0] = 0 # a primeira observação tem número de ordem zero
85 # Testar se cada linha da matriz è igual à anterior
86 for j in range(1,xno):
87 if xini < xntc - xnj - xnac: # se xini é uma coluna correspondente a um atributo
88 if np.array_equal(xmatriz[j,0:xsalto+1],
xmatriz[j-1,0:xsalto+1]): # se uma linha é igual à anterior fica com o mesmo valor
de ordem
90 elif xini+xsalto == xntc and
np.array_equal(xmatriz[j,0:xsalto-xnac-xnj+1], xmatriz[j-1,0
:xsalto-xnac-xnj+1]): # semelhante ao anterior, no caso de ser o último bloco
91 xordem[j]=xordem[j-1]
92 else: # no caso de não serem iguais a ordem da linha fica com o valor seguinte ao da anterior
93 xordem[j]=xordem[j-1]+1
94 xpos = xmatriz[:,xsalto+1] # depois da matriz ordenada, é guardada
a posição que cada linha/observação tem no dataset
95 xini += xsalto # é atualizada a coluna inicial do próximo bloco
96 print('{0:10d}'.format(i+1), end='\r')
97 print(xll+'»»» As observações estão ordenadas.') 98
99 # Determinar as observações redundantes e as inconsistentes
100 print('{0:10d}/{1} ... A determinar redundâncias e inconsistências
...'.format(0, xno), end='\r')
101 xor = np.zeros((xno), dtype=int) # lista de redundâncias - guarda a posição das observações redundantes
102 xpr = 0 # posição das observações redundantes na lista de
redundâncias (nº de observações redundantes sem contar com a última de cada grupo)
103 xoi = np.zeros((xno,2), dtype=int) # lista de inconsistências -
guarda a posição das observações inconsistentes e respetivo número da inconsistência
104 xpi = 0 # posição das observações inconsistentes na lista de
inconsistências (nº de observações inconsistentes)
105 xnoi = -1 # número de inconsistências diferentes - 1, para
corresponder ao índice de xoi (que começa em zero)
106 xleitura_c = xmatriz[:,xsalto] # leitura da coluna do atributo classe
107 if xordem[0] == xordem[1]: # testa se a primeira observações da
matriz ordenada é igual à seguinte, ou seja, se os atributos são iguais
108 if (xleitura_c[0] != xleitura_c[1]): # se o valor da classe é
diferente a primeira linha da matriz faz parte duma inconsistência
109 xnoi += 1 # foi encontrada a primeira inconsistência
110 xoi[xnoi,:] = [xpos[0],xnoi] # é guardada a posição da primeira
linha e respetivo nº de inconsistência
111 xpi += 1
112 for i in range(1,xno): # para as restantes linhas
113 if xordem[i] == xordem[i-1]: # se os atributos de uma linha são
iguais aos da linha anterior é redundância ou inconsistência
114 if xleitura_c[i] == xleitura_c[i-1]: # se os valores da classe
são iguais a linha é redundante com a anterior
115 if (xpos[i-1] not in xoi[:xpi,0]) and (xpos[i-1] not in
xor[:xpr]): # se a linha anterior não era inconsistente nem redundante
116 xor[xpr] = xpos[i-1] # acrescentar a posição da linha
anterior à lista de redundâncias (num par redundante, é a primeira linha que é marcada)
117 # marca-se a anterior porque R Rx I a segunda era retirada e não era marcada como inconsistente, assim fica Rx RI I 118 else:
119 xor[xpr] = xpos[i] # se a anterior já estava marcada como inconsistente ou redundante, acrescentar a posição da linha à lista de redundâncias
120 xpr += 1
121 else: # se os valores da classe são diferentes as linhas são inconsistentes
122 if (xpos[i-1] not in xoi[:xpi,0]) and (xpos[i-1] not in
xor[:xpr]): # se a linha anterior não era inconsistente nem redundante
124 xoi[xpi,:] = [xpos[i-1],xnoi] # acrescentar a posição da linha anterior à lista de inconsistências
125 xpi += 1
126 xoi[xpi,:] = [xpos[i],xnoi] # acrescentar a posição da linha à lista de inconsistências
127 xpi += 1
128 print('{0:10d}'.format(i+1), end='\r')
129 # Redimensionar as listas ao número de redundâncias e inconsistências, respetivamente
130 xor = xor[:xpr] 131 xoi = xoi[:xpi]
132 print(xll+'»»» As redundâncias ({0}) e inconsistências ({1}) estão
determinadas.'.format(xpr,int(xpi/2)))
133
134 # Preencher as colunas jnsq
135 xnje = 0 # número de atributos jnsq efetivos
136 xleitura_j = np.zeros((xno,xnj), dtype=xdt) # inicializar atributos jnsq
137 if xnoi > -1: # se foram encontradas inconsistências
138 xnii = np.bincount(xoi[:,1]) # contabiliza a quantidade de
observações para cada nº de inconsistência (segunda coluna de xoi dá o nº da inconsistência)
139 xniimax = np.amax(xnii) # determina o máximo de xnii 140 print('{0:10d}/{1} ... A prencher a(s) coluna(s) jnsq
...'.format(0, np.size(xnii)), end='\r')
141 xp = 0 # posição na lista xoi
142 for i in range(np.size(xnii)): # para cada nº de inconsistência de xnii
143 for j in range(xnii[i]): # para cada valor inferior à quantidade de observações para cada nº de inconsistência
144 # Determinar o código binário para preencher as colunas jnsq das observações inconsistentes
145 xbin = j # posição de uma observação na quantidade de observações do seu nº de inconsistência
146 for k in range(xnj): # para cada coluna jnsq
147 xleitura_j[xoi[xp,0],k] = xbin%2 # resto da divisão –
valor binário para a coluna jnsq
148 xbin = xbin//2 # quociente da divisão - valor que passa
para a coluna jnsq seguinte
149 if xleitura_j[xoi[xp,0],k] > 0 and k+1 > xnje: # se uma
coluna jnsq tiver um valor não nulo e ainda não tiver sido contabilizada como efetiva
150 xnje += 1 # a coluna jnsq passa a efetiva
151 xp += 1
152 print('{0:10d}'.format(i), end='\r') 153 # popular os atributos jnsq
154 xds_d[:,xna:xna+xnj] = xleitura_j[...]
155 if xnje == 1:
156 print(xll+'»»» A coluna jnsq está preenchida.')
157 elif xnje == 0:
158 print(xll+'»»» Não existem atributos jnsq.') 159 else:
160 print(xll+'»»» As colunas jnsq ({0}) estão preenchidas.'.format(xnje))
161 # Atualizar o metadado xnje - número de atributos jnsq efetivos 162 xds_d.attrs['xnje'] = xnje
163
165 print('{0:10d}/{1} ... A construir a matriz disjunta ...'.format(0, int((xno-1)*xno/2/xf)), end='\r')
166 xleitura_m = np.zeros((xna+xnj), dtype=xdt) 167 xleitura_c = xds_d[...,xna+xnj:xna+xnj+xnac]
168 xnlma = 0 # inicializar o nº de linhas da matriz disjunta A
169 xitr = 0 # contabiliza o nº de iterações
170 for i in range(xno-1):
171 if (i not in xor): # não são consideradas as observações redundantes
172 xleitura_mi = xds_d[i,:xna+xnj] # ler os valores dos atributos e das colunas jnsq da observação a comparar
173 for j in range(i+1, xno): # compara a observação atual com as
observações das linhas seguintes
174 if (j not in xor) and (xleitura_c[i]!=xleitura_c[j]): # caso a observação a comparar não seja redundante e tenha valor de classe
diferente
175 xleitura_mj = xds_d[j,:xna+xnj] # ler os valores dos atributos e das colunas jnsq da observação com a qual se compara 176 xleitura_m = np.absolute(np.subtract(xleitura_mi, xleitura_mj)) # verifica se os elementos da observação atual e da observação com a qual se compara são iguais (=0) ou diferentes (=1) 177 xds_ma[xnlma,:] = xleitura_m # atualiza o dataset da matriz disjunta
178 xnlma += 1
179 xitr += 1
180 if xitr%xf == 0:
181 print('{0:10d}'.format(xitr//xf), end='\r')
182 # Atualizar o metadado xnlma - número de linhas da matriz disjunta 183 xds_d.attrs['xnlma'] = xnlma
184 print(xll+'»»» A matriz disjunta está construída.') 185
186 # Fechar o ficheiro da BD 187 xfich.close()
188
189 # Imprimir mensagem final com o tempo decorrido
190 print('\n»»» O script "'+sys.argv[0]+'" foi executado com sucesso em
'+fintervalotempo(xti, time.time())+'.\n')