Wybierz losowe linie z pliku

240

W skrypcie Bash chcę wybrać N losowych linii z pliku wejściowego i wyprowadzić do innego pliku.

Jak można to zrobić?

użytkownik121196
źródło
Sortuj plik losowo i wybierz N pierwszych wierszy.
Piotr Praszmo
Zobacz także stackoverflow.com/questions/12354659/… .
Acumenus
31
to nie jest duplikat - chce N linii kontra 1 linia.
OneSolitaryNoob
1
Nie zgadzam się z tym, sort -Rponieważ powoduje to nadmierną pracę, szczególnie w przypadku długich plików. Można użyć $RANDOM, % wc -l, jot, sed -n(à la stackoverflow.com/a/6022431/563329 ) i funkcjonalność bash (tablice, przekierowania poleceń, etc), aby zdefiniować własną peekfunkcję, która będzie faktycznie działać na plikach 5.000.000 linii.
izomorfizmy

Odpowiedzi:

627

Użyj shuftej -nopcji, jak pokazano poniżej, aby uzyskać Nlosowe linie:

shuf -n N input > output
dogbane
źródło
2
Jeśli potrzebujesz tylko losowego zestawu linii, a nie w losowej kolejności, shuf jest bardzo nieefektywny (w przypadku dużego pliku): lepiej jest próbkować rezerwuar, jak w tej odpowiedzi .
petrelharp
Uruchomiłem to w pliku wierszy 500M, aby wyodrębnić 1000 wierszy i zajęło to 13 minut. Plik nie był dostępny od miesięcy i znajduje się na dysku SSD Amazon EC2.
T. Brian Jones
więc czy w gruncie rzeczy jest to bardziej przypadkowe sort -R?
Mona Jalal,
1
@MonaJalal nie jest po prostu szybsza, ponieważ wcale nie musi porównywać linii.
rogerdpack
Czy ostatecznie daje tę samą linię więcej niż raz?
Frederick Nord
161

Sortuj plik losowo i wybierz pierwsze 100wiersze:

$ sort -R input | head -n 100 >output
użytkownik881480
źródło
43
sortfaktycznie sortuje identyczne linie razem, więc jeśli możesz mieć zduplikowane linie i masz shuf(narzędzie GNU) zainstalowane, lepiej do tego użyć.
Kevin
22
Andalso, to na pewno sprawi, że będziesz musiał dużo czekać, jeśli masz dość duży plik - 80kk linii - podczas gdy shuf -ndziała dość natychmiastowo.
Rubens
28
sort -R nie jest dostępny w systemie Mac OS X (10.9)
Mirko Ebert
2
@ tfb785: sort -Rjest prawdopodobnie opcją GNU, zainstaluj GNU coreutils. btw, shufjest również częścią coreutils.
jfs
1
@JFSebastian Kod: sort -R input | head -n <num_lines>. Plik wejściowy miał 279 GB, z liniami 2bi +. Nie mogę tego jednak udostępnić. W każdym razie chodzi o to, że możesz zachować niektóre linie w pamięci za pomocą losowego wybierania, aby dokonać losowego wyboru tego, co chcesz wydrukować. Sortowanie posortuje cały plik, niezależnie od twoich potrzeb.
Rubens
18

Cóż Zgodnie z komentarzem do odpowiedzi shuf przetasował 78 000 000 000 linii w niecałą minutę.

Wyzwanie przyjęte...

Najpierw potrzebowałem pliku 78.000.000.000 linii:

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

To daje mi plik z 78 miliardami nowych linii ;-)

Teraz część 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

Wąskim gardłem był procesor i nie używający wielu wątków, przypiął 1 rdzeń na 100%, a pozostałych 15 nie było używanych.

Python jest tym, czego regularnie używam, więc tego użyję, aby przyspieszyć:

#!/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))

To dało mi niecałą minutę:

$ time ./shuf.py         










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

Zrobiłem to na Lenovo X1 Extreme 2. generacji z i9 i Samsung NVMe, co daje mi dużą prędkość odczytu i zapisu.

Wiem, że może być szybciej, ale zostawię trochę miejsca, aby dać innym szansę.

Źródło licznika linii : Luther Blissett

Stein van Broekhoven
źródło