Pilih garis acak dari file

240

Dalam skrip Bash, saya ingin memilih garis acak N dari file input dan output ke file lain.

Bagaimana ini bisa dilakukan?

pengguna121196
sumber
Sortir file secara acak dan pilih N baris pertama.
Piotr Praszmo
31
ini bukan duplikat - dia ingin N baris vs 1 baris.
OneSolitaryNoob
1
Saya tidak setuju dengan sort -Rkarena melakukan banyak pekerjaan berlebih, terutama untuk file yang panjang. Anda dapat menggunakan $RANDOM, % wc -l, jot, sed -n(à la stackoverflow.com/a/6022431/563329 ), dan fungsi bash (array, pengalihan perintah, dll) untuk menentukan sendiri peekfungsi yang benar-benar akan berjalan pada file 5.000.000-line.
isomorfisma

Jawaban:

627

Gunakan shufdengan -nopsi seperti yang ditunjukkan di bawah ini, untuk mendapatkan Ngaris acak:

shuf -n N input > output
dogbane
sumber
2
Jika Anda hanya perlu satu set garis acak, bukan dalam urutan acak, maka shuf sangat tidak efisien (untuk file besar): lebih baik melakukan pengambilan sampel reservoir, seperti dalam jawaban ini .
petrelharp
Saya menjalankan ini pada file baris 500 juta untuk mengekstrak 1.000 baris dan butuh 13 menit. File itu belum diakses dalam beberapa bulan, dan ada di Drive Amazon EC2 SSD.
T. Brian Jones
jadi apakah ini pada dasarnya lebih acak sort -R?
Mona Jalal
1
@OnaJalal nggak cepat, karena tidak perlu membandingkan garis sama sekali.
rogerdpack
Apakah pada akhirnya menghasilkan garis yang sama lebih dari satu kali?
Frederick Nord
161

Sortir file secara acak dan pilih 100baris pertama :

$ sort -R input | head -n 100 >output
pengguna881480
sumber
43
sortsebenarnya mengurutkan garis yang identik bersama-sama, jadi jika Anda mungkin memiliki garis duplikat dan Anda telah menginstal shuf(alat gnu), lebih baik menggunakannya untuk ini.
Kevin
22
Andalso, ini pasti akan membuat Anda menunggu banyak jika Anda memiliki file yang sangat besar - baris 80kk -, sedangkan, shuf -nbertindak cukup instan.
Rubens
28
sort -R tidak tersedia di bawah Mac OS X (10.9)
Mirko Ebert
3
@ tfb785: sort -Rmungkin opsi GNU, instal GNU coreutils. btw, shufjuga bagian dari coreutils.
jfs
1
@JFSebastian Kode: sort -R input | head -n <num_lines>. File input adalah 279GB, dengan 2bi + baris. Tidak bisa membagikannya. Pokoknya, intinya adalah Anda dapat menyimpan beberapa baris dalam memori dengan shuffle untuk melakukan pemilihan acak apa yang akan dihasilkan. Sortir akan mengurutkan seluruh file, terlepas dari apa kebutuhan Anda.
Rubens
18

Yah Menurut komentar pada jawaban shuf dia mengocok 78.000 000 baris dalam waktu kurang dari satu menit.

Tantangan diterima...

EDIT: Saya mengalahkan rekor saya sendiri

powershuf melakukannya dalam 0,047 detik

$ time ./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null 
./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null  0.02s user 0.01s system 80% cpu 0.047 total

Alasannya sangat cepat, baik saya tidak membaca seluruh file dan hanya memindahkan pointer file 10 kali dan mencetak baris setelah pointer.

Gitlab Repo

Usaha lama

Pertama saya membutuhkan file 78.000.000.000 baris:

seq 1 78 | xargs -n 1 -P 16 -I% seq 1 1000 | xargs -n 1 -P 16 -I% echo "" > lines_78000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000.txt > lines_78000000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000000.txt > lines_78000000000.txt

Ini memberi saya file dengan 78 Miliar baris baru ;-)

Sekarang untuk bagian shuf:

$ time shuf -n 10 lines_78000000000.txt










shuf -n 10 lines_78000000000.txt  2171.20s user 22.17s system 99% cpu 36:35.80 total

Hambatannya adalah CPU dan tidak menggunakan banyak utas, itu disematkan 1 inti pada 100% 15 lainnya tidak digunakan.

Python adalah apa yang saya gunakan secara teratur sehingga itulah yang akan saya gunakan untuk membuatnya lebih cepat:

#!/bin/python3
import random
f = open("lines_78000000000.txt", "rt")
count = 0
while 1:
  buffer = f.read(65536)
  if not buffer: break
  count += buffer.count('\n')

for i in range(10):
  f.readline(random.randint(1, count))

Ini membuat saya kurang dari satu menit:

$ time ./shuf.py         










./shuf.py  42.57s user 16.19s system 98% cpu 59.752 total

Saya melakukan ini pada Lenovo X1 ekstrim 2nd gen dengan i9 dan Samsung NVMe yang memberi saya banyak kecepatan baca dan tulis.

Saya tahu ini bisa lebih cepat tetapi saya akan meninggalkan beberapa ruang untuk mencoba yang lain.

Sumber penghitung garis : Luther Blissett

Stein van Broekhoven
sumber
Nah, menurut deskripsi Anda tentang fungsi dalam powershuf, sepertinya itu hanya acak. Menggunakan file dengan hanya dua baris, satu panjangnya 1 karakter, yang lain panjangnya 20 karakter, saya berharap kedua baris akan dipilih dengan peluang yang sama. Ini tampaknya tidak menjadi masalah dengan program Anda.
Xhienne