Phần đầu ra của mỗi dòng thành một tệp riêng biệt


14

Tôi có một tập tin như thế này:

a   AGTACTTCCAGGAACGGTGCACTCTCC
b   ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT
c   ATATTAAATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCATCCACTCCACAC
d   ATCAGTTTAATATCTGATACGTCCTCTATCCGAGGACAATATATTAAATGGA
e   TTTGGCTAAGATCAAGTGTAGTATCTGTTCTTATAAGTTTAATATCTGATATGTCCTCTATCTGA

Tôi muốn tạo tập tin a.seqcó chứa trình tự AGTACTTCCAGGAACGGTGCACTCTCC. Tương tự b.seqchứa ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT. Nói tóm lại, Cột 1 nên được sử dụng làm tên tệp đầu ra có phần mở rộng .seqvà sau đó nó phải có chuỗi cột2 tương ứng trong đó. Tôi có thể làm điều này bằng cách viết một tập lệnh perl nhưng bất cứ điều gì trên dòng lệnh sẽ hữu ích. Hy vọng được nghe sớm.

Câu trả lời:


16

Phản hồi nhanh của tôi sẽ có awknhưng nếu bạn đang xử lý nhiều dòng thì tôi đang nói về hàng triệu người, bạn sẽ thấy lợi ích thực sự từ việc chuyển sang ngôn ngữ lập trình "thực".

Với ý nghĩ đó (và awkđã được coi là một câu trả lời) Tôi đã viết một vài triển khai bằng các ngôn ngữ khác nhau và đánh giá chúng trên cùng một bộ dữ liệu 10.000 dòng trên SSD PCI-E.

me* (C)                0m1.734s
me (C++)               0m1.991s
me (Python/Pypy)       0m2.390s
me (perl)              0m3.024s
Thor+Glenn (sed|sh)    0m3.353s
me (python)            0m3.359s
jasonwryan+Thor (awk)  0m3.779s
rush (while read)      0m6.011s
Thor (sed)             1m30.947s
me (parallel)          4m9.429s

Nhìn thoáng qua, C trông có vẻ tốt nhất nhưng nó là một con lợn để chạy nhanh như vậy. Pypy và C ++ dễ dàng hơn nhiều để viết và thực hiện đủ tốt trừ khi bạn nói về nhiều tỷ dòng. Nếu đó là trường hợp, một bản nâng cấp để thực hiện tất cả trong RAM hoặc trên SSD có thể là một khoản đầu tư tốt hơn so với cải tiến mã.

Rõ ràng trong khoảng thời gian tôi trải qua, bạn có thể đã xử lý vài trăm triệu bản ghi trong tùy chọn chậm nhất . Nếu bạn chỉ có thể viết awkhoặc vòng lặp Bash, hãy làm điều đó và tiếp tục với cuộc sống. Tôi rõ ràng đã có quá nhiều thời gian rảnh rỗi ngày hôm nay.

Tôi cũng đã thử nghiệm một số tùy chọn đa luồng (trong C ++ và Python và hybrid với GNU parallel) nhưng tổng phí của các luồng hoàn toàn vượt trội hơn bất kỳ lợi ích nào cho một thao tác đơn giản như vậy (tách chuỗi, viết).

Perl

awk( gawkở đây) thực sự sẽ là cổng gọi đầu tiên của tôi để kiểm tra dữ liệu như thế này nhưng bạn có thể thực hiện những điều tương tự trong Perl. Cú pháp tương tự nhưng với một tay cầm viết tốt hơn một chút.

perl -ane 'open(my $fh, ">", $F[0].".seq"); print $fh $F[1]; close $fh;' infile

Con trăn

Tôi thích Python. Đó là ngôn ngữ công việc hàng ngày của tôi và nó chỉ là một ngôn ngữ tốt đẹp, vững chắc và cực kỳ dễ đọc. Ngay cả một người mới bắt đầu cũng có thể đoán những gì đang xảy ra ở đây.

