Como extraio o primeiro inteiro da string de texto em uma coluna de um arquivo delimitado por tabulação?

3

Eu trabalho em Genética Médica e muitas vezes tenho arquivos de texto delimitados onde em uma coluna (ex. coluna 5) há uma string de texto com uma "mutação" em nosso jargão:
c.2458C>T ou c.45_46delAA ou c.749_754delinsTG

Da mesma forma, em outro arquivo, ele pode ser:: p.Glu34* ou p.Ala78_Arg80del ou p.L378Ffs*11

Os c. e p. devem estar lá, mas podem ser omitidos. Pode haver qualquer número de caracteres não numéricos. Os números são sempre inteiros e geralmente 1-14 ou mais dígitos.

Eu quero adicionar uma nova coluna em algum lugar no meu arquivo, que tem apenas o primeiro inteiro , como 2458 ou 45 ou 749 no primeiro exemplo. Então eu quero usar este inteiro como um valor de chave para procurar vários valores em uma tabela de pesquisa.

Alguns dos meus arquivos têm 70.000 linhas, portanto, a edição manual não é possível ...

Quanto mais básica for a solução, melhor. Pode ser feito com bash, sed ou awk?

Uma tabela de exemplo seria (conforme interpretado corretamente abaixo):

1       2       3       4       c.2458C>T
a   b   c   d   c.45_46delAA
a1  b2  c3  d4  p.Ala78_Arg80del

(Nota: as colunas são delimitadas por tabulação, não delimitadas por espaço)

Há uma especificação para esse formato da Sociedade de Variação do Genoma Humano . Nenhum programa usa esse formato (espero!), Mas as pessoas o usam em publicações e relatórios médicos. Formatos mais recentes, como o Formato de Chamada Variante foram introduzidos, que estão longe mais analisável.

    
por minnimalist 06.04.2015 / 00:57

4 respostas

4

Usando o texto de exemplo de @ John1024, isso é específico do GNU-awk

gawk -F '\t' -v OFS='\t' 'match($5, /[[:digit:]]+/, m) {$(++NF) = m[0]} 1' file

produz

1   2   3   4   c.2458C>T   6   2458
a   b   c   d   c.45_46delAA    or  f   45
a1  b2  c3  d4  p.Ala78_Arg80del    f6  78

Ou perl

perl -F'\t' -lane 'print join "\t", @F, $F[4]=~/(\d+)/' file
    
por 06.04.2015 / 03:12
4

A resposta GNU / AWK de Glenn Jackman é elegante, mas um pouco mais simples é

awk 'BEGIN {FS=OFS="\t"} match($5,/[0-9]+/,arr) {print $0,arr[0]}' file
    
por 06.04.2015 / 12:56
3

Com base na sua descrição, suponha que tenhamos, como entrada, um arquivo separado por tabulações como:

$ cat file
1       2       3       4       c.2458C>T       6
a       b       c       d       c.45_46delAA or f
a1      b2      c3      d4      p.Ala78_Arg80del        f6

Usando sed

Para encontrar o primeiro inteiro da quinta coluna:

$ sed -r 's/([^\t]*\t){4}[^[:digit:]]*([[:digit:]]+).*//' file
2458
45
78

O acima foi testado no GNU sed . Para o OSX ou outro sistema BSD, tente:

sed -E 's/([^\t]*\t){4}[^[:digit:]]*([[:digit:]][[:digit:]]*).*//' file

Usando o awk

$ awk '{sub(/^[^[:digit:]]*/, "", $5); sub(/[^[:digit:]].*/, "", $5); print $5;}' file
2458
45
78
    
por 06.04.2015 / 01:51
1

Com sed , você pode substituir por ocorrência - assim, basta solicitar o quinto \t ab> -delimitado [1] e para qualquer número dentro dele descartando outras correspondências possíveis:

sed 's/[^\t0-9]*\([0-9]*\)[^\t]*//5' <infile

Depois de fazer uma cópia para a minha área de transferência dos outros exemplos aqui eu fiz:

xsel -bo | unexpand -a | sed ...

... para unexpand -a ll <> < tab > - seqüências de espaço dimensionado em uma real <> < tab > . E imprimiu ...

1   2   3   4   2458    6
a   b   c   d   45
a1  b2  c3  d4  78  f6

... que apenas isola o primeiro inteiro na quinta coluna. Eu não tenho certeza se é isso que você quer, no entanto. Se você quer apenas o primeiro inteiro da quinta coluna em uma linha própria, é muito mais fácil (e muito mais rápido) .

<infile \
 cut -f5 | tr -cs '0-9\n' \t |
 expand -t1,2,4 | cut -d' ' -f-2

... que primeiro cut s o quinto tab -eliminado [2] campo de dados por linha completo (para evitar problemas que podem ser causados por múltiplos inteiros por campo) e, em seguida, tr ansula em uma única < tab > a cada -s queezed sequência de caracteres -c omplementary ao conjunto de \n ewlines e 0-9 padrão dígitos [3] .

Isso significa que na saída o primeiro inteiro estará no primeiro ou no segundo campo - porque o primeiro campo agora está vazio (liderado por uma < tab >) ou sua sequência de dígitos dependendo se foi prefixado como você nota. Então eu expand o primeiro e o segundo < tab > posiciona posições em uma linha para um único espaço uma peça, e o terceiro para espaços - que efetivamente preenche uma lista de campos delimitados por espaço em ter um primeiro campo vazio ou um terceiro campo vazio. De lá, posso apenas cut dos dois primeiros campos.

 2458
 45
 78

... foram os meus resultados para o exemplo que usei porque todos eram liderados por [cp]. e, por isso, todos tinham guia mas aqueles sem seria cambaleante para a esquerda. Para condensar adicionalmente todos os resultados em uma única linha com cada número inteiro separado por um único espaço, basta anexar |xargs ao comando e obter:

2458 45 78

Notas

  1. Tenha em atenção que a \t escape não é uma norma em que sed está em causa - e no contexto de uma classe de caracteres [bracket-expression] é indiscutivelmente até mesmo explicitamente contrário ao padrão como \ barra invertida e t caracteres devem cada um representar-se lá. Eu usei o escape aqui para demonstrar mais claramente uma intenção legível - mas você provavelmente deve usar um literal < tab > em seu lugar.

  2. cut delimita em os caracteres < tab > por padrão, e, nesse caso, a opção comum -d [delim-char] é desnecessária, mas também adicionou essa nota para explicar por que. / p>

  3. Como indicado no link, o padrão POSIX requer que a classe de caractere [:digit:] inclua os caracteres 0123456789 em todas as localidades e nessa ordem de classificação e classificada antes de qualquer outra inclusões nessa classe. Non-C-locales também podem incluir outros conjuntos de numerais localizados - que um GNU tr provavelmente não manipulará apropriadamente, já que eles provavelmente são representados por múltiplos bytes - mas somente o conjunto de numeral padrão é mais provavelmente o menos Resultado surpreendente na maioria dos casos de qualquer maneira, e então usando [:digit:] a menos que você definitivamente queira combinar caracteres tanto no conjunto numeral arábico padrão quanto em algum outro conjunto de numerais dependente de localidade outro provavelmente não é aconselhável.

por 06.04.2015 / 07:34