Iterando através de um csv e imprimindo elementos específicos

2

Eu tenho um problema interessante e não tenho certeza de como lidar melhor com a iteração de uma lista. Com o seguinte formato -

element, date, unixTime, value
CZ, 12/27/2007 15:55, 1198788900, 42346
CZ, 12/27/2007 17:30, 1198794600, -10543
I, 12/27/2007 19:05, 1198800300, 4475

Eu quero percorrer, para cada elemento único e cada data, e obter o sinal da coluna "valor". Por exemplo, eu gostaria de 2 linhas para 27/12/2007, uma para ambas, CZ e I. CZ seria negativa porque a linha que aconteceu na última parte do dia tinha um valor negativo, e eu seria positivo. Então faça isso novamente para 28/12, 29/12, etc., para muitos elementos diferentes. Eu poderia repetir isso com um loop aninhado, mas é um arquivo gigante e seria muito melhor encontrar uma maneira de percorrer o arquivo, classificado por data, apenas uma vez. Estou um pouco preso em como fazer isso de forma mais eficiente. Não tenho certeza se o bash é adequado para isso, ou alguma outra linguagem como perl ou python.

    
por brainfreeze 01.03.2017 / 22:23

3 respostas

1

Vamos chamar os dados de entrada foo :

echo 'element, date, unixTime, value
CZ, 12/27/2007 15:55, 1198788900, 42346
CZ, 12/27/2007 17:30, 1198794600, -10543
I, 12/27/2007 19:05, 1198800300, 4475' > foo

Execute o GNU datamash em foo :

datamash -t, --header-in -g 1 last 4 < foo

Saída:

CZ, -10543
I, 4475

Os nomes de comutadores mais formais para datamash podem tornar o acima mais claro, além de termos adicionado nomes de cabeçalho (se a contagem de colunas for inconveniente):

datamash --field-separator=',' --header-in --group=element last " value" < foo
    
por 01.03.2017 / 22:40
1

Assumindo que @agc interpretou corretamente a saída que você precisa, o antigo awk pode ser usado.

awk -F, 'NR>1{a[$1]=$4}END{for(x in a){print x","a[x]}}' foo
CZ, -10543
I, 4475
    
por 01.03.2017 / 22:52
0

O fato de seu arquivo ser pré-classificado torna-o tratável usando sed em que, a qualquer momento, o sed pattern space não contém mais do que 2 lines .

sed -e '
   $!{
      N
      /^\([^,]*\),[[:space:]]\{1,\}\([^[:space:]]\{1,\}\)[[:space:]].*[[:space:]]\([^[:space:]]\{1,\}\)\n,[[:space:]]\{1,\}[[:space:]]/D
   }
   s/^\([^,]*\),[[:space:]]\{1,\}\([^[:space:]]\{1,\}\)[[:space:]].*[[:space:]]\([^[:space:]]\{1,\}\)\(\n\)/ /
   /\n/!s/^\([^,]*\),[[:space:]]\{1,\}\([^[:space:]]\{1,\}\)[[:space:]].*[[:space:]]\([^[:space:]]\{1,\}\)/ /
   P;D
' yourfile

Breve

We always keep 2 lines in the pattern space and note the time when there
is a change in the 1st field. So long as we keep encountering the same
first two fields, we keep chopping off the previous line and reading in
the next. And on a transition we print the 1st and last fields of the 
previous line, print it, remove upto the newline, and go back for more
with whats left in the pattern space.
    
por 02.03.2017 / 08:02