Partilhar via


pairwise_dist_fl()

Aplica-se a: ✅Microsoft FabricAzure Data Explorer

Calcule distâncias em pares entre entidades com base em várias variáveis nominais e numéricas.

A função pairwise_dist_fl() é uma UDF (função definida pelo usuário) que calcula a distância multivariada entre pontos de dados pertencentes à mesma partição, levando em consideração variáveis nominais e numéricas.

  • Todos os campos de string, além dos nomes de entidade e partição, são considerados variáveis nominais. A distância é igual a 1 se os valores forem diferentes e 0 se forem iguais.
  • Todos os campos numéricos são considerados variáveis numéricas. Eles são normalizados transformando-se em pontuações z e a distância é calculada como o valor absoluto da diferença. A distância multivariada total entre os pontos de dados é calculada como a média das distâncias entre as variáveis.

Uma distância próxima de zero significa que as entidades são semelhantes e uma distância acima de 1 significa que elas são diferentes. Da mesma forma, uma entidade com uma distância média próxima ou superior a uma indica que ela é diferente de muitas outras entidades na partição, indicando um possível outlier.

A saída da função são as distâncias em pares entre entidades na mesma partição. Ele pode ser usado no estado em que se encontra para procurar pares semelhantes ou diferentes. como entidades com distâncias mínimas compartilham muitas características comuns. Ele também pode ser facilmente transformado em uma matriz de distância (veja o exemplo de uso) ou usado como entrada para algoritmos de detecção de clustering ou outlier.

Sintaxe

pairwise_dist_fl(entidade, partição)

Saiba mais sobre as convenções de sintaxe.

Parâmetros

Nome Digitar Obrigatória Descrição
entidade string ✔️ O nome da coluna da tabela de entrada que contém os nomes ou IDs das entidades para as quais as distâncias serão calculadas.
partition string ✔️ O nome da coluna da tabela de entrada que contém a partição ou o escopo, de modo que as distâncias sejam calculadas para todos os pares de entidades na mesma partição.

Definição de função

Você pode definir a função inserindo seu código como uma função definida por consulta ou criando-a como uma função armazenada em seu banco de dados, da seguinte maneira:

Defina a função usando a instrução let a seguir. Nenhuma permissão é necessária.

Importante

Uma instrução let não pode ser executada sozinha. Ele deve ser seguido por uma instrução de expressão tabular. Para executar um exemplo funcional de pairwise_dist_fl(), consulte Exemplo.

let pairwise_dist_fl = (tbl:(*), id_col:string, partition_col:string)
{
    let generic_dist = (value1:dynamic, value2:dynamic) 
    {
        // Calculates the distance between two values; treats all strings as nominal values and numbers as numerical,
        // can be extended to other data types or tweaked by adding weights or changing formulas.
            iff(gettype(value1[0]) == "string", todouble(tostring(value1[0]) != tostring(value2[0])), abs(todouble(value1[0]) - todouble(value2[0])))
    };
    let T = (tbl | extend _entity = column_ifexists(id_col, ''), _partition = column_ifexists(partition_col, '') | project-reorder _entity, _partition);
    let sum_data = (
        // Calculates summary statistics to be used for normalization.
        T
        | project-reorder _entity
        | project _partition, p = pack_array(*)
        | mv-expand with_itemindex=idx p
        | summarize count(), avg(todouble(p)), stdev(todouble(p)) by _partition, idx
        | sort by _partition, idx asc
        | summarize make_list(avg_p), make_list(stdev_p) by _partition
    );
    let normalized_data = (
        // Performs normalization on numerical variables by substrcting mean and scaling by standard deviation. Other normalization techniques can be used
        // by adding metrics to previous function and using here.
        T
        | project _partition, p = pack_array(*)
        | join kind = leftouter (sum_data) on _partition
        | mv-apply p, list_avg_p, list_stdev_p on (
            extend normalized = iff((not(isnan(todouble(list_avg_p))) and (list_stdev_p > 0)), pack_array((todouble(p) - todouble(list_avg_p))/todouble(list_stdev_p)), p)
            | summarize a = make_list(normalized) by _partition
        )
        | project _partition, a
    );
    let dist_data = (
        // Calculates distances of included variables and sums them up to get a multivariate distance between all entities under the same partition.
        normalized_data
        | join kind = inner (normalized_data) on _partition
        | project entity = tostring(a[0]), entity1 = tostring(a1[0]), a = array_slice(a, 1, -1), a1 = array_slice(a1, 1, -1), _partition
        | mv-apply a, a1 on 
        (
            project d = generic_dist(pack_array(a), pack_array(a1))
            | summarize d = make_list(d)
        )
        | extend dist = bin((1.0*array_sum(d)-1.0)/array_length(d), 0.0001) // -1 cancels the artifact distance calculated between entity names appearing in the bag and normalizes by number of features        
        | project-away d
        | where entity != entity1
        | sort by _partition asc, entity asc, dist asc
    );
    dist_data
};
// Write your query to use the function here.

