Ich muss eine bed
Datei 10000 Mal zufällig sortieren und jedes Mal die obersten 1000 Zeilen nehmen. Derzeit verwende ich den folgenden Code:
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
Dies dauert für jede Datei fast 6 Stunden. Ich habe ungefähr 150 davon zu erarbeiten. Gibt es dafür eine schnellere Lösung?
Ein Beispiel der Daten (myfile.bed_sorted), die ich habe:
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
Sie können eine Datei in Teile von jeweils 1000 Zeilen aufteilen, sodass Sie mit einem einzigen Aufruf von mehr Dateien erhaltensort
. Haben Sie auch überprüft, obhead
es etwas schneller ist alstail
weil es nicht die gesamte Datei lesen muss?head
hier verwendet.sort -R
wird ein "zufälliger Hash von Schlüsseln" verwendet. Das Erstellen des Hashs ist reine Zeitverschwendung und dauert wahrscheinlich länger als alles andere. Es wäre besser, die Zeilen in ein Array einzulesen und diese dann mithilfe von Indizes zu mischen. Persönlich würde ich dafür verwendenperl
; Sie könnten es tun,bash
aber Sie benötigen eine Funktion, um Zufallszahlen zu generieren.perl
Person! Könnten Sie mir bitte helfen?shuf
stattdessensort -R
, es ist erheblich schneller. Wenn Sie dies im Speicher tun (siehe Perl-Antwort), wird natürlich alles übertroffen, was ein erneutes Lesen der gesamten Datei in der Shell erfordert.Antworten:
Angenommen, Sie haben genügend Speicher, um die Datei zu schlürfen, können Sie es versuchen
Da Sie dies 10000 Mal tun möchten, würde ich empfehlen, die Wiederholung in das Skript zu integrieren und die Indizes anstelle des Arrays selbst zu mischen, um die Dinge zu beschleunigen:
Die oben genannten Dateien erstellten 10000 Dateien mit jeweils 1000 Zeilen aus einer Datei, die 37000 Zeilen enthielt (Ihre Beispieldatei wurde 1000 Mal wiederholt). Wie Sie sehen, hat es auf meinem System etwas mehr als drei Minuten gedauert.
Erläuterung
use List::Util 'shuffle';
: Dies importiert ein Perl-Modul, das dieshuffle()
Funktion zum Randomisieren eines Arrays bereitstellt .@l=<>;
: Laden Sie die Eingabedatei (<>
) in das Array@l
.for $i (1..10000){}
: Führen Sie dies 10000 Mal aus.@r=shuffle(0..$#l);
:$#l
ist die Anzahl der Elemente in,@l
so@r
ist jetzt eine zufällige Liste der Indexnummern des Arrays@l
(die Zeilen der Eingabedatei).open(my $fh, ">","file.$i.bed");
: Öffnen Sie eine Datei, diefile.$i.bed
zum Schreiben aufgerufen wird .$i
nimmt Werte von 1 bis 10000 an.print $fh @l[@r[0..999]]
: Nehmen Sie die ersten 1000 Indizes im gemischten Array und drucken Sie die entsprechenden Zeilen (Elemente von@l
).Ein anderer Ansatz ist zu verwenden
shuf
( danke @frostschutz ):quelle
shuf
wie von Frostschutz vorgeschlagen :for i in {1..10000}; do shuf -n 1000 file.bed > file.$i.bed; done
. Das hat auf meinem System ca. 1 Minute gedauert. Für die letzten 1000 Zeilen brauchen Sie nurtail -n 1000
.sys
Zeit hat, was Datei-E / A wäre - dies sollte nicht so völlig anders sein als dasshuf
, das ~ 30s hatsys
. Also habe ich die Perl hier getestet (Ausschneiden und Einfügen) und O_O hat 1000 Dateien erstellt, aber alle Dateien waren leer ...Wenn Sie möchten, dass ein Benchmark sieht, wie schnell dies möglich ist, kopieren Sie ihn, fügen Sie ihn ein
10kshuffle.cpp
und kompilieren Sie ihng++ 10kshuffle.cpp -o 10kshuffle
. Sie können es dann ausführen:Wo
filename
ist ein Basispfad für die Ausgabedateien zu verwenden? sie werden genannt werdenfilename.0
,filename.1
usw. , und jeder enthält die ersten 1000 Zeilen eines shuffle. Es schreibt den Namen jeder Datei, wie es geht.Auf einem einzelnen 3,5-GHz-Kern läuft dies in ~ 20 Sekunden:
data.txt
Es wurden 37000 Zeilen aus der Frage dupliziert. Wenn Sie anstelle der ersten 1000 Zeilen die gesamte Zufallswiedergabe in der Ausgabedatei wünschen, ändern Sie Zeile 54 in:quelle
Ihre Frage hat also einen Unix-Aspekt, aber es lohnt sich, zuerst Ihr grundlegendes Problem zu lösen und dann nach einem Unix-y-Weg zu suchen, um diese Lösung zu implementieren.
Sie müssen 10.000 Beispiele mit einer Größe von jeweils 1.000 aus einer Datei mit einer unbekannten, großen Anzahl von Zeilen erstellen. Dies ist in einem einzigen Durchgang der Datei möglich, wenn Sie 10.000 x 1.000 Zeilen im Speicher halten können. Wenn Sie nicht so viele Zeilen im Speicher halten können, können Sie dies trotzdem in einem einzigen Durchgang tun, wenn Sie wissen, wie viele Zeilen Ihre Datei enthält. Wenn Sie nicht wissen, wie viele Zeilen Ihre Datei enthält, benötigen Sie einen zusätzlichen Durchgang, um die Anzahl der Zeilen zu zählen.
In dem schwierigeren Fall, wenn Sie die Anzahl der Zeilen nicht kennen, führt der Algorithmus für jedes Sample Folgendes aus (parallel, wobei die Samples im Speicher bleiben):
n > 1000
) die Wahrscheinlichkeit an1000 / n
und verwerfen Sie eine zufällige Zeile aus den bereits ausgewählten Zeilen. (Aufgrund der Wahrscheinlichkeit, dass einige Zeilen verworfen werden, müssen wir das Sample bis zum Ende der Eingabe im Speicher halten.)Eine elegante Möglichkeit, den zweiten Schritt zu implementieren, besteht darin, eine zufällige Ganzzahl
k
in zu generieren[1, n]
. Wennk <= 1000
dann die Zeile einschließen und die vorhandenek
-te Zeile durch diese ersetzen . Hier ist eine Standardbeschreibung des Algorithmus: http://en.wikipedia.org/wiki/Reservoir_samplingWenn Sie die Anzahl der Zeilen kennen
R
, dann:s
von 0(1000 - s) / (R - n + 1)
und sofort ausgeben (und Stichprobengröße erhöhens
)Wie geht das unter Unix?
awk
scheint die Antwort für diesen Beitrag im Internet zu sein (ich kann nicht für die Richtigkeit bürgen, aber der Code ist da) https://news.ycombinator.com/item?id=4840043quelle