Segmentando com robustez um arquivo csv contendo dados ruidosos

5

Digamos que eu tenha um arquivo csv com essas colunas:

timestamp,cpu,memory

Um exemplo da forma dos dados reais seria este gráfico:

Tendoosdadosvisualizadosdestaforma,éfácilparaoolhohumanodescobrirondequalquerumadasatividadesintensivasde4cpucomeçaetermina.

Oqueseriaumamaneirafácil,usandoferramentasdelinhacmdpadrãounix,idealmentenãorecorrendoaRouoctaveetc,para"grep" apenas as 8 linhas do arquivo que indicam o início e o fim dessas 4 atividades , com base na coluna "cpu"?

Obviamente, isso é um pouco confuso, e há ruído nos dados, mas qualquer solução que me dê 8 linhas que estão em algum lugar perto do início / fim é apreciada.

Um pequeno trecho do arquivo csv, mostrando apenas a primeira dessas quatro explosões de atividade:

10:44:21.310,0,53567488
10:44:22.310,1.56257,53575680
10:44:23.326,0,53854208
10:44:24.325,34.3761,57405440
10:44:25.325,73.43985,61747200
10:44:26.325,3.1251,69459968
10:44:27.325,0,69459968
10:44:28.325,0,69459968
10:44:29.325,65.6271,74756096
10:44:30.325,53.1267,77783040
...
10:50:56.450,35.93865,142700544
10:50:57.450,10.93785,142897152
10:50:58.450,1.56255,142897152
10:50:59.450,0,142897152
10:51:00.450,0,142897152
10:51:01.450,0,142897152

Idealmente, isso resultaria nas duas linhas a seguir.

10:44:24.325,34.3761,57405440
10:50:58.450,1.56255,142897152

No entanto, como não há atividade da CPU por alguns segundos entre 10:44:26.325 e 10:44:28.325 , não espero que a resposta seja tão inteligente e faça algo como "suavizar" os dados. Então, se um snippet csv como o anterior, por exemplo, rende as 4 linhas seguintes, isso seria suficiente.

10:44:24.325,34.3761,57405440
10:44:26.325,3.1251,69459968
10:44:29.325,65.6271,74756096
10:50:58.450,1.56255,142897152
    
por Eugene Beresovsky 15.11.2013 / 01:45

3 respostas

5

Uma maneira de fazer isso é com awk . Você pode definir um limite para obter a primeira linha em que o limite é atingido e a última linha abaixo dele. Algo como isso pode funcionar:

awk -F, -vthreshold_up=20 -vthreshold_down=10 'BEGIN {
                          cur = "gt";
                        } 
                        {
                          if (cur == "gt" && $2 > threshold_up) {
                            print;
                            cur = "lt";
                          } else if (cur = "lt" && $2 < threshold_down) {
                            print;
                            cur = "gt";
                          }
                        }' file.csv
    
por 15.11.2013 / 01:57
2

Estendendo a abordagem do jordanm, consegui construir algo surpreendentemente robusto, sem ter que recorrer a estatísticas. O script ficou um pouco mais desafortunadamente, mas isso está acabado, e agora eu posso usá-lo quantas vezes precisar, com apenas descobrir os parâmetros certos.

Eu testei em uma dúzia de arquivos de dados reais, alguns tão confusos quanto este:

OtruqueaquiéousodavariávelMIN_DURATION,queajudaaignorarpicostemporáriosequedasdeatéonúmeroespecificadodelinhas.

Uso:

grep-begin-endFIELD_SEPARATORFIELD_INDEXTHRESHOLD_UPTHRESHOLD_DOWNMIN_DURATION...

Exemplo:

grep-begin-end,23045file.csv

grep-begin-end

FIELD_SEPARATOR=$1FIELD_INDEX=$2THRESHOLD_UP=$3THRESHOLD_DOWN=$4MIN_DURATION=$5shift5awk-F$FIELD_SEPARATOR-vthreshold_up=$THRESHOLD_UP-vthreshold_down=$THRESHOLD_DOWN'BEGIN{cur="gt";
}
{
    val = $'$FIELD_INDEX';
    # strip of double quotes and convert to number
    if (substr(val, 1, 1) == "\"") { val = 0 + substr(val, 2, length(val) - 3); } else { val = 0 + val; }
    buf = "";
    if (cur == "gt")
    {
        if (val >= threshold_up)
        {
            if (buf == "")
                buf = $0;
            if (duration >= '$MIN_DURATION')
            {
                print buf;
                cur = "lt";
                duration = 0;
                buf = "";
            }
            else
            {
                duration++;
            }
        }
        else
        {
            duration = 0;
        }
    }
    else if (cur == "lt")
    {
        if (val <= threshold_down)
        {
            if (buf == "")
                buf = $0;
            if (duration >= '$MIN_DURATION')
            {
                print buf;
                cur = "gt";
                duration = 0;
                buf = "";
            }
            else
            {
                duration++;
            }
        }
        else
        {
            duration = 0;
        }
    }
}' "$@"
    
por 15.11.2013 / 05:53
0

Eu estava pensando que você poderia simplesmente escrever um pequeno script que imprima se o valor atual difere do último por mais de N. Já que seus dados parecem ser muito discretos e as mudanças acontecem de uma medida para outra, deve funcionar.

Algo como

awk -F, -v thr=100 '{if ($2-last>thr || last-$2>thr){print;} last=$2;}' foo.csv
    
por 15.11.2013 / 04:10