IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Calcul scientifique Python Discussion :

Optimisation de boucle !


Sujet :

Calcul scientifique Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Candidat au Club
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    2
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Mai 2009
    Messages : 2
    Par défaut Optimisation de boucle !
    Bonjour !

    Je pense avec un problème d'optimisation de mes boucles.

    sachant que l_u et l_f peuvent attentre 800*3600, j'aimerai pourvoir faire mon calcul en moins de 10h comme je l'ai calculé

    Avec vous une idée de comment optimiser cela ?

    Merci !




    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    user = N.array(map(int,d[:,0]))
    folder = N.array(map(str,d[:,1]))
    z = N.zeros_like(user)+1
     
    folderlist = uniq(folder)
    userlist = uniq(user)
    l_f = len(folderlist)
    l_u = len(userlist)
    size = l_u*l_f
     
    activity_count_user_folder = []
    for i in range(0,l_f):
    	print i, "out of ", len(folderlist)
    	cond1=(folder==folderlist[i])*z
    	activity_count_user = []
    	for j in range(0,l_u):
    		cond2 = (user==userlist[j])
    		cm = cond1*cond2
    		activity_count_user.append(cm.sum())
    	activity_count_user_folder.append(activity_count_user)

  2. #2
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Par défaut
    Ce n'est pas vraiment du calcul scientifique, là
    Mais je ne sais pas comment améliorer ton code, mais par exemple la boucle for interne peut vraisemblablement être supprimée avec qqch comme :
    activity_count_user.extend(user_list[N.where(userlist == user)])
    si tant est que userlist est un tableau Numpy.

  3. #3
    Membre Expert
    Avatar de DelphiManiac
    Homme Profil pro
    Homme à tout faire
    Inscrit en
    Mars 2002
    Messages
    1 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Homme à tout faire
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 147
    Par défaut
    Au premier coup d'œil, utiliser xrange au lien de range devrait pas mal améliorer, mais je ne sais pas de quel ordre.

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Je comprends bien à peu près ce que fait ce bout de code mais des points restent un peu brumeux pour moi.

    Est-ce que N est un alias pour le mot numpy ?

    Je ne connais pas la notation d[:,0] si bien que je ne vois pas ce qu'est la variable obtenue par user = N.array(map(int,d[:,0]))

    Puis je ne sais pas ce qu'est uniq:
    s'agirait-il de la fonction unique() existant dans Numpy qui serait mal écrite ou d'un alias ?
    Si bien que je ne sais pas exactement ce qu'on obtient comme variable userlist = uniq(user)

    Que font les expressions folder==folderlist[i] et user==userlist[j] ?
    Que fait l'expression cond1=(folder==folderlist[i])*z ?
    Il y a des tableaux de booléens mais je ne comprends pas précisément.


    Il faudrait aussi connaître la suite parce qu'on ne voit comme résultat de ce code que l'affichage de i, "out of ", len(folderlist).
    Selon ce à quoi sert activity_count_user_folder , il est possible d'envisager donner un autre type ou non à cette variable ou à d'autres.



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    print str(i) +  "out of " +  str(len(folderlist))
    # est plus rapide que
    print i, "out of ", len(folderlist)
    mais en en restant là ce n'est pas ce qui va beaucoup changer les choses.

    Un petit gain supplémentaire dans l'affichage peut ètre obtenu par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for i in xrange(0,l_f):
        if i%1000==0:
    	print str(i) +  "out of " +  str(len(folderlist)

  5. #5
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    D'ailleurs est-il nécéssaire de recalculer la longueur de folderlist à chaque itération ?

    De plus, évite les recherches de membre (le point . ) dans les boucles gourmandes.
    Tu peux stocker les deux fonctions append dans des variables locales, que tu utiliseras dans la boucle.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    folder_append = activity_count_user_folder.append
     
    for i in ...:
        # le code
        folder_append(...)

  6. #6
    Membre habitué
    Inscrit en
    Septembre 2007
    Messages
    9
    Détails du profil
    Informations forums :
    Inscription : Septembre 2007
    Messages : 9
    Par défaut
    Je ne suis pas expert en python (enfin pas encore ;-) ) , mais je me demande pourquoi tu utilise le range pour ta boucle?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    for i in range(0,l_f):
    	print i, "out of ", len(folderlist)
    	cond1=(folder==folderlist[i])*z
    Ne serait-il pas plus rapide de balayer directement la liste, car ta variable i, n'est utile que pour l'affichage du print.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    for folder_loop in folderlist:
    	cond1=(folder==folder_loop)*z
    Et l'affichage comme tout le monde le sais est un fonction lente et je ne vois pas trop l'intérêt d'afficher l'indice de la boucle à chaque parcours surtout pour un parcours de l_f : 800*3600 fois (presque 3 millions) juste pour de l'affichage.

    Si l'affichage du print ne durait qu'1 petite milliseconde, tu prendrait 48 minutes rien que pour afficher l'indice de ta boucle!
    Si le print met 5 ms tu perds presque 4 heures rien que pour l'affichage!

    Toujours réfléchir à ce que l'on met dans un boucle.
    Exemple :
    rappeler la fonction len(folderlist) 3 millions de fois ne sert à rien car tu connait déjà le résultat : l_f.

  7. #7
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Bon, j'ai compris
    1

    d[:,0] signifie "la colonne 0" de l'array bidimensionnel d.
    d[8,:] est la "8ième ligne" de l'array bidimensionnel d.

    L' array d[0,:] est un iterable que map() peut donc recevoir en argument.
    map(int,d[:,0]) fait des éléments de l'array des entiers.

    Ce qui me perturbait, c'est qu'on applique array() au produit de map() alors que d paraissait être un array dès le départ.
    Mais c'est parce que map() renvoie toujours une liste , donc pour retrouver un array, on est obligé d'appliquer array() sur le résultat de map(). L'array obtenu est une ligne puisque les éléments de map(int,d[:,0]) sont des entiers et non pas des sous-listes.

    d[8,:] peut se représenter aussi par d[8] parce que 'éventuels ':' après le 8 sont sous-entendus. Pour une colonne la notation d[:,x] est obligée.
    d[:,0] signifie donc "colonne 0 d'un array bidimensionnel" , mais s'il s'agit d'un array de dimension 3, la signification devient alors "les lignes 0 de tous les tableaux bidimensionnels qui sont en premier indice", ou "tous les tableaux bidimensionnels d'indice antépénultiéme égal à 0" pour un array de dimension 4, etc.

    Cependant il est certain que d est un array bidimensionnel parce que si ce n'était pas le cas la fonction int() dans le map() ne pourrait pas s'appliquer à des éléments qui seraient des lignes ou de nature pire encore, donc non transformables en entier.



    2
    z = N.zeros_like(user)+1 crée un tableau rempli de zéros de même shape que le tableau user, c'est à dire en l'occurence un tableau-ligne , puis ajoute 1 ---> tableau-ligne rempli de 1: [1 1 1 1 1 .....]




    3

    user==userlist[5] est équivalent à
    [user[0]==userlist[5] user[1]==userlist[5] user[2]==userlist[5] user[3]==userlist[5] user[4]==userlist[5] user[5]==userlist[5] user[6]==userlist[5]....]
    Donc cond2 = user==userlist[5] sera un tableau rempli de False sauf aux positions où la valeur du tableau sera celle de la position 5 de la liste. Finalement c'est un moyen de trouver toutes les positions où se trouve un élément donné.
    Par exemple si
    userlist = [3 , 89 , 3 , 50 , 20 , 3 , 7]
    et user = [3 89 3 50 20 3 7]
    alors user==userlist[5] sera [ True False True False False True False]

    C'est la même chose pour
    cond1=(folder==folderlist[i])*z
    sauf que le fait de multiplier par z transforme les True en 1 et le False en 0, ce qui permet un décompte.

    user et folder sont issus de d,
    donc ces tableaux-lignes ont la même longueur.
    Je prends donc la même longueur pour un exemple avec
    folderlist = ['8','1','22','13','46','52','22']
    et folder = ['8' '1' '22' '13' '46' '52' '22']
    on a (folder==folderlist[2])*z = [0 0 1 0 0 0 1]




    4
    Je pense que d est un array bidimensionnel du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    [[ 3  8]
     [89  1]
     [ 3 22]
     [50 13]
     [20 46]
     [ 3 52]
     [ 7 22]]
    qui collationne les couples (utilisateur,numero de folder) chaque fois qu'un utilisateur consulte un folder. Dans la colonne 0, les numéros d'utilisateurs peuvent apparaître plusieurs fois, et de même pour les numéros de dossiers dans la colonne 1.
    Pour éviter d'itérer sur des séquences contenant des éléments répétés, je pense que la fonction uniq() a pour mission de produire une liste sans doublons à partir d'un tableau qui en contient.
    Je ne sais pas comment elle est codée. Perso j'utilise
    li_sans_doublons = list(set(li))
    quand la liste li n'est pas trop grande. Je n'ai pas testé la vitesse, mais à mon avis ça doit être foufroyant.



    5
    Bon, il semble donc que le code passe en revue les folders (itération sur i) et que
    pour chaque folder i:

    - il fait la somme des 1 qui apparraissent dans
    (folder==folderlist[i])*z * (user==userlist[j])
    c'est le nombre de fois que l'utilisateur j a utilisé le folder i

    - il ajoute ce nombre à activity_count_user
    Il n'y a donc dans activity_count_user d'un folder i que des nombres de consulations de ce dossier i, sans liaison avec les identités des utilisateurs mais ce nombre de consultation est identique au nombre d'utilisateurs.

    Et finalement activity_count_user_folder liste les listes d'activités de tous les folders.



    Bon, tout ceci, c'est évidemment si je ne me trompe pas sur la nature de d.
    On en est réduit à reconstituer les questions, quand il n'y a pas assez de renseignements qui sont donnés.


    Mais je donne les réponses aussi ! 6 - Pour ce qui est d'optimiser:



    - j'ai testé la fonction sum() et je me suis aperçu que
    [False False True False False False True].sum() donne 2.
    Donc ce n'est pas la peine de se casser la tête avec z = N.zeros_like(user)+1 :
    cond1 peut s'écrire simplement cond1=(folder==folderlist[i])
    Les décomptes se feront sur des tableaux de booléens.





    - ensuite,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	for j in range(0,l_u):
    		cond2 = (user==userlist[j])
    c'est lourd. Remplacer par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	for ut in userlist:
    		cond2 = (user==ut)



    - puis,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    	activity_count_user = []
    	for ut in userlist:
    		cond2 = (user==ut)
    		activity_count_user.append((cond1*cond2).sum())
    évite de mettre à chaque fois une valeur cond1*cond2 dans une variable cm




    - et hop, encore une fois et plus de variable cond2 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    	activity_count_user = []
    	for ut in userlist:
    		activity_count_user.append((cond1*(user==ut)).sum())



    - et on peut finalement mettre ça sous forme de list comprehension:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    activity_count_user = [ (cond1*(user==ut)).sum()  for  ut in userlist ]




    - on fait la même chose pour
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for i in range(0,l_f):
    	print i, "out of ", len(folderlist)
    	cond1=(folder==folderlist[i])*z
    on remplace par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for fo in folderlist:
    	print i, "out of ", len(folderlist)
    	cond1=(folder==fo)




    - et finalement encoe, pour éviter une affectation de valeur à une variable cond1, on fait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    activity_count_user_folder = []
    for i,fo in enumerate(folderlist):
    	print i, "out of ", l_f
    	activity_count_user = [((folder==fo)*(user==ut)).sum() for ut in userlist ]
    	activity_count_user_folder.append(activity_count_user)





    - si on aime la concision et qu'on est prêt à se passer de l'impression de i, "out of ", l_f
    on finit par deux list comprehension imbriquées:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #
    activity_count_user_folder = [ [ ((folder==fo)*(user==ut)).sum() for ut in userlist ] for fo in folderlist ]
    #




    - et miracle, si on aime la concision (un peu alambiquée quand même) ET qu'on tienne à son print i, "out of ", l_f , Python assure toujours :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    def fprint(i):
        if i//100==0:
            print i, "out of ", l_f
    
    activity_count_user_folder = []
    [ (activity_count_user_folder.append( [ ((folder==fo)*(user==ut)).sum() for ut in userlist ] ) , fprint(i) )  for i,fo in enumerate(folderlist) ]
    print activity_count_user_folder
    #


    En sommant directement sur des booléens, en itérant directement dans les listes et non plus les indices, en éliminant des affectations de variables, et en utilisant des listes comprehension, ça devrait s'accélérer notablement.
    J'aimerais bien avoir un retour, vu le mal que je me suis donné pour comprendre la question.
    #

Discussions similaires

  1. Comment optimiser plusieurs boucles FOR-END imbriquées
    Par totoc1001 dans le forum MATLAB
    Réponses: 26
    Dernier message: 13/05/2007, 17h59
  2. [Tableaux] Optimisation de boucles
    Par xdoreau dans le forum Langage
    Réponses: 4
    Dernier message: 12/02/2007, 11h28
  3. Optimisation de boucle 'while..do'
    Par delphi5user dans le forum Delphi
    Réponses: 10
    Dernier message: 25/07/2006, 22h37
  4. Probleme optimisation de boucles
    Par panda31 dans le forum C
    Réponses: 13
    Dernier message: 06/04/2006, 15h10
  5. Réponses: 4
    Dernier message: 17/01/2006, 19h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo