Calcule la entropía de bloque

12

Una vez necesitaba escribir una función que calcule la entropía de bloques de una serie de símbolos dada para un tamaño de bloque dado y me sorprendió lo corto que fue el resultado. Por lo tanto, te desafío a codegolf tal función. No te estoy diciendo lo que hice por ahora (y en qué idioma), pero lo haré en una semana más o menos, si a nadie se le ocurrieron las mismas o mejores ideas primero.

Definición de la entropía de bloque:

Dada una secuencia de símbolos A = A_1, ..., A_n y un tamaño de bloque m:

  • Un bloque de tamaño m es un segmento de m elementos consecutivos de la secuencia de símbolos, es decir, A_i, ..., A_ (i + m − 1) para cualquier i apropiado.
  • Si x es una secuencia de símbolos de tamaño m, N (x) indica el número de bloques de A que son idénticos a x.
  • p (x) es la probabilidad de que un bloque de A sea idéntico a una secuencia de símbolos x de tamaño m, es decir, p (x) = N (x) / (n − m + 1)
  • Finalmente, la entropía de bloque para el tamaño de bloque m de A es el promedio de −log (p (x)) sobre todos los bloques x de tamaño m en A o (que es equivalente) la suma de −p (x) · log (p (x)) sobre cada x de tamaño m que ocurre en A. (Puede elegir cualquier logaritmo razonable que desee).

Restricciones y aclaraciones:

  • Su función debe tomar la secuencia de símbolos A, así como el tamaño de bloque m como argumento.
  • Puede suponer que los símbolos se representan como enteros basados ​​en cero o en otro formato conveniente.
  • Su programa debería ser capaz de tomar cualquier argumento razonable en teoría y en realidad debería ser capaz de calcular el caso de ejemplo (ver más abajo) en una computadora estándar.
  • Las funciones y bibliotecas incorporadas están permitidas, siempre que no realicen grandes porciones del procedimiento en una llamada, es decir, extraigan todos los bloques de tamaño m de A, contando el número de ocurrencias de un bloque dado x o calculando las entropías a partir de una secuencia de valores p: tienes que hacer esas cosas tú mismo.

Prueba:

[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]

Las primeras entropías de bloque de esta secuencia son (para el logaritmo natural):

  • 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
fuente
@ m.buettner: Si considera que su solución es limítrofe con respecto a mis reglas, aún podría intentarlo; realmente solo quiero evitar soluciones en la línea de entropy(probabilities(blocks(A,m))).
Wrzlprmft
¿No es habitual usar log base 2 para esto?
Jonathan Van Matre
Los valores para la entropía al final son positivos, pero el logaritmo de una probabilidad es negativo o cero. Por lo tanto, falta un signo negativo en la fórmula para la entropía.
Heiko Oberdiek
@JonathanVanMatre: Hasta donde yo sé, depende de la disciplina que es el más utilizado del logaritmo. De todos modos, no debería importar tanto para el desafío y, por lo tanto, puede usar cualquier base razonable que desee.
Wrzlprmft
@HeikoOberdiek: Gracias, lo olvidé.
Wrzlprmft

Respuestas:

6

Mathematica - 81 78 75 72 67 65 62 56 bytes

No he jugado golf en Mathematica antes, así que supongo que hay margen de mejora. Este no se ajusta a las reglas debido a las funciones Partitiony Tally, pero es bastante bueno, así que pensé en publicarlo de todos modos.

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

Esto funciona con cualquier conjunto de símbolos, y puede usarse como

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

Aquí hay una versión un tanto descuidada:

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

Probablemente se ejecutará más rápido si aplico Ndirectamente al resultado de Tally.

Por cierto, Mathematica realmente tiene una Entropyfunción, que reduce esto a 28 bytes , pero eso definitivamente va en contra de las reglas.

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

Por otro lado, aquí hay una versión de 128 bytes que se vuelve a implementar Partitiony 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]&

Sin 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
fuente
Partitiony Tallyno son casos límite, están rompiendo las reglas directamente, ya que están "extrayendo todos los bloques de tamaño m de A" y "contando el número de ocurrencias de un bloque dado x", respectivamente, en una llamada. Aún así, después de todo lo que sé sobre Mathematica, no me sorprendería si hubiera una solución decente sin ellos.
Wrzlprmft
1
@Wrzlprmft He agregado una versión no tan golf donde reimplemo Partitiony Tally.
Martin Ender
3

Perl, 140 bytes

El siguiente script de Perl define una función Eque toma la secuencia de símbolos, seguida del tamaño del segmento como argumentos.

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}

Versión sin golf con prueba

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;

Resultado:

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

Símbolos:

Los símbolos no están restringidos a enteros, porque se usa la coincidencia de patrones basada en cadenas. La representación de cadena de un símbolo no debe contener la coma, ya que se utiliza como delimitador. Por supuesto, diferentes símbolos deben tener diferentes representaciones de cadena.

En la versión de golf, la representación de cadena de los símbolos no debe contener caracteres especiales de patrones. Los cuatro bytes adicionales \Q... \E no son necesarios para los números.

Heiko Oberdiek
fuente
Puede ser aproximadamente 1/4 más corto: 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}; donde $ses una referencia, $ry %hse restablecen a undef con la primera asignación, las listas son claves hash (con poca ayuda de $;, y algunas x, desafortunadamente), y un poco menos complicadas en general, creo.
user2846289
@VadimR: ¡Listo! Debido a los cambios sustanciales que sugiero, usted hace una respuesta. El espacio values %hno es necesario, por lo tanto, su solución solo necesita 106 bytes.
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

Ajustado para no romper más las reglas y tener un algoritmo ligeramente más lindo. Ajustado para ser más pequeño

Versión antigua:

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

¡Mi primer script de Python! Véalo en acción: http://pythonfiddle.com/entropy

alexander-brett
fuente
Agradable hasta ahora, pero desafortunadamente, el uso de la countfunción es totalmente contrario a las reglas, ya que es "contar el número de ocurrencias de un bloque x dado".
Wrzlprmft
Además, algunos consejos para jugar al golf: puede guardar algunos caracteres agrupando cada línea excepto la primera en una (separadas por ;si es necesario). Tampoco se necesitan los corchetes en la última línea.
Wrzlprmft
Buena respuesta. Una vez más, algunos consejos de golf: la conversión completa de booleano a entero (es decir, and 1 or 0) es innecesaria. Además, puede guardar algunos caracteres predefiniendo range(N).
Wrzlprmft
1

Python con Numpy, 146 143 Bytes

Como prometí, aquí está mi propia solución. Requiere una entrada de enteros no negativos:

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)

La desventaja es que este estalla la memoria para un gran mo max(A).

Aquí está la versión mayormente no comentada y comentada:

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
fuente
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
fuente
3
Bienvenido a PPCG.SE! Este es un desafío de código de golf, donde el objetivo es resolver un problema en la menor cantidad de caracteres posible. Agregue una versión sin comentarios, espacios en blanco mínimos y nombres de variables de un solo carácter (y cualquier otro acceso directo que pueda imaginar), así como la cantidad de bytes en ese código.
Martin Ender