1 #@ MODIF Table Utilitai DATE 16/10/2007 AUTEUR REZETTE C.REZETTE
2 # -*- coding: iso-8859-1 -*-
3 # CONFIGURATION MANAGEMENT OF EDF VERSION
4 # ======================================================================
5 # COPYRIGHT (C) 1991 - 2004 EDF R&D WWW.CODE-ASTER.ORG
6 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
7 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
8 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
9 # (AT YOUR OPTION) ANY LATER VERSION.
11 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
12 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
13 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
14 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
16 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
17 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
18 # 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
19 # ======================================================================
21 # RESPONSABLE MCOURTOI M.COURTOIS
22 __all__ = ['Table', 'merge']
28 from types import ListType, TupleType, IntType, LongType, FloatType, ComplexType, \
29 DictType, StringType, StringTypes, UnicodeType, NoneType
30 EnumTypes = (ListType, TupleType)
31 NumberTypes = (IntType, LongType, FloatType, ComplexType)
34 from Macro.externe_mess import UTMESS
36 if not sys.modules.has_key('Graph'):
38 from Utilitai import Graph
42 # formats de base (identiques à ceux du module Graph)
44 'csep' : ' ', # séparateur
45 'ccom' : '#', # commentaire
46 'cdeb' : '', # début de ligne
47 'cfin' : '\n', # fin de ligne
48 'sepch' : ';', # séparateur entre deux lignes d'une cellule
49 'formK' : '%-8s', # chaines
50 'formR' : '%12.5E', # réels
51 'formI' : '%8d' # entiers
53 # type par défaut des chaines de caractères
56 # ------------------------------------------------------------------------------
57 # ------------------------------------------------------------------------------
58 # ------------------------------------------------------------------------------
59 class TableBase(object):
60 """Classe pour partager les méthodes d'impression entre Table et Colonne
61 (c'est surtout utile pour vérifier que l'extraction et les filtres sur les
62 colonnes sont corrects).
73 return self.ReprTable()
74 def Croise(self, **kargs):
75 raise NotImplementedError, 'Must be defined in a derived class'
78 """Retourne le nombre de ligne dans la Table/Colonne.
82 # ------------------------------------------------------------------------------
83 def Impr(self, FICHIER=None, FORMAT='TABLEAU', dform=None, **opts):
84 """Impresssion de la Table selon le format spécifié.
85 FICHIER : nom du(des) fichier(s). Si None, on dirige vers stdout
86 dform : dictionnaire de formats d'impression (format des réels,
87 commentaires, saut de ligne...)
91 'TABLEAU' : { 'mode' : 'a', 'driver' : self.ImprTableau, },
92 'ASTER' : { 'mode' : 'a', 'driver' : self.ImprTableau, },
93 'XMGRACE' : { 'mode' : 'a', 'driver' : self.ImprGraph, },
94 'AGRAF' : { 'mode' : 'a', 'driver' : self.ImprTableau, },
95 'TABLEAU_CROISE' : { 'mode' : 'a', 'driver' : self.ImprTabCroise, },
100 'dform' : DicForm.copy(),
101 'mode' : para[FORMAT]['mode'],
103 if dform != None and type(dform) == DictType:
104 kargs['dform'].update(dform)
108 if not kargs.get('PAGINATION'):
109 # call the associated driver
110 para[FORMAT]['driver'](**kargs)
113 if not type(kargs['PAGINATION']) in EnumTypes:
114 ppag = [kargs['PAGINATION'],]
116 ppag = list(kargs['PAGINATION'])
117 del kargs['PAGINATION']
119 # paramètres hors ceux de la pagination
120 lkeep = [p for p in self.para if ppag.count(p)==0]
121 # création des listes des valeurs distinctes
124 lvp = getattr(self,p).values()
127 if it != None and lvn.count(it) == 0:
131 # création des n-uplets
132 s = '[['+','.join(['x'+str(i) for i in range(npag)])+'] '
133 s += ' '.join(['for x'+str(i)+' in lvd['+str(i)+']' for i in range(npag)])+']'
136 except SyntaxError, s:
137 UTMESS('F','Table','Erreur lors de la construction des n-uplets')
138 # pour chaque n-uplet, on imprime la sous-table
141 for i in range(npag):
142 tab = tab & (getattr(tab,ppag[i]) == nup[i])
145 tab.titr += sl+ppag[i]+': '+str(nup[i])
146 tab[lkeep].Impr(**kargs)
148 # ------------------------------------------------------------------------------
149 def ImprTableau(self,**kargs):
150 """Impression au format TABLEAU ou ASTER
153 if kargs.get('FICHIER')<>None:
154 f=open(kargs['FICHIER'],kargs['mode'])
158 f.write(self.ReprTable(**kargs) + '\n')
160 if kargs.get('FICHIER')<>None:
163 # ------------------------------------------------------------------------------
164 def ReprTable(self,FORMAT='TABLEAU',dform=None,**ignore):
165 """Représentation d'une Table ou d'une Colonne sous forme d'un tableau.
170 if not type(para) in EnumTypes:
174 dform = DicForm.copy()
175 # est-ce que l'attribut .type est renseigné ?
176 typdef=typ<>[None]*len(typ)
178 # ['']+ pour ajouter un séparateur en début de ligne
180 # lmax : largeur max des colonnes = max(form{K,R,I},len(parametre))
184 larg_max=max([len(str(p))] + \
185 [len(FMT(dform,k,t) % 0) for k in ('formK','formR','formI')])
186 lspa.append(FMT(dform,'formK',t,larg_max,str(p)) % p)
187 lmax.append(larg_max)
189 stype=dform['csep'].join([''] + \
190 [FMT(dform,'formK',typ[i],lmax[i]) % typ[i] for i in range(len(para))])
191 txt.append(dform['ccom'])
192 txt.append(dform['ccom']+'-'*80)
193 txt.append(dform['ccom'])
194 ASTER=(FORMAT=='ASTER')
196 txt.append('#DEBUT_TABLE')
199 txt.extend(['#TITRE '+lig for lig in self.titr.split('\n')])
201 txt.extend([dform['ccom']+lig for lig in self.titr.split('\n')])
202 txt.append(dform['csep'].join(lspa))
212 if type(rep) is FloatType:
213 lig.append(FMT(dform,'formR',t,lmax[i]) % rep)
215 elif type(rep) in (IntType, LongType):
216 lig.append(FMT(dform,'formI',t,lmax[i]) % rep)
223 s=FMT(dform,'formK',t,lmax[i],rep) % str(rep)
224 # format AGRAF = TABLEAU + '\' devant les chaines de caractères !
229 lig2 = [dform['sepch'].join(ch.splitlines()) for ch in lig]
230 txt.append(dform['csep'].join(lig2))
232 txt.append('#FIN_TABLE')
233 # ajout du debut de ligne
234 if dform['cdeb']<>'':
235 txt=[dform['cdeb']+t for t in txt]
237 return dform['cfin'].join(txt)
238 # ------------------------------------------------------------------------------
239 def ImprTabCroise(self,**kargs):
240 """Impression au format TABLEAU_CROISE d'une table ayant 3 paramètres.
242 # création du tableau croisé et impression au format TABLEAU
244 kargs['FORMAT']='TABLEAU'
246 # ------------------------------------------------------------------------------
247 def ImprGraph(self, **kargs):
248 """Impression au format XMGRACE : via le module Graph
251 if len(self.para) != 2:
252 UTMESS('A','Table','La table doit avoir exactement deux paramètres '\
253 'pour une impression au format XMGRACE.')
255 # suppression des lignes contenant une cellule vide
256 tnv = getattr(self, self.para[0]).NON_VIDE() \
257 & getattr(self, self.para[1]).NON_VIDE()
261 'Val' : [getattr(tnv, tnv.para[0]).values(),
262 getattr(tnv, tnv.para[1]).values()],
265 if args['LEGENDE']==None: del args['LEGENDE']
266 Graph.AjoutParaCourbe(dicC, args)
267 graph.AjoutCourbe(**dicC)
269 # Surcharge des propriétés du graphique et des axes
270 # (bloc quasiment identique dans impr_fonction_ops)
271 if args.get('TITRE'): graph.Titre=args['TITRE']
272 if args.get('BORNE_X'):
273 graph.Min_X=args['BORNE_X'][0]
274 graph.Max_X=args['BORNE_X'][1]
275 if args.get('BORNE_Y'):
276 graph.Min_Y=args['BORNE_Y'][0]
277 graph.Max_Y=args['BORNE_Y'][1]
278 if args.get('LEGENDE_X'): graph.Legende_X=args['LEGENDE_X']
279 if args.get('LEGENDE_Y'): graph.Legende_Y=args['LEGENDE_Y']
280 if args.get('ECHELLE_X'): graph.Echelle_X=args['ECHELLE_X']
281 if args.get('ECHELLE_Y'): graph.Echelle_Y=args['ECHELLE_Y']
282 if args.get('GRILLE_X'): graph.Grille_X=args['GRILLE_X']
283 if args.get('GRILLE_Y'): graph.Grille_Y=args['GRILLE_Y']
288 UTMESS('A','Table','Les cellules ne doivent contenir que des nombres réels')
290 # ------------------------------------------------------------------------------
291 # ------------------------------------------------------------------------------
292 # ------------------------------------------------------------------------------
293 class Table(TableBase):
294 """Une table est construite comme une liste de lignes, chaque ligne est
296 On crée puis on ajoute les lignes avec la méthode append :
298 t.append(dict(a=1,b=2))
299 t.append(dict(a=3,b=4))
300 La méthode __iter__ définit un itérateur sur les lignes de la table,
301 __repr__ retourne une représentation de la table, utilisée par "print t".
302 Grace à la classe Colonne et à sa méthode _extract, il est possible
303 de construire une sous-table qui satisfait un critère donné.
304 Le critère est donné par une fonction Python qui retourne vrai
305 ou faux si la valeur d'une colonne respecte le critère ou non.
309 soustable = t.a._extract(critere)
310 t.a retourne un objet intermédiaire de la classe Colonne qui mémorise
311 le nom de la colonne demandée (a, ici).
313 # ------------------------------------------------------------------------------
314 def __init__(self, rows=[], para=[], typ=[], titr=''):
315 """Constructeur de la Table :
316 rows : liste des lignes (dict)
317 para : liste des paramètres
318 type : liste des types des paramètres
319 titr : titre de la table
321 self.rows = [r for r in rows if r.values() != [None]*len(r.values())]
322 self.para = list(para)
324 if self.para.count(i) != 1 :
325 UTMESS('F','Table','Parametre en double: %s' %i)
326 if len(typ) == len(self.para):
327 self.type = list(typ)
329 self.type = [None]*len(self.para)
332 # ------------------------------------------------------------------------------
334 """Retourne une copie de la table.
339 return Table(rows, self.para[:], self.type[:], self.titr)
341 # ------------------------------------------------------------------------------
342 def append(self, obj):
343 """Ajoute une ligne (type dict) qui peut éventuellement définir un
344 nouveau paramètre."""
347 if not p in self.para:
349 self.type.append(_typaster(obj[p]))
351 ip=self.para.index(p)
352 self.type[ip]=_typaster(obj[p], self.type[ip])
353 self.rows.append(obj)
355 # ------------------------------------------------------------------------------
356 def SansColonneVide(self):
357 """Retourne une copie de la table dans laquelle on a supprimé les colonnes
358 vides (les lignes vides sont automatiquement supprimées).
363 if len(tab[para]) == 0:
367 # ------------------------------------------------------------------------------
368 def __setitem__(self, k_para, k_value):
369 """Ajoute une colonne k_para dont les valeurs sont dans k_value"""
372 if k_para in self.para :
373 UTMESS('F','Table','(setitem) Le parametre %s existe déjà.' % k_para)
374 self.para.append(k_para)
375 self.type.append(_typaster(k_value[0]))
379 row[k_para]=k_value[i]
380 self.type[-1]=_typaster(k_value[i], self.type[-1])
384 for j in range(i,len(k_value)):
385 self.append({k_para:k_value[j]})
387 # ------------------------------------------------------------------------------
388 def fromfunction(self, nom_para, funct, l_para=None, const=None):
389 """Ajoute une colonne `nom_para` en évaluant la fonction `funct` sur
390 la valeur des paramètres `l_para` (qui doivent exister dans la table).
391 Si `l_para` n'est pas fourni, on prend `funct`.nompar (FORMULE Aster).
392 On peut passer un dictionnaire de constantes dans `const`. Quand on
393 utilise une FORMULE Aster, les constantes sont prises dans le contexte
397 if not hasattr(funct, '__call__'):
398 UTMESS('F', 'Table', "(fromfunction) '%s' n'a pas d'attribut '__call__'." \
400 if nom_para in self.para :
401 UTMESS('F','Table','Le parametre %s existe déjà.' % nom_para)
403 if not hasattr(funct, 'nompar'):
404 UTMESS('F', 'Table', "(fromfunction) '%s' n'a pas d'attribut 'nompar'." \
406 l_para = funct.nompar
407 if not type(l_para) in EnumTypes:
409 not_found = ', '.join([p for p in l_para if not p in self.para])
411 UTMESS('F','Table','Parametre(s) absent(s) de la table : %s' % not_found)
414 if type(const) is not DictType:
415 UTMESS('F', 'Table', "L'argument 'const' doit etre de type 'dict'.")
416 # liste des valeurs des paramètres
419 vals = getattr(self, para).values()
421 tabpar = transpose.transpose(tabpar)
422 # évaluation de la fonction sur ces paramètres
425 # si un paramètre est absent, on ne peut pas évaluer la formule
429 vectval.append(funct(*lpar, **const))
430 # ajout de la colonne
431 self[nom_para] = vectval
433 # ------------------------------------------------------------------------------
435 """Itère sur les lignes de la Table"""
436 return iter(self.rows)
438 # ------------------------------------------------------------------------------
439 def __getattr__(self, column):
440 """Construit un objet intermediaire (couple table, colonne)"""
442 if not column in self.para:
445 typ=self.type[self.para.index(column)]
446 return Colonne(self, column, typ)
448 # ------------------------------------------------------------------------------
449 def sort(self, CLES=None, ORDRE='CROISSANT'):
451 CLES : liste des clés de tri
452 ORDRE : CROISSANT ou DECROISSANT
454 # par défaut, on prend tous les paramètres
457 # vérification des arguments
458 if not type(CLES) in EnumTypes:
462 not_found = ', '.join([p for p in CLES if not p in self.para])
464 UTMESS('F', 'Table', 'Parametre(s) absent(s) de la table : %s' % not_found)
465 if not ORDRE in ('CROISSANT', 'DECROISSANT'):
466 UTMESS('F', 'Table', 'Valeur incorrecte pour ORDRE : %s' % ORDRE)
468 self.rows = sort_table(self.rows, self.para, CLES, (ORDRE=='DECROISSANT'))
470 # ------------------------------------------------------------------------------
471 def __delitem__(self, args):
472 """Supprime les colonnes correspondantes aux éléments de args """
473 if not type(args) in EnumTypes:
479 del new_type[new_para.index(item)]
480 new_para.remove(item)
481 for line in new_rows:
483 return Table(new_rows, new_para, new_type, self.titr)
485 # ------------------------------------------------------------------------------
486 def __getitem__(self, args):
487 """Extrait la sous table composée des colonnes dont les paramètres sont dans args """
488 if not type(args) in EnumTypes:
495 for item in new_para:
496 if not item in self.para:
498 new_type.append(self.type[self.para.index(item)])
501 for item in new_para:
502 new_line[item]=line.get(item)
503 new_rows.append(new_line)
504 return Table(new_rows, new_para, new_type, self.titr)
506 # ------------------------------------------------------------------------------
507 def __and__(self, other):
508 """Intersection de deux tables (opérateur &)"""
509 if other.para<>self.para:
510 UTMESS('A','Table','Les paramètres sont différents')
513 tmp = [ r for r in self if r in other.rows ]
514 return Table(tmp, self.para, self.type, self.titr)
516 # ------------------------------------------------------------------------------
517 def __or__(self, other):
518 """Union de deux tables (opérateur |)"""
519 if other.para<>self.para:
520 UTMESS('A','Table','Les paramètres sont différents')
524 tmp.extend([ r for r in other if r not in self ])
525 return Table(tmp, self.para, self.type[:], self.titr)
527 # ------------------------------------------------------------------------------
529 """Renvoie la table sous la forme d'un dictionnaire de listes dont les
530 clés sont les paramètres.
533 for column in self.para:
534 dico[column]=Colonne(self, column).values()
537 # ------------------------------------------------------------------------------
538 def dict_CREA_TABLE(self):
539 """Renvoie le dictionnaire des mots-clés à fournir à la commande CREA_TABLE
540 pour produire une table_sdaster.
542 dico={ 'TITRE' : ['%-80s' % lig for lig in self.titr.split('\n')],
544 # remplissage de chaque occurence (pour chaque paramètre) du mot-clé facteur LISTE
545 for i in range(len(self.para)):
546 # nom du paramètre et type si K*
547 d={ 'PARA' : self.para[i], }
550 UTMESS('F', 'Table', 'Type du paramètre %s non défini.' %\
554 if not typ in ('K8', 'K16', 'K24'):
555 UTMESS('A','Table','Type du paramètre %s forcé à %s' % (self.para[i],Kdef))
562 # valeurs sans trou / avec trou
563 vals=getattr(self, self.para[i]).values()
564 if vals.count(None)==0:
567 d['NUME_LIGN'] = [j+1 for j in range(len(vals)) if vals[j]<>None]
568 d[mc] = [v for v in vals if v <>None]
570 UTMESS('I','Table','Colonne %s vide' % self.para[i])
572 dico['LISTE'].append(d)
573 if len(dico['LISTE'])==0:
574 UTMESS('F','Table','La table est vide')
577 # ------------------------------------------------------------------------------
578 def Array(self,Para,Champ):
579 """Renvoie sous forme de NumArray le résultat d'une extraction dans une table
580 méthode utile à macr_recal
583 __Rep = self[Para,Champ].values()
584 F = Numeric.zeros((len(__Rep[Para]),2), Numeric.Float)
585 for i in range(len(__Rep[Para])):
586 F[i][0] = __Rep[Para][i]
587 F[i][1] = __Rep[Champ][i]
591 # ------------------------------------------------------------------------------
593 """Retourne un tableau croisé P3(P1,P2) à partir d'une table ayant
594 trois paramètres (P1, P2, P3).
596 if len(self.para)<>3:
597 UTMESS('A', 'Table', 'La table doit avoir exactement trois paramètres.')
599 py, px, pz = self.para
600 ly, lx, lz = [getattr(self,p).values() for p in self.para]
602 #lpz='%s=f(%s,%s)' % (pz,px,py)
603 lpz='%s/%s' % (px,py)
605 # attention aux doublons dans lx et ly
607 if it<>None and new_para.count(it)==0:
611 if it<>None and newx.count(it)==0:
616 taux = (getattr(self,px)==x)
620 new_type=[self.type[0],] + [self.type[2]]*len(ly)
622 if new_titr<>'': new_titr+='\n'
623 new_titr+=pz + ' FONCTION DE ' + px + ' ET ' + py
624 return Table(new_rows, new_para, new_type, new_titr)
626 # ------------------------------------------------------------------------------
627 def Renomme(self, pold, pnew):
628 """Renomme le paramètre `pold` en `pnew`.
630 if not pold in self.para:
631 raise KeyError, 'Paramètre %s inexistant dans cette table' % pold
632 elif self.para.count(pnew)>0:
633 raise KeyError, 'Le paramètre %s existe déjà dans la table' % pnew
635 self.para[self.para.index(pold)] = pnew
637 lig[pnew] = lig[pold]
640 # ------------------------------------------------------------------------------
641 # ------------------------------------------------------------------------------
642 # ------------------------------------------------------------------------------
643 class Colonne(TableBase):
644 """Classe intermédiaire pour mémoriser un couple (table, nom de colonne)
645 et exprimer les critères d'extraction sous une forme naturelle en python
646 en surchargeant les operateurs <, >, <> et =.
647 Alors on peut écrire la requete simple :
649 Ainsi que des requetes plus complexes :
650 soustable=t.a<10 and t.b <4
652 soustable=t.a<10 or t.b <4
653 Les "alias" EQ, NE, LE, LT, GE, GT permettent à la macro IMPR_TABLE
654 d'utiliser directement le mot-clé utilisateur CRIT_COMP défini dans le
655 catalogue : getattr(Table,CRIT_COMP).
657 # ------------------------------------------------------------------------------
658 def __init__(self, table, column, typ=None):
659 """Constructeur (objet Table associé, paramètre de la colonne, type du
663 self.rows=self.Table.rows
668 # ------------------------------------------------------------------------------
669 def _extract(self, fun):
670 """Construit une table avec les lignes de self.Table
671 dont l'élément de nom self.para satisfait le critère fun,
672 fun est une fonction qui retourne vrai ou faux
674 return Table([row for row in self.Table if fun(row.get(self.para))], self.Table.para, self.Table.type, self.Table.titr)
676 # ------------------------------------------------------------------------------
677 def __le__(self, VALE):
678 if type(VALE) in EnumTypes :
682 return self._extract(lambda v: v<>None and v<=crit)
684 # ------------------------------------------------------------------------------
685 def __lt__(self, VALE):
686 if type(VALE) in EnumTypes :
690 return self._extract(lambda v: v<>None and v<crit)
692 # ------------------------------------------------------------------------------
693 def __ge__(self, VALE):
694 if type(VALE) in EnumTypes :
698 return self._extract(lambda v: v<>None and v>=crit)
700 # ------------------------------------------------------------------------------
701 def __gt__(self, VALE):
702 if type(VALE) in EnumTypes :
706 return self._extract(lambda v: v<>None and v>crit)
708 # ------------------------------------------------------------------------------
709 def __eq__(self, VALE, CRITERE='RELATIF', PRECISION=0.):
710 if not type(VALE) in EnumTypes :
712 if type(VALE[0]) in StringTypes:
713 stripVALE = [value.strip() for value in VALE]
714 return self._extract(lambda v: str(v).strip() in stripVALE)
717 return self._extract(lambda v : v in VALE)
718 elif CRITERE=='ABSOLU':
719 return self._extract(lambda v : _func_test_abs(v, VALE, PRECISION))
721 return self._extract(lambda v : _func_test_rela(v, VALE, PRECISION))
723 # ------------------------------------------------------------------------------
724 def REGEXP(self, regexp):
725 """Retient les lignes dont le paramètre satisfait l'expression
728 if not type(regexp) in StringTypes:
729 return self._extract(lambda v : False)
730 return self._extract(lambda v : v != None and re.search(regexp, v) != None)
732 # ------------------------------------------------------------------------------
733 def __ne__(self, VALE, CRITERE='RELATIF', PRECISION=0.):
734 if not type(VALE) in EnumTypes :
736 if type(VALE[0]) in StringTypes:
737 stripVALE = [value.strip() for value in VALE]
738 return self._extract(lambda v: str(v).strip() not in stripVALE)
741 return self._extract(lambda v : v not in VALE)
742 elif CRITERE=='ABSOLU':
743 return self._extract(lambda v : not (_func_test_abs(v, VALE, PRECISION)))
745 return self._extract(lambda v : not (_func_test_rela(v, VALE, PRECISION)))
747 # ------------------------------------------------------------------------------
749 # important pour les performances de récupérer le max une fois pour toutes
751 return self._extract(lambda v: v==maxi)
753 # ------------------------------------------------------------------------------
755 # important pour les performances de récupérer le min une fois pour toutes
757 return self._extract(lambda v: v==mini)
759 # ------------------------------------------------------------------------------
761 # important pour les performances de récupérer le max une fois pour toutes
762 abs_maxi=max([abs(v) for v in self.values() if type(v) in NumberTypes])
763 return self._extract(lambda v: v==abs_maxi or v==-abs_maxi)
765 # ------------------------------------------------------------------------------
767 # important pour les performances de récupérer le min une fois pour toutes
768 abs_mini=min([abs(v) for v in self.values() if type(v) in NumberTypes])
769 # tester le type de v est trop long donc pas de abs(v)
770 return self._extract(lambda v: v==abs_mini or v==-abs_mini)
772 # ------------------------------------------------------------------------------
774 """Itère sur les éléments de la colonne"""
775 for row in self.Table:
776 # si l'élément n'est pas présent on retourne None
777 yield row.get(self.para)
778 #yield row[self.para]
780 # ------------------------------------------------------------------------------
781 def __getitem__(self, i):
782 """Retourne la ième valeur d'une colonne"""
783 return self.values()[i]
785 # ------------------------------------------------------------------------------
787 """Renvoie la liste des valeurs"""
788 return [r.get(self.para,None) for r in self.Table]
790 def not_none_values(self):
791 """Renvoie la liste des valeurs non 'None'"""
792 return [val for val in self.values() if val != None]
794 # ------------------------------------------------------------------------------
795 # équivalences avec les opérateurs dans Aster
803 return self.__eq__(None)
805 return self.__ne__(None)
807 # ------------------------------------------------------------------------------
808 # ------------------------------------------------------------------------------
809 # ------------------------------------------------------------------------------
810 def sort_table(rows, l_para, w_para, reverse=False):
811 """Sort list of dict.
813 l_para : list of the keys of dict
814 w_para : keys of the sort
816 c_para=[i for i in l_para if i not in w_para]
818 # rename sort keys by "__" + number + para
819 # ("__" to avoid conflict with existing parameters)
821 new_key= '__'+str(w_para.index(i))+i
822 for row in new_rows :
825 # rename others parameters by "___" + para
826 # ("___" to be after sort keys)
829 for row in new_rows :
838 old_key= '__'+str(w_para.index(i))+i
839 for row in new_rows :
844 for row in new_rows :
849 # ------------------------------------------------------------------------------
850 def FMT(dform, nform, typAster=None, larg=0, val=''):
851 """Retourne un format d'impression Python à partir d'un type Aster ('R','I',
852 'K8', 'K16'...). Si typAster==None, retourne dform[nform].
853 larg : largeur minimale du format (val permet de ne pas ajouter des blancs
854 si la chaine à afficher est plus longue que le format, on prend le partie
855 de ne pas tronquer les chaines)
859 elif typAster in ('I', 'R'):
861 # convertit %12.5E en %-12s
862 fmt=re.sub('([0-9]+)[\.0-9]*[diueEfFgG]+','-\g<1>s',dform['form'+typAster])
867 fmt='%-'+typAster[1:]+'s'
868 # on ajoute éventuellement des blancs pour atteindre la largeur demandée
870 fmt=' '*max(min(larg-len(val),larg-len(fmt % 0)),0) + fmt
873 # ------------------------------------------------------------------------------
874 def merge(tab1, tab2, labels=[]):
875 """Assemble les deux tables tb1 et tb2 selon une liste de labels communs.
877 - les lignes de tb2 sont ajoutés à celles de tb1,
879 - si on trouve les valeurs de tb2 sur les labels dans tb1 (et une seule fois),
880 on surcharge tb1 avec les lignes de tb2 ;
881 - sinon on ajoute la ligne de tb2 à la fin de tb1.
885 if type(labels) not in EnumTypes:
888 if key not in tb1.para : UTMESS('F','Table','Erreur, label non présent %s' % key)
889 if key not in tb2.para : UTMESS('F','Table','Erreur, label non présent %s' % key)
890 # ensemble des paramètres et des types
894 if i not in tb1.para:
896 n_type.append(tb2.type[tb2.para.index(i)])
897 # restriction des lignes aux labels communs (peu cher en cpu)
900 for i1 in range(len(rows1)):
901 tu1 = tuple(map(rows1[i1].__getitem__, labels))
902 if dlab1.get(tu1, '') == '':
906 # restriction des lignes aux labels communs (peu cher en cpu)
909 for i2 in range(len(rows2)):
910 tu2 = tuple(map(rows2[i2].__getitem__, labels))
911 if dlab2.get(tu2, '') == '':
915 # creation de dic1 : dictionnaire de correspondance entre les
916 # lignes a merger dans les deux tableaux
918 for cle in dlab1.keys():
919 if dlab1[cle] == None or cle == ():
921 for cle in dlab2.keys():
922 if dlab2[cle] == None or cle == ():
924 for cle in dlab2.keys():
925 if dlab1.has_key(cle):
926 dic1[dlab2[cle]] = dlab1[cle]
927 # insertion des valeurs de tb2 dans tb1 quand les labels sont communs
928 # (et uniques dans chaque table) OU ajout de la ligne de tb2 dans tb1
933 rows1[dic1[i2]].update(r2)
936 # concaténation des titres + info sur le merge
937 tit = '\n'.join([tb1.titr, tb2.titr, 'MERGE avec labels=%s' % repr(labels)])
938 return Table(rows1, n_para, n_type, tit)
940 # ------------------------------------------------------------------------------
941 def _typaster(obj, prev=None, strict=False):
942 """Retourne le type Aster ('R', 'I', Kdef) correspondant à l'objet obj.
943 Si prev est fourni, on vérifie que obj est du type prev.
944 Si strict=False, on autorise que obj ne soit pas du type prev s'ils sont
945 tous les deux numériques ; dans ce cas, on retourne le "type enveloppe" 'R'.
950 StringType : Kdef, UnicodeType : Kdef,
953 if type(obj) in dtyp.keys():
954 typobj=dtyp[type(obj)]
955 if prev in [None, typobj]:
957 elif strict: # prev<>None et typobj<>prev et strict
958 raise TypeError, "La valeur %s n'est pas de type %s" % (repr(obj),repr(prev))
959 elif prev in ('I','R') and typobj in ('I','R'):
962 raise TypeError, "La valeur %s n'est pas compatible avec le type %s" \
963 % (repr(obj),repr(prev))
965 raise TypeError, 'Une table ne peut contenir que des entiers, réels ' \
966 'ou chaines de caractères.'
968 # ------------------------------------------------------------------------------
969 # fonctions utilitaires
970 def _func_test_abs(v, VALE, PRECISION):
971 """Retourne True si v est parmi VALE à PRECISION près en absolu
974 if v != None and (x-PRECISION <= v <= x+PRECISION):
978 def _func_test_rela(v, VALE, PRECISION):
979 """Retourne True si v est parmi VALE à PRECISION près en relatif
982 if v != None and (x*(1.-PRECISION) <= v <= x*(1.+PRECISION)):