Her satırın bir kısmını ayrı bir dosyaya çıktılar


14

Ben böyle bir dosya var:

a   AGTACTTCCAGGAACGGTGCACTCTCC
b   ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT
c   ATATTAAATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCATCCACTCCACAC
d   ATCAGTTTAATATCTGATACGTCCTCTATCCGAGGACAATATATTAAATGGA
e   TTTGGCTAAGATCAAGTGTAGTATCTGTTCTTATAAGTTTAATATCTGATATGTCCTCTATCTGA

a.seqDizi içeren bir dosya yapmak istiyorum AGTACTTCCAGGAACGGTGCACTCTCC. Benzer şekilde b.seqiçerir ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT. Kısacası, Sütun1 uzantısı olan çıktı dosyası adı olarak kullanılmalı .seqve içinde sütun2 dizisi bulunmalıdır. Bunu bir perl betiği yazarak yapabilirim ama komut satırındaki herhangi bir şey yardımcı olacaktır. Yakında duymayı umuyoruz.

Yanıtlar:


16

Hızlı yanıtım olurdu, awkancak çok sayıda satır işliyorsanız - ve milyonlar hakkında konuşuyorsanız - muhtemelen "gerçek" bir programlama diline geçmenin gerçek bir yararı göreceksiniz.

Bunu göz önünde bulundurarak (ve awkzaten bir cevap olarak alınıyor) farklı dillerde birkaç uygulama yazdım ve bunları bir PCI-E SSD'deki aynı 10.000 satırlı veri kümesinde karşılaştırdım.

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

Bir bakışta C en iyi görünüyor ancak bu kadar hızlı koşmak bir domuzdu. Milyarlarca satırdan bahsetmediğiniz sürece Pypy ve C ++ 'ın yazılması ve yeterince iyi performans göstermesi çok daha kolaydır . Bu durumda, tüm bunları RAM'de veya SSD'de yapmak için yapılan bir yükseltme, kod geliştirmeden daha iyi bir yatırım olabilir.

Açıkçası bunlardan geçerken harcadığım zaman muhtemelen en yavaş seçenekte birkaç yüz milyon kayıt işleyebilirdiniz . Sadece awkBash döngüleri yazabilir veya yazabiliyorsanız , bunu yapın ve hayata devam edin. Bugün çok fazla boş zamanım vardı.

Ayrıca bazı çok iş parçacıklı seçenekleri (C ++ ve Python ve GNU ile melezler parallel) test ettim ama iş parçacığı bu tür basit bir işlem (dize bölme, yazma) için herhangi bir fayda tamamen ağır basar.

Perl

awk( gawkburada) dürüst olmak gerekirse, bu tür verileri test etmek için ilk çağrı portum olurdu, ancak Perl'de oldukça benzer şeyler yapabilirsiniz. Benzer sözdizimi ancak biraz daha iyi yazma tutamacına sahip.

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

piton

Ben gibi Python. Bu benim günlük iş dilim ve sadece güzel, sağlam ve inanılmaz derecede okunabilir bir dil. Yeni başlayanlar bile burada neler olduğunu tahmin edebilir.

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

Dağıtımınızın pythonikilisinin Python'un tek uygulaması olmadığını hatırlamanız gerekir . Aynı testi Pypy aracılığıyla yürüttüğümde, daha fazla mantık optimizasyonu olmadan C'den daha hızlıydı . Python'u "yavaş dil" olarak yazmadan önce bunu aklınızdan çıkarmayın.

C

İşlemcimi gerçekten ne elde edebileceğimizi görmek için bu örneğe başladım ama açıkçası C, uzun süre dokunmadıysanız kodlamak için bir kabus. Bunu genişletmek çok basit olsa da, 100 karakterlik çizgilerle sınırlı olmanın ek bir dezavantajı var, sadece ihtiyacım yoktu.

Orijinal versiyonum C ++ ve pypy'den daha yavaştı ama bloglamadan sonra Julian Klode'den biraz yardım aldım . Bu sürüm, tweaked IO tamponları nedeniyle artık en hızlı. Ayrıca her şeyden çok daha uzun ve daha ilgili.

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

İyi performans gösterir ve yazmak gerçek C'den çok daha kolaydır. Bütün bunlar mantığı basitleştirebileceğiniz anlamına gelir. strtokC bir domuz, çünkü tüm dizeyi işler ve sonra o yorucu bellek ayırma yapmak gerekir. Bu sekmeye çarpana kadar çizgi boyunca kayar ve segmentleri ihtiyacımız olduğu kadar dışarı çekeriz.

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

(Moreutils sürümü değil). Güzel bir özlü sözdizimi ama OMGSLOW. Yanlış kullanıyor olabilirim.

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

Test kablo demeti jeneratörü

İşte 100000 satırlık [ATGC] * 64 için veri oluşturucum. Hızlı değil ve gelişmeler çok açıktır.

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

2
Performans için tüm seçeneklerinizi numaralandırmanın , akla ilk gelen şeyle gitmek kadar savurgan olabileceğini belirtmeliyim. awkon milyondan az bir şey için hala iyi bir cevap. Bunu [lineer olarak] bir milyar satıra kadar ölçeklendirseniz bile, C sizi Perl'den sadece 1.5 saat ve awk'den 3.6 saat tasarruf ediyor.
Oli

Şimdi C ++ sürümüm çok daha hızlı, belki de büyük veri kümelerinin daha basit metin işleme için C ++ düşünürdüm. Neredeyse iki kat daha hızlı olduğunu ve işte birçok çizgiler milyarlarca gidince saat farkı.
Oli



1
Test koşumunuzun üretim hızının rastgele sayı üretecine bağlı olduğunu düşünüyorum. Bunu veren her numarayı kullanarak daha hızlı yapmak veya bir homojen dağılımını, örneğin oluşturabilir: 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

Saf kabuk uygulaması:

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

12

Kullanma awk:

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

Atanandan file, her kayıttaki ( $2) ikinci alanı , adın sonuna eklenmiş olan ilk alanın ( $1) adındaki bir dosyaya yazdırın .seq.

As Thor işaret yorumlarda bunun akıllıca olacaktır, böylece büyük bir veri kümesi için, sen dosya tanımlayıcıları tüketmesine yazılı sonra her dosyayı kapatın :

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

Merhaba Bu çok teşekkürler .. Kodu biraz açıklayabilir misiniz?
user3138373

@ user3138373 Umut yardımcı olur ...
jasonwryan

Yardımcı olur .. Teşekkürler Neden printf yerine iş yazdırmıyor?
user3138373

3
Çok satır varsa, tüm kullanılabilir dosya tanımlayıcıları kullanılır, bu nedenle muhtemelen bir close($1".seq").
Thor

1
@Thor, doğru. awkYine de GNU'lar gibi bazı uygulamalar bunun nasıl çözüleceğini biliyor.
Stéphane Chazelas

3

GNU sed ile bunu yapmanın bir yolu:

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

Veya daha verimli, glenn jackman'ın önerdiği gibi :

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

1
Bu serin olsa da, her satır için harici bir komut oluşturmak zorunda oldukça verimsiz. Sed tüm ham komutları sahip olmak biraz daha iyi olurdu ve çıktıyı "sh" boru
Glenn Jackman

1
@glennjackman: Bunu yapmanın ilginç bir alternatif yoluydu. Girdi büyükse, awkmuhtemelen en etkili araçtır. Tabii ki shher çizgi için yumurtlama konusunda haklısın , alternatif olarak boru seçeneğini ekledim.
Thor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.