Adicione cada duas linhas consecutivas em uma coluna e divida-a com a primeira e Print

2

Eu tenho um arquivo de entrada de quase 2000 linhas. Eu tenho que fazer um cálculo e imprimi-lo na terceira coluna.

Exemplo do que tenho a fazer com o arquivo de entrada:

n ID1_1 n/(n+k)
k ID1_2

Arquivo de entrada:

 10 ID1_1
 20 ID1_2
  1 ID3_1
  9 ID3_2
 20 ID20_1
 15 ID2_1
300 ID2_2

Resultado esperado:

 10 ID1_1 0.33
 20 ID1_2 
200 ID3_1 0.11
  9 ID3_2
 20 ID20_1 /*I would just leave it like that*/
 15 ID2_1 0.047
300 ID2_2

Você tem alguma maneira fácil de resolver isso? Obrigado.

    
por Srilakshmi 17.02.2015 / 16:30

5 respostas

1

Através do python.

#!/usr/bin/python3
import re
import sys
fil = sys.argv[1]
with open(fil) as f:
    m = re.split(r'[\n\r]+(?= *\d+\s+ID\d+_1)', f.read())
    l = []
    for i in m:
        l.append(re.sub(r'(?s)^(\s*(\d+)\s+([^_]+)_1)([\n\r]+\s*(\d+)\s+_2)$', \
             lambda m: m.group(1) + " "+ str(float(m.group(2))/(float(m.group(2))+float(m.group(5)))) +  m.group(4),i))
    print('\n'.join(l), end = "")

Salve o script acima como script.py e, em seguida, execute-o por

python3 script.py inputfile

Exemplo:

$ python3 f.py file
 10 ID1_1 0.3333333333333333
 20 ID1_2
  1 ID3_1 0.1
  9 ID3_2
 20 ID20_1
 15 ID2_1 0.047619047619047616
300 ID2_2
    
por 17.02.2015 / 22:27
3

Dada a entrada que você mostra, o seguinte deve funcionar:

<infile sed -e '$!N;2i\' -e '3k 
s|\(\(.*  *\).*_1\)\n\(\(.*  *\).*_2\)$|[ ]Pd+/p[]pc|;t
s|^[ _ID0-9]*|[&]pc|;P;D' | dc

Para mim, imprime ...

 10 ID1_1 .333
 20 ID1_2
  1 ID3_1 .100
  9 ID3_2
 20 ID20_1
 15 ID2_1 .047
300 ID2_2

... porque eu configurei a precisão de dc para 3, mas com uma precisão de 10 ...

 10 ID1_1 .3333333333
 20 ID1_2
  1 ID3_1 .1000000000
  9 ID3_2
 20 ID20_1
 15 ID2_1 .0476190476
300 ID2_2

Além da precisão de saída, ela também difere do seu resultado esperado na terceira linha - mas eu acho que isso é mais devido a um erro de digitação na pergunta?

De qualquer forma, para entender isso, você deve considerar que primeiro devo analisar a saída em dois formulários para dc - dc P rint [string] sem a seguinte \n ewline, ou será p rint um número ou [string] com um. Além disso, possivelmente e x ecuting uma string como dc macro, pode fazer pouco mais com eles. Mas, com números, é muito capaz.

Portanto, com sed , primeiro anexei a linha $!N ext à atual, se a linha atual for ! the $ last. Na segunda linha, eu i nster a string 3k para stdout - que é um comando dc para definir a precisão como 3.

Então eu tentei uma substituição:

s|\(\(.*  *\).*_1\)\n\(\(.*  *\).*_2\)$|[ ]Pd+/p[]pc|

Isso só será bem-sucedido se o espaço padrão contiver pelo menos um espaço seguido em algum momento por _1 seguido imediatamente por um caractere \n ewline seguido em algum ponto por pelo menos um espaço seguido em algum ponto por _2 seguido imediatamente pelo $ final do espaço padrão.

Isso significa que a substituição acima afeta apenas pares de linhas como ...

...ID_1
...ID_2

... e não outros. Quando isso afeta, ele transforma seu conteúdo em um script dc viável. Ele é o próximo t ests se a substituição foi bem-sucedida e, em caso afirmativo, ela se ramifica do script, imprimindo assim os resultados da substituição e não executando mais comandos sed . dc usa a saída padrão de sed como entrada padrão, assim, por exemplo, depois que sed altera as duas primeiras linhas para ficar assim:

[ 10 ID_1 ]P10d20+/p[ 20 ID_2]pc

