Encontrando as diferentes combinações possíveis

Encontrando as diferentes combinações possíveis

O arquivo A possui linhas de genes:

A,B,C,D,E
P,Q,R
G,D,V,K
L,Q,X,I,U,G e assim por diante.

Tomando cada linha de cada vez, como podemos obter o seguinte tipo de saída:

Para a primeira linha:

A,B,C
B,C,D
C,D,E

Para a segunda linha:

P,Q,R

Para a terceira linha:

G, D, V
D, V, K

Essencialmente, o que eu gostaria é encontrar “trigêmeos” de genes de cada linha. O primeiro trigêmeo terá os três primeiros genes. O segundo trigêmeo terá o segundo, terceiro e quarto genes. O último trigêmeo terminará com o último gene da linha.
Conseguir isso manualmente será uma tarefa gigantesca. Como ainda não dominei os scripts Linux, Perl ou Python para poder escrever um script para isso, a ajuda desta comunidade será apreciada!

Responder1

Usando awk:

function wprint() {
    print w[1], w[2], w[3];
}

function wshift(e) {
    w[1] = w[2]; w[2] = w[3]; w[3] = e;
}

BEGIN { FS = OFS = "," }

{
    wshift($1);
    wshift($2);
    wshift($3);
    wprint();

    for (i = 4; i <= NF; ++i) {
        wshift($i);
        wprint();
    }
}

Então:

$ awk -f script data.in
A,B,C
B,C,D
C,D,E
P,Q,R
G,D,V
D,V,K
L,Q,X
Q,X,I
X,I,U
I,U,G

O awkscript usa uma janela móvel de três elementos, w. Para cada linha de entrada, ele preenche os três elementos da janela com os três primeiros campos e os imprime como uma lista separada por vírgulas (seguida por uma nova linha). Em seguida, ele itera sobre os campos restantes na linha, deslocando-os para a janela e imprimindo a janela para cada elemento.

Se qualquer linha nos dados de entrada contiver menos de dois campos, você obterá coisas como

A,,

ou

A,B,

na saída.

Se você tiver certeza de que cada linha de entrada tem pelo menos três campos (ou se quiser desconsiderar qualquer linha que não tenha), então você pode encurtar awkum pouco o script:

function wprint() {
    print w[1], w[2], w[3];
}

function wshift(e) {
    w[1] = w[2]; w[2] = w[3]; w[3] = e;
}

BEGIN { FS = OFS = "," }

{
    for (i = 1; i <= NF; ++i) {
        wshift($i);
        if (i >= 3) {
            wprint();
        }
    }
}

Uma generalização da primeira variação do script com tamanho de janela variável:

function wprint(i) {
    for (i = 1; i < n; ++i) {
        printf("%s%s", w[i], OFS);
    }
    print w[n]
}

function wshift(e,i) {
    for (i = 1; i < n; ++i) {
        w[i] = w[i + 1];
    }
    w[n] = e;
}

BEGIN { FS = OFS = "," }

{
    for (i = 1; i <= n; ++i) {
        wshift($i);
    }
    wprint();

    for (i = n + 1; i <= NF; ++i) {
        wshift($i);
        wprint();
    }
}

Usando isso:

$ awk -v n=4 -f script data.in
A,B,C,D
B,C,D,E
P,Q,R,
G,D,V,K
L,Q,X,I
Q,X,I,U
X,I,U,G

Responder2

Com perl:

perl -F, -le 'BEGIN { $, = "," } while(@F >= 3) { print @F[0..2]; shift @F }' file

Com awk:

awk -F, -v OFS=, 'NF>=3 { for(i=1; i<=NF-2; i++) print $i, $(i+1), $(i+2) }' file

Responder3

Usando Perl, podemos resolver isso como:

perl -lne '/(?:([^,]+)(?=((?:,[^,]+){2}))(?{ print $1,$2 }))*$/' yourfile
perl -F, -lne '$,=","; print shift @F, @F[0..1] while @F >= 3' 
perl -F, -lne '$,=","; print splice @F, 0, 3, @F[1,2] while @F >= 3'

que pode ser escrito de forma expandida conforme mostrado abaixo:

perl -lne '
   m/
      (?:                       # set up a do-while loop
         ([^,]+)                # first field which shall be deleted after printing
         (?=((?:,[^,]+){2}))    # lookahead and remember the next 2 fields
         (?{ print $1,$2 })     # print the first field + next 2 fields
      )*                        # loop back for more
      $                         # till we hit the end of line
   /x;
' yourfile

E com sed podemos fazer isso com uma variedade de comandos:

sed -e '
   /,$/!s/$/,/     # add a dummy comma at the EOL

   s/,/\n&/3;ta    # while there still are 3 elements in the line jump to label "a"
   d               # else quit processing this line any further

   :a              # main action
   P               # print the leading portion, i.e., that which is left of the first newline in the pattern space
   s/\n//          # take away the marker

   s/,/\n/;tb      # get ready to delete the first field
   :b

   D               # delete the first field, and apply the sed code all over from the beginning to what remains in the pattern space
' yourfile

DC também pode fazer isso:

sed -e 's/[^,]*/[&]/g;y/,/ /' gene_data.in |
dc -e '
[q]sq                            # macro for quitting
[SM z0<a]sa                      # macro to store stack -> register "M"
[LMd SS zlk>b c]sb               # macro to put register "M" -> register "S"
[LS zlk>c]sc                     # macro to put register "S" -> stack
[n44an dn44an rdn10anr z3!>d]sd  # macro to print 1st three stack elements
[zsk lax lbx lcx ldx c]se        # macro that initializes & calls all other macros
[?z3>q lex z0=?]s?               # while loop to read in file line by line and run macro "e" on each line
l?x                              # main()
'

Resultados

A,B,C
B,C,D
C,D,E
D,E,F
E,F,G
P,Q,R
G,D,V
D,V,K
L,Q,X
Q,X,I
X,I,U
I,U,G

informação relacionada