Necesito ordenar un bed
archivo al azar 10000 veces y tomar las 1000 filas superiores cada vez. Actualmente, estoy usando el siguiente código:
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
Se tarda casi 6 horas en hacer esto para cada archivo. Tengo alrededor de 150 de ellos para resolver. ¿Hay una solución más rápida para esto?
Una muestra de los datos (myfile.bed_sorted) que tengo:
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
puede, err, dividir un archivo en pedazos de 1000 líneas cada uno, para obtener más archivos en una sola llamadasort
. Además, ¿ha verificado sihead
es un poco más rápido quetail
porque no necesita leer todo el archivo?head
aquí.sort -R
utiliza un "hash aleatorio de claves". Crear el hash es una pérdida total de tiempo y probablemente lleva más tiempo que cualquier otra cosa. Sería mejor leer las líneas en una matriz y luego mezclarlas usando índices. Personalmente, lo usaríaperl
para eso; podrías hacerlobash
pero necesitarás una función para generar números aleatorios.perl
persona! ¿Podrías ayudarme por favor?shuf
lugar de hacerlosort -R
, es considerablemente más rápido. Por supuesto, hacerlo en la memoria (ver la respuesta de Perl) superará cualquier cosa que requiera volver a leer todo el archivo en el shell.Respuestas:
Suponiendo que tiene suficiente memoria para sorber el archivo, podría intentar
Como desea hacer esto 10000 veces, recomendaría integrar la repetición en el guión y mezclar los índices en lugar de la matriz en sí para acelerar las cosas:
Lo anterior creó 10000 archivos de 1000 líneas cada uno de un archivo que contenía 37000 filas (su archivo de ejemplo se repitió 1000 veces). Como puede ver, tardó un poco más de tres minutos en mi sistema.
Explicación
use List::Util 'shuffle';
: esto importa un módulo Perl que proporciona lashuffle()
función que aleatoriza una matriz.@l=<>;
: carga el archivo de entrada (<>
) en la matriz@l
.for $i (1..10000){}
: ejecuta esto 10000 veces.@r=shuffle(0..$#l);
:$#l
es el número de elementos, por@l
lo@r
que ahora es una lista aleatoria de los números de índice de la matriz@l
(las líneas del archivo de entrada).open(my $fh, ">","file.$i.bed");
: abre un archivo llamadofile.$i.bed
para escritura.$i
tomará valores de 1 a 10000.print $fh @l[@r[0..999]]
: tome los primeros 1000 índices en la matriz aleatoria e imprima las líneas correspondientes (elementos de@l
).Otro enfoque es usar
shuf
( gracias @frostschutz ):fuente
shuf
como sugiere frostschutz:for i in {1..10000}; do shuf -n 1000 file.bed > file.$i.bed; done
. Eso tomó ~ 1 minuto en mi sistema. En cuanto a las últimas 1000 líneas, todo lo que necesitas estail -n 1000
.sys
tiempo, que sería E / S de archivo, esto no debería ser tan diferente alshuf
que tiene ~ 30 segundossys
. Así que probé el perl aquí (cortar y pegar) y O_O creó 1000 archivos pero todos los archivos estaban vacíos ...Si desea un punto de referencia para ver qué tan rápido se puede hacer, copie y pegue esto
10kshuffle.cpp
y compílelog++ 10kshuffle.cpp -o 10kshuffle
. Luego puedes ejecutarlo:Dónde
filename
es una ruta base para usar para los archivos de salida; serán nombradosfilename.0
,filename.1
etc., y cada uno contiene las primeras 1000 líneas de una mezcla aleatoria. Escribe el nombre de cada archivo a medida que avanza.En un solo núcleo de 3.5 Ghz, esto se ejecuta en ~ 20 segundos:
data.txt
se duplicaron 37000 líneas de la pregunta. Si desea la barajadura completa en el archivo de salida en lugar de las primeras 1000 líneas, cambie la línea 54 a:fuente
Entonces, hay un aspecto de Unix en su pregunta, pero vale la pena resolver su problema fundamental primero y luego tratar de encontrar una forma de Unix para implementar esa solución.
Debe crear 10,000 muestras de tamaño 1,000 cada una a partir de un archivo con un gran número desconocido de filas. Es posible hacer esto en una sola pasada del archivo si puede contener 10,000 x 1,000 filas en la memoria. Si no puede mantener tantas filas en la memoria, puede hacerlo de una sola pasada si sabe cuántas filas contiene su archivo. Si no sabe cuántas filas contiene su archivo, necesita una pasada adicional para contar el número de filas.
El algoritmo, en el caso más difícil cuando no conoce el número de filas, es hacer lo siguiente para cada muestra (en paralelo, manteniendo las muestras en la memoria):
n > 1000
), inclúyala con la probabilidad1000 / n
y descarte una fila aleatoria de las filas que ya ha seleccionado. (debido a la probabilidad de descartar algunas filas, necesitamos mantener la muestra en la memoria hasta el final de la entrada)Una manera elegante de aplicar el segundo paso es generar un entero aleatorio
k
en[1, n]
. Sik <= 1000
luego incluye la fila y reemplaza lak
fila-ésima existente con ella. Aquí hay una descripción más estándar del algoritmo: http://en.wikipedia.org/wiki/Reservoir_samplingSi conoce el número de filas
R
, entonces:s
de 0(1000 - s) / (R - n + 1)
y envíela de inmediato (e incremente el tamaño de la muestras
)¿Cómo hacer esto en Unix?
awk
parece ser la respuesta según esta publicación en Internet (no puedo garantizar su corrección, pero el código está ahí) https://news.ycombinator.com/item?id=4840043fuente