with open("infile", "r") as f:
    for line in f:
        id, chunk = line.split()
        with open(id + ".seq", "w") as fw:
            fw.write(chunk)

Bạn phải nhớ rằng pythonnhị phân phân phối của bạn không phải là triển khai Python duy nhất ngoài đó. Khi tôi chạy thử nghiệm tương tự này thông qua Pypy, nó nhanh hơn C mà không cần tối ưu hóa logic nữa. Hãy ghi nhớ điều đó trước khi viết Python thành "ngôn ngữ chậm".

C

Tôi đã bắt đầu ví dụ này để xem những gì chúng ta thực sự có thể khiến CPU của mình làm được nhưng thật lòng mà nói, C là một cơn ác mộng khi viết mã nếu bạn không chạm vào nó trong một thời gian dài. Điều này có thêm nhược điểm là bị giới hạn ở các dòng 100 char mặc dù rất đơn giản để mở rộng điều đó, tôi chỉ không cần nó.

Phiên bản gốc của tôi chậm hơn C ++ và pypy nhưng sau khi viết blog về nó, tôi đã nhận được sự giúp đỡ từ Julian Klode . Phiên bản này bây giờ là nhanh nhất vì bộ đệm IO được điều chỉnh của nó. Nó cũng là một nhiều hơn và tham gia nhiều hơn so với bất cứ điều gì khác.

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>

#define BUFLEN (8 * 1024)

int main(void) {
    FILE *fp;
    FILE *fpout;

    char line[100];
    char *id;
    char *token;
    char *buf = malloc(BUFLEN);

    fp = fopen("infile", "r");

    setvbuf ( fp , buf , _IOLBF, BUFLEN );
    while (fgets(line, 100, fp) != NULL) {
        id = strtok(line, "\t");
        token = strtok(NULL, "\t");

        char *fnout = malloc(strlen(id)+5);
        fnout = strcat(fnout, id);
        fnout = strcat(fnout, ".seq");

        fpout = fopen(fnout, "w");
        setvbuf ( fpout , NULL , _IONBF , 0 );
        fprintf(fpout, "%s", token);
        fclose(fpout);
    }
    fclose(fp);

    return 0;
}

C ++

Thực hiện tốt và là nhiều dễ dàng hơn để viết hơn thực C. Bạn có tất cả các loại điều mà nắm tay bạn (đặc biệt là khi nói đến các chuỗi và đầu vào). Tất cả điều đó có nghĩa là bạn thực sự có thể đơn giản hóa logic xuống. strtoktrong C là một con heo vì nó xử lý toàn bộ chuỗi và sau đó chúng ta cần thực hiện tất cả việc phân bổ bộ nhớ mệt mỏi đó. Điều này chỉ chạy dọc theo dòng cho đến khi nó chạm vào tab và chúng tôi kéo các phân đoạn ra khi chúng tôi cần chúng.

#include <fstream>
#include <string>
using namespace std;

int main(void) {
    ifstream in("infile");
    ofstream out;
    string line;

    while(getline(in, line)) {
        string::size_type tab = line.find('\t', 0);
        string filename = line.substr(0, tab) + ".seq";
        out.open(filename.c_str());
        out << line.substr(tab + 1);
        out.close();
    }

    in.close();
}

Song song GNU

(Không phải phiên bản moreutils). Đó là một cú pháp ngắn gọn súc tích nhưng OMGSLOW. Tôi có thể đang sử dụng nó sai.

parallel --colsep '\t' echo {2} \> {1}.seq <infile

Kiểm tra máy phát điện khai thác

Đây là trình tạo dữ liệu của tôi cho 100000 dòng [ATGC] * 64. Nó không nhanh và cải tiến rất đáng hoan nghênh.

cat /dev/urandom | tr -dc 'ATGC' | fold -w 64 | awk 'NR>100000{exit}{printf NR"\t"$0"\n"}' > infile

