Calculer l'entropie du bloc

12

J'ai eu besoin d'écrire une fonction qui calcule l'entropie de blocs d'une série de symboles donnée pour une taille de bloc donnée et j'ai été surpris de la brièveté du résultat. Je vous mets donc au défi de coder une telle fonction. Je ne vous dis pas ce que j'ai fait pour l'instant (et dans quelle langue), mais je le ferai dans une semaine environ, si personne ne propose les mêmes ou meilleures idées en premier.

Définition de l'entropie du bloc:

Étant donné une séquence de symboles A = A_1,…, A_n et une taille de bloc m:

  • Un bloc de taille m est un segment de m éléments consécutifs de la séquence de symboles, c'est-à-dire A_i,…, A_ (i + m − 1) pour tout i approprié.
  • Si x est une séquence de symboles de taille m, N (x) désigne le nombre de blocs de A qui sont identiques à x.
  • p (x) est la probabilité qu'un bloc de A soit identique à une séquence de symboles x de taille m, c'est-à-dire p (x) = N (x) / (n − m + 1)
  • Enfin, l'entropie des blocs pour la taille de bloc m de A est la moyenne de −log (p (x)) sur tous les blocs x de taille m dans A ou (ce qui est équivalent) la somme de −p (x) · log (p (x)) sur chaque x de taille m se produisant dans A. (Vous pouvez choisir n'importe quel logarithme raisonnable que vous souhaitez.)

Restrictions et clarifications:

  • Votre fonction doit prendre comme argument la séquence de symboles A ainsi que la taille de bloc m.
  • Vous pouvez supposer que les symboles sont représentés sous forme d'entiers à base zéro ou dans un autre format pratique.
  • Votre programme devrait être capable de prendre n'importe quel argument raisonnable en théorie et en réalité devrait être capable de calculer l'exemple de cas (voir ci-dessous) sur un ordinateur standard.
  • Les fonctions et bibliothèques intégrées sont autorisées, tant qu'elles n'effectuent pas de grandes parties de la procédure en un seul appel, c'est-à-dire extraire tous les blocs de taille m de A, compter le nombre d'occurrences d'un bloc x donné ou calculer les entropies à partir d'une séquence de valeurs p - vous devez faire ces choses vous-même.

Tester:

[2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, 2, 2, 4, 4, 4, 1, 1, 1, 0, 4, 1,
2, 2, 4, 0, 1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 0, 2, 1, 4, 3,
0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, 2, 3, 0, 0, 4, 4, 2, 3, 1, 3, 1, 1,
3, 1, 3, 1, 0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 3, 0, 0, 4, 4, 1, 0,
2, 3, 0, 0, 1, 4, 4, 3]

Les premières entropies de blocs de cette séquence sont (pour le logarithme naturel):

  • m = 1: 1,599
  • m = 2: 3,065
  • m = 3: 4,067
  • m = 4: 4,412
  • m = 5: 4,535
  • m = 6: 4,554
Wrzlprmft
la source
@ m.buettner: Si vous considérez votre solution comme limite par rapport à mes règles, vous pouvez toujours l'essayer - je ne veux vraiment éviter que des solutions dans le sens de entropy(probabilities(blocks(A,m))).
Wrzlprmft
N'est-il pas habituel d'utiliser le journal de base 2 pour cela?
Jonathan Van Matre
Les valeurs de l'entropie à la fin sont positives, mais le logarithme d'une probabilité est négatif ou nul. Par conséquent, un signe négatif manque dans la formule de l'entropie.
Heiko Oberdiek
@JonathanVanMatre: Pour autant que je sache, cela dépend de la discipline qui est le plus utilisé du logarithme. Quoi qu'il en soit, cela ne devrait pas avoir beaucoup d'importance pour le défi et vous pouvez donc utiliser la base raisonnable que vous souhaitez.
Wrzlprmft
@HeikoOberdiek: Merci, j'ai oublié ça.
Wrzlprmft

Réponses:

6

Mathematica - 81 78 75 72 67 65 62 56 octets

Je n'ai jamais joué au golf à Mathematica auparavant, donc je suppose qu'il y a place à amélioration. Celui-ci n'est pas tout à fait conforme aux règles en raison des fonctions Partitionet Tally, mais il est assez soigné, j'ai donc pensé le publier de toute façon.

