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

Source Code for Module pipe_control.plotting

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