Cálculo aritmético em linha para número de colunas não fixas

1

Eu tenho um arquivo de entrada com um número de coluna não fixo no qual eu gostaria de fazer alguns cálculos aritméticos em:

input.txt
ID1    4651455    234     4651765    392     4652423      470
ID2    16181020   176    16184958    869     16185889     347    16187777     231

O arquivo de entrada possui campos separados por tabulação sempre com um ID exclusivo na coluna $1 (não duplicado). Nem todas as linhas têm o mesmo número de colunas.

O que eu gostaria de alcançar é um arquivo separado por tabulações da seguinte forma:

 output1.txt
 ID1     76    266   
 ID2     3762   62   1541  

Basicamente, ele imprimiria o $1 do arquivo original, depois, começaria da segunda coluna do mesmo arquivo ( $4 ) e subtrairia ao seu valor as duas colunas anteriores ( $4 - $3 - $2 ) faça o mesmo com todas as colunas pares do arquivo de entrada (por exemplo, $6 - $5 - $4 ; $8 - $7 - $6 ; ...). No meu conhecimento, isso pode ser feito com awk print , mas só sei como lidar com isso quando meu arquivo tem um número fixo de colunas em cada linha.

Uma saída ainda mais ideal para minhas necessidades seria a seguinte:

output2.txt
ID1    234    76    392    266   470
ID2    176   3762   869    62    347   1541  231

Basicamente, ele deve imprimir o $1 do arquivo original e, em seguida, intercalar a impressão das colunas ímpares do arquivo de entrada para as colunas, como em output1.txt .

    
por aechchiki 30.08.2017 / 13:27

2 respostas

2
$ awk -v OFS='\t' -f script.awk file
ID1     76      266
ID2     3762    62      1541

Onde script.awk é

{ printf("%s", $1) }
{ for (i=4; i<=NF; i+=2) { printf("%s%d", OFS, $i - $(i-1) - $(i-2)) } }
{ printf("%s", RS) }

Todos os blocos serão executados para cada linha de entrada do arquivo.

O primeiro bloco gera o ID.

O segundo bloco itera sobre os campos como você descreveu e gera os dados separados por OFS (o separador do campo de saída, definido como uma guia na linha de comando).

O bloco final simplesmente exibe o separador de registro RS , que é uma nova linha por padrão.

Alternativamente,

BEGIN { OFS = "\t" }
{ printf("%s", $1) }
{ for (i=4; i<=NF; i+=2) { printf("%s%d", OFS, $i - $(i-1) - $(i-2)) } }
{ printf("%s", RS) }

para se livrar da necessidade de -v OFS='\t' .

Como "one-liner":

$ awk 'BEGIN { OFS = "\t" }
{ printf("%s", $1) }
{ for (i=4; i<=NF; i+=2) { printf("%s%d", OFS, $i - $(i-1) - $(i-2)) } }
{ printf("%s", RS) }' file

Para o problema prolongado:

{ printf("%s", $1) }
{ for (i=4; i<=NF; i+=2) { printf("%s%d%s%d", OFS, $(i-1), OFS, $i - $(i-1) - $(i-2)) } }
{ printf("%s%d%s%s", OFS, $NF, OFS, RS) }

Isso geraria o seguinte do arquivo original diretamente:

ID1     234     76      392     266     470
ID2     176     3762    869     62      347     1541    231
    
por 30.08.2017 / 13:44
2
Solução

awk :

awk '{ r=$1; for(i=4;i<=NF;i+=2) r=r"\t"$i-$(i-1)-$(i-2); printf "%s\n",r }' OFS='\t' file
  • r=$1 - capturando o primeiro campo

  • for(i=4;i<=NF;i+=2) - iterando pelos campos par

  • $i-$(i-1)-$(i-2) - realizando a subtração necessária

A saída:

ID1 76  266
ID2 3762    62  1541
    
por 30.08.2017 / 13:41