Indice de diversité de Simpson

19

L' indice Simpson est une mesure de la diversité d'une collection d'articles avec des doublons. Il s'agit simplement de la probabilité de tirer au hasard deux éléments différents lors de la cueillette sans remplacement.

Avec des narticles dans des groupes d' n_1, ..., n_karticles identiques, la probabilité de deux articles différents est

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

Par exemple, si vous avez 3 pommes, 2 bananes et 1 carotte, l'indice de diversité est

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

Alternativement, le nombre de paires non ordonnées d'articles différents est 3*2 + 3*1 + 2*1 = 11globalement de 15 paires, et 11/15 = 0.7333.

Contribution:

Une chaîne de caractères Aà Z. Ou, une liste de ces personnages. Sa longueur sera d'au moins 2. Vous ne pouvez pas supposer qu'il est trié.

Production:

L'indice de diversité Simpson des caractères de cette chaîne, c'est-à-dire la probabilité que deux caractères pris au hasard avec remplacement soient différents. Il s'agit d'un nombre compris entre 0 et 1 inclus.

Lors de la sortie d'un flottant, affichez au moins 4 chiffres, tout en terminant les sorties exactes comme 1ou 1.0ou 0.375sont OK.

Vous ne pouvez pas utiliser de fonctions intégrées qui calculent spécifiquement des indices de diversité ou des mesures d'entropie. L'échantillonnage aléatoire réel est très bien, tant que vous obtenez une précision suffisante sur les cas de test.

Cas de test

AAABBC 0.73333
ACBABA 0.73333
WWW 0.0
CODE 1.0
PROGRAMMING 0.94545

Classement

Voici un classement par langue, gracieuseté de Martin Büttner .

Pour vous assurer que votre réponse apparaît, veuillez commencer votre réponse avec un titre, en utilisant le modèle Markdown suivant:

# Language Name, N bytes

Nest la taille de votre soumission. Si vous améliorez votre score, vous pouvez conserver les anciens scores dans le titre, en les rayant. Par exemple:

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

xnor
la source
Vous utilisez l'indice de Gini-Simpson, alors qu'une meilleure mesure à utiliser est l'indice de Simpson inverse, c'est-à-dire le nombre effectif de types.
Joe Z.
1
Fondamentalement 1/au lieu de 1-. [Coup de chapeau de statisticien amateur]
Joe Z.

Réponses:

5

Python 2, 72

L'entrée peut être une chaîne ou une liste.

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

Je sais déjà que ce serait 2 octets plus court en Python 3, alors ne me conseillez pas :)

feersum
la source
Que font les équerres <>en position 36? Je n'ai jamais vu cette syntaxe auparavant.
ApproachingDarknessFish
@TuttiFruttiJacuzzi: c'est un synonyme de !=.
RemcoGerlich
1
@TuttiFruttiJacuzzi Ce n'est que du python 2 sauf si vousfrom __future__ import barry_as_FLUFL
matsjoyce
@ Vioz - Pas avec le l=len(s);dedans
Sp3000
@ Sp3000 Oui, je n'ai pas remarqué combien de fois il a été utilisé.
Kade
4

Pyth - 19 13 12 11 octets

Merci à @isaacg de m'avoir parlé de n

Utilise une approche de force brute avec .cune fonction de combinaisons.

csnMK.cz2lK

Essayez-le ici en ligne .

Suite de tests .

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
Maltysen
la source
Vous pouvez remplacer .{par n- ils sont équivalents ici.
isaacg
@isaacg oh ne savait pas que ça éclate automatiquement, cool.
Maltysen
4

SQL (PostgreSQL), 182 octets

En fonction des 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

Explication

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

Utilisation et test

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
MickyT
la source
4

J, 26 octets

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

la partie cool

J'ai trouvé les comptes de chaque caractère en saisissant </.la chaîne contre elle-même ( ~pour réflexive) puis en comptant les lettres de chaque case.

protiste
la source
1
(#&:>@</.~)peut être (#/.~)et (<:*])peut être (*<:). Si vous utilisez une fonction appropriée, cela donne (1-(#/.~)+/@:%&(*<:)#). Comme les accolades environnantes ne sont généralement pas comptées ici (départ 1-(#/.~)+/@:%&(*<:)#, le corps de la fonction), cela donne 20 octets.
randomra
4

Python 3, 66 58 octets

Cela utilise la formule de comptage simple fournie dans la question, rien de trop compliqué. C'est une fonction lambda anonyme, donc pour l'utiliser, vous devez lui donner un nom.

8 octets enregistrés (!) Grâce à Sp3000.

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

Usage:

>>> 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
Kade
la source
3

APL, 39 36 octets

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

Cela crée une monade sans nom.

{
  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))
}

