Tách tập tin cho mỗi 10000 số (không phải dòng)


8

Tôi có một tập tin trông như sau:

chr19   61336212        +       0       0       CG      CGT    
chr19   61336213        -       0       0       CG      CGG    
chr19   61336218        +       0       0       CG      CGG    
chr19   61336219        -       0       0       CG      CGC    
chr19   61336268        +       0       0       CG      CGG    
chr19   61336269        -       0       0       CG      CGA    
chr19   61336402        +       0       0       CG      CGG    
chr19   61336403        -       0       0       CG      CGT    

Tôi muốn tách tệp này cho mỗi 10000 khoảng thời gian của trường thứ 2 (KHÔNG phải dòng, nhưng khoảng số). Vì vậy, đối với tệp này, tôi muốn tách từ dòng đầu tiên (dòng có 61336212) thành dòng có hoặc lên đến 61346211 (61336212 + 9999), sau đó từ 61346212 đến 61356211, v.v. Như bạn có thể thấy các số trong trường / cột thứ 2 không được 'điền'.

Có cách nào để làm việc này không?


Trong ví dụ của bạn, nếu số tiếp theo sau 61346211 là 61346220, bạn có thể mong đợi tệp đầu ra thứ hai sẽ bao gồm phạm vi bắt đầu từ 61346212 hoặc 61346220 không?
Joe Lee-Moyet

phạm vi thứ hai sẽ bao gồm từ 61346212.
agathusia

Câu trả lời:


13
awk 'NR==1 {n=$2}
     {
       file = sprintf("file.%.4d", ($2-n)/10000)
       if (file != last_file) {
         close(last_file)
         last_file = file
       }
       print > file
     }'

Sẽ viết thư cho file.0000, file.0001... (số lượng là int(($2-n)/10000)nơi n$2cho dòng đầu tiên).

Lưu ý rằng chúng tôi đóng các tệp một khi chúng tôi đã dừng ghi vào chúng vì nếu không, bạn sẽ đạt đến giới hạn về số lượng tệp mở đồng thời sau vài trăm tệp (GNU awkcó thể hoạt động xung quanh giới hạn đó, nhưng sau đó hiệu suất sẽ giảm nhanh chóng).

Chúng tôi cho rằng những con số này luôn tăng lên.


3
bạn có thể giải thích những gì đang xảy ra?
Fiximan 17/8/2015

Bạn có thể giải thích những gì đang xảy ra ở đây? Cũng giống như nhận xét bên dưới là có độ dài tên tệp đầu ra không đổi, chẳng hạn như tệp.0000, tệp.0001 thay vì tệp.1 tệp.2 .. tệp.100 .. tệp..2320?
agathusia

1
@Fiximan, tôi không cảm thấy mình có thể giải thích nhiều hơn mà không diễn giải mã. Phần nào bạn thấy không rõ ràng?
Stéphane Chazelas 17/8/2015

Vâng, tôi hiểu thế hệ tên tệp file = ..., nhưng làm thế nào để lặp lại hoạt động? Không có phần nào nói n = n + 10000cũng không phải là một lower_boundary <= $2 < upper_boundaryphần. Nói chung, toàn bộ if (file != last_file) { close(last_file) ; last_file = file }là ra khỏi giải đấu của tôi
Fiximan

1
@Fixman, vâng, đó là những gì tôi gọi là paraphrasing if (file != last_file): nếu tệp hiện tại không giống với tệp trước đó, hãy đóng tệp trước đó (vì vậy chỉ có một tệp được mở tại một thời điểm (chúng tôi không cần giữ chúng tất cả mở như các giải pháp khác làm))
Stéphane Chazelas

7

Hack phiên bản một lót. Có lẽ phù hợp với Code Golf hơn diễn đàn này. Điều này tạo ra split1, split2, split3, v.v., dưới dạng tên tệp.

awk '{if($2>b+9999){a++;b=$2}print >"split" a}' file.txt

Để có các tệp đầu ra có tên split001, split002, split003, liên quan đến phần bổ sung này sprintf:

awk '{if($2>b+9999){a++;b=$2}print >sprintf("split%03d",a)}' file.txt

