Índice de diversidade Simpson


19

O índice Simpson é uma medida da diversidade de uma coleção de itens com duplicatas. É simplesmente a probabilidade de desenhar dois itens diferentes ao escolher sem substituição uniforme de forma aleatória.

Com nitens em grupos de n_1, ..., n_kitens idênticos, a probabilidade de dois itens diferentes é

$$ 1- \ sum_ {i = 1} ^ k \ frac {n_i (n_i-1)} {n (n -1)} $$

Por exemplo, se você tiver 3 maçãs, 2 bananas e 1 cenoura, o índice de diversidade será

D = 1 - (6 + 2 + 0)/30 = 0.7333

Como alternativa, o número de pares não ordenados de itens diferentes está 3*2 + 3*1 + 2*1 = 11fora de 15 pares no total, e 11/15 = 0.7333.

Entrada:

Uma sequência de caracteres Apara Z. Ou, uma lista desses caracteres. Seu comprimento será pelo menos 2. Você não pode assumir que ele esteja classificado.

Resultado:

O índice de diversidade Simpson de caracteres nessa sequência, ou seja, a probabilidade de dois caracteres escolhidos aleatoriamente com a substituição serem diferentes. Este é um número entre 0 e 1, inclusive.

Ao emitir um flutuador, exiba pelo menos 4 dígitos, embora encerre saídas exatas como 1ou 1.0ou 0.375estão OK.

Você não pode usar built-ins que computem especificamente índices de diversidade ou medidas de entropia. A amostragem aleatória real é boa, desde que você obtenha precisão suficiente nos casos de teste.

Casos de teste

AAABBC 0.73333
ACBABA 0.73333
WWW 0.0
CODE 1.0
PROGRAMMING 0.94545

Entre os melhores

Aqui está uma tabela de classificação por idioma, cortesia de Martin Büttner .

Para garantir que sua resposta seja exibida, inicie-a com um título, usando o seguinte modelo de remarcação:

# Language Name, N bytes

onde Nestá o tamanho do seu envio. Se você melhorar sua pontuação, poderá manter as pontuações antigas no título, identificando-as. Por exemplo:

# Ruby, <s>104</s> <s>101</s> 96 bytes


Você está usando o índice Gini-Simpson, quando uma medida muito melhor a ser usada é o índice inverso de Simpson, também conhecido como número efetivo de tipos.
Joe Z.

11
Basicamente, em 1/vez de 1-. [estatístico amador fala alto]
Joe Z.

Respostas:


5

Python 2, 72

A entrada pode ser uma sequência ou uma lista.

def f(s):l=len(s);return sum(s[i%l]<>s[i/l]for i in range(l*l))/(l-1.)/l

Eu já sei que seria 2 bytes mais curto no Python 3, então por favor não me avise :)


O que os colchetes angulares estão <>fazendo na posição 36? Eu nunca vi essa sintaxe antes.
ApproachingDarknessFish

@TuttiFruttiJacuzzi: é sinônimo de !=.
RemcoGerlich

11
@TuttiFruttiJacuzzi É apenas python 2, a menos que você seja #from __future__ import barry_as_FLUFL
matsjoyce

@ Vioz- Não com o l=len(s);
SP3000

@ Sp3000 Certo, não percebeu quantas vezes foi usado.
Kade

4

Pitão - 19 13 12 11 bytes

Obrigado a @isaacg por me contar sobre n

Usa abordagem de força bruta com .cfunção de combinações.

csnMK.cz2lK

Experimente aqui online .

Conjunto de teste .

c                Float division
 s               Sum (works with True and False)
  nM             Map uniqueness
   K             Assign value to K and use value
    .c 2         Combinations of length 2
      z          Of input
 lK              Length of K

