Comparando Índices de duas cadeias de caracteres

3

Eu tenho duas cadeias duplas de tamanho igual, que se parecem com isso:

"$con" - (consiste em espaços e * 's):

                        *   ******       *** ** *                  **         

e "$prot" - (consiste em letras e -s):

M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ

Como faço para extrair a string de letras na mesma posição em $prot correspondente à posição de * em $con .

    
por user2662478 28.08.2013 / 01:51

5 respostas

1

Se suas strings não contiverem nenhuma nova linha, aqui está uma solução usando apenas ferramentas POSIX. Ele funciona colocando todos os caracteres em uma linha separada, unindo os dois arquivos e extraindo as linhas onde o primeiro arquivo (agora a primeira coluna) tinha um * .

con_file=$(mktemp)
echo "$con" | sed -e 's/./&\
/g' >"$con_file"
prot_lines=$(echo "$prot" | sed -e 's/./&\
/g')
prot_extract=$(echo "$prot_lines" |
               paste -d "$con_file" - |
               sed -n 's/^* //p' |
               tr -d '\n')

Outra abordagem, talvez mais direta e, sem dúvida, mais rápida, é escrever um loop no awk.

echo "$prot" | awk -v filter="$con" '{
    for (i=1; i<=length; i++) {
        if (substr(filter, i, 1) == "*") printf "%c", substr($0, i, 1);
    }
}
END {printf "\n"}'

Ainda outra abordagem seria converter o filtro em uma lista de índices. A conversão é lenta, mas espero que este seja o método mais rápido se você tiver uma lista muito longa de proteínas para cada conjunto de índices.

indices=$(echo "$con" |
          sed 's/\*\**/,&\n/g' |
          awk -F , 'BEGIN {start = 1}
                    /\*/ {start += length($1);
                          printf "%d-", start;
                          start += length($2);
                          printf "%d,", start - 1}')
indices=${indices%,}
echo "$prot" | cut "$indices"
    
por 28.08.2013 / 03:07
0

Você pode usar este script Perl para fazer o que quiser:

#!/usr/bin/perl

$con  ='                        *   ******       *** ** *                  **         ';
$prot ='M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ';

@c_con = split(//, $con);
@c_prot = split(//, $prot);

@i_con = grep { $c_con[$_] eq '*' } 0 .. $#c_con;
map { print "index: $_, value: @c_prot[$_]\n" } @i_con;

Exemplo

A execução produzirá uma lista de índices e valores nesses índices em $prot .

$ ./extracvals.pl 
index: 24, value: R
index: 28, value: Y
index: 29, value: Y
index: 30, value: H
index: 31, value: Q
index: 32, value: F
index: 33, value: L
index: 41, value: M
index: 42, value: K
index: 43, value: A
index: 45, value: K
index: 46, value: A
index: 48, value: W
index: 67, value: G
index: 68, value: L

Como funciona?

O script cria 2 strings, conforme descrito pelo OP, $con e $prot . Essas seqüências de caracteres, em seguida, são lidas em 2 matrizes, de modo que cada caractere na seqüência de caracteres ocupa uma célula na matriz. Isso é feito usando split funciton de Perl:

@c_con = split(//, $con);
@c_prot = split(//, $prot);

Os dois novos arrays, @c_con (contém $con ) e @c_prot (contém $prot ).

Em seguida, usamos a função grep do Perl para encontrar todos os índices na matriz @c_con que têm o valor '*' . Esta lista de índices é armazenada em outro array, @i_con .

Finalmente, usamos a função map do Perl para imprimir o valor do índice e o valor correspondente na matriz @c_prot em um determinado índice. A função map recebe cada valor da matriz @i_con e avalia o comando nas chaves:

{ print "index: $_, value: @c_prot[$_]\n" }

para cada um desses valores. O índice é armazenado na variável temporária do Perl, $_ , pois map itera através da matriz @i_con .

    
por 28.08.2013 / 02:29
0

Eu trabalhei uma solução semelhante ao slm (ele me bateu nisso!) Mas se o OP quiser evitar codificar tudo em perl:

#!/bin/sh

con="                        *   ******       *** ** *                  **"
prot="M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ"

# put the con and prot variables into our environment variables
export con prot

# then call perl
result=$(perl -e 'my @x = split(//, "$ENV{con}"); 
    my @i = grep { $x[$_] eq "*" } 0 .. $#x; 
    print join("", map { substr("$ENV{prot}", $_, 1) } @i );' )

# now we have your answer in $result
echo "$result"

# then once finished with con and prot, unset them from the environment
unset con prot

$ENV é uma variável reservada em perl contendo um hash de todos os nomes e valores de variáveis ambiente , portanto, obter o valor de $ prot (que usamos export para configurá-lo) pode Basta ser buscado via $ENV{prot} .

Em vez de dividir as matrizes como o slm, usei substr , que retorna uma sub-string de uma determinada string (primeiro argumento), no offset (2º argumento) do tamanho do set (3o argumento). Além disso, parecemos usar soluções idênticas ( bastante assustadoras, cue a música da Twilight Zone ):)

    
por 28.08.2013 / 02:37
0

Se pudermos usar o gnu grep:

cut -c$(grep -Fbo '*' <<<_"$con" | cut -f1 -d: | paste -sd,) <<<"$prot"

Tenho certeza de que pode ser mais golfado, mas funciona bem. -bo faz com que o grep produza o (b) deslocamento de yte com (o) nly-matches (não linhas inteiras), o que, como efeito colateral, faz com que o grep encontre todas as correspondências, não apenas uma por linha. Em seguida, manipulamos isso em uma lista de números separados por vírgula, que podemos alimentar como uma opção de linha de comando para cut . O detalhe chato é que o grep acha que os índices de caracteres são baseados em 0 e o corte acha que eles são baseados em 1, então precisamos deslocar $con por um caractere; daí, o _ .

$ con="                        *   ******       *** ** *                  **"
$ prot="M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ"
$ printf "%s\n%s\n" "$con" "$prot"
                        *   ******       *** ** *                  **
M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ
$ cut -c"$(grep -Fbo '*' <<<_"$con" | cut -f1 -d: | paste -sd,)" <<<"$prot"
RYYHQFLMKAKAWGL
    
por 28.08.2013 / 05:11
0

Uma solução simples usando os recursos do shell Bash (viu uma tag /bash na pergunta):

con="                        *   ******       *** ** *                  **"
prot="M-ASDFRMKAWRGMLMI----WSGRCYYYYHQFLIMASDFRMKAMKAWWSGRCYNSHPPAAQVFYWLGLLSDVAGSALEAQ"

i=0
# Iterate until the index is less than the con string length  
while [ $i -lt ${#con} ]
do 
    # Get the current element of the con "character array"  
    c=${con:$i:1}
    if [[ $c == '*' ]]
    then
        # Get the corresponding element from the prot character array  
        p=${prot:$i:1}
        echo $i, $c, $p
    fi
    i=$((i+1))
done

A saída é:

24, *, R
28, *, Y
29, *, Y
30, *, H
31, *, Q
32, *, F
33, *, L
41, *, M
42, *, K
43, *, A
45, *, K
46, *, A
48, *, W
67, *, G
68, *, L

Claro, você pode alterar a declaração echo para controlar o que é impresso.

Fonte: Bash: Dividir string na matriz de caracteres

    
por 28.08.2013 / 08:51