Histograma usando gnuplot?

202

Eu sei como criar um histograma (basta usar "with boxes") no gnuplot se o meu arquivo .dat já tiver dados em bin corretamente. Existe uma maneira de obter uma lista de números e fazer com que o gnuplot forneça um histograma com base nos intervalos e tamanhos de lixeira fornecidos pelo usuário?

Maria
fonte
2
Se você não receber uma resposta, existem outras ferramentas destinadas a fazer essas coisas. Eu uso o Root ( root.cern.ch ), muitos outros por aqui usam R e existem pelo menos algumas outras opções.
dmckee --- ex-moderador gatinho
1
Bin é o intervalo de valores coletados juntos para cada barra no histograma. Cada compartimento tem um limite inferior e superior, e todos os dados com um valor nesse intervalo são contados nessa barra. Lixeira significa que meu arquivo de dados já está organizado por quantos pontos de dados caem dentro de cada lixeira, para que esteja pronto para ser plotado como um histograma.
mary

Respostas:

225

sim, e é rápido e simples, embora muito oculto:

binwidth=5
bin(x,width)=width*floor(x/width)

plot 'datafile' using (bin($1,binwidth)):(1.0) smooth freq with boxes

confira help smooth freqpara ver por que o acima faz um histograma

para lidar com intervalos, basta definir a variável xrange.

Born2Smile
fonte
11
Acho que a resposta de @ ChrisW abaixo traz um ponto importante a ser observado para quem quer fazer um histograma no Gnuplot.
Abhinav
2
Tenha muito cuidado, isso só funciona se não houver uma bandeja "ausente" no conjunto ... Essa função fixa o valor y de uma bandeja ausente ao valor y da bandeja anterior não ausente. Isso pode ser muito enganador !!!
PinkFloyd
1
Eu adicionaria set boxwidth binwidthacima. Foi realmente útil para mim.
Jaakko
90

Tenho algumas correções / adições à resposta muito útil do Born2Smile:

  1. Caixas vazias faziam com que a caixa da bandeja adjacente se estendesse incorretamente ao seu espaço; evite isso usandoset boxwidth binwidth
  2. Na versão do Born2Smile, os compartimentos são renderizados como centralizados no limite inferior. Estritamente, eles deveriam se estender do limite inferior ao limite superior. Isso pode ser corrigido modificando a binfunção:bin(x,width)=width*floor(x/width) + width/2.0
mas90
fonte
10
Na verdade, essa segunda parte deverá ser bin(x,width)=width*floor(x/width) + binwidth/2.0(cálculos de ponto flutuante)
bgw
8
Você quer dizer bin(x,width)=width*floor(x/width) + width/2.0. Se estamos passando widthcomo argumento, use-o. :-)
Mitar 15/05
78

Tenha muito cuidado: todas as respostas desta página estão implicitamente tomando a decisão de onde o compartimento começa - a borda esquerda da bandeja mais à esquerda, se você quiser - fora das mãos do usuário. Se o usuário estiver combinando alguma dessas funções para classificar dados com sua própria decisão sobre o início da classificação (como é feito no blog vinculado acima), as funções acima estão incorretas. Com um ponto de partida arbitrário para classificar 'Min', a função correta é:

bin(x) = width*(floor((x-Min)/width)+0.5) + Min

Você pode ver por que isso está correto seqüencialmente (ajuda a desenhar algumas caixas e um ponto em algum lugar em uma delas). Subtraia Min do seu ponto de dados para ver a que distância ele está. Em seguida, divida por largura de caixa para trabalhar efetivamente em unidades de 'bandejas'. Em seguida, 'coloque o piso' no resultado para ir para a borda esquerda dessa bandeja, adicione 0,5 para ir para o meio da bandeja, multiplique pela largura para que você não esteja mais trabalhando em unidades de caixas, mas em uma escala absoluta novamente e, finalmente, adicione novamente o deslocamento mínimo que você subtraiu no início.

Considere esta função em ação:

Min = 0.25 # where binning starts
Max = 2.25 # where binning ends
n = 2 # the number of bins
width = (Max-Min)/n # binwidth; evaluates to 1.0
bin(x) = width*(floor((x-Min)/width)+0.5) + Min

por exemplo, o valor 1.1 realmente cai no compartimento esquerdo:

  • essa função mapeia corretamente para o centro da bandeja esquerda (0,75);
  • A resposta do Born2Smile, bin (x) = largura * piso (x / largura), mapeia incorretamente para 1;
  • resposta da mas90, bin (x) = largura * piso (x / largura) + largura da caixa / 2,0, mapeia incorretamente para 1,5.

A resposta do Born2Smile só está correta se os limites do compartimento ocorrerem em (n + 0,5) * largura do bin (onde n é executado sobre números inteiros). A resposta do mas90 está correta apenas se os limites do compartimento ocorrerem em n * largura de caixa.

ChrisW
fonte
48

Deseja traçar um gráfico como este? insira a descrição da imagem aqui sim? Então você pode dar uma olhada no artigo do meu blog: http://gnuplot-surprising.blogspot.com/2011/09/statistic-analysis-and-histogram.html

Linhas principais do código:

n=100 #number of intervals
max=3. #max value
min=-3. #min value
width=(max-min)/n #interval width
#function used to map a value to the intervals
hist(x,width)=width*floor(x/width)+width/2.0
set boxwidth width*0.9
set style fill solid 0.5 # fill style

#count and plot
plot "data.dat" u (hist($1,width)):(1.0) smooth freq w boxes lc rgb"green" notitle
hsxz
fonte
10

Como de costume, o Gnuplot é uma ferramenta fantástica para plotar gráficos de aparência agradável e pode ser feita para executar todos os tipos de cálculos. No entanto , o objetivo é plotar dados em vez de servir como calculadora, e geralmente é mais fácil usar um programa externo (por exemplo, Octave) para fazer cálculos mais "complicados", salvar esses dados em um arquivo e usar o Gnuplot para produzir o gráfico. Para o problema acima, verifique se a função "hist" está em Octave [freq,bins]=hist(data), e plote isso no Gnuplot usando

set style histogram rowstacked gap 0
set style fill solid 0.5 border lt -1
plot "./data.dat" smooth freq with boxes
Dai
fonte
7

Achei essa discussão extremamente útil, mas experimentei alguns problemas de "arredondamento".

Mais precisamente, usando uma largura de caixa de 0,05, notei que, com as técnicas apresentadas aqui acima, os pontos de dados que lêem 0,1 e 0,15 caem na mesma bandeja. Esse (comportamento obviamente indesejado) provavelmente ocorre devido à função "andar".

A seguir, é minha pequena contribuição para tentar contornar isso.

bin(x,width,n)=x<=n*width? width*(n-1) + 0.5*binwidth:bin(x,width,n+1)
binwidth = 0.05
set boxwidth binwidth
plot "data.dat" u (bin($1,binwidth,1)):(1.0) smooth freq with boxes

Este método recursivo é para x> = 0; pode-se generalizar isso com mais declarações condicionais para obter algo ainda mais geral.

Alex
fonte
6

Não precisamos usar o método recursivo, pode ser lento. Minha solução é usar uma função definida pelo usuário, em vez da função instrinsic int ou floor.

rint(x)=(x-int(x)>0.9999)?int(x)+1:int(x)

Esta função dará rint(0.0003/0.0001)=3, enquanto int(0.0003/0.0001)=floor(0.0003/0.0001)=2.

Por quê? Por favor, veja a função Perl int e zeros de preenchimento

JOE
fonte
4

Eu tenho uma pequena modificação na solução do Born2Smile.

Eu sei que isso não faz muito sentido, mas você pode querer apenas por precaução. Se seus dados forem inteiros e você precisar de um tamanho de lixeira flutuante (talvez para comparação com outro conjunto de dados ou densidade de plotagem em uma grade mais fina), será necessário adicionar um número aleatório entre 0 e 1 no piso interno. Caso contrário, haverá picos devido a erro de arredondamento. floor(x/width+0.5)não funcionará porque criará um padrão que não é fiel aos dados originais.

