Como eu computo a média dos dados do arquivo ASCII no bash?

7

No bash eu posso fazer algumas medições de tempo a partir de um arquivo de log como este

grep "time:" myLogfile.txt | cut -d' ' -f 3 >> timeMeasurements.txt

#timeMeasurements.txt
2.5
3.5
2.0
...

Agora, gostaria de calcular o valor médio dos valores em timeMeasurements.txt . Qual é a maneira mais rápida de fazer isso em bash?
Eu sei que existe o gnuplot e o R, mas parece que é necessário escrever um script longo para qualquer um deles.

    
por mcExchange 05.04.2017 / 11:40

6 respostas

11

Outra maneira de usar sed e bc :

sed 's/^/n+=1;x+=/;$ascale=1;x/n' timemeasurements.txt | bc

A expressão sed converte a entrada para algo assim:

n+=1;x+=2.5
n+=1;x+=3.5
n+=1;x+=2.0
scale=1;x/n

Isso é canalizado para bc , que avalia linha por linha.

    
por Digital Trauma 05.04.2017 / 19:31
13

Obrigatório GNU datamash version

$ datamash mean 1 < file
2.6666666666667

ASIDE : parece que isso deve ser nativamente em bc (ou seja, sem usar o shell ou um programa externo , para fazer um loop sobre os valores de entrada). A implementação bc do GNU inclui uma função read() - no entanto, parece ser frustrantemente difícil conseguir que ela detecte o fim da entrada. O melhor que consegui fazer é:

#!/usr/bin/bc

scale = 6
while( (x = read()) ) {
  s += x
  c += 1
}
s/c
quit

em que você pode enviar a entrada de arquivo para desde que você termine a entrada com qualquer caractere não numérico , por exemplo

$ { cat file; echo '@'; } | ./mean.bc
2.666666
    
por steeldriver 05.04.2017 / 14:35
12

Você pode usar awk . Bash em si não é muito bom em matemática ...

awk 'BEGIN { lines=0; total=0 } { lines++; total+= } END { print total/lines }' timeMeasurements.txt

Notas

  • lines=0; total=0 definiu as variáveis para 0
  • lines++ aumentar lines em um para cada linha
  • total+= adiciona o valor em cada linha ao total em execução
  • print total/lines quando terminar, divida o total pelo número de valores
por Zanna 05.04.2017 / 12:03
8

Como adaptar o comando R de este post de U & amp; L :

$ Rscript -e 'd<-scan("stdin", quiet=TRUE)' -e 'cat(mean(d), sep="\n")' < foo
2.666667
    
por muru 05.04.2017 / 12:47
5

Você pode usar bc da calculadora básica, em um loop while com read :

count=0; sum=0; while read -r num; do ((count++)); sum=$(echo "$sum + $num" | bc); done < timeMeasurement.txt; echo "scale=2; $sum / $count" | bc -l

Ou mais legível:

count=0
sum=0
while read -r num
do
  ((count++))
  sum=$(echo "$sum + $num" | bc)
done < timeMeasurement.txt
echo "scale=2; $sum / $count" | bc -l

Explicação:

  • Primeiro, definimos a contagem de valores e a soma total como as variáveis contar e somar, com valores de 0.
  • Leia o arquivo linha por linha, definindo o valor na linha como a variável num. Usamos a construção while read -r num; do ... ; done < timeMeasurements.txt para fazer isso. Isso significa que faremos algo para cada linha do arquivo.
  • Dentro do loop while, incremente a variável count em um para cada linha com bash arithmetic ((count++)) .
  • Use a substituição do comando bash $(...) com echo canalizado para bc para adicionar o valor da variável num para essa linha do arquivo, para a soma da variável num de todas as linhas anteriores. bc é usado porque o bash não lida bem com a aritmética de ponto flutuante.

Neste ponto, o loop termina, a variável de contagem contém o número de valores de medição de tempo, a variável soma contém a soma das medições de tempo.

  • Use echo com nossas variáveis para criar o cálculo médio que é passado para bc . A parte scale=2 informa bc quantas figuras significativas exibir.
por Arronical 05.04.2017 / 14:35
4

O datamash parece ser uma boa opção, mas mesmo reconhecendo que minha resposta pode ser exagerada, caso você queira fazer um pouco mais do que apenas uma média, a oitava não é tão detalhada:

$ octave
octave:1> load timeMeasurements.txt 
octave:2> mean(timeMeasurements)
ans =  2.6667

Se você está fazendo médias, lembre-se que a mesma média pode vir de comportamentos muito diferentes, então o desvio padrão também é geralmente relevante:

octave:3> std(timeMeasurements)
ans =  0.76376

ou até mesmo um histograma simples é fácil de fazer:

octave:4> hist(timeMeasurements)

Além disso, acho que o datamash não está nos repositórios do apt-get para trusty, apenas para versões mais recentes.

Editar:

Oneliner, para usos mais amigáveis ao script:

octave -q --eval "m = load(\"timeMeasurements.txt\"); mean(m)"
    
por jmmut 06.04.2017 / 14:39