Exemplo

O exemplo a seguir usa o operador invoke para executar a função.

Para usar uma função definida por consulta, invoque-a após a definição da função inserida.

let pairwise_dist_fl = (tbl:(*), id_col:string, partition_col:string)
{
    let generic_dist = (value1:dynamic, value2:dynamic) 
    {
        // Calculates the distance between two values; treats all strings as nominal values and numbers as numerical,
        // can be extended to other data types or tweaked by adding weights or changing formulas.
            iff(gettype(value1[0]) == "string", todouble(tostring(value1[0]) != tostring(value2[0])), abs(todouble(value1[0]) - todouble(value2[0])))
    };
    let T = (tbl | extend _entity = column_ifexists(id_col, ''), _partition = column_ifexists(partition_col, '') | project-reorder _entity, _partition);
    let sum_data = (
        // Calculates summary statistics to be used for normalization.
        T
        | project-reorder _entity
        | project _partition, p = pack_array(*)
        | mv-expand with_itemindex=idx p
        | summarize count(), avg(todouble(p)), stdev(todouble(p)) by _partition, idx
        | sort by _partition, idx asc
        | summarize make_list(avg_p), make_list(stdev_p) by _partition
    );
    let normalized_data = (
        // Performs normalization on numerical variables by substrcting mean and scaling by standard deviation. Other normalization techniques can be used
        // by adding metrics to previous function and using here.
        T
        | project _partition, p = pack_array(*)
        | join kind = leftouter (sum_data) on _partition
        | mv-apply p, list_avg_p, list_stdev_p on (
            extend normalized = iff((not(isnan(todouble(list_avg_p))) and (list_stdev_p > 0)), pack_array((todouble(p) - todouble(list_avg_p))/todouble(list_stdev_p)), p)
            | summarize a = make_list(normalized) by _partition
        )
        | project _partition, a
    );
    let dist_data = (
        // Calculates distances of included variables and sums them up to get a multivariate distance between all entities under the same partition.
        normalized_data
        | join kind = inner (normalized_data) on _partition
        | project entity = tostring(a[0]), entity1 = tostring(a1[0]), a = array_slice(a, 1, -1), a1 = array_slice(a1, 1, -1), _partition
        | mv-apply a, a1 on 
        (
            project d = generic_dist(pack_array(a), pack_array(a1))
            | summarize d = make_list(d)
        )
        | extend dist = bin((1.0*array_sum(d)-1.0)/array_length(d), 0.0001) // -1 cancels the artifact distance calculated between entity names appearing in the bag and normalizes by number of features        
        | project-away d
        | where entity != entity1
        | sort by _partition asc, entity asc, dist asc
    );
    dist_data
};
//
let raw_data = datatable(name:string, gender: string, height:int, weight:int, limbs:int, accessory:string, type:string)[
    'Andy',     'M',    160,    80,     4,  'Hat',      'Person',
    'Betsy',    'F',    170,    70,     4,  'Bag',      'Person',
    'Cindy',    'F',    130,    30,     4,  'Hat',      'Person',
    'Dan',      'M',    190,    105,    4,  'Hat',      'Person',
    'Elmie',    'M',    110,    30,     4,  'Toy',      'Person',
    'Franny',   'F',    170,    65,     4,  'Bag',      'Person',
    'Godzilla', '?',    260,    210,    5,  'Tail',     'Person',
    'Hannie',   'F',    112,    28,     4,  'Toy',      'Person',
    'Ivie',     'F',    105,    20,     4,  'Toy',      'Person',
    'Johnnie',  'M',    107,    21,     4,  'Toy',      'Person',
    'Kyle',     'M',    175,    76,     4,  'Hat',      'Person',
    'Laura',    'F',    180,    70,     4,  'Bag',      'Person',
    'Mary',     'F',    160,    60,     4,  'Bag',      'Person',
    'Noah',     'M',    178,    90,     4,  'Hat',      'Person',
    'Odelia',   'F',    186,    76,     4,  'Bag',      'Person',
    'Paul',     'M',    158,    69,     4,  'Bag',      'Person',
    'Qui',      'F',    168,    62,     4,  'Bag',      'Person',
    'Ronnie',   'M',    108,    26,     4,  'Toy',      'Person',
    'Sonic',    'F',    52,     20,     6,  'Tail',     'Pet',
    'Tweety',   'F',    52,     20,     6,  'Tail',     'Pet' ,
    'Ulfie',    'M',    39,     29,     4,  'Wings',    'Pet',
    'Vinnie',   'F',    53,     22,     1,  'Tail',     'Pet',
    'Waldo',    'F',    51,     21,     4,  'Tail',     'Pet',
    'Xander',   'M',    50,     24,     4,  'Tail',     'Pet'
];
raw_data
| invoke pairwise_dist_fl('name', 'type')
| where _partition == 'Person' | sort by entity asc, entity1 asc
| evaluate pivot (entity, max(dist), entity1) | sort by entity1 asc