... dc lida com essa entrada da seguinte maneira:

  • [ 10 ID_1 ] - empurre a string entre os colchetes para o topo da pilha (o que empurra tudo já na pilha para baixo em um)
  • P - P rint o topo da pilha sem um \n ewline e pop off (o que coloca todos os valores na pilha abaixo em um)
  • 10 - empurre o número 10 para o topo da pilha
  • d - d uplique o topo da pilha
  • 20 - insira o número 20 no topo da pilha
  • + - adicione o topo da pilha e o 2cd do topo da pilha (enquanto estala ambos) e empurre o resultado para o topo da pilha
  • / - divide o 2cd do topo da pilha (agora nosso d uplicated 10 ) pelo topo da pilha (nosso resultado 10 20 + ) (enquanto estala ambos) e empurre o resultado para o topo da pilha
  • p - p rint o topo da pilha (sem aparecê-lo) seguido por um \n ewline à direita.
  • [ 20 ID_2] - empurra a corda para o topo da pilha
  • p - p rint o topo da pilha (novamente, sem aparecê-lo) seguido por um \n ewline à direita
  • c - c aprende a pilha

E assim dc imprime:

 10 ID1_1 .333
 20 ID1_2

Mas se sed não coincidir e alterar o espaço de padrão com êxito conforme já descrito, ele ficará com outras linhas para manipular. Nesse caso, sed imprime a primeira seqüência de [ ID_0-9]* entre [ e ] , além de anexar os comandos pc . Em seguida, ele usa P rints de espaço de padrão até o primeiro \n ewline que está ocorrendo no espaço padrão e, em seguida, D eletria o mesmo antes de recomeçar com o que resta. E assim sed trabalha em uma linha de frente, imprimindo o script dc em dc a todo momento.

Isso significa que o arquivo inteiro é processado in-stream, pois dc e sed fornecem saída enquanto processam. Dessa forma - desde que sua entrada seja semelhante ao exemplo da pergunta - você poderia facilmente processar 2 milhões de linhas da mesma maneira, ou então manipular um arquivo de log em tempo real.

    
por 17.02.2015 / 22:24
1

Você pode fazer tudo com um único comando awk :

$ awk '{if(NR%2){n=$1;last=$0;}else{print last,n/(n+$1)"\n"$0}}' file
10 ID1_1 0.333333
 20 ID1_2
  1 ID3_1 0.1
  9 ID3_2
 15 ID2_1 0.047619
300 ID2_2

A idéia é simplesmente verificar se a linha atual é numerada e i) se for, imprimimos a linha anterior ( last ) junto com o cálculo desejado e ii) se não for, salvamos a corrente linha como last e o primeiro campo como n .

Você pode controlar o número de casas decimais impressas usando printf :

$ awk '{if(NR%2){n=$1;last=$0;}else{printf "%s %.2f\n%s\n",last,n/(n+$1),$0}}' file
10 ID1_1 0.33
 20 ID1_2
  1 ID3_1 0.10
  9 ID3_2
 15 ID2_1 0.05
300 ID2_2

Aqui está a mesma coisa básica em Perl:

$ perl -lane 'if($.%2){$n=$F[0];$last=$_;}
              else{printf "%s %.2f\n%s\n",$last,$n/($n+$F[0]),$_}' file
10 ID1_1 0.33
 20 ID1_2
  1 ID3_1 0.10
  9 ID3_2
 15 ID2_1 0.05
300 ID2_2
    
por 17.02.2015 / 17:04
1

Supondo que sua entrada esteja em um arquivo chamado a.txt :

paste a.txt <(awk 'NR%2?ORS=FS:ORS=RS' a.txt | awk '{print $1/($1+$3)}' | sed G)

O primeiro awk trará o par sucessivo de linhas em uma única linha. O próximo awk fará o cálculo desejado. O sed inserirá uma nova linha no resultado e redirecionará a saída para paste , o que trará ela e a entrada juntos.

Isso dará o seguinte:

10 ID1_1    0.333333
20 ID1_2
1 ID3_1     0.1
9 ID3_2
15 ID2_1    0.047619
300 ID2_2
    
por 17.02.2015 / 16:45
1

Após a edição do OP (consulte o termo adicionado):

awk '
/ID.*_1/{
    n=$1
    idx=$2
    sub("_1","_2",idx)
    printf s"%s",$0
    s="\n"}
$2==idx{
    printf " %.2f\n%s",n/(n+$1),$0}
END{
    print""}' file
    
por 17.02.2015 / 18:50