Como operar em todas as colunas com datamash?

1

Suponha que eu tenha o seguinte arquivo de dados:

111 222 333
444 555 666
777 888 999

Eu sou capaz de calcular a soma por coluna com o GNU Datamash assim:

cat foo | datamash -t\  sum 1 sum 2 sum 3
1332 1665 1998

Como eu faria isso com o datamash se eu não soubesse o número de colunas no meu arquivo de dados?

Estou perguntando porque, por exemplo, cut suporta símbolos de fim de intervalo como - para seu seletor de campo.

    
por w177us 22.02.2018 / 16:44

4 respostas

1

cols=$( awk '{print NF; exit}' foo); cat foo | datamash -t\  sum 1-$cols

ou

cat foo | datamash -t\  sum 1-$( awk '{print NF; exit}' foo)

datamash tem um recurso para especificar intervalos de coluna, portanto, calcule o número de colunas e use esse resultado como parte da especificação do intervalo. Na minha solução de exemplo, usei awk para verificar apenas a primeira linha do arquivo e sair, mas você pode usar qualquer outra coisa que lhe agrade. datamash tem uma função -check cuja saída inclui o número de colunas, mas em um formato que ainda precisa ser analisado para o número específico que é de seu interesse.

    
por 22.02.2018 / 17:19
1

Não vejo uma opção para especificar um intervalo desconhecido no manual do datamash

Experimente este perl one-liner

$ perl -lane '$s[$_]+=$F[$_] for 0..$#F; END{print join " ", @s}' ip.txt
1332 1665 1998
  • A opção -a dividirá automaticamente a linha de entrada nos espaços em branco, os resultados serão salvos em @F array
  • for 0..$#F para fazer um loop sobre a matriz, $#F fornece o índice do último elemento
  • $s[$_]+=$F[$_] salve a soma em @s array, por padrão o valor inicial será 0 no contexto numérico. $_ terá o valor do índice para cada iteração
  • END{print join " ", @s} após processar todas as linhas de entrada, imprimir o conteúdo da matriz @s com espaço como separador
por 23.02.2018 / 05:20
0

Eu não sei sobre datamash , mas aqui está uma awk solution:

$ awk '{ for( col=1; col<=NF; col++ ) { totals[col]+=$col } } END { for( col=0; col<length(totals); col++ ) {printf "%s ", totals[col]}; printf "\n" } ' input
1332 1665 1998

Para tornar esse script awk mais legível:

{      // execute on all records
  for( col=1; col<=NF; col++ ) { 
    totals[col]+=$col 
  }; 
} 
END {  // execute after all records processed
  for( col=0; col<length(totals); col++ ) {
    printf "%s ", totals[col]
  }; 
  printf "\n";
} 
    
por 22.02.2018 / 17:02
0

Usando datamash e bash :

n=($(datamash -W check < foo)); datamash -W sum 1-${n[2]} < foo

Saída:

1332    1665    1998

Como funciona:

  1. datamash -W check < foo produz a string "3 linhas, 3 campos" .

  2. n=($(datamash -W check < foo)) carrega essa string em uma matriz $n . Queremos o número de campos, que seria ${n[2]} .

  3. datamash -W sum 1-${n[2]} < foo faz o resto.

Isso também pode ser feito com um shell POSIX , usando uma complexa string de formatação printf em vez de uma matriz, mas é mais rápido:

datamash -W sum 1-$(printf '%0.0s%0.0s%s%0.0s' $(datamash -W check < foo)) < foo

Também pode ser feito com ferramentas de shell:

datamash -W sum 1-$(head -1 foo | wc -w) < foo
    
por 17.12.2018 / 04:50