Bir veri dosyasından belirli sayıda satırı rastgele çizme


13

Gibi bir veri listem var

12345
23456
67891
-20000
200
600
20
...

Bu veri kümesinin boyutunun (yani dosya satırlarının) olduğunu varsayalım N. mBu veri dosyasından rastgele çizgiler çizmek istiyorum . Bu nedenle, çıktı iki dosya olmalıdır, biri bu mveri satırlarını içeren dosya ve diğeri N-mveri satırlarını içerir.

Bir Linux komutu kullanarak bunu yapmanın bir yolu var mı?


1
Çizgilerin sırası hakkında endişeli misiniz? Örneğin. Kaynak sırasını korumak mı istiyorsunuz yoksa bu dizinin kendisinin de rastgele olmasını ve satırların rastgele seçilmesini mi istiyorsunuz?
Peter.O

Yanıtlar:


18

Bu en etkili yol olmayabilir, ancak işe yarar:

shuf <file> > tmp
head -n $m tmp > out1
tail -n +$(( m + 1 )) tmp > out2

İle $msatır sayısını ihtiva etmektedir.


@userunknown, sort -Rrastgelelikle ilgilenir. Bunun cevabını aşağıya indirip indirmediğinizden emin değilsiniz, ancak önce mangada arayın.
Rob Wouters

2
Not sort -Rolarak grupları aynı çizgiler: Tam olarak rastgele girişini sıralamak değildir. Girişidir Yani eğer örneğin foo, foo, bar, barve m = 2, sonra bir dosya hem içerecektir foos ve diğer ikisi de içerecektir bars. GNU coreutils shufgiriş satırlarını rastgele seçer . Ayrıca, geçici bir dosyaya ihtiyacınız yoktur .
Gilles 'SO- kötü olmayı kes'

neden olmasın shuf <file> |head -n $m?
emanuele

@emanuele: Çünkü hem kafaya hem de kuyruğa iki ayrı dosyada ihtiyacımız var.
Rob Wouters

5

Bu bash / awk betiği satırları rastgele seçer ve her iki çıktı dosyasındaki orijinal diziyi korur.

awk -v m=4 -v N=$(wc -l <file) -v out1=/tmp/out1 -v out2=/tmp/out2 \
 'BEGIN{ srand()
         do{ lnb = 1 + int(rand()*N)
             if ( !(lnb in R) ) {
                 R[lnb] = 1
                 ct++ }
         } while (ct<m)
  } { if (R[NR]==1) print > out1 
      else          print > out2       
  }' file
cat /tmp/out1
echo ========
cat /tmp/out2

Çıktı, söz konusu verilere göre.

12345
23456
200
600
========
67891
-20000
20

4

Her şeyde olduğu gibi Unix de, O TM için bir fayda var .

Günün programı: split
splitbir dosyayı birçok farklı şekilde, -bbayt, -lsatır, -nçıkış dosyalarının sayısını böler . -lSeçeneği kullanacağız . Sadece ilk rastgele çizgiler ve değil almak istediğimiz için m, yaparız sortrastgele ilk dosyayı. Hakkında okumak istiyorsanız sort, cevabım için buraya bakın .

Şimdi, gerçek kod. Oldukça basit, gerçekten:

sort -R input_file | split -l $m output_prefix

Bu, biri satırlı, mbiri N-msatırlı, output_prefixaave adında iki dosya oluşturur output_prefixab. mİstediğiniz daha büyük dosyanın olduğundan emin olun yoksa birkaç dosya m(ve bir tane ile N % m) alırsınız .

Doğru boyutu kullandığınızdan emin olmak istiyorsanız, bunu yapmak için küçük bir kod İşte:

m=10 # size you want one file to be
N=$(wc -l input_file)
m=$(( m > N/2 ? m : N - m ))
sort -R input_file | split -l $m output_prefix

Edit: Bazı sortuygulamalar bir -Rbayrak yok dikkatimi çekti . Eğer varsa perl, yerine koyabilirsiniz perl -e 'use List::Util qw/shuffle/; print shuffle <>;'.


1
Ne yazık ki, sort -Rsadece bazı tür sürümlerinde (muhtemelen GNU sürümü) olduğu görülmektedir. Diğer platformlar için stdin'i randomize eden hiçbir şey yapmayan 'randline' adlı bir araç yazdım. İhtiyacı olan herkes için beesbuzz.biz/code adresindedir . (Dosya içeriğini oldukça fazla karıştırma eğilimindeyim.)
Kabarık

1
Not sort -Rolarak grupları aynı çizgiler: Tam olarak rastgele girişini sıralamak değildir. Girişidir Yani eğer örneğin foo, foo, bar, barve m = 2, sonra bir dosya hem içerecektir foos ve diğer ikisi de içerecektir bars. GNU coreutils shufgiriş satırlarını rastgele seçer . Ayrıca, çıktı dosyası adlarını headve tailyerine kullanarak da seçebilirsinizsplit .
Gilles 'SO- kötü olmayı bırak'

4

Çizgileri yeniden sıralamayı önemsemiyorsanız ve GNU coreutils'iniz varsa (örneğin, shuf6.0 sürümünde göründüğünden çok eski olmayan gömülü Linux veya Cygwin'de ), shuf(“shuffle”) bir dosyanın satırlarını rastgele yeniden sıralar. Böylece dosyayı karıştırabilir ve ilk m satırını bir dosyaya geri kalanı başka bir dosyaya gönderebilirsiniz.

Bu gönderiyi yapmanın ideal bir yolu yok. Sadece zincirleme yapamazsınız headve tailçünkü headönünüzde tampon olur. Kullanabilirsiniz split, ancak çıktı dosyası adlarıyla ilgili herhangi bir esneklik elde edemezsiniz. awkElbette kullanabilirsiniz :