Vous pouvez l' essayer en ligne !

Alex A.
la source
2

Pyth, 13 octets

csnM*zz*lztlz

Une traduction littérale de la solution de @ feersum.

orlp
la source
2

CJam, 25 octets

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

Essayez-le en ligne

Mise en œuvre assez directe de la formule de la question.

Explication:

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.
Reto Koradi
la source
1

J, 37 octets

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

mais je crois qu'il peut encore être raccourci.

Exemple

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

Ceci est juste une version tacite de la fonction suivante:

   fun =: 3 : 0
a1=.+/"1 (~.y)=/y
N=.(+/a1)*(<:+/a1)
n=.a1*a1-1
1-(+/n)%N
)
gar
la source
Après un peu de golf supplémentaire et en faire une fonction appropriée: (1-(%&([:+/]*<:)+/)@(+/"1@=))donne 29 octets. 27 si on ne compte pas les accolades entourant la fonction (1-(%&([:+/]*<:)+/)@(+/"1@=))comme c'est courant ici. Notes: =yest exactement (~.=/])yet la conjonction de composition ( x u&v y= (v x) u (v y)) a également été très utile.
randomra
Merci pour les suggestions! J'apprends toujours à écrire des expressions tacites moi-même. Pour l'instant, j'utilise 13: 0 pour générer des définitions tacites partie par partie et les combiner.
gar
1

C, 89

Le score concerne funiquement la fonction et exclut les espaces inutiles, qui ne sont inclus que pour plus de clarté. la mainfonction est uniquement destinée aux tests.

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]));
}

Il compare simplement chaque caractère avec tous les autres caractères, puis divise par le nombre total de comparaisons.

Level River St
la source
1

Python 3, 56

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

Compte les paires d'éléments inégaux, puis divise par le nombre de ces paires.

xnor
la source
1

Haskell, 83 octets

Je sais que je suis en retard, j'ai trouvé ça, j'avais oublié de poster. Un peu inélégant avec Haskell m'obligeant à convertir des entiers en nombres que vous pouvez diviser les uns par les autres.

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

CJam, 23 octets

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

Au niveau des octets, il s'agit d'une amélioration très mineure par rapport à la réponse de @ RetoKoradi , mais elle utilise une astuce intéressante :

La somme des n premiers entiers non négatifs est égale à n (n - 1) / 2 , que nous pouvons utiliser pour calculer le numérateur et le dénominateur, tous deux divisés par 2 , de la fraction dans la formule de la question.

Essayez-le en ligne dans l' interpréteur CJam .

Comment ça fonctionne

 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.
Dennis
la source
0

APL, 26 octets

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

Explication:

  • ≢⍵: obtenir la longueur de la première dimension de . Étant donné que c'est censé être une chaîne, cela signifie la longueur de la chaîne.
  • {⍴⍵}⌸⍵: pour chaque élément unique dans , obtenez les longueurs de chaque dimension de la liste des occurrences. Cela donne le nombre de fois qu'un élément se produit pour chaque élément, sous forme de 1×≢⍵matrice.
  • ,: concatène les deux le long de l'axe horizontal. Étant donné que ≢⍵est un scalaire et que l'autre valeur est une colonne, nous obtenons une 2×≢⍵matrice dans laquelle la première colonne a le nombre de fois qu'un élément se produit pour chaque élément, et la deuxième colonne a le nombre total d'éléments.
  • {⍵×⍵-1}: pour chaque cellule de la matrice, calculez N(N-1).
  • ÷/: réduire les lignes par division. Cela divise la valeur de chaque article par la valeur du total.
  • +/: additionne le résultat pour chaque ligne.
  • 1-: soustraire de 1
marinus
la source