Package Bio :: Package SubsMat
[hide private]
[frames] | no frames]

Source Code for Package Bio.SubsMat

  1  """Substitution matrices, log odds matrices, and operations on them. 
  2  """ 
  3  import re 
  4  import string 
  5  import sys 
  6  import copy 
  7  import math 
  8  # BioPython imports 
  9  from Bio import Alphabet 
 10  from Bio.SubsMat import FreqTable 
 11  # from Bio.Tools import statfns 
 12   
 13  log = math.log 
 14  # Matrix types 
 15  NOTYPE = 0 
 16  ACCREP = 1 
 17  OBSFREQ = 2 
 18  SUBS = 3 
 19  EXPFREQ = 4 
 20  LO = 5 
 21  EPSILON = 0.00000000000001 
22 -class BadMatrix(Exception):
23 """Exception raised when verifying a matrix"""
24 - def __str__(self):
25 return "Bad Matrix"
26 BadMatrixError = BadMatrix() 27 28 # 5/2001 added the following: 29 # * Methods for subtraction, addition and multiplication of matrices 30 # * Generation of an expected frequency table from an observed frequency matrix 31 # * Calculation of linear correlation coefficient between two matrices. Needs 32 # Bio.Tools.statfns 33 # * Calculation of relative entropy is now done using the _make_relative_entropy method 34 # and is stored in the member self.relative_entropy 35 # * Calculation of entropy is now done using the _make_entropy method and is stored in 36 # the member self.entropy 37 # * Jensen-Shannon distance between the distributions from which the matrices are 38 # derived. This is a distance function based on the distribution's entropies. 39 # 40 # Substitution matrix routines 41 # Iddo Friedberg idoerg@cc.huji.ac.il 42 # Biopython license applies (http://biopython.org) 43 # 44 # General: 45 # ------- 46 # You should have python 2.0 or above. 47 # http://www.python.org 48 # You should have biopython (http://biopython.org) installed. 49 # 50 # This module provides a class and a few routines for generating 51 # substitution matrices, similar ot BLOSUM or PAM matrices, but based on 52 # user-provided data. 53 # The class used for these matrices is SeqMat 54 # 55 # Matrices are implemented as a user dictionary. Each index contains a 56 # 2-tuple, which are the two residue/nucleotide types replaced. The value 57 # differs according to the matrix's purpose: e.g in a log-odds frequency 58 # matrix, the value would be log(Pij/(Pi*Pj)) where: 59 # Pij: frequency of substitution of letter (residue/nucletide) i by j 60 # Pi, Pj: expected frequencies of i and j, respectively. 61 # 62 # Usage: 63 # ----- 64 # The following section is layed out in the order by which most people wish 65 # to generate a log-odds matrix. Of course, interim matrices can be 66 # generated and investigated. Most people just want a log-odds matrix, 67 # that's all. 68 # 69 # Generating an Accepted Replacement Matrix: 70 # ----------------------------------------- 71 # Initially, you should generate an accepted replacement matrix 72 # (ARM) from your data. The values in ARM are the _counted_ number of 73 # replacements according to your data. The data could be a set of pairs 74 # or multiple alignments. So for instance if Alanine was replaced by 75 # Cysteine 10 times, and Cysteine by Alanine 12 times, the corresponding 76 # ARM entries would be: 77 # ['A','C']: 10, ['C','A'] 12 78 # as order doesn't matter, user can already provide only one entry: 79 # ['A','C']: 22 80 # A SeqMat instance may be initialized with either a full (first 81 # method of counting: 10, 12) or half (the latter method, 22) matrices. A 82 # Full protein alphabet matrix would be of the size 20x20 = 400. A Half 83 # matrix of that alphabet would be 20x20/2 + 20/2 = 210. That is because 84 # same-letter entries don't change. (The matrix diagonal). Given an 85 # alphabet size of N: 86 # Full matrix size:N*N 87 # Half matrix size: N(N+1)/2 88 # 89 # If you provide a full matrix, the constructore will create a half-matrix 90 # automatically. 91 # If you provide a half-matrix, make sure 92 # of a (low, high) sorted order in the keys: there should only be 93 # a ('A','C') not a ('C','A'). 94 # 95 # Internal functions: 96 # 97 # Generating the observed frequency matrix (OFM): 98 # ---------------------------------------------- 99 # Use: OFM = _build_obs_freq_mat(ARM) 100 # The OFM is generated from the ARM, only instead of replacement counts, it 101 # contains replacement frequencies. 102 # Generating an expected frequency matrix (EFM): 103 # --------------------------------------------- 104 # Use: EFM = _build_exp_freq_mat(OFM,exp_freq_table) 105 # exp_freq_table: should be a freqTableC instantiation. See freqTable.py for 106 # detailed information. Briefly, the expected frequency table has the 107 # frequencies of appearance for each member of the alphabet 108 # Generating a substitution frequency matrix (SFM): 109 # ------------------------------------------------ 110 # Use: SFM = _build_subs_mat(OFM,EFM) 111 # Accepts an OFM, EFM. Provides the division product of the corresponding 112 # values. 113 # Generating a log-odds matrix (LOM): 114 # ---------------------------------- 115 # Use: LOM=_build_log_odds_mat(SFM[,logbase=10,factor=10.0,roundit=1]) 116 # Accepts an SFM. logbase: base of the logarithm used to generate the 117 # log-odds values. factor: factor used to multiply the log-odds values. 118 # roundit: default - true. Whether to round the values. 119 # Each entry is generated by log(LOM[key])*factor 120 # And rounded if required. 121 # 122 # External: 123 # --------- 124 # In most cases, users will want to generate a log-odds matrix only, without 125 # explicitly calling the OFM --> EFM --> SFM stages. The function 126 # build_log_odds_matrix does that. User provides an ARM and an expected 127 # frequency table. The function returns the log-odds matrix 128 #
129 -class SeqMat(dict):
130 """A Generic sequence matrix class 131 The key is a 2-tuple containing the letter indices of the matrix. Those 132 should be sorted in the tuple (low, high). Because each matrix is dealt 133 with as a half-matrix.""" 134
135 - def _alphabet_from_matrix(self):
136 ab_dict = {} 137 s = '' 138 for i in self.keys(): 139 ab_dict[i[0]] = 1 140 ab_dict[i[1]] = 1 141 letters_list = ab_dict.keys() 142 letters_list.sort() 143 for i in letters_list: 144 s = s + i 145 self.alphabet.letters = s
146
147 - def __init__(self,data=None, alphabet=None, 148 mat_type=NOTYPE,mat_name='',build_later=0):
149 # User may supply: 150 # data: matrix itself 151 # mat_type: its type. See below 152 # mat_name: its name. See below. 153 # alphabet: an instance of Bio.Alphabet, or a subclass. If not 154 # supplied, constructor builds its own from that matrix.""" 155 # build_later: skip the matrix size assertion. User will build the 156 # matrix after creating the instance. Constructor builds a half matrix 157 # filled with zeroes. 158 159 assert type(mat_type) == type(1) 160 assert type(mat_name) == type('') 161 162 # "data" may be: 163 # 1) None --> then self.data is an empty dictionary 164 # 2) type({}) --> then self.data takes the items in data 165 # 3) An instance of SeqMat 166 # This whole creation-during-execution is done to avoid changing 167 # default values, the way Python does because default values are 168 # created when the function is defined, not when it is created. 169 assert (type(data) == type({}) or isinstance(data,dict) or 170 data == None) 171 if data == None: 172 data = {} 173 else: 174 self.update(data) 175 if alphabet == None: 176 alphabet = Alphabet.Alphabet() 177 assert Alphabet.generic_alphabet.contains(alphabet) 178 self.alphabet = alphabet 179 180 # If passed alphabet is empty, use the letters in the matrix itself 181 if not self.alphabet.letters: 182 self._alphabet_from_matrix() 183 # Assert matrix size: half or full 184 if not build_later: 185 N = len(self.alphabet.letters) 186 assert len(self) == N**2 or len(self) == N*(N+1)/2 187 self.ab_list = list(self.alphabet.letters) 188 self.ab_list.sort() 189 # type can be: ACCREP, OBSFREQ, SUBS, EXPFREQ, LO 190 self.mat_type = mat_type 191 # Names: a string like "BLOSUM62" or "PAM250" 192 self.mat_name = mat_name 193 if build_later: 194 self._init_zero() 195 else: 196 # Convert full to half if matrix is not already a log-odds matrix 197 if self.mat_type != LO: 198 self._full_to_half() 199 self._correct_matrix() 200 self.sum_letters = {} 201 self.relative_entropy = 0
202
203 - def _correct_matrix(self):
204 keylist = self.keys() 205 for key in keylist: 206 if key[0] > key[1]: 207 self[(key[1],key[0])] = self[key] 208 del self[key]
209
210 - def _full_to_half(self):
211 """ 212 Convert a full-matrix to a half-matrix 213 """ 214 # For instance: two entries ('A','C'):13 and ('C','A'):20 will be summed 215 # into ('A','C'): 33 and the index ('C','A') will be deleted 216 # alphabet.letters:('A','A') and ('C','C') will remain the same. 217 218 N = len(self.alphabet.letters) 219 # Do nothing if this is already a half-matrix 220 if len(self) == N*(N+1)/2: 221 return 222 for i in self.ab_list: 223 for j in self.ab_list[:self.ab_list.index(i)+1]: 224 if i != j: 225 self[j,i] = self[j,i] + self[i,j] 226 del self[i,j]
227
228 - def _init_zero(self):
229 for i in self.ab_list: 230 for j in self.ab_list[:self.ab_list.index(i)+1]: 231 self[j,i] = 0.
232
233 - def make_relative_entropy(self,obs_freq_mat):
234 """if this matrix is a log-odds matrix, return its entropy 235 Needs the observed frequency matrix for that""" 236 ent = 0. 237 if self.mat_type == LO: 238 for i in self.keys(): 239 ent += obs_freq_mat[i]*self[i]/log(2) 240 elif self.mat_type == SUBS: 241 for i in self.keys(): 242 if self[i] > EPSILON: 243 ent += obs_freq_mat[i]*log(self[i])/log(2) 244 else: 245 raise TypeError("entropy: substitution or log-odds matrices only") 246 self.relative_entropy = ent
247 #
248 - def make_entropy(self):
249 self.entropy = 0 250 for i in self.keys(): 251 if self[i] > EPSILON: 252 self.entropy += self[i]*log(self[i])/log(2) 253 self.entropy = -self.entropy
254 - def letter_sum(self,letter):
255 assert letter in self.alphabet.letters 256 sum = 0. 257 for i in self.keys(): 258 if letter in i: 259 if i[0] == i[1]: 260 sum += self[i] 261 else: 262 sum += (self[i] / 2.) 263 return sum
264
265 - def all_letters_sum(self):
266 for letter in self.alphabet.letters: 267 self.sum_letters[letter] = self.letter_sum(letter)
268 - def print_full_mat(self,f=None,format="%4d",topformat="%4s", 269 alphabet=None,factor=1,non_sym=None):
270 f = f or sys.stdout 271 # create a temporary dictionary, which holds the full matrix for 272 # printing 273 assert non_sym == None or type(non_sym) == type(1.) or \ 274 type(non_sym) == type(1) 275 full_mat = copy.copy(self) 276 for i in self: 277 if i[0] != i[1]: 278 full_mat[(i[1],i[0])] = full_mat[i] 279 if not alphabet: 280 alphabet = self.ab_list 281 topline = '' 282 for i in alphabet: 283 topline = topline + topformat % i 284 topline = topline + '\n' 285 f.write(topline) 286 for i in alphabet: 287 outline = i 288 for j in alphabet: 289 if alphabet.index(j) > alphabet.index(i) and non_sym is not None: 290 val = non_sym 291 else: 292 val = full_mat[i,j] 293 val *= factor 294 if val <= -999: 295 cur_str = ' ND' 296 else: 297 cur_str = format % val 298 299 outline = outline+cur_str 300 outline = outline+'\n' 301 f.write(outline)
302
303 - def print_mat(self,f=None,format="%4d",bottomformat="%4s", 304 alphabet=None,factor=1):
305 """Print a nice half-matrix. f=sys.stdout to see on the screen 306 User may pass own alphabet, which should contain all letters in the 307 alphabet of the matrix, but may be in a different order. This 308 order will be the order of the letters on the axes""" 309 310 f = f or sys.stdout 311 if not alphabet: 312 alphabet = self.ab_list 313 bottomline = '' 314 for i in alphabet: 315 bottomline = bottomline + bottomformat % i 316 bottomline = bottomline + '\n' 317 for i in alphabet: 318 outline = i 319 for j in alphabet[:alphabet.index(i)+1]: 320 try: 321 val = self[j,i] 322 except KeyError: 323 val = self[i,j] 324 val *= factor 325 if val == -999: 326 cur_str = ' ND' 327 else: 328 cur_str = format % val 329 330 outline = outline+cur_str 331 outline = outline+'\n' 332 f.write(outline) 333 f.write(bottomline)
334 - def __sub__(self,other):
335 """ returns a number which is the subtraction product of the two matrices""" 336 mat_diff = 0 337 for i in self.keys(): 338 mat_diff += (self[i] - other[i]) 339 return mat_diff
340 - def __mul__(self,other):
341 """ returns a matrix for which each entry is the multiplication product of the 342 two matrices passed""" 343 new_mat = copy.copy(self) 344 for i in self.keys(): 345 new_mat[i] *= other[i] 346 return new_mat
347 - def __sum__(self, other):
348 new_mat = copy.copy(self) 349 for i in self.keys(): 350 new_mat[i] += other[i] 351 return new_mat
352
353 -def _build_obs_freq_mat(acc_rep_mat):
354 """ 355 build_obs_freq_mat(acc_rep_mat): 356 Build the observed frequency matrix, from an accepted replacements matrix 357 The accRep matrix should be generated by the user. 358 """ 359 # Note: acc_rep_mat should already be a half_matrix!! 360 sum = 0. 361 for i in acc_rep_mat.values(): 362 sum += i 363 obs_freq_mat = SeqMat(alphabet=acc_rep_mat.alphabet,build_later=1) 364 for i in acc_rep_mat.keys(): 365 obs_freq_mat[i] = acc_rep_mat[i]/sum 366 obs_freq_mat.mat_type = OBSFREQ 367 return obs_freq_mat
368
369 -def _exp_freq_table_from_obs_freq(obs_freq_mat):
370 exp_freq_table = {} 371 for i in obs_freq_mat.alphabet.letters: 372 exp_freq_table[i] = 0. 373 for i in obs_freq_mat.keys(): 374 if i[0] == i[1]: 375 exp_freq_table[i[0]] += obs_freq_mat[i] 376 else: 377 exp_freq_table[i[0]] += obs_freq_mat[i] / 2. 378 exp_freq_table[i[1]] += obs_freq_mat[i] / 2. 379 return FreqTable.FreqTable(exp_freq_table,FreqTable.FREQ)
380
381 -def _build_exp_freq_mat(exp_freq_table):
382 """Build an expected frequency matrix 383 exp_freq_table: should be a FreqTable instance 384 """ 385 exp_freq_mat = SeqMat(alphabet=exp_freq_table.alphabet,build_later=1) 386 for i in exp_freq_mat.keys(): 387 if i[0] == i[1]: 388 exp_freq_mat[i] = exp_freq_table[i[0]]**2 389 else: 390 exp_freq_mat[i] = 2.0*exp_freq_table[i[0]]*exp_freq_table[i[1]] 391 exp_freq_mat.mat_type = EXPFREQ 392 return exp_freq_mat
393 # 394 # Build the substitution matrix 395 #
396 -def _build_subs_mat(obs_freq_mat,exp_freq_mat):
397 """ Build the substitution matrix """ 398 if obs_freq_mat.ab_list != exp_freq_mat.ab_list: 399 raise ValueError("Alphabet mismatch in passed matrices") 400 subs_mat = SeqMat(obs_freq_mat) 401 for i in obs_freq_mat.keys(): 402 subs_mat[i] = obs_freq_mat[i]/exp_freq_mat[i] 403 404 subs_mat.mat_type = SUBS 405 return subs_mat
406 407 # 408 # Build a log-odds matrix 409 #
410 -def _build_log_odds_mat(subs_mat,logbase=2,factor=10.0,round_digit=0,keep_nd=0):
411 """_build_log_odds_mat(subs_mat,logbase=10,factor=10.0,round_digit=1): 412 Build a log-odds matrix 413 logbase=2: base of logarithm used to build (default 2) 414 factor=10.: a factor by which each matrix entry is multiplied 415 round_digit: roundoff place after decimal point 416 keep_nd: if true, keeps the -999 value for non-determined values (for which there 417 are no substitutions in the frequency substitutions matrix). If false, plants the 418 minimum log-odds value of the matrix in entries containing -999 419 """ 420 lo_mat = SeqMat(subs_mat) 421 for i in subs_mat.keys(): 422 if subs_mat[i] < EPSILON: 423 lo_mat[i] = -999 424 else: 425 lo_mat[i] = round(factor*log(subs_mat[i])/log(logbase),round_digit) 426 lo_mat.mat_type = LO 427 mat_min = min(lo_mat.values()) 428 if not keep_nd: 429 for i in lo_mat.keys(): 430 if lo_mat[i] <= -999: 431 lo_mat[i] = mat_min 432 return lo_mat
433 434 # 435 # External function. User provides an accepted replacement matrix, and, 436 # optionally the following: expected frequency table, log base, mult. factor, 437 # and rounding factor. Generates a log-odds matrix, calling internal SubsMat 438 # functions. 439 #
440 -def make_log_odds_matrix(acc_rep_mat,exp_freq_table=None,logbase=2, 441 factor=1.,round_digit=9,keep_nd=0):
442 obs_freq_mat = _build_obs_freq_mat(acc_rep_mat) 443 if not exp_freq_table: 444 exp_freq_table = _exp_freq_table_from_obs_freq(obs_freq_mat) 445 exp_freq_mat = _build_exp_freq_mat(exp_freq_table) 446 subs_mat = _build_subs_mat(obs_freq_mat, exp_freq_mat) 447 lo_mat = _build_log_odds_mat(subs_mat,logbase,factor,round_digit,keep_nd) 448 lo_mat.make_relative_entropy(obs_freq_mat) 449 return lo_mat
450 -def observed_frequency_to_substitution_matrix(obs_freq_mat):
451 exp_freq_table = _exp_freq_table_from_obs_freq(obs_freq_mat) 452 exp_freq_mat = _build_exp_freq_mat(exp_freq_table) 453 subs_mat = _build_subs_mat(obs_freq_mat, exp_freq_mat) 454 return subs_mat
455 -def read_text_matrix(data_file,mat_type=NOTYPE):
456 matrix = {} 457 tmp = string.split(data_file.read(),"\n") 458 table=[] 459 for i in tmp: 460 table.append(string.split(i)) 461 # remove records beginning with ``#'' 462 for rec in table[:]: 463 if (rec.count('#') > 0): 464 table.remove(rec) 465 466 # remove null lists 467 while (table.count([]) > 0): 468 table.remove([]) 469 # build a dictionary 470 alphabet = table[0] 471 j = 0 472 for rec in table[1:]: 473 # print j 474 row = alphabet[j] 475 # row = rec[0] 476 if re.compile('[A-z\*]').match(rec[0]): 477 first_col = 1 478 else: 479 first_col = 0 480 i = 0 481 for field in rec[first_col:]: 482 col = alphabet[i] 483 matrix[(row,col)] = string.atof(field) 484 i += 1 485 j += 1 486 # delete entries with an asterisk 487 for i in matrix.keys(): 488 if '*' in i: del(matrix[i]) 489 ret_mat = SeqMat(matrix,mat_type=mat_type) 490 return ret_mat
491 492 diagNO = 1 493 diagONLY = 2 494 diagALL = 3 495
496 -def two_mat_relative_entropy(mat_1,mat_2,logbase=2,diag=diagALL):
497 rel_ent = 0. 498 key_list_1 = mat_1.keys(); key_list_2 = mat_2.keys() 499 key_list_1.sort(); key_list_2.sort() 500 key_list = [] 501 sum_ent_1 = 0.; sum_ent_2 = 0. 502 for i in key_list_1: 503 if i in key_list_2: 504 key_list.append(i) 505 if len(key_list_1) != len(key_list_2): 506 507 sys.stderr.write("Warning:first matrix has more entries than the second\n") 508 if key_list_1 != key_list_2: 509 sys.stderr.write("Warning: indices not the same between matrices\n") 510 for key in key_list: 511 if diag == diagNO and key[0] == key[1]: 512 continue 513 if diag == diagONLY and key[0] != key[1]: 514 continue 515 if mat_1[key] > EPSILON and mat_2[key] > EPSILON: 516 sum_ent_1 += mat_1[key] 517 sum_ent_2 += mat_2[key] 518 519 for key in key_list: 520 if diag == diagNO and key[0] == key[1]: 521 continue 522 if diag == diagONLY and key[0] != key[1]: 523 continue 524 if mat_1[key] > EPSILON and mat_2[key] > EPSILON: 525 val_1 = mat_1[key] / sum_ent_1 526 val_2 = mat_2[key] / sum_ent_2 527 # rel_ent += mat_1[key] * log(mat_1[key]/mat_2[key])/log(logbase) 528 rel_ent += val_1 * log(val_1/val_2)/log(logbase) 529 return rel_ent
530 531 ## Gives the linear correlation coefficient between two matrices 532 #def two_mat_correlation(mat_1, mat_2): 533 # Wait for the statistical package before uncommenting this 534 # 535 # corr_list = [] 536 # assert mat_1.ab_list == mat_2.ab_list 537 # for ab_pair in mat_1.keys(): 538 # try: 539 # corr_list.append((mat_1[ab_pair], mat_2[ab_pair])) 540 # except KeyError: 541 # sys.stderr.write("Error:two_mat_correlation: %s is not a common key\n" % 542 # mat_1) 543 # return statfns.corr(corr_list) 544 545 # Jensen-Shannon Distance 546 # Need to input observed frequency matrices
547 -def two_mat_DJS(mat_1,mat_2,pi_1=0.5,pi_2=0.5):
548 assert mat_1.ab_list == mat_2.ab_list 549 assert pi_1 > 0 and pi_2 > 0 and pi_1< 1 and pi_2 <1 550 assert not (pi_1 + pi_2 - 1.0 > EPSILON) 551 sum_mat = SeqMat(build_later=1) 552 sum_mat.ab_list = mat_1.ab_list 553 for i in mat_1.keys(): 554 sum_mat[i] = pi_1 * mat_1[i] + pi_2 * mat_2[i] 555 sum_mat.make_entropy() 556 mat_1.make_entropy() 557 mat_2.make_entropy() 558 # print mat_1.entropy, mat_2.entropy 559 dJS = sum_mat.entropy - pi_1 * mat_1.entropy - pi_2 *mat_2.entropy 560 return dJS
561 562 """ 563 This isn't working yet. Boo hoo! 564 def two_mat_print(mat_1, mat_2, f=None,alphabet=None,factor_1=1, factor_2=1, 565 format="%4d",bottomformat="%4s",topformat="%4s", 566 topindent=7*" ", bottomindent=1*" "): 567 f = f or sys.stdout 568 if not alphabet: 569 assert mat_1.ab_list == mat_2.ab_list 570 alphabet = mat_1.ab_list 571 len_alphabet = len(alphabet) 572 print_mat = {} 573 topline = topindent 574 bottomline = bottomindent 575 for i in alphabet: 576 bottomline += bottomformat % i 577 topline += topformat % alphabet[len_alphabet-alphabet.index(i)-1] 578 topline += '\n' 579 bottomline += '\n' 580 f.write(topline) 581 for i in alphabet: 582 for j in alphabet: 583 print_mat[i,j] = -999 584 diag_1 = {}; diag_2 = {} 585 for i in alphabet: 586 for j in alphabet[:alphabet.index(i)+1]: 587 if i == j: 588 diag_1[i] = mat_1[(i,i)] 589 diag_2[i] = mat_2[(alphabet[len_alphabet-alphabet.index(i)-1], 590 alphabet[len_alphabet-alphabet.index(i)-1])] 591 else: 592 if i > j: 593 key = (j,i) 594 else: 595 key = (i,j) 596 mat_2_key = [alphabet[len_alphabet-alphabet.index(key[0])-1], 597 alphabet[len_alphabet-alphabet.index(key[1])-1]] 598 # print mat_2_key 599 mat_2_key.sort(); mat_2_key = tuple(mat_2_key) 600 # print key ,"||", mat_2_key 601 print_mat[key] = mat_2[mat_2_key] 602 print_mat[(key[1],key[0])] = mat_1[key] 603 for i in alphabet: 604 outline = i 605 for j in alphabet: 606 if i == j: 607 if diag_1[i] == -999: 608 val_1 = ' ND' 609 else: 610 val_1 = format % (diag_1[i]*factor_1) 611 if diag_2[i] == -999: 612 val_2 = ' ND' 613 else: 614 val_2 = format % (diag_2[i]*factor_2) 615 cur_str = val_1 + " " + val_2 616 else: 617 if print_mat[(i,j)] == -999: 618 val = ' ND' 619 elif alphabet.index(i) > alphabet.index(j): 620 val = format % (print_mat[(i,j)]*factor_1) 621 else: 622 val = format % (print_mat[(i,j)]*factor_2) 623 cur_str = val 624 outline += cur_str 625 outline += bottomformat % (alphabet[len_alphabet-alphabet.index(i)-1] + 626 '\n') 627 f.write(outline) 628 f.write(bottomline) 629 """ 630