381 votes

Sélectionner des lignes aléatoires dans un fichier

Dans un script de Bash, je veux extraire N lignes aléatoires du fichier d'entrée et les sortir dans un autre fichier.

Comment cela peut-il être fait ?

0 votes

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

1 votes

35 votes

Ce n'est pas un doublon -- il veut N lignes contre 1 ligne.

849voto

dogbane Points 85749

Utilice shuf avec le -n comme indiqué ci-dessous, pour obtenir N des lignes aléatoires :

shuf -n N input > output

9 votes

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) : le mieux est de faire un échantillonnage par réservoir, comme dans cette réponse .

6 votes

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

1 votes

Alors est-ce que c'est essentiellement plus aléatoire que sort -R ?

201voto

user881480 Points 788

Trier le fichier au hasard et choisir le premier 100 lignes :

lines=100
input_file=/usr/share/dict/words

# This is the basic selection method
<$input_file sort -R | head -n $lines

# If the file has duplicates that must never cause duplicate results
<$input_file sort | uniq        | sort -R | head -n $lines

# If the file has blank lines that must be filtered, use sed
<$input_file sed $'/^[ \t]*$/d' | sort -R | head -n $lines

Bien sûr. <$input_file peut être remplacé par n'importe quelle entrée standard canalisée. Cette ( sort -R y $'...\t...' pour obtenir sed pour faire correspondre les caractères de tabulation) fonctionne avec GNU/Linux et BSD/macOS.

49 votes

sort trie en fait les lignes identiques ensemble, donc si vous avez des lignes dupliquées et vous avez shuf (un outil gnu) installé, il est préférable de l'utiliser pour cela.

26 votes

Et aussi, cela va certainement vous faire attendre un montón si vous avez un fichier considérablement énorme -- 80kk lignes --, alors que, shuf -n agit assez instantanément.

28 votes

Sort -R n'est pas disponible sous Mac OS X (10.9)

31voto

D'après un commentaire sur la réponse à la question sur le brassage, il a brassé 78 000 000 000 de lignes en moins d'une minute.

Défi accepté...

EDIT : J'ai battu mon propre record

powershuf l'a fait en 0.047 secondes

$ 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 c'est si rapide, c'est que je ne lis pas tout le fichier et je me contente de déplacer le pointeur de fichier 10 fois et d'imprimer la ligne qui suit le pointeur.

Repo Gitlab

Vieille tentative

J'avais d'abord 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 d'euros 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 le CPU et le fait de ne pas utiliser de threads multiples, il a bloqué un cœur à 100%, les 15 autres n'ont pas été utilisés.

Python est ce que j'utilise régulièrement, c'est donc ce que je vais utiliser pour accélérer le processus :

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

Ça 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 le i9 et Samsung NVMe qui me donne beaucoup de vitesse de lecture et d'écriture.

Je sais que ça peut aller plus vite mais je laisse un peu de place pour que d'autres puissent essayer.

Compteur de lignes source : Luther Blissett

9 votes

Eh bien, d'après 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 de 1 caractère et l'autre de 20 caractères, je m'attends à ce que les deux lignes soient choisies avec les mêmes chances. Cela ne semble pas être le cas avec votre programme.

0 votes

Il y avait un problème avec les fichiers de moins de 4KB et d'autres erreurs mathématiques qui rendaient le système horrible avec les petits fichiers. Je les ai corrigées pour autant que j'ai pu trouver les problèmes, s'il vous plaît donnez-lui un autre essai.

2 votes

Salut Stein. Cela ne semble pas fonctionner. L'avez-vous testé de la manière que j'ai suggérée dans mon commentaire ci-dessus ? Avant de faire quelque chose de plus rapide que shuf, je pense que vous devriez vous concentrer sur la réalisation de quelque chose qui fonctionne aussi précisément que shuf. Je doute vraiment que quelqu'un puisse battre shuf avec un programme python. BTW, à moins que vous n'utilisiez l'option -r shuf n'affiche pas deux fois la même ligne, ce qui prend bien sûr du temps de traitement supplémentaire.

11voto

Merlin Points 539

Mon option préférée est très rapide, j'ai échantillonné un fichier de données délimité par des tabulations avec 13 colonnes, 23.1M lignes, 2.0GB non compressé.

# randomly sample select 5% of lines in file
# including header row, exclude blank lines, new seed

time \
awk 'BEGIN  {srand()} 
     !/^$/  { if (rand() <= .05 || FNR==1) print > "data-sample.txt"}' data.txt

# awk  tsv004  3.76s user 1.46s system 91% cpu 5.716 total

2 votes

C'est génial et super rapide.

1voto

Andelf Points 64
seq 1 100 | python3 -c 'print(__import__("random").choice(__import__("sys").stdin.readlines()))'

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X