2013-07-31 6 views
0

J'ai une donnée d'entrée donnée (par un tiers et donc inaltérable) dans la structure suivante: La donnée est une liste de 4-tuples, chaque 4-tuple représentant une sortie. Le premier élément de chaque sortie est une liste de catégories de longueur 1 à 5 choisies parmi un total de 20 catégories possibles (sans répétitions); le deuxième élément est le nombre de participants impliqués; le troisième est un objet datetime indiquant le début de la sortie; et le dernier et quatrième élément est un objet datetime indiquant la fin de la sortie.Accélération des transformations de données

Maintenant, je dois transformer ces données dans le format suivant: Pour chaque catégorie j'ai besoin de calculer (a) le nombre de sorties de cette catégorie, (b) le temps total passé, (c) le temps moyen passé par sortie, (d) le total des «heures-homme», c'est-à-dire la somme de la durée de chaque sortie multipliée par le nombre de participants de la même sortie, et e) les «heures-personnes» moyennes par sortie.

Ma première tentative naïve est la suivante:

def transform (data): 
    t = defaultdict (lambda: (0, 0, 0)) 

    for row in data: 
     delta = row [3] - row [2] 
     hours = delta.days * 24 + delta.seconds/3600 
     manHours = row [1] * hours 
     for cat in row [0]: 
      t [cat] = (t [cat] [0] + 1, t [cat] [1] + hours, t [cat] [2] + manHours) 

    return {k: (v [0], v [1], v [1]/v [0], v [2], v [2]/v [0]) for k, v in t.items() } 

et je suis ce profil avec les éléments suivants:

cats = [_ for _ in range (20) ] 
for test in range (1000): 
    data = [ (random.sample (cats, random.randint (1, 5)), random.randint (2, 40), datetime.datetime (2013, 1, 1, 8), datetime.datetime (2013, 1, 1, 9)) for _ in range (1000) ] 
    transform (data) 

utilisant -m cProfile. J'ai lu plusieurs fois sur stackoverflow les avantages incroyables d'itertools pour l'itération, le groupement, le comptage, etc., jusqu'à ce que les utilisateurs préfèrent utiliser des outils plutôt que des dictées ou des listes simples.

Je voudrais profiter de ce module, mais je ne sais pas comment tirer le meilleur parti de ce module. Par conséquent:

a) De quelle manière la fonction de transformation peut-elle être optimisée dans le temps (accélérée)?

b) De quelle manière itertools peut-il m'aider dans cette entreprise?

Je vous remercie d'avance pour vos réponses.

-

Pour référence: Sur ma boîte (AMD Phenom II Quad, 4 Go de RAM, 4 Go swap) en utilisant Python 3.3.1 (default, Apr 17 2013, 22:30:32) [GCC 4.7.3] on linux le profileur recrache: 1000 2.027 0.002 2.042 0.002 trans.py:6(transform). Passer de pyhton3 à pypy n'est pas une option.

EDIT: données exemples (en utilisant ISO-représentation) ou utiliser le second extrait de code pour créer (évidemment pas la vie réelle) des données:

[([6, 4, 15], 3, '2013-07-31T17:23:00', '2013-07-31T18:40:00'), ([9, 18, 5], 15, '2013-07-08T17:49:00', '2013-07-08T18:57:00'), ([7, 14, 17, 12, 0], 18, '2013-07-20T08:16:00', '2013-07-20T09:06:00'), ([6, 1], 32, '2013-07-31T07:14:00', '2013-07-31T09:01:00'), ([17, 7], 7, '2013-07-05T06:59:00', '2013-07-05T07:52:00')] 

2013-08-02: profilage pillmuncher de idée, malheureusement, a donné lieu à l'utilisation numpy étant 360% plus lent que sans l'utiliser:

1000 1.828 0.002 1.842 0.002 prof.py:8(transform) #original function 
1000 0.159 0.000 8.457 0.008 prof.py:43(transform3) #numpy function 
+1

Avez-vous trouvé que cette partie de votre code est un goulot d'étranglement? – Blender

+0

Malheureusement, c'est en effet. Nous devons exécuter cette transformation assez souvent et sur différents sous-ensembles du total des données, ce qui empêche la mise en cache. – Hyperboreus

+0

Et d'ailleurs, je suis toujours intrigué par la façon d'optimiser le code et comment obtenir le jus entier de modules que je n'utilise pas souvent. – Hyperboreus

Répondre

0

Vous pouvez probablement utiliser numpy:

from collections import defaultdict 
from datetime import datetime 

import numpy as np 

def transform(data): 
    pair_type = np.dtype([('team_size', int), ('duration', 'timedelta64[s]')]) 
    rec_array = np.core.records.array 
    total = np.sum 
    mean = np.mean 
    one_hour = np.timedelta64(1, 'h') 
    tmp = defaultdict(list) 
    for categories, team_size, begin, end in data: 
     for category in categories: 
      tmp[category].append((team_size, end - begin)) 
    for category, pairs in tmp.items(): 
     pairs = rec_array(pairs, dtype=pair_type) 
     hours = pairs.duration/one_hour 
     man_hours = pairs.team_size * hours 
     yield category, (
       len(pairs), 
       total(hours), 
       mean(hours), 
       total(man_hours), 
       mean(man_hours)) 

some_data = ... 
result = dict(transform(some_data)) 

Je ne sais pas si c'est plus rapide. Si vous l'essayez, veuillez rapporter le résultat.

Aussi, mon fu numpy n'est pas terrible. Donc, si quelqu'un sait comment l'améliorer, s'il vous plaît dites-moi.

+0

Merci. Je suis maintenant assis à une machine différente. Dès que je serai de nouveau assis devant la boîte de référence, je présenterai votre solution sur mon HW et je vous communiquerai les résultats. – Hyperboreus

+0

Merci encore pour votre idée. Malheureusement, numpy ralentit encore plus. Jetez un oeil à l'édition que j'ai faite à ma question. Néanmoins, merci pour l'entrée. – Hyperboreus

Questions connexes