Saya perlu mengurutkan bed
file secara acak 10.000 kali dan mengambil 1000 baris teratas setiap kali. Saat ini, saya menggunakan kode berikut:
for i in {1..100}; do
for j in {1..100}; do
sort -R myfile.bed_sorted | tail -n 1000 > myfile.bed.$i.$j.bed
done
done
Butuh hampir 6 jam untuk melakukan ini untuk setiap file. Saya memiliki sekitar 150 di antaranya untuk dikerjakan. Apakah ada solusi yang lebih cepat untuk ini?
Contoh data (myfile.bed_sorted) Saya punya:
chr1 111763899 111766405 peak1424 1000 . 3224.030 -1 -1
chr1 144533459 144534584 peak1537 998 . 3219.260 -1 -1
chr8 42149384 42151246 peak30658 998 . 3217.620 -1 -1
chr2 70369299 70370655 peak16886 996 . 3211.600 -1 -1
chr8 11348914 11352994 peak30334 990 . 3194.180 -1 -1
chr21 26828820 26830352 peak19503 988 . 3187.820 -1 -1
chr16 68789901 68791150 peak11894 988 . 3187.360 -1 -1
chr6 11458964 11462245 peak26362 983 . 3169.750 -1 -1
chr1 235113793 235117308 peak2894 982 . 3166.000 -1 -1
chr6 16419968 16422194 peak26522 979 . 3158.520 -1 -1
chr6 315344 321339 peak26159 978 . 3156.320 -1 -1
chr1 111756584 111759633 peak1421 964 . 3110.520 -1 -1
chrX 12995098 12997685 peak33121 961 . 3100.000 -1 -1
chr9 37408601 37410262 peak32066 961 . 3100.000 -1 -1
chr9 132648603 132651523 peak32810 961 . 3100.000 -1 -1
chr8 146103178 146104943 peak31706 961 . 3100.000 -1 -1
chr8 135611963 135614649 peak31592 961 . 3100.000 -1 -1
chr8 128312253 128315935 peak31469 961 . 3100.000 -1 -1
chr8 128221486 128223644 peak31465 961 . 3100.000 -1 -1
chr8 101510621 101514237 peak31185 961 . 3100.000 -1 -1
chr8 101504210 101508005 peak31184 961 . 3100.000 -1 -1
chr7 8173062 8174642 peak28743 961 . 3100.000 -1 -1
chr7 5563424 5570618 peak28669 961 . 3100.000 -1 -1
chr7 55600455 55603724 peak29192 961 . 3100.000 -1 -1
chr7 35767878 35770820 peak28976 961 . 3100.000 -1 -1
chr7 28518260 28519837 peak28923 961 . 3100.000 -1 -1
chr7 104652502 104654747 peak29684 961 . 3100.000 -1 -1
chr6 6586316 6590136 peak26279 961 . 3100.000 -1 -1
chr6 52362185 52364270 peak27366 961 . 3100.000 -1 -1
chr6 407805 413348 peak26180 961 . 3100.000 -1 -1
chr6 32936987 32941352 peak26978 961 . 3100.000 -1 -1
chr6 226477 229964 peak26144 961 . 3100.000 -1 -1
chr6 157017923 157020836 peak28371 961 . 3100.000 -1 -1
chr6 137422769 137425128 peak28064 961 . 3100.000 -1 -1
chr5 149789084 149793727 peak25705 961 . 3100.000 -1 -1
chr5 149778033 149783125 peak25702 961 . 3100.000 -1 -1
chr5 149183766 149185906 peak25695 961 . 3100.000 -1 -1
split
dapat, err, membagi file menjadi potongan 1000 baris masing-masing, sehingga Anda akan mendapatkan lebih banyak file dalam satu panggilansort
. Juga, sudahkah Anda memeriksa jikahead
sedikit lebih cepat daripadatail
karena tidak perlu membaca seluruh file?head
sini.sort -R
menggunakan "hash kunci acak". Membuat hash adalah buang-buang waktu dan mungkin membutuhkan waktu lebih lama dari yang lainnya. Akan lebih baik untuk membaca baris menjadi array dan kemudian mengocoknya menggunakan indeks. Secara pribadi, saya akan gunakanperl
untuk itu; Anda bisa melakukannya denganbash
tetapi Anda akan membutuhkan fungsi untuk menghasilkan angka acak.perl
orang! Bisakah Anda membantu saya?shuf
alih-alihsort -R
, ini jauh lebih cepat. Tentu saja, melakukannya di memori (lihat Perl jawaban) akan mengalahkan apa pun yang mengharuskan membaca ulang seluruh file di shell.Jawaban:
Dengan asumsi Anda memiliki cukup memori untuk menyeruput file, Anda bisa mencoba
Karena Anda ingin melakukan ini sebanyak 10.000 kali, saya akan merekomendasikan untuk mengintegrasikan pengulangan ke dalam skrip dan mengacak indeks daripada array itu sendiri untuk mempercepat:
Di atas menciptakan 10.000 file 1000 baris masing-masing dari file yang berisi 37000 baris (file contoh Anda diulang 1000 kali). Seperti yang Anda lihat, butuh sedikit lebih dari tiga menit pada sistem saya.
Penjelasan
use List::Util 'shuffle';
: ini mengimpor modul Perl yang menyediakanshuffle()
fungsi yang mengacak array.@l=<>;
: memuat file input (<>
) ke dalam array@l
.for $i (1..10000){}
: jalankan 10000 kali ini.@r=shuffle(0..$#l);
:$#l
adalah jumlah elemen dalam@l
jadi@r
sekarang daftar acak nomor indeks array@l
(baris file input).open(my $fh, ">","file.$i.bed");
: buka file yang dipanggilfile.$i.bed
untuk menulis.$i
akan mengambil nilai dari 1 hingga 10.000.print $fh @l[@r[0..999]]
: ambil 1000 indeks pertama dalam array acak dan cetak baris yang sesuai (elemen@l
).Pendekatan lain adalah menggunakan
shuf
( terima kasih @frostschutz ):sumber
shuf
seperti yang disarankan oleh frostschutz:for i in {1..10000}; do shuf -n 1000 file.bed > file.$i.bed; done
. Butuh ~ 1 menit pada sistem saya. Adapun 1000 baris terakhir, yang Anda butuhkan adalahtail -n 1000
.sys
waktu, yang akan menjadi file I / O - ini seharusnya tidak begitu berbeda dari yangshuf
ada, yang memiliki ~ 30-ansys
. Jadi saya menguji perl satu di sini (cut n 'paste) dan O_O itu menciptakan 1000 file tetapi semua file kosong ...Jika Anda ingin tolok ukur untuk melihat seberapa cepat dapat dilakukan, salin tempel ini ke dalam
10kshuffle.cpp
dan kompilasig++ 10kshuffle.cpp -o 10kshuffle
. Anda kemudian dapat menjalankannya:Di mana
filename
path dasar untuk digunakan untuk file output; mereka akan dinamaifilename.0
,,filename.1
dll. dan masing-masing berisi 1000 baris pertama dari shuffle. Itu menulis nama setiap file saat berjalan.Pada inti 3,5 Ghz tunggal, ini berjalan dalam ~ 20 detik:
data.txt
adalah 37.000 baris digandakan dari pertanyaan. Jika Anda ingin seluruh shuffle dalam file output daripada 1000 baris pertama, ubah baris 54 ke:sumber
Jadi ada aspek Unix untuk pertanyaan Anda, tetapi ada baiknya memecahkan masalah mendasar Anda terlebih dahulu dan kemudian mencoba menemukan cara Unix-y untuk mengimplementasikan solusi itu.
Anda perlu membuat 10.000 sampel berukuran 1.000 masing-masing dari file dengan jumlah baris yang tidak diketahui. Dimungkinkan untuk melakukan ini dalam satu lintasan file jika Anda dapat menyimpan 10.000 x 1.000 baris dalam memori. Jika Anda tidak dapat menahan banyak baris dalam memori, Anda masih dapat melakukannya dalam satu pass tunggal jika Anda tahu berapa banyak baris file Anda. Jika Anda tidak tahu berapa banyak baris file Anda, Anda perlu satu pass tambahan untuk menghitung jumlah baris.
Algoritme, dalam kasus yang lebih sulit ketika Anda tidak tahu jumlah baris, adalah melakukan hal berikut untuk setiap sampel (secara paralel, menyimpan sampel dalam memori):
n > 1000
), sertakan dengan probabilitas1000 / n
dan buang baris acak dari baris yang telah Anda pilih. (karena kemungkinan membuang beberapa baris, kami perlu menyimpan sampel dalam memori sampai akhir input)Cara elegan untuk menerapkan langkah kedua adalah menghasilkan bilangan bulat acak
k
di[1, n]
. Jikak <= 1000
kemudian sertakan baris dan ganti baris yang adak
dengan itu. Berikut ini deskripsi yang lebih standar tentang algoritme: http://en.wikipedia.org/wiki/Reservoir_samplingJika Anda tahu jumlah baris
R
, maka:s
dari 0(1000 - s) / (R - n + 1)
dan hasilkan segera (dan menambah ukuran sampels
)Bagaimana melakukan ini di Unix?
awk
tampaknya menjadi jawaban per posting ini di Internet (saya tidak bisa menjamin kebenarannya, tetapi kodenya ada di sana) https://news.ycombinator.com/item?id=4840043sumber