Réindexation d'un gros fichier CSV

11

J'ai parcouru les réponses dans ce fil utile , mais mon problème semble être suffisamment différent pour que je ne puisse pas penser à une bonne réponse (au moins avec sed).

J'ai un gros fichier CSV (200+ Go) avec des lignes qui ressemblent à ceci:

<alphanumerical_identifier>,<number>

<alphanumerical_identifier>est unique sur l'ensemble du fichier. Je voudrais créer un fichier séparé qui remplace la première colonne par un index , c'est-à-dire

<index>,<number>

pour que nous obtenions:

1, <number>
2, <number>
3, <number>

Peut awkgénérer un index croissant sans charger le fichier complet en mémoire?

Étant donné que l'indice augmente de façon monotone, il peut être encore mieux de simplement laisser tomber l'indice. La solution serait-elle si différente?, À savoir:

<number>
<number>
<number>
Amelio Vazquez-Reina
la source
Je ne suis pas sûr de la faisabilité de cette solution. Mais que diriez-vous de générer autant de nombres que dans le fichier CSV dans un fichier séparé, puis d'ajouter simplement la deuxième colonne du fichier CSV à ce fichier?
Ramesh
@Ramesh C'est très bien tant que la sortie est correcte.
Amelio Vazquez-Reina
2
Je soupçonne que je ne comprends pas quelque chose; sinon, awk -F, '{print ++n, $2}'cela fonctionnerait. Ou awk -F, '{print $2}'pour la deuxième variation.
G-Man dit `` Réintègre Monica '' le
2
@ G-Man, c'est probablement ça, mais FNRcela servirait aussi bien que++n
iruvar
1
Je vérifierais que vous pouvez vraiment vous débarrasser de cet identifiant Uniq ... pourquoi ne pas ajouter une première (3e) colonne avec l'index, tout en conservant l'identifiant? est pas identifiant utilisé partout ailleurs?
Olivier Dulac

Réponses:

13

Pas près d'un terminal pour tester, mais qu'en est-il de la nlcommande souvent ignorée ? Quelque chose comme:

cut -f 2 -d , original.csv | nl -w 1 -p -s , > numbered.csv

évêque
la source
1
PS: un fichier CSV de 200 Go ? Wow, et je pensais que travailler avec la base de données des numéros portés d'Amérique du Nord en tant que CSV (quelques DVD) était énorme!
évêque le
1
Cela fonctionne, bien qu'il y ait un grand blanc après le numéro. Je le remplacerais par:cut -d, -f 2- /tmp/aa | nl -w 1 -p -s ,
Ángel
@ Angel: Merci d'avoir mis à jour ma réponse pour utiliser l'option largeur -w 1au lieu de la numérotation à gauche.
évêque le
Merci @bishop - Où vont les noms de fichiers d'entrée et de sortie?
Amelio Vazquez-Reina
1
@ user815423426 Oui, la cutcommande avant le symbole de canal ( |) ne vous donnera que la deuxième colonne, ayant effectivement des numéros de ligne implicites.
évêque le
7

Voici quelques approches, mais aucune approcheront la vitesse de la cutet nlsolution ci - dessus:

  1. awk

    awk -F, '{$1=NR;print $1","$2;}' file.csv > newfile.csv
  2. Perl

    perl -pe 's/[^,]+/$./' file.csv > newfile.csv

    ou

    perl -F, -ane '$F[0]=$.; print join ",", @F' file.csv
  3. Shell (mais je ne le recommande pas pour un fichier 200G, cela prendra du temps)

    i=1; while IFS=, read foo num; do 
            printf "%d,%s\n" $((i++)) $num; 
    done < file.csv > newfile.csv

Les solutions ci-dessus sont triées par ordre de vitesse. J'ai testé sur mon ordinateur portable et un fichier de 40M et ils ont pris (moyenne de 10 exécutions) 2,2282 (awk), 2,4555 (1er perl), 3,1825s (2e perl) et un énorme 48,6035s pour le shell. La solution très intelligente cutque nlvous avez déjà était environ 4 fois plus rapide à 0,6078 s.

terdon
la source
Bien, merci pour les statistiques! Le résultat du shell me surprend quelque peu. Si vous remplacez printfpar echo, le timing s'améliore-t-il de manière significative?
évêque le
2
Fichier 40G traité en 2,2282 secondes? Où puis-je me procurer cet ordinateur portable?
John B
2
@JohnB umm, oui, désolé, c'était 40M, pas G :)
terdon
J'aime la réinitialisation de l' $1approche avec awk. La cutsolution est certainement beaucoup plus rapide, mais il faut s'y attendre car elle ne remplace <alphanumerical_identifier>rien. Je pense que le plus rapide awkpourrait être quelque chose comme variante: mawk 'BEGIN{FS=OFS=","}{$1=NR}1' file.csv > newfile.csv.
John B
@JohnB ah, oui, je suppose que l'utilisation OFSau lieu de l'impression explicite ,serait légèrement plus rapide et cela pourrait bien entraîner une différence significative dans un fichier énorme.
terdon