Sélectionner des lignes aléatoires dans un fichier


240

Dans un script Bash, je veux sélectionner N lignes aléatoires du fichier d'entrée et sortir vers un autre fichier.

Comment cela peut-il être fait?


Triez le fichier au hasard et choisissez N premières lignes.
Piotr Praszmo


31
ce n'est pas un doublon - il veut N lignes vs 1 ligne.
OneSolitaryNoob


1
Je ne suis pas d'accord sort -Rcar cela fait beaucoup de travail excessif, en particulier pour les longs fichiers. Vous pouvez utiliser $RANDOM, % wc -l, jot, sed -n(à la stackoverflow.com/a/6022431/563329 ), et la fonctionnalité de bash (tableaux, redirection de commande, etc.) pour définir votre propre peekfonction qui fait fonctionner sur des fichiers 5.000.000 ligne.
isomorphismes

Réponses:


627

Utilisez shufavec l' -noption comme indiqué ci-dessous, pour obtenir Ndes lignes aléatoires:

shuf -n N input > output

2
Si vous avez juste besoin d'un ensemble aléatoire de lignes, pas dans un ordre aléatoire, alors shuf est très inefficace (pour les gros fichiers): mieux vaut faire un échantillonnage de réservoir, comme dans cette réponse .
petrelharp

J'ai exécuté cela sur un fichier de lignes de 500 millions pour extraire 1 000 lignes et cela a pris 13 minutes. Le fichier n'a pas été consulté depuis des mois et se trouve sur un disque SSD Amazon EC2.
T. Brian Jones

est-ce donc essentiellement plus aléatoire que cela sort -R?
Mona Jalal

1
@MonaJalal nope juste plus rapide, car il n'a pas du tout à comparer les lignes.
rogerdpack

Donne-t-elle finalement la même ligne plus d'une fois?
Frederick Nord

161

Triez le fichier au hasard et choisissez les premières 100lignes:

$ sort -R input | head -n 100 >output

43
sorttrie les lignes identiques ensemble, donc si vous pouvez avoir des lignes en double et que vous avez shuf(un outil gnu) installé, il est préférable de l'utiliser pour cela.
Kevin

22
De plus, cela va certainement vous faire attendre beaucoup si vous avez un fichier considérablement énorme - 80kk lignes -, alors que, shuf -nagit assez instantanément.
Rubens

28
sort -R n'est pas disponible sous Mac OS X (10.9)
Mirko Ebert

3
@ tfb785: sort -Rest probablement l'option GNU, installez GNU coreutils. btw, shuffait également partie de coreutils.
jfs

1
@JFSebastian Le code: sort -R input | head -n <num_lines>. Le fichier d'entrée était de 279 Go, avec 2 lignes + bi. Je ne peux pas le partager, cependant. Quoi qu'il en soit, le fait est que vous pouvez garder certaines lignes en mémoire avec lecture aléatoire pour faire la sélection aléatoire de ce qui doit être sorti. Trier va trier le fichier entier , quels que soient vos besoins.
Rubens

18

Eh bien, selon un commentaire sur la réponse shuf, il a mélangé 78 000 000 000 lignes en moins d'une minute.

Défi accepté...

EDIT: j'ai battu mon propre record

powershuf l'a fait en 0,047 seconde

$ 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

La raison pour laquelle il est si rapide, eh bien je ne lis pas tout le fichier et je déplace simplement le pointeur de fichier 10 fois et j'imprime la ligne après le pointeur.

Gitlab Repo

Vieille tentative

J'ai d'abord eu besoin d'un fichier de 78.000.000.000 lignes:

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

Cela me donne un fichier avec 78 milliards de nouvelles lignes ;-)

Maintenant pour la partie 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

Le goulot d'étranglement était CPU et n'utilisant pas plusieurs threads, il a épinglé 1 cœur à 100%, les 15 autres n'ont pas été utilisés.

Python est ce que j'utilise régulièrement, c'est ce que j'utiliserai pour accélérer les choses:

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

Cela m'a pris un peu moins d'une minute:

$ time ./shuf.py         










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

Je l'ai fait sur un Lenovo X1 extreme 2nd gen avec l'i9 et le Samsung NVMe, ce qui me donne beaucoup de vitesse de lecture et d'écriture.

Je sais que ça peut aller plus vite mais je vais laisser de la place pour essayer les autres.

Compteur de ligne source: Luther Blissett


Eh bien, selon votre description du fonctionnement interne de powershuf, il semble que ce soit juste aléatoire. En utilisant un fichier avec seulement deux lignes, l'une avec 1 caractère, l'autre avec 20 caractères, je m'attends à ce que les deux lignes soient choisies avec des chances égales. Cela ne semble pas être le cas avec votre programme.
xhienne Il y a
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.