O objetivo deste tutorial, ou artigo técnico, é explicar um pouco sobre como utilizar as medidas de similaridades para dados categóricos ou binários, em um dataset multirrótulo.
Vou utilizar aqui uma porção do dataset GpositiveGO para ilustrar a explicação. Na verdade esse é um dos 10 folds do conjunto de treinamento do dataset GpositiveGO que eu criei usando este código aqui: https://github.com/cissagatto/CrossValidationMultiLabel. Preferi usar algo mais curto para facilitar o desenvolvimento do tutorial.
O código está disponível neste repositório no meu Github: https://github.com/cissagatto/MultiLabelSimilaritiesMeasures
# setando a pasta onde o arquivo está salvo
setwd("/home/elaine/MultiLabelSimilaritiesMeasures/GpositiveGO/CrossValidation/Tr")
# abrindo o FOLD 1
dados = foreign::read.arff("GpositiveGO-Split-Tr-1.arff")O arquivo “datasets.csv” foi criado com base nos datasets disponívels no site https://cometa.ujaen.es/datasets/. É um arquivo com informações dos 74 datasets disponíveis no site, incluindo também o número de neurônios para ser utilizado no mapa de Kohonen. Este dataset tem as seguintes características:
setwd("/home/elaine/MultiLabelSimilaritiesMeasures/")
datasets = read.csv("datasets.csv")
ds = datasets[29,]
print(t(ds))## 29
## Id "29"
## Name "GpositiveGO"
## Instances "519"
## Attributes "916"
## Inputs "912"
## Labels "4"
## Labelsets "7"
## Single "2"
## Max.freq "206"
## Card "1.0077"
## Dens "0.2519"
## Mean.IR "3.8605"
## Scumble "1"
## TCS "10.1478"
## AttStart "1"
## AttEnd "912"
## LabelStart "913"
## LabelEnd "916"
## Distinct "0"
## xn "2"
## yn "2"
## gridn "4"
Aqui vou separar algumas informações em variáveis para que possamos utilizá-las no código.
start.label = as.numeric(ds$LabelStart) # coluna onde começa o espaço de rótulos no dataset
end.label = as.numeric(ds$LabelEnd) # coluna onde termina o espaço de rótulos
num.labels = as.numeric(ds$Labels) # número de rótulos do espaço de rótulosTambém vou separar o espaço de rótulos dos outros atributos. As medidas de similaridades serão aplicadas apenas nos rótulos.
labels = data.frame(dados[,start.label:end.label])
head(labels)## Label1 Label2 Label3 Label4
## 1 1 0 0 0
## 2 1 0 0 0
## 3 1 0 0 0
## 4 1 0 0 0
## 5 1 0 0 0
## 6 1 0 0 0
Relembrando: um dataset é composto por atributos e instâncias (também chamadas de observações ou exemplos). Os atributos podem ser atributos de entrada ou de saída. Em nosso caso, os rótulos são os atributos de saída e eles são compostos por valores 0 ou 1, indicando se a instância pertence ou não àquele rótulo.
Vamos precisar também dos pacotes a seguir. Verifique se eles estão instalados.
library(dplyr)##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(progress)Agora vamos entender um pouco a respeito das medidas que vamos trabalhar aqui. Todas elas são baseadas em uma tabela chamada de tabela de contingência.
A partir dessa tabela de contingência conseguimos obter as seguintes informações básicas:
a = [Label1 == 1 E Label2 == 1]. Proporção de uns (1) que os rótulos compartilham nas mesmas posições. Também podemos dizer que são os encontros positivos entre os dois rótulos em questão, pois os dois estão presentes;
b = [Label1 == 0 E Label2 == 1]. Proporção de uns (1) no primeiro rótulo e zeros (0) no segundo rótulo nas mesmas posições. Aqui podemos dizer que o Label1 está presente e o Label2 ausente;
c = [Label1 == 1 E Label2 == 0]. Proporção de zeros (0) no primeiro rótulo e uns (1) no segundo rótulo nas mesmas posições. Também podemos entender que o Label1 está ausente e o Label2 presente;
d = [Label1 == 0 E Label2 == 0]. Proporção de zeros (0) que ambas os rótulos compartilham nas mesmas posições. Neste caso, temos que nem Label1 e nem Label2 estão presentes, portanto, são os encontros negativos.
Basicamente é uma tabela que conta a frequência de pares de rótulos. Como temos vários rótulos em nosso dataset, então temos que calulcar uma tabela dessa para cada combinação. Em nosso exemplo temos 4 rótulos: Label1, Label2, Label3 e Label4. Então, temos que calcular a tabela de contingência para todos os pares de rótulos possíveis desse conjunto.
Ainda a partir da tabela de contingência podemos calcular as probabilidades marginais:
p1 = a + b, onde a representa 11 e b representa 10 na tabela
p2 = c + d, onde a representa 01 e b representa 00 na tabela
p3 = a + c, onde a representa 11 e b representa 01 na tabela
p4 = b + d, onde a representa 10 e b representa 00 na tabela
p5 = a + d, onde a representa 11 e b representa 00 na tabela
p6 = b + c, onde a representa 10 e b representa 01 na tabela
Para exemplificar, vamos pegar o Label1 e o Label2 do dataset GpositiveGO:
print(labels$Label1)## [1] 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
## [38] 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
## [75] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1
## [112] 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0
## [149] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## [186] 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0
## [223] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
## [260] 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
## [297] 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
## [334] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1
## [371] 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0
## [408] 0 0 0 0 0 0 0 0
## Levels: 0 1
print(labels$Label2)## [1] 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## [38] 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
## [75] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## [112] 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
## [149] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## [186] 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0
## [223] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## [260] 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## [297] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
## [334] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## [371] 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
## [408] 0 0 0 0 0 0 0 0
## Levels: 0 1
Observaram que ao final está escrito “levels 0 and 1”? Bem, precisamos converter os fatores em número:
x = as.numeric(levels(labels$Label1))[labels$Label1]
head(x)## [1] 1 1 1 1 1 1
y = as.numeric(levels(labels$Label2))[labels$Label2]
head(y)## [1] 0 0 0 0 0 0
Notem que agora não aparece mais a palavra levels! Feitos estes preparativos vamos calcular os valores da tabela. Apenas reforçando que estou usando head() para printar no console de saída apenas os primeiros resultados.
Primeiro vou fazer o passo a passo, apenas pra mostrar bem didaticamente o que está acontecendo. Depois vou mostrar como fazer o cálculo de forma mais objetiva. No caso do a, sabemos que devemos contar a frequencia em que ambos os rótulos estão presentes. Lembrando que no R FALSE é igual a zero e TRUE é igual a 1.
# verificando onde o Label1 é igual a 1
res1 = (x == 1)
head(res1)## [1] TRUE TRUE TRUE TRUE TRUE TRUE
# verificando onde o Label2 é igual a 1
res2 = (y == 1)
head(res2)## [1] FALSE FALSE FALSE FALSE FALSE FALSE
# verificando quando os dois rótulos estão presentes
res3 = (res1 == 1 & res2 == 1)
head(res3)## [1] FALSE FALSE FALSE FALSE FALSE FALSE
# somando
sum(res3)## [1] 1
# ou podemos fazer diretamente:
a = sum( (x == 1) & (y == 1) )
a## [1] 1
O resultado final nos diz que os rótulos Label1 e Label2 estão presentes juntos apenas uma vez! Assim, podemos ir formando a nossa tabela de contingência, vejam:
Para calcular b, devemos contar a frequencia em que o rótulo Label1 está presente e o Label2 ausente.
# verificando onde o Label1 é igual a 1
res1 = (x == 1)
head(res1)## [1] TRUE TRUE TRUE TRUE TRUE TRUE
# verificando onde o Label2 é igual a 0
res2 = (y == 0)
head(res2)## [1] TRUE TRUE TRUE TRUE TRUE TRUE
# aqui é onde verificamos Label1=1 e Label2=0
res3 = (res1 == 1 & res2 == 0)
head(res3)## [1] FALSE FALSE FALSE FALSE FALSE FALSE
# somando
sum(res3)## [1] 1
# ou podemos fazer diretamente:
b = sum( (x == 1) & (y == 0) )
b## [1] 138
Podemos entender que o rótulo Label1 aparece sozinho 138 vezes. Atualizando a tabela:
O c é basicamente o inverso do b, vamos verificar quantas vezes o rótulo Label2 está presente e o Label1 ausente.
# verificando onde o Label1 é igual a 0
res1 = (x == 0)
head(res1)## [1] FALSE FALSE FALSE FALSE FALSE FALSE
# verificando onde o Label2 é igual a 1
res2 = (y == 1)
head(res2)## [1] FALSE FALSE FALSE FALSE FALSE FALSE
# aqui é onde verificamos Label1=0 e Label2=1
res3 = (res1 == 0 & res2 == 1)
head(res3)## [1] FALSE FALSE FALSE FALSE FALSE FALSE
# somando
sum(res3)## [1] 1
# ou podemos fazer diretamente:
c = sum( (x == 0) & (y == 1) )
c## [1] 14
Podemos entender que o rótulo Label2 aparece sozinho 14 vezes. A tabela ficará da seguinte forma:
Já o d é o inverso do a, aqui queremos contar quantas vezes ambos os rótulos estão ausentes.
# verificando onde o Label1 é igual a 0
res1 = (x == 0)
head(res1)## [1] FALSE FALSE FALSE FALSE FALSE FALSE
# verificando onde o Label2 é igual a 0
res2 = (y == 0)
head(res2)## [1] TRUE TRUE TRUE TRUE TRUE TRUE
# aqui é onde verificamos Label1=0 e Label2=0
res3 = (res1 == 0 & res2 == 0)
head(res3)## [1] FALSE FALSE FALSE FALSE FALSE FALSE
# somando
sum(res3)## [1] 1
# ou podemos fazer diretamente:
d = sum( (x == 0) & (y == 0) )
d## [1] 262
O resultado final nos diz que os rótulos Label1 e Label2 nunca estão presentes juntos 262 vezes! A tabela ficará da seguinte forma:
Bem, mas convenhamos que fazer isso semi-automatizado para todos os rótulos dará muito trabalho. Se o espaço de rótulos tiver 500 rótulo, teremos de fazer esse cálculo muitas vezes. O ideal é que automatizemos todo o processo. Na seção “Calculando para todos os rótulos” eu mostrarei para vocês como calcular tudo isso de forma automática, independente da quantidade de rótulos do dataset.
ab = sum(a+b)
print(ab)## [1] 139
ac = sum(a+c)
print(ac)## [1] 15
bd = sum(b+d)
print(bd)## [1] 400
cd = sum(c+d)
print(cd)## [1] 276
ad = sum(a+d)
print(ad)## [1] 263
bc = sum(b+c)
print(bc)## [1] 152
n = a + b + c + d
print(n)## [1] 415
Resumindo o que temos até agora:
Podemos então concluir que neste dataset os rótulos Label1 e Label2 tem pouca correlação entre eles já que só ocorrem juntos uma única vez. Isso também pode ser confirmado quando olhamos para o total de vezes em que eles nunca aparecem juntos: 262. Também podemos concluir que o rótulo Label1 é predominante, 138 ocorrências, enquanto que o rótulo Label2 não, apenas 14 ocorrências. Como podemos notar, esta tabela nos fornece bastante informação a respeito dos rótulos individualmente, e também de suas ocorrências com os outros rótulos.
Duas variáveis binárias são chamadas de não correlacionadas se elas compartilham covariância zero, ou seja, ad - bc = 0. Em nosso exemplo não existe covariância.
(ab-bc)==0## [1] FALSE
Para facilitar a nossa vida, vamos criar funções para cada cálculo, de forma que será possível computar tudo para todos os pares de rótulos do dataset. Para isso, vamos precisar utilizar uma matriz. Nosso dataset de exemplo tem 4 rótulos, portanto, temos 4 x 4 combinações de rótulos, o que nos dá 16 pares de rótulos. Assim, vamos construir uma matriz quadrada L X L onde L é o número de rótulos do dataset. Em nosso caso será uma matriz com 4 X 4, o que nos dá as 16 posições que precisamos para armazenar o resultado das 16 combinações.
Chamarei a função de “build.matrix.corr.” e passarei como parâmetros o espaço de rótulos e o número total de rótulos do espaço de rótulos. Na primeira linha dentro da função criamos a matriz propriamente dita, enquanto na segunda e terceira linhas apenas nomeamos as linhas e colunas de acordo com os nomes de rótulos do dataset. Ao final, retornamos a matriz construida.
# num.labels = número de rótulos do espaço de rótulos
# labels = espaço de rótulos
build.matrix.corr <- function(labels, num.labels){
matrix.corr <- matrix(nrow=num.labels, ncol=num.labels, data=0)
colnames(matrix.corr) <- colnames(labels)
rownames(matrix.corr) <- colnames(labels)
return(matrix.corr)
gc()
}Para cada função, nós vamos passar dois parâmetros, x e y, que serão os valores nas posições correspondentes de cada rótulo. Nós vamos considerar que nosso espaço de rótulos é uma matriz, então cada posição será computada individualmente. As imagens a seguir ilustram isto. A primeira imagem é a matriz de correlação resultante, isto é, a matriz que armazerá os resultados dos cálculo para todos os pares de rótulos.
Imagem 1
Esta segunda imagem ilustra os pares de rótulos que estão sendo computados.
Imagem 2
A terceira imagem ilustra o espaço de rótulos como uma matriz. As linhas são as instâncias e as colunas são os rótulos. Cada célula corresponde ao valor 1 ou 0. Assim, vamos calcular L11 com L11, depois L11 com L12, L11 com L13, L11 com L14 e assim por diante.
Imagem 3
A seguir apresento as funções para calcular cada item da tabela de contingência. Observe que elas são simples e objetivas. Dei o nome de *compute.___* para não haver problemas com outras bibliotecas ou outros objetos que estejam sendo usados no ambiente R durante a execução do código. As funções abaixo podem ser ainda mais simplificadas, no entanto para ser um pouco mais didático, as deixei num formato mais comum. Por exemplo, poderiam ficar assim:
compute.a <- function(x, y)sum(x == 1 & y == 1)FUNÇÕES:
# proporção de 1s que ambas as variáveis compartilham nas mesmas posições
# correspondências positivas entre x e y: x e y == 1
compute.a <- function(x, y){
return(sum(x == 1 & y == 1))
}
# proporção de 0s na primeira variável e 1s na segunda variável nas mesmas posições
# x ausente: x == 0 e y == 1
compute.b <- function(x, y){
return(sum(x == 0 & y == 1))
}
# proporção de 1s na primeira variável e 0s na segunda variável nas mesmas posições
# y ausente: x == 1 e y == 0
compute.c <- function(x, y){
return(sum(x == 1 & y == 0))
}
# proporção de zeros que ambas as variáveis compartilham
# correspondências positivas entre x e y: x and y == 0
compute.d <- function(x,y,m){
return(sum(x == 0 & y == 0))
}
# marginal probabilities
# p1 = a + b --> proporção de uns na primeira variável
compute.ab <- function(a, b){
return(a+b)
}
# p2 = a + c --> proporção de uns na segunda variável
compute.ac <- function(a, c){
return(a+c)
}
# p3 = a + d --> diagonal (11) (00)
compute.ad <- function(a, d){
return(a+d)
}
# p4 = b + c --> diagonal (10) (01)
compute.bc <- function(b, c){
return(b+c)
}
# p5 = b + d --> proporção de zeros na segunda variável
compute.bd <- function(b, d){
return(b+d)
}
# p6 = c + d --> proporção de zeros na primeira variável
compute.cd <- function(c, d){
return(c+d)
}
# all
compute.n <- function(a,b,c,d){
return(a+b+c+d)
}Essas funções computam a ocorrência para apenas as posições correntes de x e y. Elas devem ser chamadas dentro de uma outra função para preencher a matriz resultante, como veremos a seguir.
Para cada item da tabela de contingência criaremos uma matriz. Ficará assim:
Portanto, estamos trabalhando com operações de matrizes!!! A imagem a seguir ilustra a matriz a:
O mesmo pode ser aplicado às matrizes b, c e d. Quanto às matrizes de probabilidades marginais, vejamos como fica a matriz ab:
Novamente, o mesmo serve como base para as outras matrizes. Quanto à função, primeiro nós precisamos construir a matrizes e depois preenchê-las. Estou passando dois parâmetros nesta função, que é o espaço de rótulos e o número de rótulos do dataset. O código da função para calcular a, b, c e d para todos os rótulos é mostrado abaixo:
# num.labels = número de rótulos do espaço de rótulos
# labels = espaço de rótulos
compute.cont.table <- function(labels, num.labels){
retorno = list() # lista para retornar os resultados
# construindo as respectivas matrizes
ma <- build.matrix.corr(labels,num.labels) # matriz a
mb <- build.matrix.corr(labels,num.labels) # matriz b
mc <- build.matrix.corr(labels,num.labels) # matriz c
md <- build.matrix.corr(labels,num.labels) # matriz d
# o tamanha 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]
# convertendo os fatores para números
x = as.numeric(levels(x))[x]
y = as.numeric(levels(y))[y]
# 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()
}Usando a função
res1 = compute.cont.table(labels, num.labels)
res1## $ma
## Label1 Label2 Label3 Label4
## Label1 139 1 0 1
## Label2 1 15 0 0
## Label3 0 0 167 2
## Label4 1 0 2 98
##
## $mb
## Label1 Label2 Label3 Label4
## Label1 0 14 167 97
## Label2 138 0 167 98
## Label3 139 15 0 96
## Label4 138 15 165 0
##
## $mc
## Label1 Label2 Label3 Label4
## Label1 0 138 139 138
## Label2 14 0 15 15
## Label3 167 167 0 165
## Label4 97 98 96 0
##
## $md
## Label1 Label2 Label3 Label4
## Label1 276 262 109 179
## Label2 262 400 233 302
## Label3 109 233 248 152
## Label4 179 302 152 317
Agora podemos fazer algo parecido para calcular as probabilidades marginais para todos os rótulos:
# num.labels = número de rótulos do espaço de rótulos
# labels = espaço de rótulos
# a, b, c, d = matrizes
compute.marg.probs <- function(labels, num.labels, a, b, c, d){
retorno = list() # lista para retornar os resultados
# construindo as respectivas matrizes
mab <- build.matrix.corr(labels, num.labels) # matriz ab
mac <- build.matrix.corr(labels, num.labels) # matriz ac
mad <- build.matrix.corr(labels, num.labels) # matriz ad
mbc <- build.matrix.corr(labels, num.labels) # matriz bc
mbd <- build.matrix.corr(labels, num.labels) # matriz bd
mcd <- build.matrix.corr(labels, num.labels) # matriz cd
mn <- build.matrix.corr(labels, num.labels) # matriz n
u = (num.labels*num.labels) # número de linhas e colunas da matriz resultante
pb <- progress_bar$new(total = u) # barra de progresso
# linha
for (i in 1:num.labels){
# coluna
for (j in 1:num.labels){
# matriz ab
x = ma[i,j]
y = mb[i,j]
mab[i,j] = compute.ab(x,y)
# matriz ac
w = ma[i,j]
v = mc[i,j]
mac[i,j] = compute.ac(w,v)
# matriz ad
e = ma[i,j]
f = md[i,j]
mad[i,j] = compute.ad(e,f)
# matriz bc
g = mb[i,j]
h = mc[i,j]
mbc[i,j] = compute.bc(g,h)
# matriz bd
k = mb[i,j]
l = md[i,j]
mbd[i,j] = compute.bd(k,l)
# matriz cd
m = mc[i,j]
n = md[i,j]
mcd[i,j] = compute.cd(m,n)
# matriz n
o = ma[i,j]
p = mb[i,j]
q = mc[i,j]
r = md[i,j]
mn[i,j] = compute.n(o,p,q,r)
# barra de progresso
pb$tick()
Sys.sleep(1/u)
gc()
} # end intern for
#j = j + 1
gc()
} # enf extern for
# valores a serem retornados
retorno$mab = mab
retorno$mac = mac
retorno$mad = mad
retorno$mbc = mbc
retorno$mbd = mbd
retorno$mcd = mcd
retorno$mn = mn
return(retorno)
gc()
}Usando a função
# obtendo os resultados de a, b, c e d que foram calculados na função anterior
ma = res1$ma # matriz a
mb = res1$mb # matriz b
mc = res1$mc # matriz c
md = res1$md # matriz d
res2 = compute.marg.probs(labels, num.labels, ma, mb, mc, md)
res2## $mab
## Label1 Label2 Label3 Label4
## Label1 139 15 167 98
## Label2 139 15 167 98
## Label3 139 15 167 98
## Label4 139 15 167 98
##
## $mac
## Label1 Label2 Label3 Label4
## Label1 139 139 139 139
## Label2 15 15 15 15
## Label3 167 167 167 167
## Label4 98 98 98 98
##
## $mad
## Label1 Label2 Label3 Label4
## Label1 415 263 109 180
## Label2 263 415 233 302
## Label3 109 233 415 154
## Label4 180 302 154 415
##
## $mbc
## Label1 Label2 Label3 Label4
## Label1 0 152 306 235
## Label2 152 0 182 113
## Label3 306 182 0 261
## Label4 235 113 261 0
##
## $mbd
## Label1 Label2 Label3 Label4
## Label1 276 276 276 276
## Label2 400 400 400 400
## Label3 248 248 248 248
## Label4 317 317 317 317
##
## $mcd
## Label1 Label2 Label3 Label4
## Label1 276 400 248 317
## Label2 276 400 248 317
## Label3 276 400 248 317
## Label4 276 400 248 317
##
## $mn
## Label1 Label2 Label3 Label4
## Label1 415 415 415 415
## Label2 415 415 415 415
## Label3 415 415 415 415
## Label4 415 415 415 415
# função para calcular a covariância
# retorna 0 se (ad - bc) != 0
# retorna 1 se (ad - bc) == 0
covariance <- function(x,y){
return((x-y)==0)
}
# num.labels = número de rótulos do espaço de rótulos
# labels = espaço de rótulos
# ad, bc = matrizes
compute.covar <- function(labels, num.labels, mad, mbc){
mco <- build.matrix.corr(labels, num.labels) # matriz covariância
u = (num.labels*num.labels) # tamanho da matriz
pb <- progress_bar$new(total = u) # barra de progresso
# linha
for (i in 1:num.labels){
# coluna
for (j in 1:num.labels){
# calcula a covariância para cada posição
x = mad[i,j]
y = mbc[i,j]
mco[i,j] = covariance(x,y)
# barra de progresso
pb$tick()
Sys.sleep(1/u)
gc()
} # fim do for interno
gc()
} # fim do for externo
return(mco)
gc()
}Usando a função
mad = res2$mad # matriz ad
mbc = res2$mbc # matriz bc
res3 = compute.covar(labels, num.labels, mad, mbc)
res3## Label1 Label2 Label3 Label4
## Label1 0 0 0 0
## Label2 0 0 0 0
## Label3 0 0 0 0
## Label4 0 0 0 0
Não temos covariância neste dataset.
Agora que já aprendemos sobre a tabela de contingência e todos os cálculos e informações básicas que podemos obter dela, vamos dar um passo adiante. Nesta seção mostrarei 76 medidas de similaridade as quais converti em funções para calcular similaridades para todos os rótulos do dataset multirrótulo. Ao final do tutorial vocês encontrarão as referências nas quais eu me baseei para escrever este tutorial. Para diferenciar as minhas funções de outras existentens em outros pacotes, eu adicionei “.e” ao final do nome de todas elas.
\[ ample = \Bigg| \frac{a(c+d)}{c(a+b)} \Bigg|\\ \]
ample.e <- function(a,b,c,d){
return(abs((a*(c+d))/(c*(a+b))))
}\[ delta = max(a,b) + max(c,d) + max(a,c) + max(b,d) \\ delta' = max((a+b),(b+d)) + max((a+b),(c+d) \\ anderberg = \frac{delta - delta'}{2n} \]
anderberg.e <- function(a,b,c,d,n){
z = max(a,b)+max(c,d)+max(a,c)+max(b,c)
w = max((a+c),(b+d))+max((a+b),(c+d))
p = (z-w)/(2*n)
return(p)
}\[ baroni.1 = \frac{\sqrt{ad}+a}{\sqrt{ad} + a + b + c} \]
baroni.urbani.buser.1.e <- function(a,b,c){
return((sqrt((a*d)) + a)/((sqrt((a*d))) + a + b + c))
}\[ baroni.2 = \frac{\sqrt{ad}+a-(b+c)}{\sqrt{ad} + a + b + c} \]
baroni.urbani.buser.2.e <- function(a,b,c,d){
return((sqrt((a*d))+a-(b+c))/((sqrt((a*d))) + a + b + c))
}\[ braun.banquet = \frac{a}{max((a+b),(a+c))} \]
braun.banquet.e <- function(a,b,c){
return(a/max((a+b),(a+c)))
}\[ bray.curtis = \frac{b+c}{(2a+b+c)} \]
bray.curtis.e <- function(a,b,c){
return((b+c)/((2*a)+b+c))
}\[ canberra = (b+c)^{\frac{2}{2}} \]
canberra.e <- function(b,c){
return((b+c)^(2/2))
}\[ chord = \sqrt{2 \Bigg( 1 - \frac{a}{\sqrt( (a+b) + (a+c) )} \Bigg)} \]
chord.e <- function(a,b,c){
return(sqrt(2*(1-((a)/(sqrt((a+b)*(a+c)))))))
}\[ cityblock = b + c \]
cityblock.e <- function(b,c){
return(b+c)
}\[ cole = \frac{ \sqrt{2}(ad - bc)}{\sqrt{(ad-bc)^2-(a+b)(a+c)(b+d)(c+d)}} \]
cole.e <- function(a,b,c,d){
d1 = sqrt(2) * ((a*d)-(b*c))
d2 = (((a*d)-(b*c))^2) - ((a+b)*(a+c)*(b+d)*(c+d))
d3 = sqrt(d2)
d4 = d1/d3
return(d4)
}\[ cosine = \frac{a}{\sqrt{(a+b)(a+c)}^2} \]
cosine.e <- function(a,b,c){
return(a / sqrt((((a+b) * (a+c))))^2)
}\[ czekanowski = \frac{2a}{2a+b+c} \]
czekanowski.e <- function(a,b,c){
return((2*a)/((2*a)+b+c))
}\[ dennis = \frac{ad-bc}{\sqrt{n(a+b)(a+c)}} \]
dennis.e <- function(a,b,c,n){
return(((a*d)-(b*c))/sqrt(n*(a+b)*(a+c)))
}\[ dice = \frac{2a}{2a+b+c} \]
dice.e <- function(a,b,c){
return((2*a)/((2*a)+b+c))
}\[ dispersion = \frac{ad-bc}{(a+b+c+d)^2} \]
disperson.e <- function(a,b,c,d){
return(((a+d)-(b+c))/((a+b+c+d)^2))
}\[ driver.kroeber = \frac{a}{2} * \Big( \frac{1}{a+b} + \frac{1}{a+c} \Big) \]
driver.kroeber.e <- function(a,b,c){
return((a/2) * ((1/(a+b)) + (1/(a+c))))
}\[ euclidean = \sqrt{b+c} \]
euclidean.e <- function(b,c){
return(sqrt(b+c))
}\[ eyraud = \frac{n^2(na-(a+b)(a+c))}{(a+b)(a+c)(b+d)(c+d)} \]
eyraud.e <- function(a,b,c,d,n){
return((n^2)*((n*a)-(a+b)*(a+c))/(a+b)*(a+c)*(b+d)*(c+d))
}\[ fager.mcgowan = \frac{a}{\sqrt{(a+b)(a+c)}} - \frac{max((a+b),(a+c))}{2} \]
fager.mcgowan.e <- function(a,b,c){
return((a/sqrt((a+b)+(a+c))) - (max((a+b),(a+c))/2))
}\[ faith = \frac{a+0.5d}{a+b+c+d} \]
faith.e <- function(a,b,c,d){
return((a+(0.5*d))/(a+b+c+d))
}\[ forbes.2 = \frac{na-(a+b)(a+c)}{n*(min((a+b),(a+c))-((a+b)(a+c))} \]
forbes.2.e <- function(a,b,c,n){
return((n*a)-((a+b)*(a+c))/n*(min((a+b),(a+c))-((a+b)*(a+c))))
}\[ forbesi = \frac{na}{(a+b)(a+c)} \]
forbesi.e <- function(a,b,c,n){
return((n*a)/((a+b)*(a+c)))
}\[ fossum = \frac{n(a-0.5)^2}{(a+b)(a+c)} \]
fossum.e <- function(a,b,c,n){
return((n*(a-0.5)^2)/(a+b)*(a+c))
}\[ gilbert.well = \log a - \log n - \log \Bigg(\frac{(a+b)}{n}\Bigg) - \log \Bigg(\frac{a+c}{n}\Bigg) \]
gilbert.well.e <- function(a,b,c,n){
return(log(a) - log(n) - log((a+b)/n) - log((a+c)/n))
}\[ delta = max(a,b) + max(c,d) + max(a,c) + max(b,d) \\ delta' = max((a+b),(b+d)) + max((a+b),(c+d) \\ goodman.kruskal = \frac{delta-delta'}{2n-delta'} \]
goodman.kruskal.e <- function(){
z = max(a,b)+max(c,d)+max(a,c)+max(b,c)
w = max((a+c),(b+d))+max((a+b),(c+d))
p = (z-w)/((2*n)-w)
return(p)
}\[ gower = \frac{a+d}{\sqrt{(a+b)+(a+c)+(b+d)+(c+d)}} \]
gower.e <- function(a,b,c,d){
return((a+d)/sqrt((a+b)*(a+c)*(b+d)*(c+d)))
}\[ gower.legendre = \frac{a+d}{a+0.5(b+c)+d} \]
gower.legendre.e <- function(a,b,c,d){
return((a+d)/(a+(0.5*(b+c))+d))
}\[ hamann = \frac{(a+d)-(b+c)}{a+b+c+d} \]
hamann.e <- function(a,b,c,d){
return(((a+d)-(b+c))/(a+b+c+d))
}\[ hamming = b + c \]
hamming.e <- function(b,c){
return(b+c)
}\[ hellinger = 2\sqrt{ \Bigg( 1 - \frac{a}{\sqrt{(a+b)(a+c)}} \Bigg) } \]
hellinger.e <- function(a,b,c){
return(2 * sqrt(1 - ((a)/(sqrt((a+b)*(a+c))))))
}\[ inner.product = a + d \]
inner.product.e <- function(a,d){
return(a+d)
}\[ intersection = a \]
intersection.e <- function(a){
return(a)
}\[ jaccard = \frac{a}{a+b+c} \]
jaccard.e <- function(a,b,c){
return(a/(a+b+c))
}\[ johnson = \frac{a}{a+b} + \frac{a}{a+c} \]
johnson.e <- function(a,b,c){
return((a/(a+b)) + (a/(a+c)))
}\[ kulczynski.1 = \frac{a}{b+c} \]
kulczynski.1.e <- function(a,b,c){
return(a/(b+c))
}\[ kulczynski.2 = \frac{ \frac{a}{2}(2a+b+c)}{(a+b)(a+c)} \]
kulczynski.2.e <- function(a,b,c){
return((a/2) * ((2*a)+b+c))/(a+b)*(a+c)
}\[ lance.williams = \frac{b+c}{2a+b+c} \]
lance.williams.e <- function(a,b,c){
return((b+c)/((2*a)+b+c))
}\[ manhattan = b + c \]
manhattan.e <- function(b,c){
return(b+c)
}\[ mcconnaughey = \frac{a^2 - bc}{(a+b)(a+c)} \]
mcconnaughey.e <- function(a,b,c){
return(((a^2) - (b-c))/(a+b)*(a+c))
}\[ mean.manhanttan = \frac{b+c}{a+b+c+d} \]
mean.manhattan.e <- function(b,c,d){
return((b+c)/(a+b+c+d))
}\[ michael = \frac{4(ad - bc)}{(a+d)^2 + (b+c)^2} \]
michael.e <- function(a,b,c,d){
return(4*((a*d)-(b*c))/((a+d)^2) + ((b+c)^2))
}\[ minowski = (b+c)^{\frac{1}{1}} \]
minowski.e <- function(b,c){
return((b+c)^(1/1))
}\[ mountford = \frac{a}{0.5(ab+ac)+bc} \]
mountford.e <- function(a,b,c){
return(a/(0.5*(((a*b)+(a*c))+(b*c))))
}\[ nei.li = \frac{20}{(a+b)+(a+c)} \]
nei.li.e <- function(a,b,c){
return((2*a)/((a+b)+(a+c)))
}\[ ochiai.1 = \frac{a}{\sqrt{(a+b)(a+c)}} \]
ochiai.e <- function(a,b,c){
return(a/(sqrt((a+b)*(a+c))))
}\[ ochiai.2 = \frac{ad}{\sqrt{(a+b)(a+c)(b+d)(c+d)}} \]
ochiai.2.e <- function(a,b,c,d){
return((a*d)/sqrt((a+b)*(a+c)*(b+d)*(c+d)))
}\[ otsuka = \frac{a}{((a+b)(a+c))^{0.5}} \]
otsuka.e <- function(a,b,c){
return(a/((a+b)*(a+c))^0.5)
}\[ pattern.difference = \frac{4bc}{(a+b+c+d)^2} \]
pattern.difference.e <- function(a,b,c,d){
return((4*b*c)/((a+b+c+d)^2))
}\[ pearson.1 = \frac{n(ad-bc)^2}{(a+b)(a+c)(c+d)(b+d)} \]
pearson.1.e <- function(a,b,c,d,n){
return(n*(((a*d)-(b*c))^2)/(a+b)*(a+c)*(c+d)*(b+d))
}\[ pearson.2 = \Big( \frac{pearson.1}{n+pearson.1} \Big)^{1/2} \]
pearson.2.e <- function(a,b,c,d,n){
z = pearson.1.e(a,b,c,d,n)
w = (z/(n*z))^(1/2)
return(w)
}\[ p = \frac{ad-bc}{\sqrt{(a+b)(a+c)(b+d)(c+d)}} \\ pearson.3 = \Big( \frac{p}{n+p} \Big)^{1/2} \]
pearson.3.e <- function(a,b,c,d){
z = ((a*d)-(b*c))/sqrt((a+b)*(a+c)*(b+d)*(c+d))
w = (z/(n+p))^(1/2)
return(w)
}\[ pearson.heron.1 = \frac{ad-bc}{\sqrt{(a+b)(a+c)(b+d)(c+d)}} \]
pearson.heron.1.e <- function(a,b,c,d){
return((a*d)-(b*c)/(a+b)*(a+c)*(b+d)*(c+d))
}\[ pearson.heron.2 = \cos\Bigg( \frac{\pi\sqrt{bc}}{\sqrt{ad} + \sqrt{bc}} \Bigg) \]
pearson.heron.2.e <- function(a,b,c){
return(cos(pi*(sqrt(b*c))/sqrt(a*d)+sqrt(b*c)))
}\[ pierce = \frac{ab+bc}{ab+2bc+cd} \]
peirce.e <- function(a,b,c,d){
return((a*b)+(b*c)/(a*b)+(2*b*c)+(c*d))
}\[ roger.tanimoto = \frac{a+d}{a+2(b+c)+d} \]
roger.tanimoto.e <- function(a,b,c,d){
return((a+d)/(2*(b+c)+d))
}\[ russel.rao = \frac{a}{a+b=c+d} \]
russel.rao.e <- function(a,b,c,d){
return(a/(a+b+c+d))
}\[ shape.difference = \frac{n(b+c) - (b-c)^2}{(a+b+c+d)^2} \]
shape.differnece.e <- function(a,b,c,d){
return((n*(b+c)-((b-c)^2))/((a+b+c+d)^2))
}\[ simpson = \frac{a}{min(a+b, a+c)} \]
simpson.e <- function(a,b,c){
return(a/min((a+b),(a+c)))
}\[ size.difference = \frac{(b+c)^2}{(a+b+c+d)^2} \]
size.difference.e <- function(a,b,c,d){
return(((b+c)^2)/((a+b+c+d)^2))
}\[ sokal.michener = \frac{a+d}{a+b+c+d} \]
sokal.michener.e <- function(a,b,c,d){
return((a+d)/(a+b+c+d))
}\[ sokal.sneath.1 = \frac{a}{a+2b+2c} \]
sokal.sneath.1.e <- function(a,b,c){
return(a/(a+(2*b)+(2*c)))
}\[ sokal.sneath.2 = \frac{2(a+d)}{2a+b+c+2d} \]
sokal.sneath.2.e <- function(a,b,c,d){
return(2*(a+d)/((2*a)+b+c+(2*d)))
}\[ sokal.sneath.3 = \frac{a+d}{b+c} \]
sokal.sneath.3.e <- function(a,b,c,d){
return((a+d)/(b+c))
}\[ sokal.sneath.4 = \frac{ \frac{a}{(a+b)} + \frac{a}{(a+c)} + \frac{d}{(b+b)} + \frac{d}{(b+d)}}{4} \]
sokal.sneath.4.e <- function(a,b,c,d){
return(((a/(a+b))+(a/(a+c))+(d/(b+d))+(d/(b+d)))/4)
}\[ sokal.sneath.5 = \frac{ad}{(a+d)(a+c)(b+d)(c+d)^{0.5}} \]
sokal.sneath.5.e <- function(a,b,c,d){
return((a*d)/(a+b)*(a+c)*(b+d)*((c*d)^0.5))
}\[ sorgenfrei = \frac{a^2}{(a+b)(a+c)} \]
sorgenfrei.e <- function(a,b,c){
return((a^2)/(a+b)*(a+c))
}\[ square.euclidean = \sqrt{(b+c)^2} \]
square.euclidean.e <- function(b,c){
return(sqrt((b+c)^2))
}\[ stiles = log_{10} * \frac{n\Big(\Big|ad-bc\Big|-\frac{n}{2}\Big)^2\\}{(a+b)(a+c)(b+d)(c+d)} \]
stiles.e <- function(a,b,c,d,n){
return(log10((n*(abs((a*d)-(b*c))-(n/2))^2) /((a+b)*(a+c)*(b+d)*(c+d))))
}\[ tanimoto = \frac{a}{(a+b)+(a+c)-a} \]
tanimoto.e <- function(a,b,c){
return(a/((a+b)+(a+c)-a))
}\[ tarantula = \frac{a(c+d)}{c(a+d)} \]
tarantula.e <- function(a,b,c){
return((a*(c+d))/(c*(a+b)))
}\[ tarwid = \frac{na-(a+b)(a+c)}{na+(a+b)(a+c))} \]
tarwid.e <- function(a,b,c,n){
return((n*a) - (a+b) * (a+c) / (n*a) + (a+b) * (a+c))
}\[ 3w.jaccard = \frac{3a}{3a+b+c} \]
three.w.jaccard.e <- function(a,b,c){
return((3*a)/((3*a)+b+c))
}\[ vari = \frac{(b+c)}{4(a+b+c+d)} \]
vari.e <- function(a,b,c,d){
return((b+c)/(4*(a+b+c+d)))
}\[ yule.w = \frac{\sqrt{ad} - \sqrt{bc}}{\sqrt{ad} - \sqrt{bc}} \]
yule.w.e <- function(a,b,c,d){
return(sqrt((a*d))-sqrt((b*c))/sqrt((a*d))+sqrt((b*c)))
}\[ yuleq.1 = \frac{ad-bc}{ad+bc} \]
yuleq.1.e <- function(a,b,c,d){
return(((a*d)-(b*c))/((a*d)+(b*c)))
}\[ yuleq.2 = \frac{2bc}{ad+bc} \]
yuleq.2.e <- function(a,b,c,d){
return((2*b*c)/(a*d) + (b*c))
}Como temos muitas funções, ao invés de criar uma nova função para cada uma delas para preencher a matriz de correlação, nós vamos criar uma única função que aceitará como parâmetro o nome da medida de similariade (função) que queremos calcular. Para isto, usamos FUN o qual será substituido pela função que desejamos.
No entanto, para isto funcionar, precisaremos alterar todas as funções das medidas de similaridade para aceitar a, b, c, d e n como parâmetros. Como exemplo, a medida euclidiana está assim
euclidean.e <- function(b,c){
return(sqrt(b+c))
}Precisaremos mudar para:
euclidean.e <- function(a,b,c,d,n){
return(sqrt(b+c))
}Essa foi a forma mais objetiva que encontrei para automatizar o processo, mas existem outras maneiras. No entanto, não é escopo aqui para estudarmos como criar funções avançadas no R. Deixo por conta de vocês procurarem a respeito e tentar melhorar o código aqui disponibilizado. Bem, depois de modificar todas as funções das medidas de similariade, a função abaixo funcionará sem problemas:
compute.measure <- function(labels, num.labels, a, b, c, d, n, FUN){
retorno = list()
m <- build.matrix.corr(labels, num.labels) # matriz de correlação
u = (num.labels*num.labels) # tamanho da matriz
pb <- progress_bar$new(total = u) # barra de progresso
for (i in 1:num.labels){
for (j in 1:num.labels){
x = as.numeric(a[i,j]) # matriz a
y = as.numeric(b[i,j]) # matriz b
w = as.numeric(c[i,j]) # matriz d
z = as.numeric(d[i,j]) # matriz d
k = as.numeric(n[i,j]) # matriz n
m[i,j] = FUN(x,y,w,z,k) # chama a função passada como parâmetro
pb$tick()
Sys.sleep(1/u)
gc()
} # end intern for
gc()
} # enf extern for
return(m)
gc()
}Observe que FUN é usado na chamada da função compute.measure e depois ele também é usado na linha onde prenchemos a matriz de correlação. FUN será substituido pela função que passarmos como parâmetro.
Usando a função:
ma = res1$ma # matriz a
mb = res1$mb # matriz b
mc = res1$mc # matriz c
md = res1$md # matriz d
mn = res2$mn # matriz n
res4 = compute.measure(labels, num.labels, ma, mb, mc, md, mn, stiles.e)
res4## Label1 Label2 Label3 Label4
## Label1 2.6133374 0.5861647 2.1406194 1.7696922
## Label2 0.5861647 2.5874777 0.9452397 0.5500743
## Label3 2.1406194 0.9452397 2.6136854 1.8795939
## Label4 1.7696922 0.5500743 1.8795939 2.6122270
Além de analisar o espaço de rótulos a partir dessas funções, você também pode usar o resultado da função compute.measure para construir um grafo e, em seguida aplicar métodos de detecção de comunidades (redes complexas).
É isto pessoal. Por favor, se houver algo errado em alguma parte do texto, não importa o que seja, entrem em contato: elainececiliagatto@gmail.com. Por favor, sigam-me também em minhas redes sociais:
https://www.instagram.com/professoracissa/
https://twitter.com/professoracissa
https://www.facebook.com/ProfessoraCissa/
https://www.youtube.com/channel/UCAdow7a1SHwwWyePjLto1cg
https://www.linkedin.com/in/elainececiliagatto/
https://www.linkedin.com/company/professoracissa
https://www.embarcados.com.br/membro/elaine-cec%C3%ADlia-gatto/
Cha, Sung-Hyuk. “Comprehensive Survey on Distance/Similarity Measures between Probability Density Functions.” (2007). Disponível em: http://www.fisica.edu.uy/~cris/teaching/Cha_pdf_distances_2007.pdf
Irani, Jasmine Khushro et al. “Clustering Techniques and the Similarity Measures used in Clustering: A Survey.” International Journal of Computer Applications 134 (2016): 9-14. Disponível em: https://www.ijcaonline.org/research/volume134/number7/irani-2016-ijca-907841.pdf
Choi, Seung-Seok et al. “A Survey of Binary Similarity and Distance Measures.” Journal on Systemics, Cybernetics and Informatics 8 (2010): 43-48. Disponível em: http://www.iiisci.org/Journal/PDV/sci/pdfs/GS315JG.pdf
Gjorgjioski, Valentin et al. “Comparison of Distances for Multi-Label Classification with PCTs.” (2011). Disponível em: https://ailab.ijs.si/Dunja/SiKDD2011/Papers/Gjorgjioski_Multilabel.pdf
Garg, Ankit et al. “On Asymmetric Similarity Search.” 2015 IEEE 14th International Conference on Machine Learning and Applications (ICMLA) (2015): 649-654. Disponível em : https://ieeexplore.ieee.org/document/7424392
Warrens, Matthijs Joost. “Similarity Coefficients for Binary Data. Properties of Coefficients, Coefficient Matrices, Multi-way Metrics and Multivariate Coefficients.” Dissertation Leiden University. ISBN 978-90-8891-0524. 2008. Disponível em: https://www.researchgate.net/publication/28649906_Similarity_coefficients_for_binary_data_Properties_of_coefficients_coefficient_matrices_multi-way_metrics_and_multivariate_coefficients