1 #@ MODIF Table Utilitai DATE 06/11/2006 AUTEUR MCOURTOI M.COURTOIS
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)
35 # try/except pour utiliser hors aster
37 from Utilitai.Utmess import UTMESS
39 def UTMESS(code,sprg,texte):
40 fmt = '\n <%s> <%s> %s\n\n'
42 raise StandardError, fmt % (code,sprg,texte)
44 print fmt % (code,sprg,texte)
46 if not sys.modules.has_key('Graph'):
48 from Utilitai import Graph
52 # formats de base (identiques à ceux du module Graph)
54 'csep' : ' ', # séparateur
55 'ccom' : '#', # commentaire
56 'cdeb' : '', # début de ligne
57 'cfin' : '\n', # fin de ligne
58 'sepch' : ';', # séparateur entre deux lignes d'une cellule
59 'formK' : '%-8s', # chaines
60 'formR' : '%12.5E', # réels
61 'formI' : '%8d' # entiers
63 # type par défaut des chaines de caractères
66 # ------------------------------------------------------------------------------
67 # ------------------------------------------------------------------------------
68 # ------------------------------------------------------------------------------
69 class TableBase(object):
70 """Classe pour partager les méthodes d'impression entre Table et Colonne
71 (c'est surtout utile pour vérifier que l'extraction et les filtres sur les
72 colonnes sont corrects).
83 return self.ReprTable()
84 def Croise(self, **kargs):
85 raise NotImplementedError, 'Must be defined in a derived class'
88 """Retourne le nombre de ligne dans la Table/Colonne.
92 # ------------------------------------------------------------------------------
93 def Impr(self, FICHIER=None, FORMAT='TABLEAU', dform=None, **opts):
94 """Impresssion de la Table selon le format spécifié.
95 FICHIER : nom du(des) fichier(s). Si None, on dirige vers stdout
96 dform : dictionnaire de formats d'impression (format des réels,
97 commentaires, saut de ligne...)
101 'TABLEAU' : { 'mode' : 'a', 'driver' : self.ImprTableau, },
102 'ASTER' : { 'mode' : 'a', 'driver' : self.ImprTableau, },
103 'XMGRACE' : { 'mode' : 'a', 'driver' : self.ImprGraph, },
104 'AGRAF' : { 'mode' : 'a', 'driver' : self.ImprTableau, },
105 'TABLEAU_CROISE' : { 'mode' : 'a', 'driver' : self.ImprTabCroise, },
110 'dform' : DicForm.copy(),
111 'mode' : para[FORMAT]['mode'],
113 if dform != None and type(dform) == DictType:
114 kargs['dform'].update(dform)
118 if not kargs.get('PAGINATION'):
119 # call the associated driver
120 para[FORMAT]['driver'](**kargs)
123 if not type(kargs['PAGINATION']) in EnumTypes:
124 ppag = [kargs['PAGINATION'],]
126 ppag = list(kargs['PAGINATION'])
127 del kargs['PAGINATION']
129 # paramètres hors ceux de la pagination
130 lkeep = [p for p in self.para if ppag.count(p)==0]
131 # création des listes des valeurs distinctes
134 lvp = getattr(self,p).values()
137 if it != None and lvn.count(it) == 0:
141 # création des n-uplets
142 s = '[['+','.join(['x'+str(i) for i in range(npag)])+'] '
143 s += ' '.join(['for x'+str(i)+' in lvd['+str(i)+']' for i in range(npag)])+']'
146 except SyntaxError, s:
147 UTMESS('F','Table','Erreur lors de la construction des n-uplets')
148 # pour chaque n-uplet, on imprime la sous-table
151 for i in range(npag):
152 tab = tab & (getattr(tab,ppag[i]) == nup[i])
155 tab.titr += sl+ppag[i]+': '+str(nup[i])
156 tab[lkeep].Impr(**kargs)
158 # ------------------------------------------------------------------------------
159 def ImprTableau(self,**kargs):
160 """Impression au format TABLEAU ou ASTER
163 if kargs.get('FICHIER')<>None:
164 f=open(kargs['FICHIER'],kargs['mode'])
168 f.write(self.ReprTable(**kargs) + '\n')
170 if kargs.get('FICHIER')<>None:
173 # ------------------------------------------------------------------------------
174 def ReprTable(self,FORMAT='TABLEAU',dform=None,**ignore):
175 """Représentation d'une Table ou d'une Colonne sous forme d'un tableau.
180 if not type(para) in EnumTypes:
184 dform = DicForm.copy()
185 # est-ce que l'attribut .type est renseigné ?
186 typdef=typ<>[None]*len(typ)
188 # ['']+ pour ajouter un séparateur en début de ligne
190 # lmax : largeur max des colonnes = max(form{K,R,I},len(parametre))
194 larg_max=max([len(str(p))] + \
195 [len(FMT(dform,k,t) % 0) for k in ('formK','formR','formI')])
196 lspa.append(FMT(dform,'formK',t,larg_max,str(p)) % p)
197 lmax.append(larg_max)
199 stype=dform['csep'].join([''] + \
200 [FMT(dform,'formK',typ[i],lmax[i]) % typ[i] for i in range(len(para))])
201 txt.append(dform['ccom'])
202 txt.append(dform['ccom']+'-'*80)
203 txt.append(dform['ccom'])
204 ASTER=(FORMAT=='ASTER')
206 txt.append('#DEBUT_TABLE')
209 txt.extend(['#TITRE '+lig for lig in self.titr.split('\n')])
211 txt.extend([dform['ccom']+lig for lig in self.titr.split('\n')])
212 txt.append(dform['csep'].join(lspa))
222 if type(rep) is FloatType:
223 lig.append(FMT(dform,'formR',t,lmax[i]) % rep)
225 elif type(rep) in (IntType, LongType):
226 lig.append(FMT(dform,'formI',t,lmax[i]) % rep)
233 s=FMT(dform,'formK',t,lmax[i],rep) % str(rep)
234 # format AGRAF = TABLEAU + '\' devant les chaines de caractères !
239 lig2 = [dform['sepch'].join(ch.splitlines()) for ch in lig]
240 txt.append(dform['csep'].join(lig2))
242 txt.append('#FIN_TABLE')
243 # ajout du debut de ligne
244 if dform['cdeb']<>'':
245 txt=[dform['cdeb']+t for t in txt]
247 return dform['cfin'].join(txt)
248 # ------------------------------------------------------------------------------
249 def ImprTabCroise(self,**kargs):
250 """Impression au format TABLEAU_CROISE d'une table ayant 3 paramètres.
252 # création du tableau croisé et impression au format TABLEAU
254 kargs['FORMAT']='TABLEAU'
256 # ------------------------------------------------------------------------------
257 def ImprGraph(self, **kargs):
258 """Impression au format XMGRACE : via le module Graph
261 if len(self.para) != 2:
262 UTMESS('A','Table','La table doit avoir exactement deux paramètres '\
263 'pour une impression au format XMGRACE.')
265 # suppression des lignes contenant une cellule vide
266 tnv = getattr(self, self.para[0]).NON_VIDE() \
267 & getattr(self, self.para[1]).NON_VIDE()
271 'Val' : [getattr(tnv, tnv.para[0]).values(),
272 getattr(tnv, tnv.para[1]).values()],
275 if args['LEGENDE']==None: del args['LEGENDE']
276 Graph.AjoutParaCourbe(dicC, args)
277 graph.AjoutCourbe(**dicC)
279 # Surcharge des propriétés du graphique et des axes
280 # (bloc quasiment identique dans impr_fonction_ops)
281 if args.get('TITRE'): graph.Titre=args['TITRE']
282 if args.get('BORNE_X'):
283 graph.Min_X=args['BORNE_X'][0]
284 graph.Max_X=args['BORNE_X'][1]
285 if args.get('BORNE_Y'):
286 graph.Min_Y=args['BORNE_Y'][0]
287 graph.Max_Y=args['BORNE_Y'][1]
288 if args.get('LEGENDE_X'): graph.Legende_X=args['LEGENDE_X']
289 if args.get('LEGENDE_Y'): graph.Legende_Y=args['LEGENDE_Y']
290 if args.get('ECHELLE_X'): graph.Echelle_X=args['ECHELLE_X']
291 if args.get('ECHELLE_Y'): graph.Echelle_Y=args['ECHELLE_Y']
292 if args.get('GRILLE_X'): graph.Grille_X=args['GRILLE_X']
293 if args.get('GRILLE_Y'): graph.Grille_Y=args['GRILLE_Y']
298 UTMESS('A','Table','Les cellules ne doivent contenir que des nombres réels')
300 # ------------------------------------------------------------------------------
301 # ------------------------------------------------------------------------------
302 # ------------------------------------------------------------------------------
303 class Table(TableBase):
304 """Une table est construite comme une liste de lignes, chaque ligne est
306 On crée puis on ajoute les lignes avec la méthode append :
308 t.append(dict(a=1,b=2))
309 t.append(dict(a=3,b=4))
310 La méthode __iter__ définit un itérateur sur les lignes de la table,
311 __repr__ retourne une représentation de la table, utilisée par "print t".
312 Grace à la classe Colonne et à sa méthode _extract, il est possible
313 de construire une sous-table qui satisfait un critère donné.
314 Le critère est donné par une fonction Python qui retourne vrai
315 ou faux si la valeur d'une colonne respecte le critère ou non.
319 soustable = t.a._extract(critere)
320 t.a retourne un objet intermédiaire de la classe Colonne qui mémorise
321 le nom de la colonne demandée (a, ici).
323 # ------------------------------------------------------------------------------
324 def __init__(self, rows=[], para=[], typ=[], titr=''):
325 """Constructeur de la Table :
326 rows : liste des lignes (dict)
327 para : liste des paramètres
328 type : liste des types des paramètres
329 titr : titre de la table
331 self.rows = [r for r in rows if r.values() != [None]*len(r.values())]
332 self.para = list(para)
334 if self.para.count(i) != 1 :
335 UTMESS('F','Table','Parametre en double: %s' %i)
336 if len(typ) == len(self.para):
337 self.type = list(typ)
339 self.type = [None]*len(self.para)
342 # ------------------------------------------------------------------------------
344 """Retourne une copie de la table.
349 return Table(rows, self.para[:], self.type[:], self.titr)
351 # ------------------------------------------------------------------------------
352 def append(self, obj):
353 """Ajoute une ligne (type dict) qui peut éventuellement définir un
354 nouveau paramètre."""
357 if not p in self.para:
359 self.type.append(_typaster(obj[p]))
361 ip=self.para.index(p)
362 self.type[ip]=_typaster(obj[p], self.type[ip])
363 self.rows.append(obj)
365 # ------------------------------------------------------------------------------
366 def SansColonneVide(self):
367 """Retourne une copie de la table dans laquelle on a supprimé les colonnes
368 vides (les lignes vides sont automatiquement supprimées).
373 if len(tab[para]) == 0:
377 # ------------------------------------------------------------------------------
378 def __setitem__(self, k_para, k_value):
379 """Ajoute une colonne k_para dont les valeurs sont dans k_value"""
382 if k_para in self.para :
383 UTMESS('F','Table','(setitem) Le parametre %s existe déjà.' % k_para)
384 self.para.append(k_para)
385 self.type.append(_typaster(k_value[0]))
389 row[k_para]=k_value[i]
390 self.type[-1]=_typaster(k_value[i], self.type[-1])
394 for j in range(i,len(k_value)):
395 self.append({k_para:k_value[j]})
397 # ------------------------------------------------------------------------------
398 def fromfunction(self, nom_para, funct, l_para=None, const=None):
399 """Ajoute une colonne `nom_para` en évaluant la fonction `funct` sur
400 la valeur des paramètres `l_para` (qui doivent exister dans la table).
401 Si `l_para` n'est pas fourni, on prend `funct`.nompar (FORMULE Aster).
402 On peut passer un dictionnaire de constantes dans `const`. Quand on
403 utilise une FORMULE Aster, les constantes sont prises dans le contexte
407 if not hasattr(funct, '__call__'):
408 UTMESS('F', 'Table', "(fromfunction) '%s' n'a pas d'attribut '__call__'." \
410 if nom_para in self.para :
411 UTMESS('F','Table','Le parametre %s existe déjà.' % nom_para)
413 if not hasattr(funct, 'nompar'):
414 UTMESS('F', 'Table', "(fromfunction) '%s' n'a pas d'attribut 'nompar'." \
416 l_para = funct.nompar
417 if not type(l_para) in EnumTypes:
419 not_found = ', '.join([p for p in l_para if not p in self.para])
421 UTMESS('F','Table','Parametre(s) absent(s) de la table : %s' % not_found)
424 if type(const) is not DictType:
425 UTMESS('F', 'Table', "L'argument 'const' doit etre de type 'dict'.")
426 # liste des valeurs des paramètres
429 vals = getattr(self, para).values()
431 tabpar = transpose.transpose(tabpar)
432 # évaluation de la fonction sur ces paramètres
435 # si un paramètre est absent, on ne peut pas évaluer la formule
439 vectval.append(funct(*lpar, **const))
440 # ajout de la colonne
441 self[nom_para] = vectval
443 # ------------------------------------------------------------------------------
445 """Itère sur les lignes de la Table"""
446 return iter(self.rows)
448 # ------------------------------------------------------------------------------
449 def __getattr__(self, column):
450 """Construit un objet intermediaire (couple table, colonne)"""
452 if not column in self.para:
455 typ=self.type[self.para.index(column)]
456 return Colonne(self, column, typ)
458 # ------------------------------------------------------------------------------
459 def sort(self, CLES=None, ORDRE='CROISSANT'):
461 CLES : liste des clés de tri
462 ORDRE : CROISSANT ou DECROISSANT
464 # par défaut, on prend tous les paramètres
467 # vérification des arguments
468 if not type(CLES) in EnumTypes:
472 not_found = ', '.join([p for p in CLES if not p in self.para])
474 UTMESS('F', 'Table', 'Parametre(s) absent(s) de la table : %s' % not_found)
475 if not ORDRE in ('CROISSANT', 'DECROISSANT'):
476 UTMESS('F', 'Table', 'Valeur incorrecte pour ORDRE : %s' % ORDRE)
478 self.rows = sort_table(self.rows, self.para, CLES, (ORDRE=='DECROISSANT'))
480 # ------------------------------------------------------------------------------
481 def __delitem__(self, args):
482 """Supprime les colonnes correspondantes aux éléments de args """
483 if not type(args) in EnumTypes:
489 del new_type[new_para.index(item)]
490 new_para.remove(item)
491 for line in new_rows:
493 return Table(new_rows, new_para, new_type, self.titr)
495 # ------------------------------------------------------------------------------
496 def __getitem__(self, args):
497 """Extrait la sous table composée des colonnes dont les paramètres sont dans args """
498 if not type(args) in EnumTypes:
505 for item in new_para:
506 if not item in self.para:
508 new_type.append(self.type[self.para.index(item)])
511 for item in new_para:
512 new_line[item]=line.get(item)
513 new_rows.append(new_line)
514 return Table(new_rows, new_para, new_type, self.titr)
516 # ------------------------------------------------------------------------------
517 def __and__(self, other):
518 """Intersection de deux tables (opérateur &)"""
519 if other.para<>self.para:
520 UTMESS('A','Table','Les paramètres sont différents')
523 tmp = [ r for r in self if r in other.rows ]
524 return Table(tmp, self.para, self.type, self.titr)
526 # ------------------------------------------------------------------------------
527 def __or__(self, other):
528 """Union de deux tables (opérateur |)"""
529 if other.para<>self.para:
530 UTMESS('A','Table','Les paramètres sont différents')
534 tmp.extend([ r for r in other if r not in self ])
535 return Table(tmp, self.para, self.type[:], self.titr)
537 # ------------------------------------------------------------------------------
539 """Renvoie la table sous la forme d'un dictionnaire de listes dont les
540 clés sont les paramètres.
543 for column in self.para:
544 dico[column]=Colonne(self, column).values()
547 # ------------------------------------------------------------------------------
548 def dict_CREA_TABLE(self):
549 """Renvoie le dictionnaire des mots-clés à fournir à la commande CREA_TABLE
550 pour produire une table_sdaster.
552 dico={ 'TITRE' : ['%-80s' % lig for lig in self.titr.split('\n')],
554 # remplissage de chaque occurence (pour chaque paramètre) du mot-clé facteur LISTE
555 for i in range(len(self.para)):
556 # nom du paramètre et type si K*
557 d={ 'PARA' : self.para[i], }
560 UTMESS('F', 'Table', 'Type du paramètre %s non défini.' %\
564 if not typ in ('K8', 'K16', 'K24'):
565 UTMESS('A','Table','Type du paramètre %s forcé à %s' % (self.para[i],Kdef))
572 # valeurs sans trou / avec trou
573 vals=getattr(self, self.para[i]).values()
574 if vals.count(None)==0:
577 d['NUME_LIGN'] = [j+1 for j in range(len(vals)) if vals[j]<>None]
578 d[mc] = [v for v in vals if v <>None]
580 UTMESS('I','Table','Colonne %s vide' % self.para[i])
582 dico['LISTE'].append(d)
583 if len(dico['LISTE'])==0:
584 UTMESS('F','Table','La table est vide')
587 # ------------------------------------------------------------------------------
588 def Array(self,Para,Champ):
589 """Renvoie sous forme de NumArray le résultat d'une extraction dans une table
590 méthode utile à macr_recal
593 __Rep = self[Para,Champ].values()
594 F = Numeric.zeros((len(__Rep[Para]),2), Numeric.Float)
595 for i in range(len(__Rep[Para])):
596 F[i][0] = __Rep[Para][i]
597 F[i][1] = __Rep[Champ][i]
601 # ------------------------------------------------------------------------------
603 """Retourne un tableau croisé P3(P1,P2) à partir d'une table ayant
604 trois paramètres (P1, P2, P3).
606 if len(self.para)<>3:
607 UTMESS('A', 'Table', 'La table doit avoir exactement trois paramètres.')
609 py, px, pz = self.para
610 ly, lx, lz = [getattr(self,p).values() for p in self.para]
612 #lpz='%s=f(%s,%s)' % (pz,px,py)
613 lpz='%s/%s' % (px,py)
615 # attention aux doublons dans lx et ly
617 if it<>None and new_para.count(it)==0:
621 if it<>None and newx.count(it)==0:
626 taux = (getattr(self,px)==x)
630 new_type=[self.type[0],] + [self.type[2]]*len(ly)
632 if new_titr<>'': new_titr+='\n'
633 new_titr+=pz + ' FONCTION DE ' + px + ' ET ' + py
634 return Table(new_rows, new_para, new_type, new_titr)
636 # ------------------------------------------------------------------------------
637 def Renomme(self, pold, pnew):
638 """Renomme le paramètre `pold` en `pnew`.
640 if not pold in self.para:
641 raise KeyError, 'Paramètre %s inexistant dans cette table' % pold
642 elif self.para.count(pnew)>0:
643 raise KeyError, 'Le paramètre %s existe déjà dans la table' % pnew
645 self.para[self.para.index(pold)] = pnew
647 lig[pnew] = lig[pold]
650 # ------------------------------------------------------------------------------
651 # ------------------------------------------------------------------------------
652 # ------------------------------------------------------------------------------
653 class Colonne(TableBase):
654 """Classe intermédiaire pour mémoriser un couple (table, nom de colonne)
655 et exprimer les critères d'extraction sous une forme naturelle en python
656 en surchargeant les operateurs <, >, <> et =.
657 Alors on peut écrire la requete simple :
659 Ainsi que des requetes plus complexes :
660 soustable=t.a<10 and t.b <4
662 soustable=t.a<10 or t.b <4
663 Les "alias" EQ, NE, LE, LT, GE, GT permettent à la macro IMPR_TABLE
664 d'utiliser directement le mot-clé utilisateur CRIT_COMP défini dans le
665 catalogue : getattr(Table,CRIT_COMP).
667 # ------------------------------------------------------------------------------
668 def __init__(self, table, column, typ=None):
669 """Constructeur (objet Table associé, paramètre de la colonne, type du
673 self.rows=self.Table.rows
678 # ------------------------------------------------------------------------------
679 def _extract(self, fun):
680 """Construit une table avec les lignes de self.Table
681 dont l'élément de nom self.para satisfait le critère fun,
682 fun est une fonction qui retourne vrai ou faux
684 return Table([row for row in self.Table if fun(row.get(self.para))], self.Table.para, self.Table.type, self.Table.titr)
686 # ------------------------------------------------------------------------------
687 def __le__(self, VALE):
688 return self._extract(lambda v: v<>None and v<=VALE)
690 # ------------------------------------------------------------------------------
691 def __lt__(self, VALE):
692 return self._extract(lambda v: v<>None and v<VALE)
694 # ------------------------------------------------------------------------------
695 def __ge__(self, VALE):
696 return self._extract(lambda v: v<>None and v>=VALE)
698 # ------------------------------------------------------------------------------
699 def __gt__(self, VALE):
700 return self._extract(lambda v: v<>None and v>VALE)
702 # ------------------------------------------------------------------------------
703 def __eq__(self, VALE, CRITERE='RELATIF', PRECISION=0.):
704 if type(VALE) in EnumTypes :
705 return self._extract(lambda v: v in VALE)
706 if PRECISION==0. or not type(VALE) in NumberTypes:
707 if type(VALE) in StringTypes:
708 return self._extract(lambda v: v<>None and str(v).strip()==VALE.strip())
710 return self._extract(lambda v: v==VALE)
712 if CRITERE=='ABSOLU':
716 vmin=(1.-PRECISION)*VALE
717 vmax=(1.+PRECISION)*VALE
718 return self._extract(lambda v: v<>None and vmin<v<vmax)
720 # ------------------------------------------------------------------------------
721 def REGEXP(self, regexp):
722 """Retient les lignes dont le paramètre satisfait l'expression
725 if not type(regexp) in StringTypes:
726 return self._extract(lambda v : False)
727 return self._extract(lambda v : v != None and re.search(regexp, v) != None)
729 # ------------------------------------------------------------------------------
730 def __ne__(self, VALE, CRITERE='RELATIF', PRECISION=0.):
731 if type(VALE) in EnumTypes :
732 return self._extract(lambda v: v not in VALE)
733 if PRECISION==0. or not type(VALE) in NumberTypes:
734 if type(VALE) in StringTypes:
735 return self._extract(lambda v: v<>None and str(v).strip()<>VALE.strip())
737 return self._extract(lambda v: v<>VALE)
739 if CRITERE=='ABSOLU':
743 vmin=(1.-PRECISION)*VALE
744 vmax=(1.+PRECISION)*VALE
745 return self._extract(lambda v: v<>None and (v<vmin or vmax<v))
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.'