#!/usr/bin/python # -*- coding: utf-8 -*- """ Usage: stats_horaires.py [options] INPUTFILE [OUTPUTFILE] Calculer des statistiques à partir d'heures de sommeil contenues dans le fichier INPUTFILE, qui utilise le format décrit Options basiques: -d, --display Afficher le résultat en utilisant gnuplot -s, --standard Écrire le résultat dans la sortie standard Options de statistiques : -j, --jour S'intéresser à la durée de sommeil par jour. -c, --cycle S'intéresser à la durée des cycles de sommeil. -b, --brute Produire les données brutes sans regroupement statistique. Options pour l'étude des cycles : -p, --precision=P Précision (en minutes) utilisée pour l'étude des cycles. --premier Uniquement utiliser la première longue période de sommeil constatée dans une session (défaut : 6). --min-duree=MIN Durée minimum pour une longue période de sommeil (défaut : 45 mn). --avec-reveil Ne tenir compte que des jours avec réveil programmé. --sans-reveil Ne tenir compte que des jours sans réveil programmé. INPUTFILE Un fichier contenant des heures de sommeil décrites en suivant le format décrit ci-dessous. OUTPUTFILE Un éventuel fichier de sortie. Le format du fichier d'entrée doit être le suivant : - chaque ligne correspond à une session de sommeil (sieste, nuit). - les éléments sur chaque ligne sont séparés par des espaces. - chaque ligne commence par une date (au format YYYYMMDD) correspondant au jour du début de la session. - à la suite de la date, on trouve une liste d'heures (au format [h]h:mm) correspondant pour la première à l'heure de début de session, et pour les suivantes à toutes les heures de réveil intermédiaire ou final, et doivent être dans l'ordre chronologique. - si un réveil a été produit par un élément extérieur (réveil par exemple), l'heure est préfixée par le caractère x. On considérera que si le dernier réveil est un réveil forcé, celui-ci a été programmé. Exemple : 20081205 0:50 6:12 7:00 8:22 x8:40 Exemple d'utilisation : Affichage d'un histogramme représentant la durée de sommeil par jour : ./stats_horaires.py -d -j horaires.txt Affichage d'un histogramme représentant les durées entre deux réveils constatés : ./stats_horaires.py -d -c horaires.txt Affichage d'un histogramme représentant les durées de long sommeil en début de session : ./stats_horaires.py -d -c horaires.txt --premier Licence : GPL v3 - http://www.gnu.org/licenses/gpl.txt """ import sys, getopt, re, string from math import floor try: my_getopt = getopt.gnu_getopt except AttributeError: my_getopt = getopt.getopt try: my_getopt = getopt.gnu_getopt except AttributeError: my_getopt = getopt.getopt nom_mois = ["janvier", "fevrier", "mars", \ "avril", "mai", "juin", \ "juillet", "aout", "septembre", \ "octobre", "novembre", "decembre"] def horaire_en_mn(horaire): if horaire[0] in ("x", "X"): horaire = horaire[1:] cv = re.compile(r"([0-9]+):([0-9][0-9])") return int(cv.sub("\g<1>", horaire)) * 60 + int(cv.sub("\g<2>", horaire)) def date_en_str(date): cv = re.compile(r"([0-9]{4})([0-9][0-9])([0-9][0-9])") annee = cv.sub("\g<1>", date) mois = cv.sub("\g<2>", date) jour = cv.sub("\g<3>", date) if jour[0] == "0": jour = jour[1] return jour + "-" + nom_mois[int(mois) - 1] + "-" + annee def duree(debut, fin): if len(debut) == 0 or len(fin) == 0 or debut == fin: return 0 debutmn = horaire_en_mn(debut) finmn = horaire_en_mn(fin) if debutmn > finmn: finmn += 60 * 24 return finmn - debutmn def lire_fichier(inputFile): result = [] f = open(inputFile, 'r') for ligne in f: elems = ligne.rstrip().split(' ') if len(elems) < 3: continue if (re.match("[0-9]{8}", elems[0])): date = elems[0] else: continue elemsreel = [e for e in elems[1:] if e != ""] result.append([date, elemsreel]) return result def seulement_avec_reveil_force(stats): result = [] for date, session in stats: if session[-1][0] in ('x', 'X'): result.append([date, session]) return result def seulement_sans_reveil_force(stats): result = [] for date, session in stats: if not session[-1][0] in ('x', 'X'): result.append([date, session]) return result def calculer_par_jour(stats): result = [] for date, session in stats: if len(session) == 0: continue debut = session[0] fin = session[-1] d = duree(debut, fin) debutmn = horaire_en_mn(debut) if debutmn <= 60*12: date = str(int(date) - 1) if not len(result) == 0 and result[- 1][0] == date: result[- 1][1] += d else: result.append([date, d]) return result def calcul_durees(stats, premier, minDuree): result = [] for date, session in stats: if len(session) == 0: continue pred = session[0] for horaire in session[1:]: if not horaire[0] in ('x', 'X'): d = duree(pred, horaire) if premier: if d > minDuree: result.append(d) break else: result.append(d) pred = horaire return result def ecrire_par_jour(nb_par_jour, f): for date, minutes in nb_par_jour: f.write(str(date) + " " + str(minutes) + "\n") def sauver_par_jour(nb_par_jour, outputFile): f = open(outputFile, 'w') ecrire_par_jour(nb_par_jour, f) def afficher_par_jour(nb_par_jour): import Gnuplot g = Gnuplot.Gnuplot(debug = 1) g.title('Minutes de sommeil par jour') g("set style data histograms ") nb_par_jour_nb = [] i = 0 max_duree = 0 for date, duree in nb_par_jour: nb_par_jour_nb.append([i, float(duree) / 60]) i += 1 if duree > max_duree: max_duree = duree max_duree = float(max_duree) / 60 g("set ylabel \"durée en minutes\"") g("set xrange [-0.5:" + str(i - 0.5) + "]") g("set yrange [0:" + str(max_duree + 0.5) + "]") graph = Gnuplot.Data(nb_par_jour_nb, with_ = "boxes") g.plot(graph) raw_input('Appuyer sur une touche pour continuer...\n') def calcul_distrib(durees, precision): d_min = min(durees) d_max = max(durees) periodes = range(d_min - precision / 2, d_max - precision / 2, precision) distrib = [] for p in periodes: distrib.append([p, len([d for d in durees if d >= p and d < p + precision])]) return distrib def sauver_durees(distrib_durees, outputFile): f = open(outputFile, 'w') ecrire_durees(distrib_durees, f) def ecrire_durees(distrib_durees, f): for duree in durees: f.write(str(duree[0]) + " " + str(duree[1]) + "\n") def affichage_durees(distrib_durees, titre): import Gnuplot g = Gnuplot.Gnuplot(debug = 1) if titre != '': titre = ' (' + titre + ')' g.title('Durée de sommeil sans réveil observé' + titre) g("set style data histograms ") g("set xlabel \"Durée (en mn)\"") g("set ylabel \"Nombre de réalisations\"") g("set xrange [0:" + str(max([i for [i, j] in distrib_durees]) + precision) + "]") g("set yrange [0:" + str(max([j for [i, j] in distrib_durees]) + 0.25) + "]") graph = Gnuplot.Data(distrib_durees, with_ = "boxes") g.plot(graph) raw_input('Appuyer sur une touche pour continuer...\n') def sauver_durees_brutes(durees, outputFile): f = pen(outputFile, 'w') ecrire_durees(durees, f) def ecrire_durees_brutes(durees, f): for duree in durees: f.write(str(duree) + "\n") def affichage_durees_brutes(durees, cpetit, centier, titre): import Gnuplot g = Gnuplot.Gnuplot(debug=1) if titre != '': titre = ' (' + titre + ')' g.title('Périodes de sommeil sans réveil mesuré' + titre) durees_d = [[1, duree] for duree in durees] g("set xrange [0.8:1.2]") g("set ylabel \"durée en minutes\"") g.plot(durees_d) ligne1 = Gnuplot.Data([[0.8, int(cpetit)], [1.2, int(cpetit)]], with_ = "lines 5") g.replot(ligne1) m = int(floor(float(max([int(d) for d in durees])) / int(centier))) print "de 1 à ", m for i in range(1, m + 1): print "ligne à", int(i * centier) ligne = Gnuplot.Data([[0.8, int(i * centier)], [1.2, int(i *centier)]], with_ = "lines 5") g.replot(ligne) raw_input('Appuyer sur une touche pour continuer...\n') if __name__ == "__main__": try: opts, args = my_getopt(sys.argv[1:], "djcshp:b", ["display", "jour", "cycle", "standard", "precision=", "brute", "help", "premier", "min-duree=", "avec-reveil", "sans-reveil"]) except getopt.GetoptError, msg: # Afficher l'aide et quitter print __doc__ print "Erreur: ", msg sys.exit(2) jour = False cycle = False inputFile = "" affichage = False outputFile = "" outputStandard = False cpetit = 20 centier = 60 * 1.5 precision = 6 brute = False premier = False minDuree = 45 sansReveil = False avecReveil = False for o, a in opts: if o in ("-o", "--help"): print __doc__ sys.exit(0) if o in ("-d", "--display"): affichage = True if o in ("-j", "--jour"): jour = True if o in ("-c", "--cycle"): cycle = True if o in ("-s", "--standard"): outputStandard = True if o in ("-p", "--precision"): precision = int(a) if o in ("-b", "--brute"): brute = True if o == "--premier": premier = True if o == "--min-duree": minDuree = int(a) if o == "--avec-reveil": avecReveil = True if o == "--sans-reveil": sansReveil = True if not cycle and not jour: print __doc__ print "Erreur : aucune statistique choisie." if len(args) == 0: print __doc__ print "Erreur : aucun fichier en entrée" sys.exit(2) if len(args) > 2: print __doc__ print "Erreur : trop de paramètres" sys.exit(2) inputFile = args[0] if len(args) == 2: outputFile = args[1] if outputFile == "" and not affichage and not outputStandard: print __doc__ print "Erreur : aucune sortie choisie" sys.exit(2) titre = '' if premier: titre = "première durée longue" stats = lire_fichier(inputFile) if avecReveil: stats = seulement_avec_reveil_force(stats) if premier: titre += ', ' titre += 'avec réveil forcé' if sansReveil: stats = seulement_sans_reveil_force(stats) if premier: titre += ', ' titre += 'sans réveil forcé' if jour: nb_par_jour = calculer_par_jour(stats) if outputFile != "": sauver_par_jour(nb_par_jour, outputFile) if outputStandard: ecrire_par_jour(nb_par_jour, sys.stdout) if affichage: afficher_par_jour(nb_par_jour) if cycle: durees = calcul_durees(stats, premier, minDuree) if brute: if outputFile != "": sauver_durees_brutes(durees, outputFile) if outputStandard: ecrire_durees_brutes(durees, sys.stdout) if affichage: affichage_durees_brutes(durees, cpetit, centier, titre) else: distrib_duree = calcul_distrib(durees, precision) if outputFile != "": sauver_durees(distrib_duree, outputFile) if outputStandard: ecrire_durees(distrib_duree, sys.stdout) if affichage: affichage_durees(distrib_duree, titre)