<input shuf | awk -v m=$m '{ if (NR <= m) {print >"output1"} else {print} }'

Kullanabilirsiniz sed, bu da büyük dosyalar için belirsiz ancak muhtemelen daha hızlıdır.

<input shuf | sed -e "1,${m} w output1" -e "1,${m} d" >output2

Veya tee, platformunuz varsa verileri çoğaltmak için kullanabilirsiniz /dev/fd; m küçükse sorun değil:

<input shuf | { tee /dev/fd/3 | head -n $m >output1; } 3>&1 | tail -n +$(($m+1)) >output2

Taşınabilir olarak, her satırı sırayla göndermek için awk kullanabilirsiniz. Awk, rasgele sayı üretecini başlatmada çok iyi değildir; rasgelelik sadece kriptografi için kesinlikle uygun değildir, aynı zamanda sayısal simülasyonlar için bile çok iyi değildir. Tohum, bir saniyelik bir süreye sahip herhangi bir sistemdeki tüm awk invokasyonları için aynı olacaktır.

<input awk -v N=$(wc -l <input) -v m=3 '
    BEGIN {srand()}
    {
        if (rand() * N < m) {--m; print >"output1"} else {print >"output2"}
        --N;
    }'

Daha iyi rasgeleliğe ihtiyacınız varsa, RNG'yi terbiyeli bir şekilde tohumlayan Perl'de de aynı şeyi yapabilirsiniz.

<input perl -e '
    open OUT1, ">", "output1" or die $!;
    open OUT2, ">", "output2" or die $!;
    my $N = `wc -l <input`;
    my $m = $ARGV[0];
    while (<STDIN>) {
        if (rand($N) < $m) { --$m; print OUT1 $_; } else { print OUT2 $_; }
        --$N;
    }
    close OUT1 or die $!;
    close OUT2 or die $!;
' 42

@Gilles: For awkÖrneğin: -v N=$(wc -l <file) -v m=4... ve sadece bir yazdırır "rastgele" rastgele değer az olduğunda hattı $myerine baskı, $mrastgele çizgiler ... O görünüyor perlile aynı şeyi yapıyor olabilir rand , ama don perlderleme hatasını geçecek kadar iyi bilmiyorum : -e satır 7'de ") yazdır" yakınında sözdizimi hatası
Peter.O

@ Peter.O Teşekkürler, bir tarayıcıya yazmaktan ve dikkatsizce düzenlemeden gelir. Awk ve perl kodunu düzelttim.
Gilles 'SO- kötü olmayı bırak'

Tüm 3 yöntem iyi ve hızlı çalışıyor .. teşekkürler (+1) ... Yavaş yavaş perl etrafında başımı alıyorum ... ve bu shuförnekte özellikle ilginç ve kullanışlı bir dosya bölme .
Peter.O

Arabelleğe alma sorunu mu var? . Bir şey mi kaçırıyorum? head catAçılan ikinci testte aşağıdaki verilerin kaybına neden olur 3-4 .... TEST 1-2 { for i in {00001..10000} ;do echo $i; done; } | { head -n 5000 >out1; cat >out2; } .. TEST 3-4 { for i in {00001..10000} ;do echo $i; done; } >input; cat input | { head -n 5000 >out3; cat >out4; } ... wc -lçıkışları için sonuçlar TEST 1-2 olan 5000 5000 (iyi), ama için TEST 3-4 , 5000 4539 (iyi değil) ..
Farklılık

@ Peter.O Tekrar, teşekkürler. Gerçekten de headileride okur; okuduğu ve yazdırılmadığı atılır. Cevabımı daha az zarif ama (kesinlikle eminim) doğru çözümlerle güncelledim.
Gilles 'SO- kötü olmayı bırak'

2

Varsayım m = 7ve N = 21:

cp ints ints.bak
for i in {1..7}
do
    rnd=$((RANDOM%(21-i)+1))
    # echo $rnd;  
    sed -n "${rnd}{p,q}" 10k.dat >> mlines 
    sed -i "${rnd}d" ints 
done

Not: veya 7gibi bir değişkenle değiştirirseniz , değişken genişletmesi yapmayan -notasyon değil, kullanmanız gerekir .$1$mseq{from..to}

Dosyadan satır satır silerek çalışır, bu da kısalır, böylece kaldırılabilen satır numarası gittikçe küçülür.

Bu, daha uzun dosyalar ve birçok satır için kullanılmamalıdır, çünkü her sayı için ortalama olarak yarım dosyanın 1. dosya için ve tüm dosya 2. sed kod için okunması gerekir .


Kaldırılan çizgileri olan bir dosyaya ihtiyacı var.
Rob Wouters

"Bu m veri satırları dahil" anlamına gelmesi gerektiğini düşündüm including themama orijinal satırları da - bu nedenle including, değil consisting of, ve kullanma only, ama sanırım yorumunuz, user288609 ne anlama geliyor. Senaryomumu buna göre ayarlayacağım.
kullanıcı bilinmiyor

İyi görünüyor. ``
Rob Wouters

@user unknown: +1Yanlış yerdesiniz. Bu olmalı rnd=$((RANDOM%(N-i)+1))N = sizin örnekte 21 .. Şu anda neden nerede sedne zaman çökmesine rndkarşı değerlendirilir 0. .. Ayrıca, tüm bu dosya yeniden yazma ile çok iyi ölçeklenmez. örneğin 123 saniye 10.000 satır dosyasından 5.000 rasgele satır ayıklamak için vs daha doğrudan bir yöntem için 0.03 saniye ...
Peter.O

@ Peter.O: Haklısın (düzeltildi) ve haklısın.
kullanıcı bilinmiyor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.