Construir tabela - Adicionar coluna dependendo dos nomes dos arquivos

5

Eu tenho uma quantidade enorme de arquivos com o seguinte estilo de nomeação:

WBM_MIROC_rcp8p5_mississippi.txt
WBM_GFDL_rcp8p5_nosoc_mississippi.txt
DBH_HADGEM_rcp4p5_co2_mississippi.txt
HMH_IPSL_rcp4p5_mississippi.txt

Esses arquivos representam tabelas com (alguns deles têm um delimitador de tabulação e outro delimitador de um espaço), como segue:

YEAR MONTH DAY RES
1971 1 1 1988
1971 1 2 3829
...

Gostaria de agrupar todos os arquivos com rcp8p5 em seu nome em uma tabela grande; e faça o mesmo para os arquivos com rcp4p5 em seu nome. Mas, eu só quero colar as 4 colunas de cada arquivo para evitar a redundância das três primeiras colunas que são sempre as mesmas. Atualmente, estou usando o seguinte script:

ls |
awk -F_ '{ i=$1; m=$2; s=$3; u=$4;
          if(f[s]=="")add = $0;
          else add = sprintf("<(cut -f4 %s)",$0);
          f[s] = f[s] " " add }
          END{ for(insc in f)
                 printf "paste%s > out_%s.txt\n",f[insc],insc
          }' |bash

Não está claro o porquê, mas a saída não é a esperada. Eu tenho a seguinte saída:

YEAR MONTH DAY RES YEAR MONTH DAY RES YEAR MONTH DAY RES
1971 1     1   187 1971 1     1   143 1971 1     1   234
1971 1     2   321 1971 1     2   398 1971 1     1   754
...

Em vez disso, gostaria de ter a seguinte saída:

YEAR MONTH DAY RES RES RES
1971 1     1   187 143 234
1971 1     2   321 398 754

Poderia ser ótimo se alguém puder me dar uma dica!

    
por steve 12.10.2015 / 19:04

4 respostas

2

A resposta mais provável é que as colunas do arquivo de dados não estão separadas por abas, mas pelo espaço, por exemplo. Você pode verificar isso executando um dos -los através de cat -vet , que mostra guias reais como ^I .

Para alterar o comando cut para usar o espaço como delimitador, é necessário adicione o arg -d' ' , mas já que você já está entre aspas simples e um script awk você precisa mudar seu sprintf(...) para

sprintf("<(cut -d\" \"  -f4 %s)",$0)
    
por 12.10.2015 / 20:02
2

Para arquivos não muito grandes:

while read -r f_part
do
    awk '
        BEGIN{
            SUBSEP=" "
        }
        NR==1{
            for(i=2;i<ARGC;i++)
                $(NF+1)=$NF
            print
        }
        FNR==1{
             next
        }
        {
             RES[$1,$2,$3]=RES[$1,$2,$3] $4 " "
        }
        END{
             for(i in RES)
                 print i, RES[i]
        }' *_${f_part}_* > big_table_${f_part}
done < <(printf '%s\n' *_*_*_*txt | cut -d_ -f3 | sort -u)

Ou se tiver certeza na ordem correta em arquivos:

while read -r f_part
do 
    set -- *_${f_part}_*
    sed -i 's/\s+/:/3;s/\s\+/\t/g;s/\s*$//' "$@"
    while [ $# -gt 1 ]
    do
        join -t: $1 $2 > tmp
        mv tmp big_table_${f_part}
        shift 2
        set -- big_table_${f_part} "$@"
    done
    sed 's/:/\t/g' big_table_${f_part}
done < <(printf '%s\n' *_*_*_*txt | cut -d_ -f3 | sort -u)
    
por 12.10.2015 / 20:22
2
for f in rcp8p5 rcp4p5
do  : >"$f.txt"
    find . ! -name . -prune ! -type d -name "*_${f}_*txt" -exec \
        sh -c '
            printf "%s\t" YEAR MONTH DAY
            printf "%.0sRES\t" "$@"; echo
            sed -n "
                /^[0-9]/!d;p;:n
                n
                /^[0-9]/s/.*[[:blank:]]//p
                bn
            "  "$@" | paste
    ' --    {} + >>"$f.txt"
done

... Eu acho que eu poderia ter a idéia errada sobre o que você estava fazendo antes, e talvez isso possa compensar isso. Não tenho tanta certeza de que isso funcionará, mas, se acontecer, deve ser caminho mais rápido do que o que você está fazendo.

Basicamente find pega uma lista de arquivos com nomes correspondentes a ...8... ou ...4... e os entrega em {} + a um shell.

O shell imprime uma linha de cabeçalho começando com YEAR MONTH DAY cada, seguido por \t ab e depois imprime quantas colunas RES , pois tem argumentos.

Em seguida, sed concatena todos os argumentos do arquivo em um único fluxo e imprime a primeira linha começando com um dígito inteiro e todas as linhas que começam com um dígito que vêm depois de obter apenas o último campo impresso.

Toda a saída de sed é passada para paste , que substitui todos os ewlines \n em sua entrada por \t abs na saída.

Espero que esta versão funcione porque invocar um novo subshell e abrir um novo canal para cada arquivo da sua lista é uma idéia terrível.

Em caso afirmativo, este irá , no entanto, fazer uma nova linha em sua tabela para cada grupo de arquivos ARGMAX - o que pode não ser uma coisa ruim, mas é facilmente manipulado depois.

    
por 12.10.2015 / 20:52
1

Você também pode fazer algo como

arr=( *_rcp8p5_*.txt )
paste "${arr[@]}" | cut -f-4,$(seq -s, 8 4 $((4*${#arr[@]}))) >out_rcp8p5.txt

Isso irá processar paste all *_rcp8p5_*.txt e extrair os campos 1-4 e a cada quarto campo depois.

    
por 12.10.2015 / 23:01