2010-10-14 5 views
11

J'ai un fichier CSV dont je voudrais extraire quelques informations: pour chaque valeur distincte d'une colonne, je voudrais calculer la somme des valeurs correspondantes dans une autre colonne. Finalement, je peux le faire en Python, mais je crois qu'il pourrait y avoir une solution simple en utilisant awk.Somme conditionnelle Awk d'un fichier CSV

Cela pourrait être le fichier CSV:

2 1:2010-1-bla:bla 1.6 
2 2:2010-1-bla:bla 1.1 
2 2:2010-1-bla:bla 3.4 
2 3:2010-1-bla:bla -1.3 
2 3:2010-1-bla:bla 6.0 
2 3:2010-1-bla:bla 1.1 
2 4:2010-1-bla:bla -1.0 
2 5:2010-1-bla:bla 10.9 

Je voudrais obtenir:

1 1.6 
2 4.5 
3 5.8 
4 -1.0 
5 10.9 

Pour l'instant, je ne peux extraire:

a) valeurs de la première colonne:

awk -F ' ' '{print $(2)}' MyFile.csv | awk -F ':' '{print $(1)}' 

puis obtenez:

1 
2 
2 
3 
3 
3 
4 
5 

b) et les valeurs égales, par exemple, 1.1 dans la dernière colonne avec:

awk -F ' ' '{print $(NF)}' MyFile.csv | awk '$1 == 1.1'

puis obtenir:

1.1 
1.1 

Je ne suis pas en mesure d'extraire simultanément les colonnes qui m'intéressent, ce qui peut m'aider à la fin. Voici un exemple de sortie qui peut faciliter le calcul des sommes (je ne sais pas):

1 1.6 
2 1.1 
2 3.4 
3 -1.3 
3 6.0 
3 1.1 
4 -1.0 
5 10.9 

Edit: Merci à Elenaher, on pourrait dire l'entrée est le fichier ci-dessus.

+0

pouvez-vous nous donner un exemple d'entrée? – stew

+0

Merci à tous! C'est génial! – Wok

Répondre

12
$ awk -F"[: \t]+" '{a[$2]+=$NF}END{for(i in a) print i,a[i] }' file 
4 -1 
5 10.9 
1 1.6 
2 4.5 
3 5.8 
+0

Si court, et encore, Ça marche! Merci! – Wok

+3

+1 Certainement le plus élégant avec le '" [: \ t] + '! – ThR37

+0

J'ai finalement décidé d'accepter cette réponse car elle est vraiment plus générale et pourrait s'adapter à beaucoup de problèmes similaires en ajustant les séparateurs ou le nombre des colonnes. – Wok

1

Pour votre dernière question, vous pouvez utiliser split et afficher simultanément les deux colonnes:

cat filename | awk '{split($2,tab,":"); id = tab[1]; print id " -> " $3;}' 

qui imprime:

1 -> 1.6 
2 -> 1.1 
2 -> 3.4 
3 -> -1.3 
3 -> 6.0 
3 -> 1.1 
4 -> -1.0 
5 -> 10.9 

Pour le résultat complet, vous pouvez utiliser:

awk -F, '{ split($1,line," "); split(line[2],tab,":"); id=tab[1]; if (sums[id]=="") {sums[id] = 0;} sums[id]+=line[3];} END {for (i=1;i<=length(sums);i++) print i " -> "sums[i]}' < test 

qui imprime:

1 -> 1.6 
2 -> 4.5 
3 -> 5.8 
4 -> -1 
5 -> 10.9 
+0

Merci. Je ne connaissais pas le mot clé 'split' pour' awk'. – Wok

+0

Merci, votre code fonctionne (bien que je doive éditer l'entrée car il y avait un espace manquant qui n'est pas traité alors). – Wok

4

Cela suppose que vous avez les deux colonnes que vous avez déjà affichées: 1 1.1

BEGIN { 
    last = ""; 
    sum = 0; 
} 

{ 
    if ($1 != last) { 
     if (last != "") { 
      print last " " sum; 
     } 
     sum = 0; 
     last = $1; 
    } 
    sum = sum + $2 
} 

END { 
    print last " " sum; 
} 
+0

Cela fonctionne très bien en utilisant la sortie de la ligne d'Elenaher. – Wok

+0

Votre réponse est géniale pour répondre à ma deuxième question. J'aimerais pouvoir l'emporter plus d'une fois. – Wok

2

Donc, en supposant que votre entrée ressemble à ceci:

unique_col, to_sum 
1.3, 1 2 3 
1.3, 5 6 7 
1.4, 2 3 4 

cela devrait faire l'affaire:

$ awk -F, '{ if (seen[$1] == "") { split($2, to_sum, " "); seen[$1] = 0; for (x in to_sum) seen[$1] += to_sum[x]; }} END { for (x in seen) { if (x != "") { print x " " seen[x]; }}}' < input 
1.3 6 
1.4 9 
+0

Cela fonctionne très bien sur votre entrée, mais le mien est un peu différent. Encore merci. – Wok

+0

Ah, désolé - l'a écrit avant que vous ayez eu l'exemple, donc j'ai dû deviner = \ –

0
{ 
    b=$2;    # assign column 2 to the variable 'b' 
    sub(/:.*/, "", b); # get rid of everything after the first colon in b 
    results[b] += $3  
} 
END { for (result in results)print result " " results[result] } 
+0

Je reçois le message suivant: 'Erreur de syntaxe près du jeton inattendu' /:.*/, '' – Wok

0

Si Perl est une option :

perl -F'(\s+|:)' -lane '$h{$F[2]} += $F[-1]; END{print "$_ $h{$_}" for sort keys %h}' file

sortie:

1 1.6 
2 4.5 
3 5.8 
4 -1 
5 10.9 

Ces options de ligne de commande sont utilisées:

  • boucle -n autour de chaque ligne du fichier d'entrée
  • -l supprime les nouvelles lignes avant le traitement, et ajoute les renvoyer ensuite
  • -a mode auto-split - divisez les lignes d'entrée dans la matrice @F. Par défaut, scinder sur les espaces.
  • -e exécuter le code perl
  • -F modificateur de autosplit, dans ce cas se divise en une couleur ou d'un ou plusieurs espaces

@F est le tableau de mots dans chaque ligne, indexée en commençant par $F[0]
$F[-1] est le dernier mot
résultat de magasin dans hachage %h
A la fin, itérer les touches triées du hachage
Imprimer chaque élément $_ et la valeur de hachage $h{$_}