f=N@Tr[-Log[p=#2/Length@b&@@@Tally[b=##~Partition~1]]p]&

Cela fonctionne avec n'importe quel ensemble de symboles et peut être utilisé comme

sequence = {2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, 2, 2, 4, 4, 4, 1, 1, 
   1, 0, 4, 1, 2, 2, 4, 0, 1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 
   0, 2, 1, 4, 3, 0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, 2, 3, 0, 0, 4, 4, 
   2, 3, 1, 3, 1, 1, 3, 1, 3, 1, 0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 
   3, 0, 0, 4, 4, 1, 0, 2, 3, 0, 0, 1, 4, 4, 3};
f[sequence, 3]

> 4.06663

Voici une version quelque peu non golfée:

f[sequence_, m_] := (
    blocks = Partition[sequence, m, 1];
    probabilities = Apply[#2/Length[blocks] &, Tally[blocks], {1}];
    N[Tr[-Log[probabilities]*probabilities]]
)

Il fonctionnera probablement plus rapidement si je postule Ndirectement au résultat de Tally.

Soit dit en passant, Mathematica a en fait une Entropyfonction, qui la réduit à 28 octets , mais c'est certainement contraire aux règles.

f=N@Entropy@Partition[##,1]&

En revanche, voici une version 128 octets qui réimplémente Partitionet Tally:

f=N@Tr[-Log[p=#2/n&@@@({#[[i;;i+#2-1]],1}~Table~{i,1,(n=Length@#-#2+1)}//.{p___,{s_,x_},q___,{s_,y_},r___}:>{p,{s,x+y},q,r})]p]&

Non golfé:

f[sequence_, m_] := (
    n = Length[sequence]-m+1; (*number of blocks*)
    blocks = Table[{Take[sequence, {i, i+m-1}], 1},
                   {i, 1, n}];
    blocks = b //. {p___, {s_, x_}, q___, {s_, y_}, r___} :> {p,{s,x+y},q,r};
    probabilities = Apply[#2/n &, blocks, {1}];
    N[Tr[-Log[probabilities]*probabilities]]
)
Martin Ender
la source
Partitionet Tallyne sont pas des cas limites, ils enfreignent purement et simplement les règles, car ils «extraient tous les blocs de taille m de A» et «comptent le nombre d'occurrences d'un bloc x donné», respectivement, en un seul appel. Pourtant, après tout ce que je sais de Mathematica, je ne serais pas surpris s'il y avait une solution décente sans eux.
Wrzlprmft
1
@Wrzlprmft J'ai ajouté une version pas si golfée où je réimplémente Partitionet Tally.
Martin Ender
3

Perl, 140 octets

Le script Perl suivant définit une fonction Equi prend la séquence de symboles, suivie de la taille du segment comme arguments.

sub E{$m=pop;$E=0;%h=();$"=',';$_=",@_,";for$i(0..@_-$m){next
if$h{$s=",@_[$i..$i+$m-1],"}++;$E-=($p=s|(?=$s)||g/(@_-$m+1))*log$p;}return$E}

Version non golfée avec test

sub E { # E for "entropy"
    # E takes the sequence and segment size as arguments
    # and returns the calculated entropy.
    $m = pop;    # get segment size (last argument)
    $E = 0;      # initialize entropy
    %h = ();     # hash that remembers already calculated segments
    $" = ',';#"  # comma is used as separator
    $_ = ",@_,"; # $_ takes sequence as string, with comma as delimiters
    for $i (0 .. @_-$m) {
        $s = ",@_[$i..$i+$m-1],"; # segment
        next if$h{$s}++;          # check, if this segment is already calculated
        $p = s|(?=\Q$s\E)||g / (@_ - $m + 1); # calculate probability
             # N(x) is calculated using the substitution operator
             # with a zero-width look-ahead pattern
             # (golfed version without "\Q...\E", see below)
        $E -= $p * log($p); # update entropy
    }
    return $E
}

# Test

my @A = (
    2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, 2, 2, 4, 4, 4, 1, 1, 1, 0, 4, 1,
    2, 2, 4, 0, 1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 0, 2, 1, 4, 3,
    0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, 2, 3, 0, 0, 4, 4, 2, 3, 1, 3, 1, 1,
    3, 1, 3, 1, 0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 3, 0, 0, 4, 4, 1, 0,
    2, 3, 0, 0, 1, 4, 4, 3
);

print "m = $_: ", E(@A, $_), "\n" for 1 .. @A;

Résultat:

m = 1: 1.59938036027528
m = 2: 3.06545141203611
m = 3: 4.06663334311518
m = 4: 4.41210802885304
m = 5: 4.53546705894451
m = 6: 4.55387689160055
m = 7: 4.54329478227001
m = 8: 4.53259949315326
m = 9: 4.52178857704904
...
m = 97: 1.38629436111989
m = 98: 1.09861228866811
m = 99: 0.693147180559945
m = 100: 0

Symboles:

Les symboles ne sont pas limités aux entiers, car une correspondance de modèle basée sur des chaînes est utilisée. La représentation sous forme de chaîne d'un symbole ne doit pas contenir de virgule, car elle est utilisée comme délimiteur. Bien sûr, différents symboles doivent avoir des représentations de chaînes différentes.

Dans la version golfée, la représentation sous forme de chaîne des symboles ne doit pas contenir de caractères spéciaux de motifs. Les quatre octets supplémentaires \Q... \E ne sont pas nécessaires pour les nombres.

Heiko Oberdiek
la source
Peut - être environ 1/4 plus courte: sub f{($s,$m,$r,%h)=@_;$h{x,@$s[$_..$_+$m-1]}++for 0..@$s-$m;$r-=($_/=@$s-$m+1)*log for values %h;return$r}; où $sest une référence, $ret %hsont réinitialisés à undef avec la 1ère affectation, les listes sont des clés de hachage (avec peu d'aide $;, et certaines x- malheureusement), et un peu moins compliquées en général, je pense.
user2846289
@VadimR: intelligent! En raison des changements substantiels que je suggère, vous apportez une réponse. L'espace values %hn'est pas nécessaire, votre solution n'a donc besoin que de 106 octets.
Heiko Oberdiek
2

Python 127 152B 138B

import math
def E(A,m):N=len(A)-m+1;R=range(N);return sum(math.log(float(N)/b) for b in [sum(A[i:i+m]==A[j:j+m] for i in R) for j in R])/N

Ajusté pour ne plus enfreindre les règles et avoir un algorithme légèrement plus mignon. Ajusté pour être plus petit

Ancienne version:

import math
def E(A,m):
 N=len(A)-m+1
 B=[A[i:i+m] for i in range(N)]
 return sum([math.log(float(N)/B.count(b)) for b in B])/N

Mon tout premier script Python! Voyez-le en action: http://pythonfiddle.com/entropy

alexander-brett
la source
Bien pour l'instant, mais malheureusement, l'utilisation de la countfonction est carrément contraire aux règles, car elle "compte le nombre d'occurrences d'un bloc x donné".
Wrzlprmft
En outre, quelques conseils de golf: vous pouvez enregistrer certains personnages en entassant chaque ligne sauf la première en une (séparée par ;si nécessaire). Les crochets de la dernière ligne ne sont pas non plus nécessaires.
Wrzlprmft
Bonne réponse. Encore une fois, quelques conseils de golf: la conversion complète de booléen en entier (c'est-à-dire and 1 or 0) n'est pas nécessaire. Vous pouvez également enregistrer certains caractères en prédéfinissant range(N).
Wrzlprmft
1

Python avec Numpy, 146 143 octets

Comme promis, voici ma propre solution. Il nécessite une entrée d'entiers non négatifs:

from numpy import*
def e(A,m):
    B=zeros(m*[max(A)+1]);j=0
    while~len(A)<-j-m:B[tuple(A[j:j+m])]+=1;j+=1
    return -sum(x*log(x)for x in B[B>0]/j)

L'inconvénient est que cela éclate votre mémoire pour un grand mou max(A).

Voici la version principalement non golfée et commentée:

from numpy import *
def e(A,m):
    B = zeros(m*[max(A)+1])          # Generate (max(A)+1)^m-Array of zeroes for counting.
    for j in range(len(A)-m+1):
        B[tuple(A[j:j+m])] += 1      # Do the counting by directly using the array slice
                                     # for indexing.
    C = B[B>0]/(len(A)-m+1)          # Flatten array, take only non-zero entries,
                                     # divide for probability.
    return -sum(x*log(x) for x in C) # Calculate entropy
Wrzlprmft
la source
1

MATLAB

function E =BlockEntropy01(Series,Window,Base )

%-----------------------------------------------------------
% Calculates BLOCK ENTROPY of Series
% Series: a Vector of numbers
% Base: 2 or 10 (affects logarithm of the Calculation)
% for 2 we use log2, for 10 log10
% Windows: Length of the "Sliding" BLOCK
% E: Entropy
%-----------------------------------------------------------
% For the ENTROPY Calculations
% http://matlabdatamining.blogspot.gr/2006/....
% 11/introduction-to-entropy.html
% BlogSpot: Will Dwinnell
%-----------------------------------------------------------
% For the BLOCK ENTROPY
% http://codegolf.stackexchange.com/...
% questions/24316/calculate-the-block-entropy
%-----------------------------------------------------------
% Test (Base=10)
% Series=[2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, ....
%     2, 2, 4, 4, 4, 1, 1, 1, 0, 4, 1,2, 2, 4, 0, ....
%     1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 0, ....
%     2, 1, 4, 3,0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, ....
%     2, 3, 0, 0, 4, 4, 2, 3, 1, 3, 1, 1,3, 1, 3, 1, ....
%     0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 3, 0, 0, 4, ...
%     4, 1, 0,2, 3, 0, 0, 1, 4, 4, 3]';
%
% Results 
%
% Window=1: 1.599
% Window=2: 3.065
% Window=3: 4.067
% Window=4: 4.412
% Window=5: 4.535
% Window=6: 4.554
%-----------------------------------------------------------
n=length(Series);
D=zeros(n,Window); % Pre Allocate Memory
for k=1:Window;    D(:,k)=circshift(Series,1-k);end
D=D(1:end-Window+1,:); % Truncate Last Part
%
% Repace each Row with a "SYMBOL"
% in this Case a Number ...............
[K l]=size(D);
for k=1:K; MyData(k)=polyval(D(k,:),Base);end
clear D
%-----------------------------------------------------------
% ENTROPY CALCULATIONS on MyData
% following  Will Dwinnell
%-----------------------------------------------------------
UniqueMyData = unique(MyData);
nUniqueMyData = length(UniqueMyData);
FreqMyData = zeros(nUniqueMyData,1); % Initialization
for i = 1:nUniqueMyData
    FreqMyData(i) = ....
        sum(double(MyData == UniqueMyData(i)));
end
% Calculate sample class probabilities
P = FreqMyData / sum(FreqMyData);
% Calculate entropy in bits
% Note: floating point underflow is never an issue since we are
%   dealing only with the observed alphabet
if Base==10
    E= -sum(P .* log(P));
elseif BASE==2
    E= -sum(P .* log2(P));
else
end
end

WITH TEST SCRIPT 
%-----------------------------------------------------------
Series=[2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, ....
    2, 2, 4, 4, 4, 1, 1, 1, 0, 4, 1,2, 2, 4, 0, ....
    1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 0, ....
    2, 1, 4, 3,0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, ....
    2, 3, 0, 0, 4, 4, 2, 3, 1, 3, 1, 1,3, 1, 3, 1, ....
    0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 3, 0, 0, 4, ...
    4, 1, 0,2, 3, 0, 0, 1, 4, 4, 3]';
Base=10;
%-----------------------------------------------------------
for Window=1:6
    E =BlockEntropy01(Series,Window,Base )
end
Alexander E. Hillaris
la source
3
Bienvenue sur PPCG.SE! Il s'agit d'un défi de golf de code, où le but est de résoudre un problème avec le moins de caractères possible. Veuillez ajouter une version sans commentaires, un espace minimal et des noms de variables à un seul caractère (et tout autre raccourci auquel vous pouvez penser) ainsi que le nombre d'octets dans ce code.
Martin Ender