Bonjour,
Le titre n'est pas très explicite donc je m'empresse de vous exposer le problème. Je souhaite prévoir la production laitière d'un troupeau de vaches. Je dispose de données extraites du robot de traite de mon oncle :
1 2 3 4 5 6 7 8 9 10 | Vache Prod(L) Cons(kg) Jour Jour-dans-la-lactation Lactation 3505 01/01/2014 3133 13,17 4,5 01/01/2014 121 3 3541 01/01/2014 3514 01/01/2014 1 01/01/2014 3504 22,78 4,7 01/01/2014 82 1 4997 16,16 5,8 01/01/2014 158 1 3166 21,16 4,6 01/01/2014 252 2 ... |
Après nettoyage, ces données sont insérées dans une base SQlite :
1 2 3 4 5 6 7 8 9 10 | CREATE TABLE IF NOT EXISTS CrudeData( id INTEGER PRIMARY KEY AUTOINCREMENT, cow INTEGER NOT NULL, date DATE NOT NULL, prod REAL NOT NULL, -- L cons REAL NOT NULL, -- kg lact INTEGER NOT NULL, day INTEGER NOT NULL, UNIQUE (cow, date) ); |
Je vais à présent devoir effectuer des calculs sur ces données : moyennes mobiles, régressions linéaires… Pour cela, je les charge dans un dictionnaire :
1 2 3 4 5 6 7 8 9 10 11 12 | { ("cow", 1): { ("lact", 1): { ("crude", "dates"): ["2014-01-01", ...], ("crude", "days"): [1, 2, ...], ("crude", "prods"): [20.4, 22.0, ...], ("crude", "cons"): [11.2, 10.8, ...] }, ("lact", 2): {...} }, ("cow", 4156): {...} } |
Puis, au fil des calculs, le dictionnaire évolue :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | { ("cow", 1): { ("lact", 1): { ("crude", "dates"): ["2014-01-01", "2014-01-03", "2014-01-03" ...], ("crude", "days"): [1, 2, 3, ...], ("crude", "prods"): [20.4, 22.0, 23.1 ...], ("crude", "cons"): [11.2, 10.8, 12.3, ...], ("ma", "dates", 1): ["2014-01-03", ...], ("ma", "days", 1): [3, ...], ("ma", "prods", 1): [(20.4 + 22.0 + 23.1)/(2*1 + 1)], ("ma", "cons", 1): [(11.2 + 10.8 + 12.3)/(2*1 + 1)] }, ("lact", 2): {...} }, ("cow", 4156): {...} } |
Note : "ma" pour moving averaging et "1" pour le nombre de valeurs à prendre de chaque côté de celle centrale.
Seulement, ce dictionnaire va partir en fumée à la fin de l'exécution de mon programme, alors que je souhaiterais conserver les résultats durement acquis pour pouvoir les utiliser une prochaine fois sans devoir les recalculer. Du coup, j'instaure un système de cache : je sérialise le résultat avec pickle et le stocke dans un fichier. La fois suivante, au moment de faire le calcul, je regarde si le fichier existe et, le cas échéant, j'en charge le résultat.
Seulement, c'était sans compter la mise à jour de la base. Effectivement, les données brutes ayant été acquises vers novembre 2014, certaines lactations de certaines vaches ont été coupées avant la fin. J'aimerais donc, quand je compléterai ma base avec des données plus récentes, que les calculs pour ces lactations soient refaits, en prenant en compte les données ajoutées. Mais, comme la lactation existait lorsque j'ai commencé mes calculs, un fichier lui a été dédié en cache, que mon programme chargera alors que les données d'entrée auront changé. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | # On démarre une série d'analyses { ("cow", 1): { ("lact", 1): { ("crude", "days"): [1, 2, 3, 4, ..., 105], ... # Moyenne mobile et mise en cache { ("cow", 1): { ("lact", 1): { ("crude", "days"): [1, 2, 3, 4, ..., 105], ("ma", "days", 2): [3, 4, ..., 103], ... # Mise à jour de la base # On démarre une série d'analyses { ("cow", 1): { ("lact", 1): { ("crude", "days"): [1, 2, 3, 4, ..., 214], ... # Moyenne mobile, en cache { ("cow", 1): { ("lact", 1): { ("crude", "days"): [1, 2, 3, 4, ..., 214], ("ma", "days", 2): [3, 4, ..., 103], ... # Alors qu'il aurait fallu { ("cow", 1): { ("lact", 1): { ("crude", "days"): [1, 2, 3, 4, ..., 214], ("ma", "days", 2): [3, 4, ..., 212], ... |
Je pensais alors conserver une trace des arguments avec lesquels les calculs sont effectués, et stocker ça en base :
1 2 | key args val "cow-14-lact-1-linreg-prods-X-80" A B |
En A, j'aurais un hash de mes arguments sérialisés (a priori, je n'ai pas besoin d'être en mesure de pouvoir les retrouver, i.e. de "déhasher" puis désérialiser) et en B, j'aurais le résultat du calcul, sérialisé avec pickle.
Plusieurs questions se posent alors :
- Est-ce une approche correcte ?
- Sachant qu'il n'a pas besoin d'être lisible par un humain, le champ
key
gagnerait-il à contenir des hash plutôt que des chaînes ? - Le contenu du champ
val
serait-il mieux dans un fichier (la base de données ne permettrait de stocker uniquement la clé du calcul et ses arguments) ?
Merci !