Classificação Multirrótulo: Calculando Similaridades entre Rótulos - Parte 6
Olá pessoal! Hoje daremos continuidade à nossa série de artigos sobre similaridades entre rótulos. No último artigo eu mostrei como calcular de forma automatizada os valores da tabela de contingência. Hoje nós vamos usar aquele conhecimento pra construir uma função com as informações que precisamos. Partiu?
Gerando a tabela de contingência para todos os rótulos
Antes de mais nada, caso ainda não tenha, faça o download do código fonte mais recente, ele está no artigo anterior! Ele é necessário para prosseguirmos com a nossa série!
Vou mostrar agora a função que vai fazer o calculo de a, b, c e d para todos os rótulos e então vou explicar o que acontece.
compute.cont.table <- function(labels, num.labels){
retorno = list() # lista para retornar os resultados
# construindo as respectivas matrizes
ma <- build.matrix.sim(labels,num.labels) # matriz a
mb <- build.matrix.sim(labels,num.labels) # matriz b
mc <- build.matrix.sim(labels,num.labels) # matriz c
md <- build.matrix.sim(labels,num.labels) # matriz d
# o tamanho da matriz é o número de labels vezes ele mesmo
# exemplo: 4 x 4 = 16
u = (num.labels*num.labels)
# usarei o progress bar apenas para vermos o progresso do andamento da função
pb <- progress_bar$new(total = u)
# linha
for (i in 1:num.labels){
# coluna
for (j in 1:num.labels){
# pegando a primeira coluna a ser comparada
x = labels[,i]
# pegando a segunda coluna a ser comparada
y = labels[,j]
# calculando a, b, c e d - chama as respectivas funções
ma[i,j] = compute.a(x,y)
mb[i,j] = compute.b(x,y)
mc[i,j] = compute.c(x,y)
md[i,j] = compute.d(x,y)
# barra de progresso
pb$tick()
Sys.sleep(1/u)
gc()
} # fim do for interno
gc()
} # fim do for externo
# retorna os resultados
retorno$ma = ma
retorno$mb = mb
retorno$mc = mc
retorno$md = md
return(retorno)
gc()
}
O nome que dei para a função é compute.cont.table, e ela recebe como parâmetros labels e num.labels. Labels é o espaço de rótulos propriamente dito, enquanto que num.labels é o número total de rótulos do espaço. Em nosso caso num.labels é 5.
Note que eu usei retorno = list() logo como primeiro comando dentro da função. Essa variável retorno é do tipo lista, isto é, é uma variável que pode guardar qualquer tipo de dados. Pode ser um dataframe, uma string, um vetor, etc. O que quero retornar nessa função, são as matrizes resultantes dos cálculos.
Nas linhas seguintes eu chamei uma outra função que é denominada build.matrix.sim que recebe labels e num.labels como parâmetros. Vamos ver essa função antes de prosseguir.
build.matrix.sim <- function(labels, num.labels){
matrix.sim <- matrix(nrow=num.labels, ncol=num.labels, data=0) # linha 1
colnames(matrix.sim) <- colnames(labels) # linha 2
rownames(matrix.sim) <- colnames(labels) # linha 3
return(matrix.sim) # linha 4
gc() # linha 5
}
A função build.matrix.sim constrói uma matriz vazia, que foi o que fizemos no artigo passado, mas manualmente. O que fiz agora foi automatizar esse processo usando uma função. O primeiro comando dentro da função cria uma matriz com o número de linhas e colunas igual ao número de rótulos. Então, se temos 5 rótulos, vamos criar uma matriz 5 x 5.
Em seguida, nas linhas 2 e 3 dentro da função, dou nomes às colunas e linhas. Esses nomes são os nomes dos rótulos propriamente ditos. Uso o comando colnames para obter esses nomes e passar como parâmetro para a renomeação. Por fim, temos o retorno da função que vai retornar para quem a chamou, a matriz construída do jeito que programamos.
A função gc() na linha 5 serve para limpar a memória! É interessante usá-la sempre ao final de loops e funções.
Voltando agora a para a nossa função compute.cont.table, depois de construirmos as matrizes para a, b, c e d usando build.matrix.sim, podemos calcular o restante. O comando u = (num.labels*num.labels) computa e armazena na variável u o tamanho total da matriz, em nosso caso 25.
Em seguida, eu uso uma biblioteca para mostrar no console o progresso do cálculo: pb <- progress_bar$new(total = u). Uma barra de progresso serve exatamente para isso, mostrar o progresso! Existem várias formas de se fazer isto, mas aqui usei uma simples e objetiva. A variável u é usada aqui! Não se esqueça de adicionar e carregar a biblioteca progress no seu projeto, caso contrário não funcionará: library(progress)
Ótimo, agora começa a parte efetiva da coisa toda! Temos um for dentro de outro for e isto é necessário já que estamos trabalhando com matrizes! Não há como manipular matrizes se não percorrermos as linhas e colunas, então, um for é para a linha e o outro for é para a coluna.
Note que o for da linha é o primeiro, então a gente meio que fica travado na linha até computar todas as colunas. Somente depois de terminar todas as colunas dessa linha, é que passamos para a próxima linha e calculamos as colunas daquela linha. Isso se repete até terminar toda a matriz. Caso queria saber mais sobre matrizes, você pode ler este artigo aqui.
Dentro do for encadeado eu tenho x = labels[,i] e y = labels[,j] que correspondem à duas colunas diferentes do dataset. A variável i é a que identifica a primeira coluna, enquanto a variável j é a que identifica a segunda coluna. Essas variáveis vão sendo incrementadas (somadas a um) ao final do for para que seja possível mudar a linha e a coluna.
O comando labels[, i] nos permite pegar a coluna do dataset indicada por i, e o mesmo serve para labels[, j]. Armazeno o primeiro em x e o segundo em y para que sejam usados depois.
Na primeira passagem pelo for encadeado, temos que a coluna 1 será comparada com a coluna 2, o que corresponde aos nossos rótulos 1 e 2. Lembre-se de que i está travado em 1 neste momento, mas j vai mudando a cada iteração dentro do seu for. Na segunda passagem será comparada a coluna 1 com a coluna 3, e assim por diante, até que acabem as comparações da coluna 1 com todas as outras colunas.
Finalizado este passo, a variável i que está valendo 1 passará a valer 2, pois é somado 1 a ela, e então a coluna de comparação mudará. Agora a coluna 2 será comparada com todas as outras, e esse processo se repete até que todas as colunas tenham sido comparadas com todas as outras.
Para que os resultados sejam armazenados corretamente, usei o comando ma[i,j] = compute.a(x,y), isto é, i e j são as posições de linha e coluna na matriz resultante (ma, mb, mc e md) onde o valor calculado deve ser armazenado. Este comando, portanto, chama a função para calcular a e armazena o resultado na respectiva matriz. Faço o mesmo para b, c, e d.
Para ficar fácil de entender este processo, eu gravei um vídeo onde executo a construção desta matriz apresentando todos os detalhes de mudança de linha e coluna. Sugiro fortemente que você o assista antes de prosseguir, é bem curtinho!
Em nosso código final isso não pode acontecer pois consome mais processamento e memória, já que imprime muitos caracteres no console. Não precisamos disso tá ok! O que devemos sempre fazer é ser o mais objetivo e enxuto o possível em nossos códigos.
Após o for temos os retornos! Usamos o caractere $ para anexar coisas à lista! Por isso o comando fica assim: retorno$ma = ma. Estou dizendo com este comando que é para armazenar a matriz ma na variável de retorno ma, tornando-a acessível na variável que vai armazenar o resultado da função!
Vamos então chamar a função compute.cont.table pra verificar isso ai. Ah! Não esqueça de carregá-la no seu ambiente, senão não poderá usá-la.
res = compute.cont.table(dataset_2, 5)
Lembram-se que carregamos o nosso espaço de rótulos na variável dataset_2? Pois bem, é ela que passamos como primeiro parâmetro na função. O segundo paramêtro é o número de rótulos, que no nosso caso é 5. O resultado que obtermos ao executar é o seguinte:
Olha só que legal! Cada uma das matrizes está salva no resultado da execução da função que é a variável res. Agora eu posso manipular esses resultados do jeito que quiser! Por exemplo, vou salvar cada uma dessas matrizes em outra variável:
ma2 = res$ma
mb2 = res$mb
mc2 = res$mc
md2 = res$md
Agora posso usá-los como parâmetros para outras funções inclusive e nós vamos precisar delas para as medidas de similaridade!
Finalizando
Vou finalizando por aqui para que vocês não fiquem com a cabeça fervendo ai do outro lado!!!! Baixe aqui o projeto atualizado! No próximo artigo vamos calcular as outras matrizes! Te vejo lá.
Este artigo foi escrito por Elaine Cecília Gatto - Cissa e publicado originalmente em Prensa.li.