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

Source Code for Package Bio.SubsMat

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