조건부로 행을 숫자로 바꾸기

조건부로 행을 숫자로 바꾸기

거의 1,100만 개의 작은 파일이 포함된 디렉토리가 있습니다. 다음과 같습니다.

wa_filtering_DP15_good_pops_snps_file_1
wa_filtering_DP15_good_pops_snps_file_2
.
.
.
wa_filtering_DP15_good_pops_snps_file_11232111

각 파일에는 아래와 같이 2개의 행과 315개의 열만 있습니다.

1   0   0   0   0   0   0   0   0   0   1   2   1   
0   0   0   0   0   0   0   0   0   0   0   0   0

각 파일을 반복하고 두 행의 각 열에 0 값이 있으면 이를 9로 바꾸고 다음과 같은 결과를 얻습니다.

1   9   9   9   9   9   9   9   9   9   1   2   1   
0   9   9   9   9   9   9   9   9   9   0   0   0

누군가 내가 이 작업을 수행하는 방법을 알아내도록 도와줄 수 있습니까? 감사해요

답변1

awk해결책 은 다음과 같습니다 .

awk '{split($0,ary1,/[ ]+/); getline x; split(x,ary2,/[ ]+/); 
    for (i in ary1)if (!(ary1[i]+ary2[i])){ary1[i]=ary2[i]=9}} 
END{for (r=1;r<=NF;r++) printf ("%d ", ary1[r]); printf"\n"; 
    for (z=1;z<=NF;z++) printf ("%d ", ary2[z]); printf"\n"}' infile

설명하다:

  • split($0,ary1,/[ ]+/);ary1: 첫 번째 줄을 읽고 배열 사이에 하나 이상의 공백 구분 기호를 사용하여 배열로 분할합니다 .

  • getline x; split(x,ary2,/[ ]+/);: 두 번째 줄을 변수로 읽어서 x배열로 나눕니다 ary2.

  • for (i in ary1)if (!(ary1[i]+ary2[i])){ary1[i]=ary2[i]=9}}: 두 필드 값의 합이 다음과 같은 경우 배열의 ary1각 인덱스를 반복합니다.i(참 조건에서 트리거됩니다 !(0).) 그런 다음 두 필드의 값을 다음으로 설정합니다.if(1)9

  • for (r=1;r<=NF;r++) printf ("%d ", ary1[r]); printf"\n";ary1: 이제 각 배열의 최종 값과 다음 행을 인쇄합니다 ary2.


약 1,100만 개의 파일 전체에 적용하려면 FILENAME.out현재 읽을 입력 파일 이름을 나타내는 FILENAME 형식 으로 변경 사항을 저장하기만 하면 됩니다 awk.

awk '{split($0,ary1,/[ ]+/); getline x; split(x,ary2,/[ ]+/); 
    for (i in ary1)if (!(ary1[i]+ary2[i])){ary1[i]=ary2[i]=9}} 
END{for (r=1;r<=NF;r++) printf ("%d ", ary1[r])>FILENAME".out"; printf"\n">FILENAME".out"; 
    for (z=1;z<=NF;z++) printf ("%d ", ary2[z])>FILENAME".out"
}' wa_filtering_DP15_good_pops_snps_file_{1..11232111}

답변2

재미삼아 이건 Ruby

ruby -e '
    data = File.readlines(ARGV.shift)
               .map {|line| line.split.map(&:to_i)}
               .transpose
               .map {|(a,b)| (a==0 && b==0) ? [9,9] : [a,b]}
               .transpose
               .each {|row| puts row.join(" ")}
' file
1 9 9 9 9 9 9 9 9 9 1 2 1
0 9 9 9 9 9 9 9 9 9 0 0 0

모든 파일을 바꾸려면:

ruby -e '
    require "tempfile"
    require "pathname"
    Pathname.new("/path/to/your/files/").each_child do |pathname|
        next unless pathname.file?
        temp = Tempfile.new(pathname.basename.to_s)
        filename = pathname.to_s
        File.readlines(filename)
            .map {|line| line.split.map(&:to_i)}
            .transpose
            .map {|(a,b)| (a==0 && b==0) ? [9,9] : [a,b]}
            .transpose
            .each {|row| temp.puts row.join(" ")}
        temp.close
        File.link filename, filename+".bak"
        File.rename temp.path, filename
    end
'

답변3

이는 순수한 awk 솔루션에 비해 수백만 개의 파일에 대해 속도가 느릴 수 있는 대안입니다.