Để tránh sự cố làm chậm gawk được xác định bởi @ Stéphane Chazelas, hãy sử dụng perl:

perl -ne '(undef,$a)=split(/\s+/,$_);if($a>$b+9999){$c++;$b=$a}open(D,sprintf(">>ysplit%03d",$c));print D' <file.txt

1
Đối với phương pháp này, có cách nào để tên tệp được nhiều hơn .. liên tiếp không? Kết quả đầu ra này split1 .... split100 ... split1000, nhưng một cái gì đó nữa trong dòng split00001 ... split 00100 .. split01000 ..?
agathusia

1
Chắc chắn, thêm sprintfphép thuật bây giờ được thêm vào.
steve

Lưu ý rằng nếu đầu vào có 0, 9999, 12000, 19999, 21000, 22000, đặt 0, 9999 trong tệp1, nhưng 12000, 19999, 21000 trong tệp2 có vẻ kỳ quặc với các yêu cầu.
Stéphane Chazelas 17/8/2015

1
Lưu ý rằng điều này sẽ đạt đến giới hạn về số lượng tệp được mở đồng thời sau vài trăm tệp (GNU awk có thể hoạt động xung quanh giới hạn đó, nhưng sau đó hiệu suất giảm xuống nhanh chóng).
Stéphane Chazelas 17/8/2015

1
Vâng. Tôi chỉ nhận thấy vấn đề bạn đề cập.
agathusia

4
#!/bin/bash
first=$( head -n1 file | awk -F" +" '{print $2}' )
last=$( tail -n1 file | awk -F" +" '{print $2}' )
for (( i=$first ; i<=$last ; i=i+10000 )) ; do
   awk -v start=$i -v end=$(($i+10000)) 'BEGIN { FS == " +" } { if ( $2 >= start && $2 < end ) print $0 }' file \
   >> interval_"$i"_to_"$(( $i+10000 ))"
done

Kiểm tra với khoảng thời gian được đặt thành 100:

more inter*
::::::::::::::
interval_61336212_to_61346212
::::::::::::::
chr19   61336212        +       0       0       CG      CGT    
chr19   61336213        -       0       0       CG      CGG    
chr19   61336218        +       0       0       CG      CGG    
chr19   61336219        -       0       0       CG      CGC    
chr19   61336268        +       0       0       CG      CGG    
chr19   61336269        -       0       0       CG      CGA    
::::::::::::::
interval_61336312_to_61346312
::::::::::::::
chr19   61336402        +       0       0       CG      CGG    
chr19   61336403        -       0       0       CG      CGT  

Lưu ý: sẽ tạo ra các tệp trống trong khoảng thời gian trống; để xóa các tệp trống, thêm:

for file in interval* ; do
  if [ ! -s "$file" ] ; then
    rm "$file"
  fi
done

Sẽ chạy qua tập tin cho từng bước trong forvòng lặp, do đó không hiệu quả nhất.


3

Nếu bạn có nghĩa là chỉ tính toán không đếm dòng:

awk 'NR==1 || n+10000<$2{n=$2; portion++}{print > FILENAME "." portion}' file

Lưu ý rằng nếu đầu vào có 0, 9999, 12000, 19999, 21000, 22000, đặt 0, 9999 trong tệp1, nhưng 12000, 19999, 21000 trong tệp2 có vẻ kỳ quặc với các yêu cầu.
Stéphane Chazelas 17/8/2015

Lưu ý rằng điều này sẽ đạt đến giới hạn về số lượng tệp được mở đồng thời sau vài trăm tệp (GNU awk có thể hoạt động xung quanh giới hạn đó, nhưng sau đó hiệu suất giảm xuống nhanh chóng).
Stéphane Chazelas 17/8/2015

@ StéphaneChazelas Tôi không chắc rằng bạn hiểu rõ về bạn. Nếu bạn muốn 21000 trong tệp thứ 3, hãy sử dụng 9999 thay vì 10000.
Costas

Theo hiểu biết của tôi về câu hỏi, OP muốn các dòng có từ 0 đến 9999 trong tệp đầu tiên, 10000 đến 19999 trong tệp thứ hai.
Stéphane Chazelas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.