Você pode substituir .{por n- eles são equivalentes aqui.
Isaacg

@isaacg oh não sabia que ele se espalha automaticamente, legal.
Maltysen

4

SQL (PostgreSQL), 182 bytes

Como uma função no postgres.

CREATE FUNCTION F(TEXT)RETURNS NUMERIC AS'SELECT 1-sum(d*(d-1))/(sum(d)*(sum(d)-1))FROM(SELECT COUNT(*)d FROM(SELECT*FROM regexp_split_to_table($1,''''))I(S)GROUP BY S)A'LANGUAGE SQL

Explicação

CREATE FUNCTION F(TEXT) -- Create function f taking text parameter
RETURNS NUMERIC         -- declare return type
AS'                     -- return definition
    SELECT 1-sum(d*(d-1))/(sum(d)*(sum(d)-1)) -- Calculate simpson index
    FROM(
        SELECT COUNT(*)d  -- Count occurrences of each character
        FROM(             -- Split the string into characters
            SELECT*FROM regexp_split_to_table($1,'''')
            )I(S)
        GROUP BY S        -- group on the characters
        )A 
'
LANGUAGE SQL

Uso e execução de teste

SELECT S, F(S)
FROM (
    VALUES
    ('AAABBC'),
    ('ACBABA'),
    ('WWW'),
    ('CODE'),
    ('PROGRAMMING')
   )I(S)

S              F
-------------- -----------------------
AAABBC         0.73333333333333333333
ACBABA         0.73333333333333333333
WWW            0.00000000000000000000
CODE           1.00000000000000000000
PROGRAMMING    0.94545454545454545455

4

J, 26 bytes

