Respons jepretan saya akan awk
tetapi jika Anda sedang memproses banyak baris - dan saya berbicara tentang jutaan - Anda mungkin akan melihat manfaat nyata dari beralih ke bahasa pemrograman "nyata".
Dengan pemikiran itu (dan awk
sudah diambil sebagai jawaban) saya menulis beberapa implementasi dalam bahasa yang berbeda dan membandingkannya pada set data 10.000-baris yang sama pada PCI-E SSD.
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
Sepintas C terlihat paling baik tapi itu babi untuk bisa berlari secepat itu. Pypy dan C ++ jauh lebih mudah untuk menulis dan berkinerja cukup baik kecuali jika Anda berbicara tentang miliaran baris. Jika demikian, peningkatan untuk melakukan ini semua dalam RAM atau pada SSD mungkin merupakan investasi yang lebih baik daripada peningkatan kode.
Jelas dalam waktu yang saya habiskan melalui ini Anda mungkin bisa memproses beberapa ratus juta catatan dalam opsi paling lambat . Jika Anda hanya dapat menulis awk
atau mengulangi Bash, lakukan itu dan lanjutkan hidup. Saya jelas punya terlalu banyak waktu luang hari ini.
Saya juga menguji beberapa opsi multi-threaded (dalam C ++ dan Python dan hibrida dengan GNU parallel
) tetapi overhead threads benar-benar melebihi manfaat apa pun untuk operasi sederhana (string splitting, writing).
Perl
awk
(di gawk
sini) akan secara jujur menjadi tempat panggilan pertama saya untuk menguji data seperti ini, tetapi Anda dapat melakukan hal-hal yang serupa di Perl. Sintaks yang sama tetapi dengan pegangan tulisan yang sedikit lebih baik.
perl -ane 'open(my $fh, ">", $F[0].".seq"); print $fh $F[1]; close $fh;' infile
Python
Saya suka Python. Ini adalah pekerjaan saya sehari-hari dan hanya bahasa yang bagus, solid, dan sangat mudah dibaca. Bahkan seorang pemula mungkin bisa menebak apa yang terjadi di sini.
with open("infile", "r") as f:
for line in f:
id, chunk = line.split()
with open(id + ".seq", "w") as fw:
fw.write(chunk)
Anda harus ingat bahwa python
biner distribusi Anda bukan satu-satunya implementasi Python di luar sana. Ketika saya menjalankan tes yang sama melalui Pypy, itu lebih cepat daripada C tanpa optimasi logika lebih lanjut. Ingatlah itu sebelum menulis Python sebagai "bahasa lambat".
C
Saya memulai contoh ini untuk melihat apa yang benar-benar bisa saya lakukan dengan CPU, tetapi terus terang, C adalah mimpi buruk untuk dikodekan jika Anda belum menyentuhnya dalam waktu yang lama. Ini memiliki kelemahan tambahan yaitu terbatas pada garis 100-char meskipun sangat mudah untuk mengembangkannya, saya hanya tidak membutuhkannya.
Versi asli saya lebih lambat dari C ++ dan pypy tetapi setelah blogging tentang hal itu saya mendapat bantuan dari Julian Klode . Versi ini sekarang yang tercepat karena buffer IO yang di-tweak. Ini juga jauh lebih lama dan lebih terlibat daripada yang lainnya.
#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 ++
Berkinerja baik dan jauh lebih mudah untuk menulis daripada C. asli. Anda memiliki segala macam hal yang memegang tangan Anda (terutama ketika menyangkut string dan input). Semua itu berarti Anda benar-benar dapat menyederhanakan logika. strtok
di C adalah babi karena memproses seluruh string dan kemudian kita perlu melakukan semua alokasi memori yang melelahkan. Ini hanya melintas di sepanjang garis sampai menyentuh tab dan kami menarik segmen keluar saat kami membutuhkannya.
#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 Parallel
(Bukan versi moreutils). Ini adalah sintaksis ringkas yang bagus tetapi OMGSLOW. Saya mungkin salah menggunakannya.
parallel --colsep '\t' echo {2} \> {1}.seq <infile
Generator uji harness
Inilah data-generator saya untuk 100000 baris [ATGC] * 64. Ini tidak cepat dan perbaikan sangat disambut.
cat /dev/urandom | tr -dc 'ATGC' | fold -w 64 | awk 'NR>100000{exit}{printf NR"\t"$0"\n"}' > infile
awk
masih merupakan jawaban yang bagus untuk apapun yang kurang dari puluhan juta. Bahkan jika Anda [linear] skala ini hingga satu miliar baris, C hanya menghemat Anda 1,5 jam lebih Perl dan 3,6 jam lebih awk.paste <(yes A) <(yes T) <(yes G) <(yes C) | head -n1600000 | tr '\t' '\n' | shuf | tr -d \\n | fold -w64 | cat -n > infile
.Implementasi shell murni:
sumber
Menggunakan
awk
:Dari yang dinominasikan
file
, cetak bidang kedua di setiap catatan ($2
) ke file bernama setelah bidang pertama ($1
) dengan.seq
ditambahkan ke nama.Seperti yang ditunjukkan Thor dalam komentar, untuk dataset besar, Anda dapat menggunakan deskriptor file, jadi sebaiknya tutup setiap file setelah menulis :
sumber
close($1".seq")
.awk
implementasi seperti GNU tahu bagaimana cara mengatasinya.Ini adalah salah satu cara Anda dapat melakukannya dengan GNU sed:
Atau lebih efisien, seperti yang disarankan oleh glenn jackman :
sumber
awk
mungkin alat yang paling efisien untuk digunakan. Anda tentu saja benar tentang tidak menelurkansh
untuk setiap baris, saya telah menambahkan opsi pipa sebagai alternatif.