binwidth=0.3
bin(x,width)=width*floor(x/width+rand(0))
path4
fonte
1
Você não encontrou essas situações, mas poderá mais tarde. Você pode testá-lo com números inteiros normalmente distribuídos com um float sd e plotar histogramas com bin = 1 e bin = sd Veja o que você obtém com e sem o truque rand (0). Peguei o erro de um colaborador ao revisar seu manuscrito. Seus resultados mudaram de absolutamente absurdo para uma figura bonita, como esperado.
path4
Ok, talvez a explicação seja tão curta que não se possa entendê-la sem um caso de teste mais concreto. Farei uma breve edição da sua resposta para que eu possa desfazer o voto negativo;)
Christoph
Considere números inteiros de distribuição normal. Como eles são inteiros, muitos deles terão o mesmo x / largura. Digamos que esse número seja 1,3. Com o piso (x / largura + 0,5), todos eles serão atribuídos ao compartimento 1. Mas o que 1.3 realmente significa em termos de densidade é que 70% deles devem estar no compartimento 1 e 30% no compartimento 2. rand (0 ) mantém a densidade adequada. Portanto, 0,5 cria picos e rand (0) mantém isso verdadeiro. Aposto que o valor de hsxz será muito mais suave usando rand (0) em vez de 0,5. Não é apenas arredondamento, é arredondamento sem perturbação.
path4
3

Com relação às funções de binning, eu não esperava o resultado das funções oferecidas até o momento. Ou seja, se minha largura de caixa for 0,001, essas funções centralizarão as caixas em 0,0005 pontos, enquanto eu acho que é mais intuitivo centralizá-las nos limites de 0,001.

Em outras palavras, eu gostaria de ter

Bin 0.001 contain data from 0.0005 to 0.0014
Bin 0.002 contain data from 0.0015 to 0.0024
...

A função de binning que criei é

my_bin(x,width)     = width*(floor(x/width+0.5))

Aqui está um script para comparar algumas das funções bin oferecidas a esta:

rint(x) = (x-int(x)>0.9999)?int(x)+1:int(x)
bin(x,width)        = width*rint(x/width) + width/2.0
binc(x,width)       = width*(int(x/width)+0.5)
mitar_bin(x,width)  = width*floor(x/width) + width/2.0
my_bin(x,width)     = width*(floor(x/width+0.5))

binwidth = 0.001

data_list = "-0.1386 -0.1383 -0.1375 -0.0015 -0.0005 0.0005 0.0015 0.1375 0.1383 0.1386"

my_line = sprintf("%7s  %7s  %7s  %7s  %7s","data","bin()","binc()","mitar()","my_bin()")
print my_line
do for [i in data_list] {
    iN = i + 0
    my_line = sprintf("%+.4f  %+.4f  %+.4f  %+.4f  %+.4f",iN,bin(iN,binwidth),binc(iN,binwidth),mitar_bin(iN,binwidth),my_bin(iN,binwidth))
    print my_line
}

e aqui está a saída

   data    bin()   binc()  mitar()  my_bin()
-0.1386  -0.1375  -0.1375  -0.1385  -0.1390
-0.1383  -0.1375  -0.1375  -0.1385  -0.1380
-0.1375  -0.1365  -0.1365  -0.1375  -0.1380
-0.0015  -0.0005  -0.0005  -0.0015  -0.0010
-0.0005  +0.0005  +0.0005  -0.0005  +0.0000
+0.0005  +0.0005  +0.0005  +0.0005  +0.0010
+0.0015  +0.0015  +0.0015  +0.0015  +0.0020
+0.1375  +0.1375  +0.1375  +0.1375  +0.1380
+0.1383  +0.1385  +0.1385  +0.1385  +0.1380
+0.1386  +0.1385  +0.1385  +0.1385  +0.1390
Winston Smith
fonte