2
Tôi nên chỉ ra rằng việc liệt kê tất cả các tùy chọn cho hiệu suất của bạn có thể gây lãng phí như việc chỉ đi với điều đầu tiên nảy ra trong đầu. awkvẫn là một câu trả lời tốt cho bất cứ điều gì ít hơn hàng chục triệu. Ngay cả khi bạn [tuyến tính] quy mô này lên tới một tỷ dòng, C chỉ tiết kiệm cho bạn 1,5 giờ so với Perl và 3,6 giờ so với awk.
Oli

Bây giờ tôi C ++ phiên bản là có nên nhanh hơn nhiều, có lẽ tôi sẽ xem xét C ++ cho việc xử lý văn bản đơn giản hơn của các bộ dữ liệu khổng lồ. Nó nhanh gần gấp đôi và chênh lệch nhiều giờ khi bạn đạt tới hàng tỷ dòng.
Oli



1
Tôi nghĩ rằng tốc độ tạo ra khai thác thử nghiệm của bạn bị ràng buộc bởi trình tạo số ngẫu nhiên. Bạn có thể làm cho nó nhanh hơn bằng cách sử dụng mọi số mà nó đưa ra hoặc tạo phân phối đồng nhất, ví dụ : paste <(yes A) <(yes T) <(yes G) <(yes C) | head -n1600000 | tr '\t' '\n' | shuf | tr -d \\n | fold -w64 | cat -n > infile.
Thor

13

Thực hiện vỏ tinh khiết:

while read -r filename content ; do
    printf '%s\n' "$content" >> "${filename}.seq"
done < /source/file

12

Sử dụng awk:

awk '{printf "%s\n", $2>$1".seq"}' file

Từ đề cử file, in trường thứ hai trong mỗi bản ghi ( $2) sang tệp được đặt tên theo trường thứ nhất ( $1) có .seqgắn tên.

Như Thor chỉ ra trong các bình luận, đối với một tập dữ liệu lớn, bạn có thể sử dụng hết các mô tả tệp, vì vậy sẽ rất khôn ngoan khi đóng từng tệp sau khi viết :

awk '{printf "%s\n", $2>$1".seq"; close($1".seq")}' file

Hi Điều này hoạt động Cảm ơn rất nhiều .. Bạn có thể giải thích mã một chút?
dùng3138373

@ user3138373 Hy vọng điều đó sẽ giúp ...
jasonwryan

Nó giúp .. Cảm ơn Tại sao không in công việc thay vì printf ??
dùng3138373

3
Nếu có nhiều dòng, tất cả các mô tả tệp có sẵn sẽ được sử dụng, vì vậy bạn có thể nên thêm một close($1".seq").
Thor

1
@Thor, đúng. Một số awktriển khai như GNU biết cách khắc phục điều đó.
Stéphane Chazelas

3

Đây là một cách bạn có thể làm với GNU sed:

<infile sed -r 's:(\w+)\s+(\w+):echo \2 > \1.seq:e; d'

Hoặc hiệu quả hơn, theo đề xuất của glenn jackman :

<infile sed -r 's:(\w+)\s+(\w+):echo \2 > \1.seq:' | sh

1
Trong khi đó là mát mẻ, nó khá không hiệu quả, phải sinh ra một lệnh bên ngoài cho mỗi dòng. Sẽ tốt hơn một chút khi có đầu ra sed tất cả các lệnh thô và chuyển đầu ra thành "sh"
glenn jackman

1
@glennjackman: Đây chỉ là một cách thay thế thú vị để làm điều đó. Nếu đầu vào lớn, awkcó lẽ là công cụ hiệu quả nhất để sử dụng. Tất nhiên bạn đúng về việc không sinh sản shcho mỗi dòng, tôi đã thêm tùy chọn ống thay thế.
Thor
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.