comment calculer la moyenne des valeurs dans une colonne en considérant l'information d'une autre colonne?

file 1:

HOGBRM443983 -2522.00 19800826 HOGBRM445985 -2389.00 19801101 HOUSAM1891409 -1153.00 19811228 HOUSAM2004289 -650.00 19860101 HOUSAM2005991 -843.00 19860109 HOCANM388722 -1546.00 19860116 HOUSAM2007297 -1882.00 19860125 HOUSAM2007389 -1074.00 19860128 HOITAM801038516 -691.00 19860128 

Les colonnes 2 et 3 contiennent des valeurs et des informations sur la date de naissance (année, mois, jour) de chaque identifiant de la colonne 1, respectivement. Je veux vérifier combien d'identifiants existent dans chaque année de naissance et quelles sont les valeurs moyennes (à partir de la deuxième colonne) des identifiants pour différentes années. Par exemple, dans le file1, il y a 2, 1 et 6 identifiants entre 1980, 1981 et 1986, de sorte que le résultat devrait être:

 output: 1980 2 -2455.5 1981 1 -1153.00 1986 6 -114.33 

dans laquelle la première colonne indique l'année de naissance, la deuxième colonne affiche un nombre d'identifications dans chaque année et la troisième colonne correspond aux valeurs moyennes des identifications pour différentes années.

Considérant que les vraies données sont en effet énormes, toute suggestion serait appréciée.

La réponse Awk:

 awk '{y=substr($3,1,4); c[y]++; s[y]+=$2} END {for (y in c) {print y, c[y], (s[y]/c[y])}}' file.txt 

Avec gnu datamash :

 cut -c1-35 infile | datamash -W -g 3 count 3 mean 2 

Notez que vous avez besoin de traiter vos données en premier (j'ai utilisé la cut car c'était le choix évident avec votre échantillon d'input mais n'importe quel outil fera) afin de supprimer le mois et le jour de la date de naissance:

 HOGBRM443983 -2522.00 1980 HOGBRM445985 -2389.00 1980 HOUSAM1891409 -1153.00 1981 HOUSAM2004289 -650.00 1986 ...... 

et seulement ensuite le canaliser vers datamash .
Cela suppose également que les 3èmes colonnes sont sortingées par année (si elles ne sont pas sortingées, utilisez datamash -s -W -g ... )

Envisagez d'utiliser une database réelle.

En utilisant un sandbox Postgres mis en place dans une VM Vagrant , je l'ai fait en utilisant les étapes suivantes:

 CREATE TABLE MyData (id text, val float, bday date); INSERT INTO MyData VALUES ('HOGBRM443983',-2522.00,'1980-08-26'), ('HOGBRM445985',-2389.00,'1980-11-01'), ('HOUSAM1891409',-1153.00,'1981-12-28'), ('HOUSAM2004289',-650.00,'1986-01-01'), ('HOUSAM2005991',-843.00,'1986-01-09'), ('HOCANM388722',-1546.00,'1986-01-16'), ('HOUSAM2007297',-1882.00,'1986-01-25'), ('HOUSAM2007389',-1074.00,'1986-01-28'), ('HOITAM801038516',-691.00,'1986-01-28') ; SELECT extract(year FROM bday) AS yr, count(id) AS count, avg(val) AS average FROM mydata GROUP BY yr; 

La sortie est:

  yr | count | average ------+-------+------------------- 1981 | 1 | -1153 1980 | 2 | -2455.5 1986 | 6 | -1114.33333333333 (3 rows) 

Vous pourriez probablement gérer cela avec le traitement de text, mais vous mentionnez que datatables sont ÉNORME, et une database réelle est conçue pour ce type de calcul. (Et le blog auquel j'ai lié a toutes les étapes pour get un sandbox Postgres mis en place.)

Miller a été créé pour résoudre des problèmes comme ceux-ci:

 $ cat hogbrm.txt | \ mlr --nidx --repifs put '$3=sub(ssortingng($3),"(....).*", "\1")' \ then stats1 -a count,mean -f 2 -g 3 1980 2 -2455.500000 1981 1 -1153.000000 1986 6 -1114.333333 

Le context:

  • Utilisez --nidx car il n'y a pas d'en-tête, juste des colonnes indexées en position
  • Utilisez --repifs puisque plusieurs espaces séparent les colonnes
  • Utilisez sub pour supprimer les quatre derniers numbers de la date (colonne 3)
  • Utilisez stats1 pour calculer le nombre et la moyenne de la colonne 2 regroupés par la colonne 3