Hitung entropi blok

12

Saya pernah perlu menulis fungsi yang menghitung entropi blok dari seri simbol yang diberikan untuk ukuran blok yang diberikan dan terkejut betapa pendeknya hasilnya. Jadi saya menantang Anda untuk codegolf fungsi seperti itu. Saya tidak memberi tahu Anda apa yang saya lakukan untuk saat ini (dan dalam bahasa apa), tetapi saya akan melakukannya dalam seminggu atau lebih, jika tidak ada yang datang dengan ide yang sama atau lebih baik terlebih dahulu.

Definisi blok entropi:

Diberikan urutan simbol A = A_1, ..., A_n dan ukuran blok m:

  • Blok ukuran m adalah segmen dari m elemen berurutan dari urutan simbol, yaitu, A_i, ..., A_ (i + m − 1) untuk setiap i yang sesuai.
  • Jika x adalah urutan simbol dari ukuran m, N (x) menunjukkan jumlah blok A yang identik dengan x.
  • p (x) adalah probabilitas bahwa blok dari A identik dengan urutan simbol x ukuran m, yaitu, p (x) = N (x) / (n − m + 1)
  • Akhirnya, entropi blok untuk ukuran blok m dari A adalah rata-rata −log (p (x)) di atas semua blok x ukuran m di A atau (yang setara) jumlah −p (x) · log (p (x)) atas setiap x ukuran m yang terjadi di A. (Anda dapat memilih logaritma yang Anda inginkan.)

Pembatasan dan klarifikasi:

  • Fungsi Anda harus mengambil urutan simbol A serta ukuran blok m sebagai argumen.
  • Anda dapat mengasumsikan bahwa simbol tersebut direpresentasikan sebagai bilangan bulat berbasis nol atau dalam format lain yang sesuai.
  • Program Anda harus mampu mengambil argumen yang masuk akal secara teori dan pada kenyataannya harus mampu menghitung contoh kasus (lihat di bawah) pada komputer standar.
  • Fungsi dan pustaka built-in diperbolehkan, selama mereka tidak melakukan bagian besar dari prosedur dalam satu panggilan, yaitu, mengekstraksi semua blok ukuran m dari A, menghitung jumlah kemunculan blok tertentu x atau menghitung entropi dari urutan nilai p - Anda harus melakukan hal-hal itu sendiri.

Uji:

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

Entropi blok pertama dari urutan ini adalah (untuk logaritma 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
sumber
@ m.buettner: Jika Anda mempertimbangkan batas solusi Anda tentang aturan saya, Anda masih bisa mencobanya - Saya benar-benar hanya ingin menghindari solusi di sepanjang baris entropy(probabilities(blocks(A,m))).
Wrzlprmft
Bukankah sudah biasa menggunakan basis log 2 untuk ini?
Jonathan Van Matre
Nilai-nilai untuk entropi pada akhirnya adalah positif, tetapi logaritma probabilitas negatif atau nol. Oleh karena itu tanda negatif tidak ada dalam rumus untuk entropi.
Heiko Oberdiek
@JonathanVanMatre: Sejauh yang saya tahu, itu tergantung pada disiplin yang merupakan halaman logaritma yang paling banyak digunakan. Bagaimanapun, itu seharusnya tidak menjadi masalah banyak untuk tantangan dan dengan demikian Anda dapat menggunakan basis apa pun yang Anda inginkan.
Wrzlprmft
@ HeikoOberdiek: Terima kasih, saya lupa itu.
Wrzlprmft

Jawaban:

6

Mathematica - 81 78 75 72 67 65 62 56 byte

Saya belum pernah bermain golf di Mathematica sebelumnya, jadi saya kira ada ruang untuk perbaikan. Yang ini tidak cukup sesuai dengan aturan karena fungsi Partitiondan Tally, tapi itu cukup rapi jadi saya pikir saya tetap akan mempostingnya.

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

Ini berfungsi dengan sekumpulan simbol apa pun, dan dapat digunakan seperti

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

Berikut ini adalah versi yang agak tidak diubah:

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

Mungkin akan berjalan lebih cepat jika saya Nlangsung menerapkan ke hasil Tally.

By the way, Mathematica sebenarnya memiliki Entropyfungsi, yang mengurangi ini menjadi 28 byte , tapi itu jelas melanggar aturan.

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

Di sisi lain, berikut adalah versi 128 byte yang menambahkan Partitiondan 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]&