Saída

entity1 Andy Betsy Cindy Dan Elmie Franny Godzilla Hannie ...
Andy 0.354 0.4125 0.1887 0.4843 0.3702 1.2087 0.6265 ...
Betsy 0.354 0.416 0.4708 0.6307 0.0161 1.2051 0.4872 ...
Cindy 0.4125 0.416 0.6012 0.3575 0.3998 1.4783 0.214 ...
Dan 0.1887 0.4708 0.6012 0.673 0.487 1.0199 0.8152 ...
Elmie 0.4843 0.6307 0.3575 0.673 0.6145 1.5502 0.1565 ...
Franny 0.3702 0.0161 0.3998 0.487 0.6145 1.2213 0,471 ...
Godzilla 1.2087 1.2051 1.4783 1.0199 1.5502 1.2213 1.5495 ...
Hannie 0.6265 0.4872 0.214 0.8152 0.1565 0,471 1.5495 ...
... ... ... ... ... ... ... ... ... ...

Olhando para entidades de dois tipos diferentes, gostaríamos de calcular a distância entre entidades pertencentes ao mesmo tipo, levando em consideração variáveis nominais (como sexo ou acessório preferido) e variáveis numéricas (como número de membros, altura e peso). As variáveis numéricas estão em escalas diferentes e devem ser centralizadas e dimensionadas, o que é feito automaticamente. A saída são pares de entidades na mesma partição com distância multivariada calculada. Ele pode ser analisado diretamente, visualizado como uma matriz de distância ou gráfico de dispersão, ou usado como dados de entrada para algoritmo de detecção de outliers, calculando a distância média por entidade, com entidades com valores altos indicando outliers globais. Por exemplo, ao adicionar uma visualização opcional usando uma matriz de distância, você obtém uma tabela conforme mostrado no exemplo. No exemplo, você pode ver que:

  • Alguns pares de entidades (Betsy e Franny) têm um valor de distância baixo (próximo a 0), indicando que são semelhantes.
  • Alguns pares de entidades (Godzilla e Elmie) têm um valor de distância alto (1 ou superior), indicando que são diferentes.

A saída pode ser usada para calcular a distância média por entidade. Uma distância média alta pode indicar valores discrepantes globais. Por exemplo, podemos ver que, em média, Godzilla tem uma grande distância dos outros, indicando que é um provável outlier global.