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 ?
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 ?
Utilice shuf
avec le -n
comme indiqué ci-dessous, pour obtenir N
des lignes aléatoires :
shuf -n N input > output
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 .
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.
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.
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.
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.
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
$ 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.
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
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.
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.
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.
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
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.
0 votes
Triez le fichier au hasard et choisissez les N premières lignes.
1 votes
Voir aussi stackoverflow.com/questions/12354659/ .
35 votes
Ce n'est pas un doublon -- il veut N lignes contre 1 ligne.
2 votes
Liés : Choisir au hasard des lignes d'un fichier sans l'aspirer avec Unix
2 votes
Je ne suis pas d'accord avec
sort -R
car il fait beaucoup de travail en trop, en particulier pour les fichiers longs. Vous pouvez utiliser$RANDOM
,% wc -l
,jot
,sed -n
(à la stackoverflow.com/a/6022431/563329 ), et des fonctionnalités bash (tableaux, redirections de commandes, etc.) pour définir votre proprepeek
qui s'exécutera réellement sur des fichiers de 5 000 000 de lignes.