Tidak Disatukan:

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
sumber
Partitiondan Tallybukan merupakan kasus batas, mereka melanggar aturan secara langsung, karena mereka “mengekstraksi semua blok berukuran m dari A” dan “menghitung jumlah kemunculan blok x yang diberikan”, masing-masing, dalam satu panggilan. Namun, setelah semua yang saya tahu tentang Mathematica, saya tidak akan terkejut, jika ada solusi yang layak tanpa mereka.
Wrzlprmft
1
@Wrzlprmft Saya telah menambahkan versi tidak terlalu golf di mana saya mengimplementasikan Partitiondan Tally.
Martin Ender
3

Perl, 140 byte

Script Perl berikut mendefinisikan fungsi Eyang mengambil urutan simbol, diikuti oleh ukuran segmen sebagai argumen.

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 tidak dikoleksi dengan tes

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;

Hasil:

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

Simbol:

Simbol tidak terbatas pada bilangan bulat, karena pencocokan pola berdasarkan string digunakan. Representasi string dari simbol tidak boleh mengandung koma, karena itu digunakan sebagai pembatas. Tentu saja, simbol yang berbeda harus memiliki representasi string yang berbeda.

Dalam versi golf, representasi string dari simbol tidak boleh mengandung karakter spesial pola. Empat byte tambahan \Q... \E tidak diperlukan untuk angka.

Heiko Oberdiek
sumber
Bisa sekitar 1/4 lebih pendek: 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}; di mana $sreferensi, $rdan %hdiatur ulang ke undef dengan tugas 1, daftar adalah kunci hash (dengan sedikit bantuan $;, dan beberapa x- sayangnya), dan agak kurang rumit secara umum, saya pikir.
user2846289
@VadimR: Pintar! Karena perubahan substansial yang saya sarankan, Anda membuat jawaban. Ruang values %htidak diperlukan, jadi solusi Anda hanya membutuhkan 106 byte.
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

Disesuaikan untuk tidak melanggar aturan lagi dan memiliki algoritma yang sedikit lebih manis. Disesuaikan menjadi lebih kecil

Versi yang lebih lama:

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

Skrip Python pertama saya! Lihat dalam aksi: http://pythonfiddle.com/entropy

alexander-brett
sumber
Bagus sejauh ini, tetapi sayangnya, penggunaan countfungsi ini langsung melanggar aturan, karena ini "menghitung jumlah kemunculan dari blok x yang diberikan".
Wrzlprmft
Juga, beberapa kiat bermain golf: Anda dapat menyimpan beberapa karakter dengan menjejalkan setiap baris kecuali baris pertama menjadi satu (dipisahkan oleh ;jika perlu). Kurung kotak di baris terakhir juga tidak diperlukan.
Wrzlprmft
Jawaban bagus. Sekali lagi, beberapa kiat bermain golf: Seluruh konversi dari boolean ke integer (yaitu, and 1 or 0) tidak diperlukan. Anda juga dapat menyimpan beberapa karakter dengan menentukan sebelumnya range(N).
Wrzlprmft
1

Python dengan Numpy, 146 143 Bytes

Seperti yang dijanjikan, inilah solusi saya sendiri. Ini membutuhkan input bilangan bulat non-negatif:

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)

Kerugiannya adalah ini meledak memori Anda untuk matau max(A).

Berikut adalah versi yang kebanyakan tidak dikomunikasikan dan dikomentari:

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
sumber
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
sumber
3
Selamat datang di PPCG.SE! Ini adalah tantangan kode golf, di mana tujuannya adalah untuk menyelesaikan masalah dalam karakter sesedikit mungkin. Silakan tambahkan versi tanpa komentar, spasi putih dan nama variabel karakter tunggal (dan pintasan lain yang dapat Anda pikirkan) serta jumlah byte dalam kode itu.
Martin Ender