각 줄의 일부를 별도의 파일로 출력


14

다음과 같은 파일이 있습니다.

a   AGTACTTCCAGGAACGGTGCACTCTCC
b   ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT
c   ATATTAAATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCATCCACTCCACAC
d   ATCAGTTTAATATCTGATACGTCCTCTATCCGAGGACAATATATTAAATGGA
e   TTTGGCTAAGATCAAGTGTAGTATCTGTTCTTATAAGTTTAATATCTGATATGTCCTCTATCTGA

a.seqsequence를 포함 하는 파일을 만들고 싶습니다 AGTACTTCCAGGAACGGTGCACTCTCC. 마찬가지로을 b.seq포함 ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT합니다. 즉, Column1은 확장명을 가진 출력 파일 이름으로 사용 된 .seq다음 해당 column2 시퀀스가 ​​있어야합니다. 펄 스크립트를 작성 하여이 작업을 수행 할 수 있지만 명령 줄에있는 것이 도움이 될 것입니다. 곧들을 수 있기를 바랍니다.

답변:


16

내 대답은 awk그랬지만 수백만 줄을 처리하고 있다면 "실제"프로그래밍 언어로 전환하면 큰 이점을 얻게 될 것입니다.

이를 염두에두고 (그리고 awk이미 답변으로 채택 된) 다른 언어로 몇 가지 구현을 작성하고 PCI-E SSD의 동일한 10,000 행 데이터 세트에서 벤치마킹했습니다.

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

한눈에 C가 가장 잘 보이지만 빨리 달리는 것은 돼지였습니다. Pypy와 C ++는 수십억 줄에 대해 이야기하지 않는 한 훨씬 쉽게 작성하고 수행 할 수 있습니다 . 이 경우 RAM 또는 SSD에서이 모든 작업을 수행하는 것이 코드 개선보다 더 나은 투자 일 수 있습니다.

분명히 내가 이걸 겪었던 시간에 당신은 아마도 가장 느린 옵션으로 몇억 개의 레코드 처리했을 것이다 . 쓰기 awk또는 Bash 루프 만 쓸 수 있다면 그렇게하고 인생을 시작하십시오. 오늘은 여가 시간이 너무 많았습니다.

또한 일부 다중 스레드 옵션 (C ++ 및 Python 및 GNU와 하이브리드)을 테스트 parallel했지만 스레드의 오버 헤드는 이러한 간단한 작업 (문자열 분할, 쓰기)에 대한 이점보다 완전히 중요합니다.

awk( gawkhere) 솔직히 이와 같은 데이터 테스트를위한 첫 번째 요청 포트가 될 것이지만 Perl에서도 상당히 비슷한 일을 할 수 있습니다. 비슷한 구문이지만 약간 더 나은 쓰기 핸들이 있습니다.

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

파이썬

나는 파이썬을 좋아한다 . 그것은 나의 하루 직업 언어이고 단지 훌륭하고 견고하며 믿을 수 없을 정도로 읽기 쉬운 언어입니다. 초보자조차도 여기서 무슨 일이 일어나고 있는지 추측 할 수 있습니다.

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

배포판의 python바이너리가 파이썬의 유일한 구현은 아니라는 것을 기억해야합니다 . Pypy를 통해이 동일한 테스트를 실행했을 때 더 이상의 논리 최적화가 없으면 C보다 빠릅니다 . 파이썬을 "느린 언어"로 작성하기 전에 명심하십시오.

이 예제를 시작하여 CPU가 실제로 무엇을 할 수 있는지 알았지 만 솔직히 C는 오랫동안 만지지 않으면 코딩하는 악몽입니다. 이것은 100 문자 줄로 제한되는 단점이 있지만 그것을 확장하는 것은 매우 간단하지만 필요하지 않습니다.

