awk comando para analisar um arquivo

4

Eu tenho um arquivo de texto a seguir. Eu estou mostrando as 3 primeiras linhas.

chrom   st  end gene    strand  c1  c2  c3  c4  c5  c6  c7  c8  c9  c10 c11 c12 c13 c14
chr6    3345    3543    geneA   +   36  -23 -1  3   1250    946 416 458 475 417 58  80  2   14
chr9    1302    1389    geneB   -   8   -10 -18 -8  2896    2128    635 955 372 385 -20 31  -7  -7

Eu quero imprimir a primeira linha como é, pois é a linha de cabeçalho.

Em seguida, para as linhas subsequentes (ou seja, das linhas 2 e seguintes), quero imprimir os primeiros 5 campos como estão (até as informações da cadeia) e depois disso, se o campo (do 6º campo em diante) tiver valor > 100 imprimir esse valor como está e se o campo tiver valor < 100 apenas substituí-lo por NA.

Então, meu arquivo de saída deve ser parecido com isto (idealmente, delimitado por tabulações)

chrom   st  end gene    strand  c1  c2  c3  c4  c5  c6  c7  c8  c9  c10 c11 c12 c13 c14
chr6    3345    3543    geneA   +   NA  NA  NA  NA  1250    946 416 458 475 417 NA  NA  NA  NA
chr9    1302    1389    geneB   -   NA  NA  NA  NA  2896    2128    635 955 372 385 NA  NA  NA  NA
    
por user3138373 05.06.2014 / 18:02

3 respostas

9
awk 'NR > 1 { for (i = 6; i <= NF; i++) if ($i < 100) $i = "NA" }; 1' yourfile.txt

Expandido com comentários:

NR > 1 {                         # skipping NR == 1, the first line
    for (i = 6; i <= NF; i++)    # column 6 to the end, skipping first 5
        if ($i < 100) $i = "NA"  # self-explanatory
}

1 # print all lines; 1 evaluates to true, and default action is print

Editar: existem várias maneiras de definir OFS . A maneira mais concisa em que consigo pensar é adicionar OFS='\t' antes do nome do arquivo.

awk '...' OFS='\t' file.txt
awk -v OFS='\t' '...' file.txt
awk 'BEGIN { OFS="\t" }; ...' file.txt
    
por 05.06.2014 / 18:10
5

O jw013 já deu uma boa solução para o awk, mas desde que você mencionou o Perl:

perl -lane 'map{$_="NA" if $_<100}@F[5..$#F] if $.>1; print join "\t", "@F"' file 

Explicação

  • perl -lane : processe cada linha de entrada ( -n ) e divida-a em espaços em branco na matriz @F ( -a ) e, em seguida, execute o script fornecido por -e . O -l remove as linhas iniciais à direita de cada linha e adiciona uma \n a cada instrução print .

  • map{$_="NA" if $_<100}@F[5..$#F] : para cada elemento da matriz @F (os campos) do 6º até o final, altere esse elemento para "NA" se for menor que 100.

  • if $.>1; : o map{} anterior só será executado se esta não for a primeira linha.

  • print join "\t", "@F"' : junte cada elemento da matriz @F com uma aba (é solicitado no seu comentário à resposta do jw013) e imprima-o.

por 05.06.2014 / 18:14
2
sed '1n;s|$| |;:na
    s|\([+-] .*\) [+-]*[0-9]\{1,2\} | NA |
    t na;s| $||'

A julgar pelos dados que você mostra, não há razão para que essa pequena função sed s///;t não funcione, eu não acho. (Obrigado ao jw013 por apontar a coluna perdida da última vez.) Isso substitui todas as sequências numéricas de 1 ou 2 caracteres após +/- e em uma linha com tudo que o precede e NA até que não haja mais nada para substituir.

Aqui está outra versão sem recursão que faz uso do sed old space de h :

sed '1n;h;s|.*[+-] ||;s|$| |
    s| [+-]*[0-9]\{1,2\} | NA |g
    x;G;s|\([+-] *\).*\n||;s| $||'

Ele depende do mesmo marcador e divide a linha ali - a primeira metade é deixada intacta no espaço h old, enquanto é excluída inteiramente do espaço padrão. Em seguida, fazemos uma substituição global em todas as palavras de caractere numérico de 1,2, anexamos a h old space, e x change pattern e h old spaces e excluímos tudo entre o marcador e o \n ewline inserido como um resultado da operação de acréscimo.

    
por 05.06.2014 / 19:24

Tags