비슷한 접근 방식을 사용하여 행을 열로 바꿀 수 있습니다.

$ cat file1
1   0   0   0   0   0   0   0   0   0   1   2   1   
0   0   0   0   0   0   0   0   0   0   0   0   0

$ paste -d'-' <(head -n1 file1 |tr -s ' ' '\n') <(tail -n1 file1 |tr -s ' ' '\n')
1-0
0-0
0-0
0-0
0-0
0-0
0-0
0-0
0-0
0-0
1-0
2-0
1-0

0-0그런 다음 모든 항목을 간단한 sed로 바꾸고 9-9출력을 임시 변수에 저장할 수 있습니다.

$ f1=$(sed 's/0-0/9-9/g' <(paste -d'-' <(head -n1 file1|tr -s ' ' '\n') <(tail -n1 file1 |tr -s ' ' '\n')))
$ echo "$f1"
1-0
9-9
9-9
9-9
9-9
9-9
9-9
9-9
9-9
9-9
1-0
2-0
1-0

이제 열에서 행으로 복원할 수 있습니다. 예를 들면 다음과 같습니다.

$ awk -F'-' 'NR==FNR{printf "%s ",$1;p=1;next}p{printf "\n";p=0}{printf "%s ",$2}END{printf "\n"}' <(echo "$f1") <(echo "$f1")
1 9 9 9 9 9 9 9 9 9 1 2 1  
0 9 9 9 9 9 9 9 9 9 0 0 0  

>file1마지막 awk 명령 끝에 추가하여 file1새 내용으로 덮어쓸 수도 있습니다 .

남은 유일한 것은 모든 파일을 반복하는 것입니다. 이는 bash 루프를 통해 수행할 수 있습니다.

for f in ./wa_filtering_DP15_good_pops_snps_file_*;do
  f1=$(sed 's/0-0/9-9/g' <(paste -d'-' <(head -n1 "$f"|tr -s ' ' '\n') <(tail -n1 "$f" |tr -s ' ' '\n')))
  awk -F'-' 'NR==FNR{printf "%s ",$1;p=1;next}p{printf "\n";p=0}{printf "%s ",$2}END{printf "\n"}' <(echo "$f1") <(echo "$f1") #>"$f" #uncomment >"$f" to overwrite the files...
done

답변4

첫 번째 변형:

단일 파일의 경우:

datamash -W transpose < input.txt | sed 's/0\t0/9\t9/' | datamash transpose

많은 파일에 대해 루프에서 동일한 작업을 수행합니다.

for i in *; do datamash -W transpose < "$i" |
sed 's/0\t0/9\t9/' |
datamash transpose > "new_$i"; done

이 루프는 "new_"라는 접두사가 붙은 각 파일에 대해 변경된 새 파일을 생성합니다. 그런 다음 이전 파일을 모두 삭제하고 파일 이름에서 접두사 "new_"를 제거할 수 있습니다.

두 번째 변형:

이는 단일 파일에 대한 솔루션입니다. 여러 파일의 경우 이전 변형에 표시된 대로 루프를 사용합니다.

tr '\n' '\t' < input.txt |
awk '{
    num = NF / 2;
    for(up = 1; up <= NF; up++) {
        if(up <= num) {
            low = num + up;
            if(!$up && !$low) {
                $up = 9;    
                $low = 9;
            }
        }

        printf "%s\t", $up;

        if(up % num == 0) 
            print "";
    }
}'

설명하다

  1. tr '\n' '\t' < input.txt- 두 개의 전선을 함께 연결하십시오.
  2. awk
    • 첫 번째 행의 요소와 두 번째 행의 인접 요소를 모두 확인합니다. 예를 들면 다음과 같습니다.1그리고316,2그리고317화,그리고318, 곧.
    • 두 요소가 모두 있는 경우0, 그러면 다음과 같이 변경됩니다.9.
    • 필드를 순서대로 인쇄 -1, 2, 3, 4 ... 628, 629, 630.
    • 요소 수가 행에 있는 요소 수의 배수가 될 때마다 새 행이 추가됩니다.

입력하다

1   0   0   0   0   0   0   0   0   0   1   2   1
0   0   0   0   0   0   0   0   0   0   0   0   0

산출

1   9   9   9   9   9   9   9   9   9   1   2   1
0   9   9   9   9   9   9   9   9   9   0   0   0

관련 정보