Package pipe_control :: Module plotting
[hide private]
[frames] | no frames]

Source Code for Module pipe_control.plotting

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2013 Edward d'Auvergne                                        # 
  4  #                                                                             # 
  5  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  6  #                                                                             # 
  7  # This program is free software: you can redistribute it and/or modify        # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation, either version 3 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # This program is distributed in the hope that it will be useful,             # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 19  #                                                                             # 
 20  ############################################################################### 
 21   
 22  # Module docstring. 
 23  """Module for the plotting of data. 
 24   
 25  The numerical graph data handled in these functions consists of a 4 dimensional list or array object.  The first dimension corresponds to different graphs.  The second corresponds the different data sets within a single each graph.  The third corresponds to the data series (i.e. each data point).  The forth is a list of the information about each point, it is a list where the first element is the X value, the second is the Y value, the third is the optional dX or dY error, and the forth is the optional dY error when X errors are present (the third position is then dx). 
 26  """ 
 27   
 28   
 29  # relax module imports. 
 30  from lib.errors import RelaxError 
 31  from pipe_control import minimise 
 32  from pipe_control.mol_res_spin import spin_loop 
 33  import specific_analyses 
 34   
 35   
36 -def assemble_data(spin_id=None, x_data_name=None, y_data_name=None, plot_data=None):
37 """Return all the xy data, along with the graph type and names for the graph sets. 38 39 @keyword spin_id: The spin ID string for restricting the graph to. 40 @type spin_id: str 41 @keyword x_data_name: The category of the X-axis data. 42 @type x_data_name: str 43 @keyword y_data_name: The category of the Y-axis data. 44 @type y_data_name: str 45 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'. 46 @type plot_data: str 47 @return: The 4D graph numerical data structure, the graph type (i.e. on of 'xy', 'xydy', or 'xydxdy'), and the labels for the graph sets. 48 @rtype: list of lists of lists of float, str, and list of str 49 """ 50 51 # Initialise the 4D data structure (Gx, Sx, data point, data point info), and graph set labels. 52 data_list = False 53 data_dict = False 54 55 # The data types. 56 x_type = get_data_type(data_name=x_data_name) 57 y_type = get_data_type(data_name=y_data_name) 58 59 # Determine the graph type. 60 graph_type = classify_graph_2D(x_data_name=x_data_name, y_data_name=y_data_name, x_type=x_type, y_type=y_type) 61 62 # Assemble the different graph data structures. 63 if graph_type == 'seq-value': 64 data, set_labels, x_err_flag, y_err_flag = assemble_data_seq_value(spin_id=spin_id, x_data_name=x_data_name, y_data_name=y_data_name, plot_data=plot_data) 65 elif graph_type == 'value-value': 66 data, set_labels, x_err_flag, y_err_flag = assemble_data_scatter(spin_id=spin_id, x_data_name=x_data_name, y_data_name=y_data_name, plot_data=plot_data) 67 elif graph_type == 'seq-series': 68 data, set_labels, x_err_flag, y_err_flag = assemble_data_seq_series(spin_id=spin_id, x_data_name=x_data_name, y_data_name=y_data_name, plot_data=plot_data, x_type=x_type, y_type=y_type) 69 elif graph_type == 'series-series': 70 data, set_labels, x_err_flag, y_err_flag = assemble_data_series_series(spin_id=spin_id, x_data_name=x_data_name, y_data_name=y_data_name, plot_data=plot_data, x_type=x_type, y_type=y_type) 71 else: 72 raise RelaxError("Unknown graph type '%s'." % graph_type) 73 74 # The graph type. 75 graph_type = 'X,Y' 76 if x_err_flag: 77 graph_type = graph_type + ',dX' 78 if y_err_flag: 79 graph_type = graph_type + ',dY' 80 81 # Return the data. 82 return data, set_labels, graph_type
83 84
85 -def assemble_data_scatter(spin_id=None, x_data_name=None, y_data_name=None, plot_data='value'):
86 """Assemble the graph data for scatter type data of one value verses another. 87 88 For such data, only a single graph and set will be produced. 89 90 91 @keyword spin_id: The spin ID string for restricting the graph to. 92 @type spin_id: str 93 @keyword x_data_name: The name of the X-data or variable to plot. 94 @type x_data_name: str 95 @keyword y_data_name: The name of the Y-data or variable to plot. 96 @type y_data_name: str 97 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'. 98 @type plot_data: str 99 @return: The graph data, set labels, and flags for errors in the X and Y dimensions. 100 @rtype: list of lists of lists of numbers, list of str, bool, bool 101 """ 102 103 # Default to the assemble_data_seq_value() function, as the graphs are currently not constructed differently. 104 return assemble_data_seq_value(x_data_name=x_data_name, y_data_name=y_data_name, plot_data=plot_data)
105 106
107 -def assemble_data_seq_series(spin_id=None, x_data_name=None, y_data_name=None, plot_data='value', x_type=None, y_type=None):
108 """Assemble the graph data for residue or spin sequence verses verses list or dictionary data. 109 110 For such data, one graph will be produced. There will be one data set in this graph per series. 111 112 113 @keyword spin_id: The spin ID string for restricting the graph to. 114 @type spin_id: str 115 @keyword x_data_name: The name of the X-data or variable to plot. 116 @type x_data_name: str 117 @keyword y_data_name: The name of the Y-data or variable to plot. 118 @type y_data_name: str 119 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'. 120 @type plot_data: str 121 @keyword x_type: The type of X-data to plot. 122 @type x_type: type object 123 @keyword y_type: The type of Y-data to plot. 124 @type y_type: type object 125 @return: The graph data, set labels, and flags for errors in the X and Y dimensions. 126 @rtype: list of lists of lists of numbers, list of str, bool, bool 127 """ 128 129 # Initialise some data structures. 130 data = [[]] 131 set_labels = [] 132 x_err_flag = False 133 y_err_flag = False 134 135 # The sequence and series axes. 136 if x_data_name in ['res_num', 'spin_num']: 137 seq_axis = 'x' 138 series_type = y_type 139 else: 140 seq_axis = 'y' 141 series_type = x_type 142 143 # Determine the number of sets. 144 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True): 145 # Fetch the series data (ignore simulations). 146 if seq_axis == 'x': 147 val, err = fetch_1D_data(plot_data=plot_data, data_name=y_data_name, spin=spin, res_num=res_num) 148 else: 149 val, err = fetch_1D_data(plot_data=plot_data, data_name=x_data_name, spin=spin, res_num=res_num) 150 151 # The keys. 152 if series_type == dict: 153 keys = list(val.keys()) 154 155 # Loop over the series data. 156 for j in range(len(val)): 157 # The index or key for the data. 158 if series_type == list: 159 elem = j 160 else: 161 elem = keys[j] 162 163 # Add the set info if new. 164 if elem not in set_labels: 165 data[0].append([]) 166 set_labels.append(elem) 167 168 # Sort the set labels. 169 set_labels.sort() 170 171 # Number of data points per spin. 172 if plot_data == 'sim': 173 points = cdp.sim_number 174 else: 175 points = 1 176 177 # Loop over the spins. 178 spin_index = 0 179 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True): 180 # Loop over the data points (for simulations). 181 for i in range(points): 182 # The X and Y data. 183 x_val, x_err = fetch_1D_data(plot_data=plot_data, data_name=x_data_name, spin=spin, res_num=res_num, sim_num=i) 184 y_val, y_err = fetch_1D_data(plot_data=plot_data, data_name=y_data_name, spin=spin, res_num=res_num, sim_num=i) 185 186 # Alias the data. 187 if seq_axis == 'x': 188 series_val = y_val 189 else: 190 series_val = x_val 191 192 # Go to the next spin if there is missing xy data. 193 if x_val == None or y_val == None: 194 continue 195 196 # The error flags. 197 if x_err != None: 198 x_err_flag = True 199 if y_err != None: 200 y_err_flag = True 201 202 # The keys. 203 if series_type == dict: 204 keys = list(series_val.keys()) 205 206 # Loop over the series data. 207 for j in range(len(series_val)): 208 # The index or key for the data. 209 if series_type == list: 210 index = set_labels.index(j) 211 elem = index 212 else: 213 index = set_labels.index(keys[j]) 214 elem = set_labels[set_labels.index(keys[j])] 215 216 # Append the data. 217 if seq_axis == 'x': 218 data[0][index].append([x_val, y_val[elem]]) 219 else: 220 data[0][index].append([x_val[elem], y_val]) 221 if x_err_flag: 222 data[0][index][-1].append(x_err[elem]) 223 if y_err_flag: 224 data[0][index][-1].append(y_err[elem]) 225 226 # Increment the spin index. 227 spin_index += 1 228 229 # Return the data. 230 return data, set_labels, x_err_flag, y_err_flag
231 232
233 -def assemble_data_seq_value(spin_id=None, x_data_name=None, y_data_name=None, plot_data='value'):
234 """Assemble the graph data for residue or spin sequence verses values. 235 236 For such data, only a single graph and set will be produced. 237 238 239 @keyword spin_id: The spin ID string for restricting the graph to. 240 @type spin_id: str 241 @keyword x_data_name: The name of the X-data or variable to plot. 242 @type x_data_name: str 243 @keyword y_data_name: The name of the Y-data or variable to plot. 244 @type y_data_name: str 245 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'. 246 @type plot_data: str 247 @return: The graph data, set labels, and flags for errors in the X and Y dimensions. 248 @rtype: list of lists of lists of numbers, list of str, bool, bool 249 """ 250 251 # Initialise some data structures. 252 data = [[[]]] 253 set_labels = [] 254 x_err_flag = False 255 y_err_flag = False 256 257 # Count the different spin types. 258 spin_names = [] 259 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True): 260 # A new spin name. 261 if spin.name not in spin_names: 262 spin_names.append(spin.name) 263 264 # The number of data sets. 265 set_count = len(spin_names) 266 267 # Expand the data structures for the number of sets. 268 if set_count > 1: 269 # Expand the data array. 270 for i in range(set_count-1): 271 data[0].append([]) 272 273 # Expand the set labels for all spin data. 274 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True): 275 label = "%s spins" % spin.name 276 if label not in set_labels: 277 set_labels.append(label) 278 279 # Number of data points per spin. 280 if plot_data == 'sim': 281 points = cdp.sim_number 282 else: 283 points = 1 284 285 # Loop over the spins. 286 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True): 287 # The set index. 288 set_index = spin_names.index(spin.name) 289 290 # Loop over the data points (for simulations). 291 for i in range(points): 292 # The X and Y data. 293 x_val, x_err = fetch_1D_data(plot_data=plot_data, data_name=x_data_name, spin=spin, res_num=res_num, sim_num=i) 294 y_val, y_err = fetch_1D_data(plot_data=plot_data, data_name=y_data_name, spin=spin, res_num=res_num, sim_num=i) 295 296 # Go to the next spin if there is missing xy data. 297 if x_val == None or y_val == None: 298 continue 299 300 # The error flags. 301 if x_err != None: 302 x_err_flag = True 303 if y_err != None: 304 y_err_flag = True 305 306 # Append the data. 307 data[0][set_index].append([x_val, y_val]) 308 if x_err_flag: 309 data[0][set_index][-1].append(x_err) 310 if y_err_flag: 311 data[0][set_index][-1].append(y_err) 312 313 # Return the data. 314 return data, set_labels, x_err_flag, y_err_flag
315 316
317 -def assemble_data_series_series(spin_id=None, x_data_name=None, y_data_name=None, plot_data='value', x_type=None, y_type=None):
318 """Assemble the graph data for curves of list or dictionary data verses list or dictionary data. 319 320 For such data, one graph will be produced. There will be one data set in this graph per spin. 321 322 323 @keyword spin_id: The spin ID string for restricting the graph to. 324 @type spin_id: str 325 @keyword x_data_name: The name of the X-data or variable to plot. 326 @type x_data_name: str 327 @keyword y_data_name: The name of the Y-data or variable to plot. 328 @type y_data_name: str 329 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'. 330 @type plot_data: str 331 @keyword x_type: The type of X-data to plot. 332 @type x_type: type object 333 @keyword y_type: The type of Y-data to plot. 334 @type y_type: type object 335 @return: The graph data, set labels, and flags for errors in the X and Y dimensions. 336 @rtype: list of lists of lists of numbers, list of str, bool, bool 337 """ 338 339 # Initialise some data structures. 340 data = [[]] 341 set_labels = [] 342 x_err_flag = False 343 y_err_flag = False 344 345 # Sanity check. 346 if x_type != y_type: 347 raise RelaxError("The X data type '%s' and Y data type '%s' do not match." % (x_type, y_type)) 348 349 # Check if the dictionary keys are the values to plot. 350 keys_for_values = None 351 base_values = [] 352 if x_type == dict: 353 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True): 354 # Fetch the series data (ignore simulations). 355 x_val, x_err = fetch_1D_data(plot_data=plot_data, data_name=x_data_name, spin=spin, res_num=res_num) 356 y_val, y_err = fetch_1D_data(plot_data=plot_data, data_name=y_data_name, spin=spin, res_num=res_num) 357 358 # Go to the next spin if there is missing xy data. 359 if x_val == None or y_val == None: 360 continue 361 362 # The keys. 363 x_keys = list(x_val.keys()) 364 y_keys = list(y_val.keys()) 365 366 # The keys do not match. 367 if x_keys[0] in y_keys: 368 continue 369 370 # Are the X keys in the Y values? 371 if x_keys[0] in y_val.values(): 372 keys_for_values = 'x' 373 for key in x_keys: 374 if key not in base_values: 375 base_values.append(key) 376 377 # Are the Y keys in the X values? 378 elif y_keys[0] in x_val.values(): 379 keys_for_values = 'y' 380 for key in y_keys: 381 if key not in base_values: 382 base_values.append(key) 383 384 # Number of data points per spin. 385 if plot_data == 'sim': 386 points = cdp.sim_number 387 else: 388 points = 1 389 390 # Loop over the spins. 391 spin_index = 0 392 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True): 393 # Append a new set structure and set the name to the spin ID. 394 data[0].append([]) 395 set_labels.append("Spin %s" % id) 396 397 # Loop over the data points (for simulations). 398 for i in range(points): 399 # The X and Y data. 400 x_val, x_err = fetch_1D_data(plot_data=plot_data, data_name=x_data_name, spin=spin, res_num=res_num, sim_num=i) 401 y_val, y_err = fetch_1D_data(plot_data=plot_data, data_name=y_data_name, spin=spin, res_num=res_num, sim_num=i) 402 403 # The base values to create the curve from. 404 if keys_for_values == None: 405 base_values = x_val 406 407 # Go to the next spin if there is missing xy data. 408 if x_val == None or y_val == None: 409 continue 410 411 # The error flags. 412 if x_err != None: 413 x_err_flag = True 414 if y_err != None: 415 y_err_flag = True 416 417 # Series sanity checks. 418 if keys_for_values == None and len(x_val) != len(y_val): 419 raise RelaxError("The series data %s does not have the same number of elements as %s." % (x_val, y_val)) 420 421 # The keys. 422 if x_type == dict: 423 keys = list(x_val.keys()) 424 425 # Loop over the list data. 426 for j in range(len(base_values)): 427 # The index or key for the data. 428 if x_type == list: 429 elem = j 430 else: 431 elem = keys[j] 432 433 # Append the data. 434 if keys_for_values == None: 435 data[0][spin_index].append([x_val[elem], y_val[elem]]) 436 if x_err_flag: 437 data[0][spin_index][-1].append(x_err[elem]) 438 if y_err_flag: 439 data[0][spin_index][-1].append(y_err[elem]) 440 441 # Append the data (X keys in the Y values). 442 elif keys_for_values == 'x': 443 data[0][spin_index].append([x_val[base_values[j]], base_values[j]]) 444 if x_err_flag: 445 data[0][spin_index][-1].append(x_err[base_values[j]]) 446 if y_err_flag: 447 raise RelaxError("Y errors are not possible when the Y values are keys.") 448 449 # Append the data (Y keys in the X values). 450 elif keys_for_values == 'y': 451 data[0][spin_index].append([base_values[j], y_val[base_values[j]]]) 452 if x_err_flag: 453 raise RelaxError("X errors are not possible when the X values are keys.") 454 if y_err_flag: 455 data[0][spin_index][-1].append(y_err[base_values[j]]) 456 457 # Sort the data for better looking curves. 458 data[0][spin_index].sort() 459 460 # Increment the spin index. 461 spin_index += 1 462 463 # Return the data. 464 return data, set_labels, x_err_flag, y_err_flag
465 466
467 -def classify_graph_2D(x_data_name=None, y_data_name=None, x_type=None, y_type=None):
468 """Determine the type of graph to produce. 469 470 The graph type can be one of: 471 472 - 'seq-value', the residue or spin sequence verses the parameter value. 473 - 'seq-series', the residue or spin sequence verses the parameter value. 474 - 'value-value', a scatter plot of one value verses another. 475 - 'value-series', a curve of one value verses a list or dictionary of data. 476 - 'series-series', curves of list or dictionary data verses list or dictionary data. 477 478 @keyword x_data_name: The name of the X-data or variable to plot. 479 @type x_data_name: str 480 @keyword y_data_name: The name of the Y-data or variable to plot. 481 @type y_data_name: str 482 @keyword x_type: The type of X-data to plot. 483 @type x_type: type object 484 @keyword y_type: The type of Y-data to plot. 485 @type y_type: type object 486 @return: The graph type. 487 @rtype: str 488 """ 489 490 # Disallow certain combinations. 491 if x_data_name == y_data_name == 'res_num': 492 raise RelaxError("The X and Y-axes can not both be based on residue numbers.") 493 if x_data_name == y_data_name == 'spin_num': 494 raise RelaxError("The X and Y-axes can not both be based on residue numbers.") 495 496 # Some data type flags. 497 x_series = False 498 y_series = False 499 if x_type == list or x_type == dict: 500 x_series = True 501 if y_type == list or y_type == dict: 502 y_series = True 503 504 # The different X and Y axis sequence types. 505 if x_data_name in ['res_num', 'spin_num'] and not y_series: 506 return 'seq-value' 507 if x_data_name in ['res_num', 'spin_num'] and y_series: 508 return 'seq-series' 509 if y_data_name in ['res_num', 'spin_num'] and not x_series: 510 return 'seq-value' 511 if y_data_name in ['res_num', 'spin_num'] and x_series: 512 return 'seq-series' 513 514 # Scatter plots. 515 if not x_series and not y_series: 516 return 'value-value' 517 518 # Series-series data. 519 if not x_series and y_series: 520 return 'value-series' 521 if x_series and not y_series: 522 return 'value-series' 523 524 # Series-series data. 525 if x_series and y_series: 526 return 'series-series' 527 528 # Unknown. 529 return 'unknown'
530 531
532 -def fetch_1D_data(plot_data=None, data_name=None, spin=None, res_num=None, sim_num=None):
533 """Return the value and error for the corresponding axis. 534 535 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'. 536 @type plot_data: str 537 @keyword data_name: The name of the data or variable to plot. 538 @type data_name: str 539 @keyword spin: The spin container to fetch the values from. 540 @type spin: SpinContainer instance 541 @keyword res_num: The residue number for the given spin. 542 @type res_num: int 543 @keyword sim_num: The simulation number if simulation data is to be returned. 544 @type sim_num: int 545 @return: The value and error when available. 546 @rtype: int or float, None or float 547 """ 548 549 # Specific x and y value returning functions. 550 return_value, return_conversion_factor = get_functions(data_name=data_name) 551 552 # The residue number data. 553 if data_name == 'res_num': 554 val, err = res_num, None 555 556 # The spin number data. 557 elif data_name == 'spin_num': 558 val, err = spin.num, None 559 560 # All other data types. 561 else: 562 # Get the data. 563 if plot_data == 'sim': 564 val, err = return_value(spin, data_name, sim=sim_num) 565 else: 566 val, err = return_value(spin, data_name) 567 568 # Convert to the correct scale. 569 if isinstance(val, list): 570 for i in range(len(val)): 571 val[i] = val[i] / return_conversion_factor(data_name) 572 if err != None: 573 err[i] = err[i] / return_conversion_factor(data_name) 574 elif isinstance(val, dict): 575 for key in val.keys(): 576 val[key] = val[key] / return_conversion_factor(data_name) 577 if err != None: 578 err[key] = err[key] / return_conversion_factor(data_name) 579 elif val != None and err != None: 580 val = val / return_conversion_factor(data_name) 581 err = err / return_conversion_factor(data_name) 582 elif val != None: 583 val = val / return_conversion_factor(data_name) 584 elif err != None: 585 err = err / return_conversion_factor(data_name) 586 587 # Convert the errors to values. 588 if data_name not in ['res_num', 'spin_num'] and plot_data == 'error': 589 val = err 590 err = None 591 592 # Simulation data, so turn off errors. 593 if plot_data == 'sim': 594 err = None 595 596 # Return the data. 597 return val, err
598 599
600 -def get_functions(data_name=None):
601 """Determine the specific functions for the given data type. 602 603 @keyword data_name: The name of the data or variable to plot. 604 @type data_name: str 605 @return: The analysis specific return_value, return_conversion_factor, and data_type methods. 606 @rtype: tuple of methods or None 607 """ 608 609 # Spin data. 610 if data_name in ['res_num', 'spin_num']: 611 return None, None 612 613 # A minimisation statistic. 614 if minimise.return_data_name(data_name): 615 return minimise.return_value, minimise.return_conversion_factor 616 617 # Analysis specific value returning functions. 618 else: 619 return_value = specific_analyses.setup.get_specific_fn('return_value') 620 return_conversion_factor = specific_analyses.setup.get_specific_fn('return_conversion_factor') 621 return return_value, return_conversion_factor
622 623
624 -def get_data_type(data_name=None):
625 """Determine the type for the given data. 626 627 @keyword data_name: The name of the data or variable to plot. 628 @type data_name: str 629 @return: The data type. 630 @rtype: Python type 631 """ 632 633 # Sequence data. 634 if data_name in ['res_num', 'spin_num']: 635 return int 636 637 # A minimisation statistic. 638 if minimise.return_data_name(data_name): 639 return int 640 641 # Analysis specific value returning functions. 642 else: 643 data_type = specific_analyses.setup.get_specific_fn('data_type') 644 return data_type(data_name)
645