awk: extrai colunas delimitadas por tabulações que correspondem a substrings na primeira linha

awk: extrai colunas delimitadas por tabulações que correspondem a substrings na primeira linha

Gostaria de extrair colunas delimitadas por tabulações de um arquivo de texto ("columns.txt") no qual o cabeçalho (primeira linha) corresponde a determinadas strings listadas em outro arquivo de texto ("strings.txt").

"colunas.txt" tem esta aparência:

rs2438689   rs54666437   rs9877702046   rs025436779...
0           0            0              1
1           1            2              2 
0           1            2              0 
...         ...          ...            ...

"strings.txt" fica assim:

rs2438689
rs9877702046   
...

O arquivo de texto de saída "output.txt" deve ser semelhante a este (delimitado por tabulações):

rs2438689   rs9877702046...
0           0              
1           2               
0           2               
...         ...    

Alguma sugestão sobre como fazer isso com o awk? Obrigado!

Responder1

Em vez de Awk, que tal criar uma lista separada por vírgulas de nomes de colunas de strings.txte usá-la como uma lista de namedcols para csvtool:

$ csvtool -t TAB -u TAB namedcol "$(paste -sd, < strings.txt)" columns.txt
rs2438689   rs9877702046
0   0
1   2
0   2
... ...

ou da mesma forma com csvcut/csvformato baseado em Python csvkit:

$ csvcut -tc "$(paste -sd, < strings.txt)" columns.txt | csvformat -T
rs2438689   rs9877702046
0   0
1   2
0   2
... ...

Responder2

Comperl

$ perl -F'\t' -lane 'if(!$#ARGV){ $h{$_}=1 }
                     else{ @i = grep { $h{$F[$_]} == 1 } 0..$#F if !$c++;
                           print join "\t", @F[@i]}' strings.txt columns.txt
rs2438689   rs9877702046
0   0
1   2
0   2
  • if(!$#ARGV){ $h{$_}=1 }para o primeiro arquivo de entrada, crie um hash com o conteúdo da linha como chave
  • @i = grep { $h{$F[$_]} == 1 } 0..$#F if !$c++para a primeira linha do segundo arquivo, crie uma lista de índice de todos os nomes de colunas correspondentes do hash
  • print join "\t", @F[@i]imprima as colunas correspondentes

Responder3

Modificandominha solução para sua pergunta anterior:

awk -F '\t' -f script.awk strings.txt columns.txt

Onde script.awké

BEGIN { OFS = FS }

FNR == NR {
    columns[$1] = 1
    next
}

FNR == 1 {
    for (i = 1; i <= NF; ++i)
        if ($i in columns)
            keep[i] = 1
}

{
    nf = split($0, fields, FS)
    $0 = ""
    j = 0

    for (i = 1; i <= nf; ++i)
        if (i in keep)
            $(++j) = fields[i]

    print
}

Aqui, o FNR == NRbloco só seria executado durante a leitura do primeiro arquivo listado na linha de comando ( strings.txt). Ele preencheria a columnsmatriz com chaves que são os nomes das colunas. O resto do código émais ou menosinalterado em relação à solução antiga, exceto o formulário onde verificamos se a coluna atual é aquela que gostaríamos de manter (no FNR == 1bloco).


Abordando operguntas nos comentários:

Para sempre copiar as primeiras seis colunas e cortar os cabeçalhos das colunas em _, altere

FNR == 1 {
    for (i = 1; i <= NF; ++i)
        if ($i in columns)
            keep[i] = 1
}

em

FNR == 1 {
    for (i = 1; i <= NF; ++i) {
        sub("_.*", "", $i)
        if (i <= 6 || $i in columns)
            keep[i] = 1
    }
}

Responder4

Feito usando o script abaixo, pode demorar muito para funcionar bem

k =wc -l file1| awk '{print $1}'

for ((i=1;i<=$k;i++));  do for j in `cat file2`; do awk -v i="$i" -v j="$j" '$i == j {x=NR+k}(NR<=x){print $i}' file1; done ; done>final.txt

z=`wc -l final.txt| awk '{print $1}'`

for ((i=1;i<=$z;i++)); do j=$(($i+3)); sed -n ''$i','$j'p' final.txt >file_starting_with_$i.txt; i=$j; done

paste file_starting_with*

saída

rs2438689   rs9877702046
0       0
1       2
0       2

informação relacionada