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

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

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

a   AGTACTTCCAGGAACGGTGCACTCTCC
b   ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT
c   ATATTAAATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCATCCACTCCACAC
d   ATCAGTTTAATATCTGATACGTCCTCTATCCGAGGACAATATATTAAATGGA
e   TTTGGCTAAGATCAAGTGTAGTATCTGTTCTTATAAGTTTAATATCTGATATGTCCTCTATCTGA

a.seq시퀀스가 포함된 파일을 만들고 싶습니다 AGTACTTCCAGGAACGGTGCACTCTCC. 또한 b.seq포함됩니다 ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT. 즉, Column1은 확장자를 가진 출력 파일 이름으로 사용되어야 하며 .seq해당 열 2의 순서가 있어야 합니다. Perl 스크립트를 작성하여 이를 수행할 수 있지만 명령줄에 있는 모든 것이 도움이 될 것입니다. 곧 듣고 싶습니다.

답변1

나의 첫 번째 반응은 다음과 같았습니다. 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에서 모든 작업을 수행하도록 업그레이드하는 것이 코드 개선보다 더 나은 투자일 수 있습니다.

분명히 내가 이것을 살펴보는 동안 당신은 아마도 수억 개의 레코드를 처리했을 것입니다.가장 느린 옵션 중. 작성 또는 Bash 루프만 할 수 있다면 awk그렇게 하고 인생을 계속하십시오. 오늘은 확실히 시간이 너무 많은 것 같아요.

또한 몇 가지 멀티스레딩 옵션(C++ 및 Python과 GNU와의 혼합 parallel)을 테스트했지만 이러한 간단한 작업(문자열 분할, 쓰기)의 이점보다 스레드의 오버헤드가 훨씬 더 컸습니다.

awk( gawk여기)는 솔직히 이런 종류의 데이터를 테스트하기 위한 첫 번째 호출 포트이지만 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바이너리가 Python의 유일한 구현이 아니라는 점을 기억해야 합니다.Pypy를 통해 동일한 테스트를 실행하면,C보다 빠르다더 이상 로직 최적화가 필요하지 않습니다. Python을 "느린 언어"로 생각하기 전에 이 점을 명심하십시오.

나는 내 CPU로 실제로 무엇을 할 수 있는지 알아보기 위해 이 예제를 시작했지만 솔직히 오랫동안 C를 건드리지 않았다면 코딩하는 것은 악몽입니다. 여기에는 100자 라인으로 제한된다는 추가 단점이 있으며, 이를 확장하는 것은 매우 간단하지만 필요하지 않습니다.

내 원래 버전은 C++ 및 pypy보다 느렸지만블로깅 후나는 가지고있다Julien Claude의 도움. 이 버전은 이제 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 버전이 아님) 이것은 간결한 구문이지만 너무 느립니다. 제가 잘못 사용한 것일 수도 있습니다.

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

테스트 하네스 생성기

이것은 100000행 [ATGC]*64에 대한 데이터 생성기입니다. 속도는 빠르지 않으며 개선은 매우 환영합니다.

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

답변2

사용 awk:

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

지정된 file각 레코드( )의 두 번째 필드를 $2이름에 추가된 첫 번째 필드( )의 이름을 딴 파일로 인쇄합니다.$1.seq

~처럼토르가 지적했다설명에서 대규모 데이터 세트의 경우 파일 설명자가 부족할 수 있으므로 다음을 수행하는 것이 좋습니다.쓴 후 각 파일을 닫습니다.:

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

답변3

순수 쉘 구현:

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

답변4

GNU sed를 사용하여 이를 달성하는 한 가지 방법은 다음과 같습니다.

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

또는 제안된 대로 더 효율적으로글렌 잭맨:

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

관련 정보