Existe-t-il une commande Linux pouvant être utilisée pour échantillonner un sous-ensemble de fichier? Par exemple, un fichier contient un million de lignes et nous voulons échantillonner de manière aléatoire seulement mille lignes de ce fichier.
Pour aléatoire, je veux dire que chaque ligne a la même probabilité d'être choisie et qu'aucune des lignes choisies n'est répétitive.
head
et tail
peut choisir un sous-ensemble du fichier mais pas au hasard. Je sais que je peux toujours écrire un script python pour le faire, mais je me demandais simplement s'il existait une commande pour cet usage.
command-line
files
command
Clwen
la source
la source
Réponses:
La
shuf
commande (une partie de coreutils) peut faire ceci:Et au moins pour le moment, les versions non anciennes (ajoutées dans un commit de 2013 ), qui utiliseront l'échantillonnage de réservoir le cas échéant, ce qui signifie qu'il ne devrait pas manquer de mémoire et utilise un algorithme rapide.
la source
sort
trouve dans la même section et qu’il n’est clairement pas nécessaire de trier les entrées.shuf
Coreutils a été introduit dans la version6.0 (2006-08-15)
, et croyez-le ou non, certains systèmes raisonnablement communs (CentOS 6.5 en particulier) n’ont pas cette version: - |shuf -n
effectue l'échantillonnage de réservoir, du moins lorsque l'entrée est supérieure à 8K, ce qui correspond à la taille qu'ils ont déterminée comme points de référence. Voir le code source (par exemple, à l' adresse github.com/coreutils/coreutils/blob/master/src/shuf.c#L46 ). Désolé pour cette réponse très tardive. Apparemment, c'est nouveau il y a 6 ans.Si vous avez un très gros fichier (ce qui est une raison courante pour prélever un échantillon), vous constaterez que:
shuf
épuise la mémoire$RANDOM
ne fonctionnera pas correctement si le fichier dépasse 32 767 lignes.Si vous n'avez pas besoin "exactement" n lignes échantillonnées, vous pouvez échantillonner un rapport comme celui-ci:
cat input.txt | awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01) print $0}' > sample.txt
Cela utilise une mémoire constante , échantillonne 1% du fichier (si vous connaissez le nombre de lignes du fichier, vous pouvez ajuster ce facteur pour échantillonner un nombre de lignes limité à un nombre limité), et fonctionne avec n’importe quelle taille de fichier, mais cela ne fonctionnera pas. renvoyer un nombre précis de lignes, juste un ratio statistique.
Remarque: le code provient de: https://stackoverflow.com/questions/692312/randomly-pick-lines-from-a-file-without-slurping-it-with-unix
la source
$RANDOM
ne fonctionneront pas correctement pour les fichiers de plus de 32767 lignes. La déclaration «L’utilisation$RANDOM
n’atteint pas l’ensemble du fichier» est un peu large.awk
convivial est plus de ressources queshuf
Semblable à la solution probabiliste de @ Txangel mais approchant 100 fois plus vite.
Si vous avez besoin de hautes performances, d'une taille d'échantillon exacte et que vous souhaitez vivre avec un espace d'échantillon à la fin du fichier, vous pouvez procéder de la manière suivante (échantillonnez 1000 lignes d'un fichier de 1 m):
.. ou bien enchaîner un deuxième exemple de méthode au lieu de
head
.la source
shuf -n
Si l' astuce sur les gros fichiers manque de mémoire et que vous avez toujours besoin d'un échantillon de taille fixe et qu'un utilitaire externe peut être installé, essayez ensuite sample :La mise en garde est que l' échantillon (1000 lignes dans l'exemple) doit tenir dans la mémoire.
Déni de responsabilité: Je suis l'auteur du logiciel recommandé.
la source
/usr/local/bin
pareil avant/usr/bin/
, méfiez-vous que macOS est livré avec un échantillonneur intégré de pile d'appels appelésample
, qui fait quelque chose de complètement différent, dans/usr/bin/
.Pas au courant d'une seule commande qui pourrait faire ce que vous demandez, mais voici une boucle que j'ai mise ensemble qui peut faire le travail:
sed
prendra une ligne au hasard sur chacun des 1000 passages. Peut-être y a-t-il des solutions plus efficaces.la source
$RANDOM
la plage est comprise entre 0 et 32767. Ainsi, vous ne recevrez pas de numéros de ligne bien répartis.Vous pouvez enregistrer le code suivant dans un fichier (par exemple randextract.sh) et l'exécuter en tant que:
---- Commencer le fichier ----
---- FICHIER DE FIN ----
la source
$RANDOM$RANDOM
ne génère pas de nombres aléatoires dans toute la plage «0 à 3276732767» (par exemple, cela générera 1000100000 mais pas 1000099999).Si vous connaissez le nombre de lignes dans le fichier (comme 1e6 dans votre cas), vous pouvez faire:
Si non, vous pouvez toujours faire
Cela ferait deux passes dans le fichier, mais éviterait toujours de stocker le fichier entier en mémoire.
Un autre avantage par rapport à GNU
shuf
est qu’il préserve l’ordre des lignes dans le fichier.Notez qu'il assume
n
est le nombre de lignes dans le fichier. Si vous voulez imprimerp
les premièresn
lignes du fichier (qui a potentiellement plus de lignes), vous devez vous arrêterawk
à lan
troisième ligne comme ceci:la source
J'aime utiliser awk pour cela lorsque je souhaite conserver une ligne d'en-tête et que l'échantillon peut représenter un pourcentage approximatif du fichier. Fonctionne pour les très gros fichiers:
la source
Ou comme ceci:
De la page de manuel de bash:
la source
Si la taille du fichier n’est pas énorme, vous pouvez utiliser la méthode de tri aléatoire. Cela prend un peu plus de temps que shuf, mais aléatoirement l’ensemble des données. Donc, vous pouvez facilement faire ce qui suit pour utiliser la tête que vous avez demandée:
Cela trierait le fichier au hasard et vous donnerait les 1000 premières lignes.
la source
Comme mentionné dans la réponse acceptée, GNU
shuf
supporteshuf -n
assez bien l’ échantillonnage aléatoire simple ( ). Si des méthodes d'échantillonnage autres que celles prises en chargeshuf
sont nécessaires, utilisez tsv-sample dans TSV Utilities d' eBay . Il prend en charge plusieurs modes d'échantillonnage supplémentaires, notamment l'échantillonnage aléatoire pondéré, l'échantillonnage de Bernoulli et l'échantillonnage distinct. Les performances sont similaires à celles de GNUshuf
(les deux sont assez rapides). Disclaimer: je suis l'auteur.la source