Preciso classificar um bed
arquivo aleatoriamente 10000 vezes e ocupar as 1000 primeiras linhas de cada vez. Atualmente, estou usando o seguinte 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
Demora quase 6 horas para fazer isso para cada arquivo. Eu tenho cerca de 150 deles para serem trabalhados. Existe uma solução mais rápida para isso?
Uma amostra dos dados (myfile.bed_sorted) que tenho:
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
pode, errar, dividir um arquivo em partes de 1000 linhas cada, para obter mais arquivos em uma única chamada desort
. Além disso, você verificou sehead
é um pouco mais rápido do quetail
porque não precisa ler o arquivo inteiro?head
aqui.sort -R
utiliza um "hash aleatório de chaves". Criar o hash é uma total perda de tempo e provavelmente leva mais tempo do que qualquer outra coisa. Seria melhor ler as linhas em uma matriz e depois embaralhar isso usando índices. Pessoalmente, eu usariaperl
para isso; você poderia fazê-lo,bash
mas precisará de uma função para gerar números aleatórios.perl
pessoa! Você poderia me ajudar?shuf
vez desort -R
, é consideravelmente mais rápido. Obviamente, fazê-lo na memória (consulte a resposta Perl) superará qualquer coisa que exija reler o arquivo inteiro no shell.Respostas:
Supondo que você tenha memória suficiente para absorver o arquivo, tente
Como você deseja fazer isso 10000 vezes, recomendo integrar a repetição ao script e embaralhar os índices em vez da própria matriz para acelerar as coisas:
O acima criado 10000 arquivos de 1000 linhas cada um de um arquivo que continha 37000 linhas (seu arquivo de exemplo repetido 1000 vezes). Como você pode ver, demorou um pouco mais de três minutos no meu sistema.
Explicação
use List::Util 'shuffle';
: isso importa um módulo Perl que fornece ashuffle()
função que randomiza uma matriz.@l=<>;
: carrega o arquivo de entrada (<>
) na matriz@l
.for $i (1..10000){}
: execute isso 10000 vezes.@r=shuffle(0..$#l);
:$#l
é o número de elementos em@l
que@r
agora é uma lista aleatória dos números de índice da matriz@l
(as linhas do arquivo de entrada).open(my $fh, ">","file.$i.bed");
: abre um arquivo chamadofile.$i.bed
para gravação.$i
assumirá valores de 1 a 10000.print $fh @l[@r[0..999]]
: pegue os primeiros 1000 índices na matriz aleatória e imprima as linhas correspondentes (elementos de@l
).Outra abordagem é usar
shuf
( obrigado @frostschutz ):fonte
shuf
como sugerido por frostschutz:for i in {1..10000}; do shuf -n 1000 file.bed > file.$i.bed; done
. Isso levou ~ 1 minuto no meu sistema. Quanto às últimas 1000 linhas, tudo o que você precisa étail -n 1000
.sys
tempo, que seria a E / S do arquivo - isso não deve ser tão totalmente diferente do queshuf
aquele que tem ~ 30ssys
. Então, eu testei o perl aqui (corte n' colar) e O_O criou 1000 arquivos, mas todos os arquivos estavam vazios ...Se você deseja que um benchmark veja com que rapidez isso pode ser feito, copie e cole-o
10kshuffle.cpp
e compileg++ 10kshuffle.cpp -o 10kshuffle
. Você pode executá-lo:Onde
filename
é um caminho base a ser usado para os arquivos de saída; eles serão nomeadosfilename.0
,filename.1
etc., e cada um contém as primeiras 1000 linhas de uma reprodução aleatória. Ele escreve o nome de cada arquivo à medida que avança.Em um único núcleo de 3,5 Ghz, isso é executado em ~ 20 segundos:
data.txt
37.000 linhas duplicadas da pergunta. Se você deseja que o shuffle inteiro no arquivo de saída seja substituído pelas primeiras 1000 linhas, altere a linha 54 para:fonte
Portanto, há um aspecto do Unix na sua pergunta, mas vale a pena resolver o seu problema fundamental primeiro e depois tentar encontrar uma maneira do Unix-y de implementar essa solução.
Você precisa criar 10.000 amostras de tamanho 1.000 cada, a partir de um arquivo com um número grande e desconhecido de linhas. É possível fazer isso em uma única passagem do arquivo se você puder conter 10.000 x 1.000 linhas na memória. Se você não conseguir armazenar tantas linhas na memória, ainda poderá fazê-lo em uma única passagem se souber quantas linhas seu arquivo contém. Se você não souber quantas linhas seu arquivo contém, precisará de uma passagem adicional para contar o número de linhas.
O algoritmo, no caso mais difícil, quando você não sabe o número de linhas, é o seguinte para cada amostra (em paralelo, mantendo as amostras na memória):
n > 1000
), inclua-a com probabilidade1000 / n
e descarte uma linha aleatória das linhas que você já selecionou. (devido à probabilidade de descartar algumas linhas, precisamos manter a amostra na memória até o final da entrada)Uma maneira elegante de implementar a segunda etapa é gerar um número inteiro aleatório
k
em[1, n]
. Se, emk <= 1000
seguida, inclua a linha e substitua ak
-a linha existente por ela. Aqui está uma descrição mais padrão do algoritmo: http://en.wikipedia.org/wiki/Reservoir_samplingSe você souber o número de linhas
R
, então:s
de 0(1000 - s) / (R - n + 1)
e a produza imediatamente (e aumente o tamanho da amostras
)Como fazer isso no Unix?
awk
parece ser a resposta para esta postagem na Internet (não posso garantir sua correção, mas o código está lá) https://news.ycombinator.com/item?id=4840043fonte