1-+/((#&:>@</.~)%&(<:*])#)

a parte legal

Encontrei as contagens de cada caractere pressionando </.a corda contra si mesma ( ~por reflexivo) e depois contando as letras de cada caixa.


11
(#&:>@</.~)pode ser (#/.~)e (<:*])pode ser (*<:). Se você usar uma função adequada, isso fornece (1-(#/.~)+/@:%&(*<:)#). Como as chaves circundantes geralmente não são contadas aqui (deixando 1-(#/.~)+/@:%&(*<:)#, o corpo da função), isso fornece 20 bytes.
randomra

4

Python 3, 66 58 bytes

Isso está usando a fórmula simples de contagem fornecida na pergunta, nada muito complicado. É uma função lambda anônima; portanto, para usá-la, você precisa dar um nome a ela.

Economizou 8 bytes (!) Graças ao Sp3000.

lambda s:1-sum(x-1for x in map(s.count,s))/len(s)/~-len(s)

Uso:

>>> f=lambda s:1-sum(x-1for x in map(s.count,s))/len(s)/~-len(s)
>>> f("PROGRAMMING")
0.945454

ou

>>> (lambda s:1-sum(x-1for x in map(s.count,s))/len(s)/~-len(s))("PROGRAMMING")
0.945454

3

APL, 39 36 bytes

{n←{≢⍵}⌸⍵⋄N←≢⍵⋄1-(N-⍨N×N)÷⍨+/n-⍨n×n}

Isso cria uma mônada sem nome.

{
  n ← {≢⍵}⌸⍵               ⍝ Number of occurrences of each letter
  N ← ≢⍵                   ⍝ Number of characters in the input
  1-(N-⍨N×N)÷⍨+/n-⍨n×n     ⍝ Return 1 - sum((n*n-n)/(N*N-N))
}

Você pode experimentá-lo online !


2

Pitão, 13 bytes

csnM*zz*lztlz

Praticamente uma tradução literal da solução do @ feersum.


2

CJam, 25 bytes

l$_e`0f=_:(.*:+\,_(*d/1\-

Experimente online

Implementação bastante direta da fórmula na questão.

Explicação:

l     Get input.
$     Sort it.
_     Copy for evaluation of denominator towards the end.
e`    Run-length encoding of string.
0f=   Map letter/length pairs from RLE to only length.
      We now have a list of letter counts.
_     Copy list.
:(    Map with decrement operator. Copy now contains letter counts minus 1.
.*    Vectorized multiply. Results in list of n*(n-1) for each letter.
:+    Sum vector. This is the numerator.
\     Bring copy of input string to top.
,     Calculate length.
_(    Copy and decrement.
*     Multiply. This is the denominator, n*(n-1) for the entire string.
d     Convert to double, otherwise we would get integer division.
/     Divide.
1\-   Calculate one minus result of division to get final result.

1

J, 37 bytes

(1-([:+/]*<:)%+/*[:<:+/)([:+/"1~.=/])

mas acredito que ainda pode ser reduzido.

Exemplo

(1-([:+/]*<:)%+/*[:<:+/)([:+/"1~.=/]) 'AAABBC'

Esta é apenas uma versão tácita da seguinte função:

   fun =: 3 : 0
a1=.+/"1 (~.y)=/y
N=.(+/a1)*(<:+/a1)
n=.a1*a1-1
1-(+/n)%N
)

Após um pouco de golfe extra e torná-lo uma função adequada: (1-(%&([:+/]*<:)+/)@(+/"1@=))dá 29 bytes. 27 se não contarmos os aparelhos que cercam a função, (1-(%&([:+/]*<:)+/)@(+/"1@=))como é comum aqui. Notas: =yé exatamente (~.=/])ye a composição de composição ( x u&v y= (v x) u (v y)) também foi muito útil.
Aleatório

Obrigado pelas sugestões! Ainda estou aprendendo a escrever expressões tácitas. Por enquanto, uso 13: 0 para gerar definições tácitas parte por parte e combinar.
gar

1

C, 89

A pontuação é apenas para a função fe exclui espaços em branco desnecessários, incluídos apenas para maior clareza. a mainfunção é apenas para teste.

i,c,n;
float f(char*v){
  n=strlen(v);
  for(i=n*n;i--;)c+=v[i%n]!=v[i/n]; 
  return 1.0*c/(n*n-n);
}

main(int C,char**V){
  printf("%f",f(V[1]));
}

Ele simplesmente compara todos os caracteres com outros caracteres e depois divide pelo número total de comparações.


1

Python 3, 56

lambda s:sum(a!=b for a in s for b in s)/len(s)/~-len(s)

Conta os pares de elementos desiguais e depois divide pelo número de tais pares.


1

Haskell, 83 bytes

Eu sei que estou atrasado, achei isso, tinha esquecido de postar. Meio deselegante com Haskell exigindo que eu convertesse números inteiros em números que você pode dividir um pelo outro.

s z=(l(filter id p)-l z)/(l p-l z) where p=[c==d|c<-z,d<-z]
l=fromIntegral.length

0

CJam, 23 bytes

1r$e`{0=,~}%_:+\,,:+d/-

Byte-wise, esta é uma melhoria muito menor do que à resposta do @ RetoKoradi , mas usa um truque:

A soma dos primeiros n números inteiros não negativos é igual a n (n - 1) / 2 , que podemos usar para calcular o numerador e o denominador, ambos divididos por 2 , da fração na fórmula da pergunta.

Experimente online no intérprete CJam .

Como funciona

 r$                     e# Read a token from STDIN and sort it.
   e`                   e# Perform run-length encoding.
     {    }%            e# For each [length character] pair:
      0=                e#   Retrieve the length of the run (L).
        ,~              e#   Push 0 1 2 ... L-1.
                        e# Collect all results in an array.
            _:+         e# Push the sum of the entries of a copy.
               \,       e# Push the length of the array (L).
                 ,:+    e# Push 0 + 1 + 2 + ... + L-1 = L(L-1)/2.
                    d/  e# Cast to Double and divide.
1                     - e# Subtract the result from 1.

0

APL, 26 bytes

{1-+/÷/{⍵×⍵-1}({⍴⍵}⌸⍵),≢⍵}

Explicação:

  • ≢⍵: obtém o comprimento da primeira dimensão de . Dado que deveria ser uma string, isso significa o comprimento da string.
  • {⍴⍵}⌸⍵: para cada elemento exclusivo de , obtenha os comprimentos de cada dimensão da lista de ocorrências. Isso fornece a quantidade de vezes que um item ocorre para cada item, como uma 1×≢⍵matriz.
  • ,: concatena os dois ao longo do eixo horizontal. Desde a≢⍵ é escalar e o outro valor é uma coluna, obtemos uma 2×≢⍵matriz em que a primeira coluna tem a quantidade de vezes que um item ocorre para cada item e a segunda coluna tem a quantidade total de itens.
  • {⍵×⍵-1}: para cada célula na matriz, calcule N(N-1) .
  • ÷/: reduza as linhas por divisão. Isso divide o valor de cada item pelo valor do total.
  • +/: soma o resultado para cada linha.
  • 1-: subtraia de 1
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.