Bash; encontre a média para cada 10 minutos em um timestamp, arquivo de valor

3

Eu tenho um arquivo formatado como para cada segundo das 09:00:00 - 16:59:59, por exemplo;

[...]
10:59:49,76.77
10:59:50,38.78
10:59:51,23.23
10:59:52,12
10:59:53,26.47
10:59:54,10.2
10:59:55,32.67
10:59:56,14
10:59:57,42
10:59:58,100
10:59:59,100
[...]

Quando eu tento fazer um gráfico disso, os dados são muito agrupados para fazer sentido, então estou procurando encontrar médias para ajudá-lo a ser mais apresentável.

Como eu poderia encontrar a média de cada 10 minutos no arquivo, por exemplo;

09:00:00 - 09:09:59
09:10:00 - 09:19:59
..
..
10:00:00 - 10:09:59
10:10:00 - 10:19:59
and so on...

Eu planejei usar o awk para encontrar a média, mas me esforcei para encontrar uma maneira de extrair os clusters de 10 minutos para executá-lo;

awk -F "," '{ sum += $2; n++ } END { if (n > 0) print sum / n; }' test_file

Exemplo de código se você deseja gerar carimbos de data e hora para testes;

#!/bin/bash

seq -f "%02g" 0 59 > tmp.sec
seq -f "%02g" 30 59 > tmp.firstmin

while read line
do
    cat tmp.sec | sed "s/^/09:$line:/;s/$/,$RANDOM/"
done<"tmp.firstmin"

for i in 'seq 10 15'
do
    while read line
    do
        cat tmp.sec | sed "s/^/$i:$line:/;s/$/,$RANDOM/"
    done<"tmp.sec"
done
    
por user287811 25.04.2018 / 12:17

4 respostas

1

Você pode usar apenas : e , como separador e ignorar os segundos, mantendo apenas o minuto:

$ awk -F[:,] '{
                thisInterval = substr($2,1,1); 
                a[$1":"thisInterval"0"]+=$4; 
              } 
              END{
                    PROCINFO["sorted_in"]="@ind_str_asc"; 
                    for(t in a){print t,a[t]/600
              }
            }' 

O que precede requer o GNU awk para o PROCINFO , mas você pode apenas ordená-lo novamente depois. Também assume 600 pontos de dados por período de 10 minutos.

    
por 25.04.2018 / 12:42
0

Abordagem do awk do GNU:

Exemplo simplificado de testfile :

09:00:00,1
09:03:00,3
09:09:59,6
10:00:00,1
10:02:49,76.77
10:03:50,38.78
10:05:51,23.23
10:07:52,12
10:09:53,26.47
10:09:59,10.2
10:59:55,32.67
10:59:56,14
10:59:57,42
10:59:58,100
10:59:59,100
awk -F',' 'BEGIN{ d = "9999 01 01 " }
          { 
              gsub(":", " ", $1); 
              if (!ts) ts = mktime(d $1);
              sum += $2; cnt += 1
          }
          cnt == 1 { next }
          (mktime(d $1) - ts) == 599 {
              print sum / cnt;
              ts = sum = cnt = 0
          }' testfile

A saída:

3.33333
26.9214
    
por 25.04.2018 / 13:12
0

Você pode combinar a "0:00" no registro de data e hora para detectar o início de um novo período de dez minutos. Aqui está um exemplo em puro bash. Ele só vai lidar com valor inteiro, mas desde que calcular a média não é sua dificuldade, você deve ser capaz de adaptá-lo.

#!/bin/bash

SUM=0
while read line;
do
  # search for "hh:m0:00"
  if [ "${line:4:4}" = "0:00" ]
  then
    # reached new 10 minutes period
    # get average from sum and save it
    echo $((SUM/600)) >> results.txt

    # reset sum
    SUM=0
  fi

  # increment sum with this line value
  SUM=$(($SUM+${line:9}))
done < input.txt
    
por 25.04.2018 / 13:07
0

10 minutos = 600 segundos, então decidi somar o segundo campo de cada 600 linhas e imprimir esse valor dividido por 600, quando cada linha 600-é alcançada.

awk -F, '
NR % 600 == 1 {
    start = $1
}
NR % 600 == 0 {
    printf("%s - %s => %f\n", start, $1, avg / 600)
    avg = 0 
}
{
    avg += $2   
}
' input.txt

Resultado

09:00:00 - 09:09:59 => 49.807600
09:10:00 - 09:19:59 => 50.171900
09:20:00 - 09:29:59 => 47.775433
09:30:00 - 09:39:59 => 48.605350
09:40:00 - 09:49:59 => 49.591117
...
13:20:00 - 13:29:59 => 50.347733
13:30:00 - 13:39:59 => 50.321833
13:40:00 - 13:49:59 => 49.923333
13:50:00 - 13:59:59 => 48.644683
14:00:00 - 14:09:59 => 49.957433
...
16:00:00 - 16:09:59 => 50.333633
16:10:00 - 16:19:59 => 51.799317
16:20:00 - 16:29:59 => 50.931450
16:30:00 - 16:39:59 => 50.734167
16:40:00 - 16:49:59 => 49.857383
16:50:00 - 16:59:59 => 50.433733

Para gerar input.txt , criei dois programas, use o que você gosta. O segundo programa é mais rápido.

Primeiro

date -f <(seq -f '@%g' 21600 50399) '+%H:%M:%S' | 
awk '{
    printf("%s,%.2f\n", $0, rand() * 100)
}'

Segundo

awk '
BEGIN {
    for(i = 9; i < 17; i++) {
        for(j = 0; j < 60; j++) {
            for(k = 0; k < 60; k++) {
                printf("%02d:%02d:%02d,%.2f\n", i, j, k, rand() * 100)  
            }
        }
    }
}'
    
por 25.04.2018 / 15:24