내 원래 버전은 C ++ 및 pypy보다 느리지 만 블로그를 게시 한 후 Julian Klode의 도움을 받았습니다 . 이 버전은 조정 된 IO 버퍼로 인해 이제 가장 빠릅니다. 또한 다른 것보다 훨씬 길고 더 복잡합니다.

#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 ++

실제 C보다 성능이 뛰어나고 쓰기 가 훨씬 쉽습니다. 손을 잡는 모든 종류의 물건이 있습니다 (특히 문자열 및 입력과 관련하여). 이로 인해 실제로 로직을 단순화 할 수 있습니다. strtokC에서 전체 문자열을 처리하기 때문에 번거로운 메모리 할당을 모두 수행해야하므로 돼지입니다. 이것은 탭에 닿을 때까지 선을 따라 움직이며 필요에 따라 세그먼트를 당깁니다.

#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();
}

GNU 병렬

(moreutils 버전이 아님). 간결한 구문이지만 OMGSLOW입니다. 잘못 사용했을 수 있습니다.

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

테스트 하니스 생성기

다음은 [ATGC] * 64의 100000 줄에 대한 데이터 생성기입니다. 빠르지 않으며 개선을 매우 환영합니다.

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

2
성능에 대한 모든 옵션을 열거 하는 것이 가장 먼저 떠오르는 것만 큼 낭비가 될 수 있음을 지적해야합니다. awk여전히 수 천만 미만의 사람들에게는 훌륭한 답변입니다. 당신이 이것을 10 억 줄까지 선형 적으로 확장하더라도 C는 Perl보다 1.5 시간, awk보다 3.6 시간 만 절약합니다.
Oli

거기에 지금 내 C ++ 버전입니다 그래서 어쩌면 내가 큰 데이터 세트의 더 간단한 텍스트 처리를위한 C ++를 생각 하는데요, 훨씬 더 빨리. 거의 두 배 빠르며 수십억 줄에 도달하면 많은 시간 차이가 있습니다.
Oli



1
테스트 하니스의 생성 속도는 난수 생성기에 의해 결정됩니다. 균질 분포를 제공하거나 생성하는 모든 숫자를 사용하여 더 빠르게 만들 수 있습니다 (예 :) 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

순수한 쉘 구현 :

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

12

사용 awk:

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

지명 된 file에서 각 레코드의 두 $2번째 필드 ( $1)를 .seq이름에 추가 된 첫 번째 필드 ( ) 의 이름을 가진 파일로 인쇄하십시오 .

Thor 가 주석에서 지적한 것처럼 큰 데이터 세트의 경우 파일 설명자를 소진 할 수 있으므로 작성 후 각 파일닫는 것이 좋습니다 .

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

안녕하세요 이것은 작동합니다 감사합니다.. 코드를 조금 설명 할 수 있습니까?
user3138373

도움 @ user3138373 희망 ...
jasonwryan

도움이됩니다 .. 감사 왜 printf 대신 작업을 인쇄하지 않겠습니까 ??
user3138373

3
여러 줄이 있으면 사용 가능한 모든 파일 설명자가 사용되므로을 추가해야합니다 close($1".seq").
Thor

1
@Thor, 사실입니다. awkGNU와 같은 일부 구현은 그 문제를 해결하는 방법을 알고 있습니다.
Stéphane Chazelas

3

GNU sed로 할 수있는 방법은 다음과 같습니다.

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

또는 glenn jackman이 제안한대로보다 효율적으로 :

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

1
멋지지만 모든 라인에 대해 외부 명령을 생성 해야하는 비효율적입니다. 그것은 모든 나오지 출력 원시 명령 및 파이프 "쉬"로 출력을 갖는 조금 더 나은 것
글렌 잭맨

1
@glennjackman : 이것은 흥미로운 대안 적 방법이었습니다. 입력이 크면 awk가장 효율적인 도구 일 것입니다. 물론 sh각 줄에 대해 생성되지 않는 것에 대해 파이프 옵션을 대안으로 추가했습니다.
Thor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.