열의 연속된 두 행을 모두 추가하고 이전 행으로 나누어 인쇄합니다.

열의 연속된 두 행을 모두 추가하고 이전 행으로 나누어 인쇄합니다.

거의 2000줄의 입력 파일이 있습니다. 계산을 해서 세 번째 열에 인쇄해야 해요.

입력 파일과 관련된 작업의 예:

n ID1_1 n/(n+k)
k ID1_2

입력 파일:

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

예상 출력:

 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

간단한 해결책이 있나요? 감사해요.

답변1

표시된 입력을 고려하면 다음이 작동합니다.

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

나를 위해 그것은 인쇄됩니다 ...

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

... dc정밀도를 3으로 설정했는데 정밀도가 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

출력 정밀도 외에도 귀하의 정밀도와도 다릅니다.예상 출력세 번째 줄에 - 하지만 이것은 질문의 오타 때문인 것 같습니다.

어쨌든, 이를 이해하려면 먼저 출력을 두 가지 형식으로 구문 분석해야 한다는 점을 고려해야 합니다. dc다음 의 ewline 없이 a를 인쇄 dc하거나 숫자 또는 1과 함께 a를 인쇄합니다. 매크로 로 실행하는 것 외에는 문자열로 아무 것도 할 수 없습니다 . 하지만 수치를 보면 매우 능력이 뛰어납니다.P[string]\np[string]xdc

따라서 현재 줄이 마지막 줄이면 sed먼저 $!N현재 줄에 추가 줄을 추가합니다. 두 번째 줄에서는 stdout에 문자열을 삽입합니다. 이는 정밀도를 3으로 설정하는 명령입니다.!$i3kdc

그런 다음 교체하려고합니다.

s|\(\(.*  *\).*_1\)\n\(\(.*  *\).*_2\)$|[\1 ]P\2d\4+/p[\3]pc|

_1이는 패턴 공간에 현재 적어도 하나의 공백이 포함되어 있는 경우에만 성공합니다. 어떤 지점에서는 바로 뒤에 줄눈 문자가 오고 \n, 어떤 지점에서는 그 뒤에 적어도 하나의 공백이 오고, 어떤 지점에서는 _2바로 뒤에 $끝이 옵니다. 패턴 공간의

이는 위의 대체가 다음과 같은 라인 쌍에만 영향을 미친다는 것을 의미합니다.

...ID_1
...ID_2

...다른 사람도 아니고요. 영향을 미치면 내용을 사용 가능한 dc스크립트로 변환합니다. 다음으로 t교체가 성공했는지 확인하고 성공하면 스크립트에서 분기하여 교체 결과를 인쇄하고 더 이상 sed명령을 실행하지 않습니다. 표준 출력을 표준 입력으로 dc사용하므로 예를 들어 처음 두 줄을 다음과 같이 변경합니다.sedsed

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

... dc그런 다음 해당 입력을 다음과 같이 처리합니다.

  • [ 10 ID_1 ]- 대괄호 사이의 문자열을 스택의 맨 위로 밀어 넣습니다.(이렇게 하면 이미 스택에 있는 모든 항목이 하나 아래로 푸시됩니다.)
  • PP-뒤에 줄줄이 없이 스택의 상단을 인쇄 \n하고 팝합니다.(이렇게 하면 아래 스택의 모든 값이 1씩 증가합니다.)
  • 10- 숫자 10을 스택 맨 위로 밀어 넣습니다.
  • dd- 스택의 상단을 복사합니다.
  • 20- 숫자 20을 스택 맨 위로 밀어 넣습니다.
  • +- 스택 상단에 2cd를 추가하고 스택 상단에 2cd를 추가합니다.(두 개를 동시에 터뜨림)그리고 결과를 스택의 맨 위로 밀어넣습니다.
  • /- 스택의 맨 위에서 2cd를 나눕니다.(이제 우리는 d복사합니다 10)스택의 꼭대기에서(우리의 10 20 +결과) (두 개를 동시에 터뜨림)그리고 결과를 스택의 맨 위로 밀어넣습니다.
  • pp- 스택의 상단을 인쇄합니다.(튀기지 않고)그 뒤에는 줄줄이 표시됩니다 \n.
  • [ 20 ID_2]- 문자열을 스택의 맨 위로 밀어 넣습니다.
  • pp- 스택의 상단을 인쇄합니다.(다시 한번 말하지만 터뜨리지 마세요)그 뒤에는 \nEwline 이 따른다
  • cc- 스택을 이해하라

따라서 dc다음을 인쇄하세요.

 10 ID1_1 .333
 20 ID1_2

그러나 sed이미 설명한 것처럼 성공적인 일치가 없고 패턴 공간이 변경되면 처리할 다른 라인이 남습니다. 이 경우 sed명령을 추가하여 와 사이에 첫 번째 시퀀스를 삽입할 수도 있습니다. 그런 다음 패턴 공간에서 처음으로 나타나는 ewline까지 패턴 공간을 인쇄한 다음 나머지 부분부터 시작하기 전에 동일한 내용을 삭제합니다. 따라서 전체적으로 한 줄의 예측이 수행되고 작업 스크립트는 항상 인쇄됩니다.[ ID_0-9]*[]pcP\nDseddcdc

이는 전체 파일이 스트림에서 처리됨을 의미합니다. ` dcsed` 모두 처리되는 대로 출력을 제공하기 때문입니다. 이렇게 하면 입력이 질문의 예와 유사하다면 동일한 방식으로 200만 개의 행을 쉽게 처리하거나 실시간으로 로그 파일을 처리할 수 있습니다.

답변2

파이썬으로.

#!/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+\3_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 = "")

위의 스크립트를 다른 이름으로 저장 script.py한 후 실행해 보세요.

python3 script.py inputfile

예:

$ 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

답변3

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

아이디어는 단순히 현재 행이 짝수인지 확인하는 것입니다. i) 그렇다면 last필요한 계산과 함께 이전 행( )을 인쇄하고, ii) 그렇지 않으면 현재 행을 last첫 번째 필드 로 저장합니다 n.

다음을 사용하여 인쇄되는 소수 자릿수를 제어할 수 있습니다 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

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

답변4

OP 편집 후(추가된 용어 참조):

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

관련 정보