1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 from warnings import warn
30
31
32 from lib.errors import RelaxError
33 from lib.io import get_file_path, open_write_file
34 from lib.plotting.api import write_xy_data, write_xy_header
35 from lib.warnings import RelaxWarning
36 from pipe_control.mol_res_spin import check_mol_res_spin_data, spin_loop
37 from pipe_control.pipes import cdp_name, check_pipe
38 from pipe_control.result_files import add_result_file
39 from specific_analyses.api import return_api
40
41
42 -def assemble_data(spin_id=None, x_data_name=None, y_data_name=None, plot_data=None):
43 """Return all the xy data, along with the graph type and names for the graph sets.
44
45 @keyword spin_id: The spin ID string for restricting the graph to.
46 @type spin_id: str
47 @keyword x_data_name: The category of the X-axis data.
48 @type x_data_name: str
49 @keyword y_data_name: The category of the Y-axis data.
50 @type y_data_name: str
51 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'.
52 @type plot_data: str
53 @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.
54 @rtype: list of lists of lists of float, str, and list of str
55 """
56
57
58 data_list = False
59 data_dict = False
60
61
62 x_type = get_data_type(data_name=x_data_name)
63 y_type = get_data_type(data_name=y_data_name)
64
65
66 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)
67
68
69 if graph_type == 'seq-value':
70 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)
71 elif graph_type == 'value-value':
72 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)
73 elif graph_type == 'seq-series':
74 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)
75 elif graph_type == 'series-series':
76 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)
77 else:
78 raise RelaxError("Unknown graph type '%s'." % graph_type)
79
80
81 graph_type = 'X,Y'
82 if x_err_flag:
83 graph_type = graph_type + ',dX'
84 if y_err_flag:
85 graph_type = graph_type + ',dY'
86
87
88 return data, set_labels, graph_type
89
90
92 """Assemble the graph data for scatter type data of one value verses another.
93
94 For such data, only a single graph and set will be produced.
95
96
97 @keyword spin_id: The spin ID string for restricting the graph to.
98 @type spin_id: str
99 @keyword x_data_name: The name of the X-data or variable to plot.
100 @type x_data_name: str
101 @keyword y_data_name: The name of the Y-data or variable to plot.
102 @type y_data_name: str
103 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'.
104 @type plot_data: str
105 @return: The graph data, set labels, and flags for errors in the X and Y dimensions.
106 @rtype: list of lists of lists of numbers, list of str, bool, bool
107 """
108
109
110 return assemble_data_seq_value(x_data_name=x_data_name, y_data_name=y_data_name, plot_data=plot_data)
111
112
113 -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):
114 """Assemble the graph data for residue or spin sequence verses verses list or dictionary data.
115
116 For such data, one graph will be produced. There will be one data set in this graph per series.
117
118
119 @keyword spin_id: The spin ID string for restricting the graph to.
120 @type spin_id: str
121 @keyword x_data_name: The name of the X-data or variable to plot.
122 @type x_data_name: str
123 @keyword y_data_name: The name of the Y-data or variable to plot.
124 @type y_data_name: str
125 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'.
126 @type plot_data: str
127 @keyword x_type: The type of X-data to plot.
128 @type x_type: type object
129 @keyword y_type: The type of Y-data to plot.
130 @type y_type: type object
131 @return: The graph data, set labels, and flags for errors in the X and Y dimensions.
132 @rtype: list of lists of lists of numbers, list of str, bool, bool
133 """
134
135
136 data = [[]]
137 set_labels = []
138 x_err_flag = False
139 y_err_flag = False
140
141
142 if x_data_name in ['res_num', 'spin_num']:
143 seq_axis = 'x'
144 series_type = y_type
145 else:
146 seq_axis = 'y'
147 series_type = x_type
148
149
150 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True):
151
152 if seq_axis == 'x':
153 val, err = fetch_1D_data(plot_data=plot_data, data_name=y_data_name, spin=spin, res_num=res_num)
154 else:
155 val, err = fetch_1D_data(plot_data=plot_data, data_name=x_data_name, spin=spin, res_num=res_num)
156
157
158 if val == None:
159 continue
160
161
162 if series_type == dict:
163 keys = sorted(val.keys())
164
165
166 for j in range(len(val)):
167
168 if series_type == list:
169 elem = j
170 else:
171 elem = keys[j]
172
173
174 if elem not in set_labels:
175 data[0].append([])
176 set_labels.append(elem)
177
178
179 set_labels.sort()
180
181
182 if plot_data == 'sim':
183 points = cdp.sim_number
184 else:
185 points = 1
186
187
188 spin_index = 0
189 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True):
190
191 for i in range(points):
192
193 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)
194 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)
195
196
197 if seq_axis == 'x':
198 series_val = y_val
199 else:
200 series_val = x_val
201
202
203 if x_val == None or y_val == None:
204 continue
205
206
207 if x_err != None:
208 x_err_flag = True
209 if y_err != None:
210 y_err_flag = True
211
212
213 if series_type == dict:
214 keys = sorted(series_val.keys())
215
216
217 for j in range(len(series_val)):
218
219 if series_type == list:
220 index = set_labels.index(j)
221 elem = index
222 else:
223 index = set_labels.index(keys[j])
224 elem = set_labels[set_labels.index(keys[j])]
225
226
227 if seq_axis == 'x':
228 data[0][index].append([x_val, y_val[elem]])
229 else:
230 data[0][index].append([x_val[elem], y_val])
231 if x_err_flag:
232 data[0][index][-1].append(x_err[elem])
233 if y_err_flag:
234 data[0][index][-1].append(y_err[elem])
235
236
237 spin_index += 1
238
239
240 return data, set_labels, x_err_flag, y_err_flag
241
242
244 """Assemble the graph data for residue or spin sequence verses values.
245
246 For such data, only a single graph and set will be produced.
247
248
249 @keyword spin_id: The spin ID string for restricting the graph to.
250 @type spin_id: str
251 @keyword x_data_name: The name of the X-data or variable to plot.
252 @type x_data_name: str
253 @keyword y_data_name: The name of the Y-data or variable to plot.
254 @type y_data_name: str
255 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'.
256 @type plot_data: str
257 @return: The graph data, set labels, and flags for errors in the X and Y dimensions.
258 @rtype: list of lists of lists of numbers, list of str, bool, bool
259 """
260
261
262 data = [[[]]]
263 set_labels = []
264 x_err_flag = False
265 y_err_flag = False
266
267
268 spin_names = []
269 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True):
270
271 if spin.name not in spin_names:
272 spin_names.append(spin.name)
273
274
275 set_count = len(spin_names)
276
277
278 if set_count > 1:
279
280 for i in range(set_count-1):
281 data[0].append([])
282
283
284 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True):
285 label = "%s spins" % spin.name
286 if label not in set_labels:
287 set_labels.append(label)
288
289
290 if plot_data == 'sim':
291 points = cdp.sim_number
292 else:
293 points = 1
294
295
296 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True):
297
298 set_index = spin_names.index(spin.name)
299
300
301 for i in range(points):
302
303 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)
304 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)
305
306
307 if x_val == None or y_val == None:
308 continue
309
310
311 if x_err != None:
312 x_err_flag = True
313 if y_err != None:
314 y_err_flag = True
315
316
317 data[0][set_index].append([x_val, y_val])
318 if x_err_flag:
319 data[0][set_index][-1].append(x_err)
320 if y_err_flag:
321 data[0][set_index][-1].append(y_err)
322
323
324 return data, set_labels, x_err_flag, y_err_flag
325
326
328 """Assemble the graph data for curves of list or dictionary data verses list or dictionary data.
329
330 For such data, one graph will be produced. There will be one data set in this graph per spin.
331
332
333 @keyword spin_id: The spin ID string for restricting the graph to.
334 @type spin_id: str
335 @keyword x_data_name: The name of the X-data or variable to plot.
336 @type x_data_name: str
337 @keyword y_data_name: The name of the Y-data or variable to plot.
338 @type y_data_name: str
339 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'.
340 @type plot_data: str
341 @keyword x_type: The type of X-data to plot.
342 @type x_type: type object
343 @keyword y_type: The type of Y-data to plot.
344 @type y_type: type object
345 @return: The graph data, set labels, and flags for errors in the X and Y dimensions.
346 @rtype: list of lists of lists of numbers, list of str, bool, bool
347 """
348
349
350 data = [[]]
351 set_labels = []
352 x_err_flag = False
353 y_err_flag = False
354
355
356 if x_type != y_type:
357 raise RelaxError("The X data type '%s' and Y data type '%s' do not match." % (x_type, y_type))
358
359
360 keys_for_values = None
361 base_values = []
362 if x_type == dict:
363 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True):
364
365 x_val, x_err = fetch_1D_data(plot_data=plot_data, data_name=x_data_name, spin=spin, res_num=res_num)
366 y_val, y_err = fetch_1D_data(plot_data=plot_data, data_name=y_data_name, spin=spin, res_num=res_num)
367
368
369 if x_val == None or y_val == None:
370 continue
371
372
373 x_keys = sorted(x_val.keys())
374 y_keys = sorted(y_val.keys())
375
376
377 if x_keys[0] in y_keys:
378 continue
379
380
381 if x_keys[0] in list(y_val.values()):
382 keys_for_values = 'x'
383 for key in x_keys:
384 if key not in base_values:
385 base_values.append(key)
386
387
388 elif y_keys[0] in list(x_val.values()):
389 keys_for_values = 'y'
390 for key in y_keys:
391 if key not in base_values:
392 base_values.append(key)
393
394
395 if plot_data == 'sim':
396 points = cdp.sim_number
397 else:
398 points = 1
399
400
401 spin_index = 0
402 for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True):
403
404 data[0].append([])
405 set_labels.append("Spin %s" % id)
406
407
408 for i in range(points):
409
410 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)
411 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)
412
413
414 if keys_for_values == None:
415 base_values = x_val
416
417
418 if x_val == None or y_val == None:
419 continue
420
421
422 if x_err != None:
423 x_err_flag = True
424 if y_err != None:
425 y_err_flag = True
426
427
428 if keys_for_values == None and len(x_val) != len(y_val):
429 raise RelaxError("The series data %s does not have the same number of elements as %s." % (x_val, y_val))
430
431
432 if x_type == dict:
433 keys = sorted(x_val.keys())
434
435
436 for j in range(len(base_values)):
437
438 if x_type == list:
439 elem = j
440 else:
441 elem = keys[j]
442
443
444 if keys_for_values == None:
445 data[0][spin_index].append([x_val[elem], y_val[elem]])
446 if x_err_flag:
447 data[0][spin_index][-1].append(x_err[elem])
448 if y_err_flag:
449 data[0][spin_index][-1].append(y_err[elem])
450
451
452 elif keys_for_values == 'x':
453 data[0][spin_index].append([x_val[base_values[j]], base_values[j]])
454 if x_err_flag:
455 data[0][spin_index][-1].append(x_err[base_values[j]])
456 if y_err_flag:
457 raise RelaxError("Y errors are not possible when the Y values are keys.")
458
459
460 elif keys_for_values == 'y':
461 data[0][spin_index].append([base_values[j], y_val[base_values[j]]])
462 if x_err_flag:
463 raise RelaxError("X errors are not possible when the X values are keys.")
464 if y_err_flag:
465 data[0][spin_index][-1].append(y_err[base_values[j]])
466
467
468 data[0][spin_index].sort()
469
470
471 spin_index += 1
472
473
474 return data, set_labels, x_err_flag, y_err_flag
475
476
478 """Determine the axis information for relax data store specific data.
479
480 @keyword data_type: The axis data category (in the [X, Y] list format).
481 @type data_type: list of str
482 @keyword norm: The normalisation flag which if set to True will cause all graphs to be normalised to a starting value of 1.
483 @type norm: bool
484 @return: The axis information. This includes the sequence type, the list of lower bounds, the list of upper bounds, and the axis labels.
485 @rtype: list of str or None, list of int or None, list of int or None, list of str or None
486 """
487
488
489 axes = ['x', 'y']
490 seq_type = [None, None]
491 axis_labels = [None, None]
492 for i in range(2):
493
494 if data_type[i] == 'res_num':
495 seq_type[i] = 'res'
496
497
498 analysis_spec = False
499 if cdp_name():
500
501 analysis_spec = True
502
503
504 api = return_api()
505
506
507 if data_type[i] == 'res_num':
508
509 if seq_type[i] == 'res':
510
511 if not axis_labels[i]:
512 axis_labels[i] = "Residue number"
513
514
515 if seq_type[i] == 'spin':
516
517 if not axis_labels[i]:
518 axis_labels[i] = "Spin number"
519
520
521 if seq_type[i] == 'mixed':
522
523 if not axis_labels[i]:
524 axis_labels[i] = "Spin identification string"
525
526
527 else:
528
529 if analysis_spec and not axis_labels[i]:
530
531 units = api.return_grace_units(data_type[i])
532
533
534 axis_labels[i] = api.return_grace_string(data_type[i])
535
536
537 if units:
538 axis_labels[i] = axis_labels[i] + "\\N (" + units + ")"
539
540
541 if norm and axes[i] == 'y':
542 axis_labels[i] = axis_labels[i] + " \\N\\q(normalised)\\Q"
543
544
545 return seq_type, axis_labels
546
547
549 """Determine the type of graph to produce.
550
551 The graph type can be one of:
552
553 - 'seq-value', the residue or spin sequence verses the parameter value.
554 - 'seq-series', the residue or spin sequence verses the parameter value.
555 - 'value-value', a scatter plot of one value verses another.
556 - 'value-series', a curve of one value verses a list or dictionary of data.
557 - 'series-series', curves of list or dictionary data verses list or dictionary data.
558
559 @keyword x_data_name: The name of the X-data or variable to plot.
560 @type x_data_name: str
561 @keyword y_data_name: The name of the Y-data or variable to plot.
562 @type y_data_name: str
563 @keyword x_type: The type of X-data to plot.
564 @type x_type: type object
565 @keyword y_type: The type of Y-data to plot.
566 @type y_type: type object
567 @return: The graph type.
568 @rtype: str
569 """
570
571
572 if x_data_name == y_data_name == 'res_num':
573 raise RelaxError("The X and Y-axes can not both be based on residue numbers.")
574 if x_data_name == y_data_name == 'spin_num':
575 raise RelaxError("The X and Y-axes can not both be based on residue numbers.")
576
577
578 x_series = False
579 y_series = False
580 if x_type == list or x_type == dict:
581 x_series = True
582 if y_type == list or y_type == dict:
583 y_series = True
584
585
586 if x_data_name in ['res_num', 'spin_num'] and not y_series:
587 return 'seq-value'
588 if x_data_name in ['res_num', 'spin_num'] and y_series:
589 return 'seq-series'
590 if y_data_name in ['res_num', 'spin_num'] and not x_series:
591 return 'seq-value'
592 if y_data_name in ['res_num', 'spin_num'] and x_series:
593 return 'seq-series'
594
595
596 if not x_series and not y_series:
597 return 'value-value'
598
599
600 if not x_series and y_series:
601 return 'value-series'
602 if x_series and not y_series:
603 return 'value-series'
604
605
606 if x_series and y_series:
607 return 'series-series'
608
609
610 return 'unknown'
611
612
613 -def fetch_1D_data(plot_data=None, data_name=None, spin=None, res_num=None, sim_num=None):
614 """Return the value and error for the corresponding axis.
615
616 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'.
617 @type plot_data: str
618 @keyword data_name: The name of the data or variable to plot.
619 @type data_name: str
620 @keyword spin: The spin container to fetch the values from.
621 @type spin: SpinContainer instance
622 @keyword res_num: The residue number for the given spin.
623 @type res_num: int
624 @keyword sim_num: The simulation number if simulation data is to be returned.
625 @type sim_num: int
626 @return: The value and error when available.
627 @rtype: int or float, None or float
628 """
629
630
631 return_value, return_conversion_factor = get_functions(data_name=data_name)
632
633
634 if data_name == 'res_num':
635 val, err = res_num, None
636
637
638 elif data_name == 'spin_num':
639 val, err = spin.num, None
640
641
642 else:
643
644 if plot_data == 'sim':
645 val, err = return_value(spin, data_name, sim=sim_num)
646 else:
647 val, err = return_value(spin, data_name)
648
649
650 if isinstance(val, list):
651 for i in range(len(val)):
652 val[i] = val[i] / return_conversion_factor(data_name)
653 if err != None:
654 err[i] = err[i] / return_conversion_factor(data_name)
655 elif isinstance(val, dict):
656 for key in val:
657 val[key] = val[key] / return_conversion_factor(data_name)
658 if err != None:
659 err[key] = err[key] / return_conversion_factor(data_name)
660 elif val != None and err != None:
661 val = val / return_conversion_factor(data_name)
662 err = err / return_conversion_factor(data_name)
663 elif val != None:
664 val = val / return_conversion_factor(data_name)
665 elif err != None:
666 err = err / return_conversion_factor(data_name)
667
668
669 if data_name not in ['res_num', 'spin_num'] and plot_data == 'error':
670 val = err
671 err = None
672
673
674 if plot_data == 'sim':
675 err = None
676
677
678 return val, err
679
680
682 """Determine the specific functions for the given data type.
683
684 @keyword data_name: The name of the data or variable to plot.
685 @type data_name: str
686 @return: The analysis specific return_value, return_conversion_factor, and data_type methods.
687 @rtype: tuple of methods or None
688 """
689
690
691 if data_name in ['res_num', 'spin_num']:
692 return None, None
693
694
695 else:
696 api = return_api()
697 return api.return_value, api.return_conversion_factor
698
699
701 """Determine the type for the given data.
702
703 @keyword data_name: The name of the data or variable to plot.
704 @type data_name: str
705 @return: The data type.
706 @rtype: Python type
707 """
708
709
710 if data_name in ['res_num', 'spin_num']:
711 return int
712
713
714 api = return_api()
715 return api.data_type(data_name)
716
717
718 -def write_xy(format='grace', x_data_type='res_num', y_data_type=None, spin_id=None, plot_data='value', norm_type='first', file=None, dir=None, force=False, norm=True):
719 """Writing data to a file.
720
721 @keyword format: The specific backend to use. The currently support backends are 'grace'.
722 @type format: str
723 @keyword x_data_type: The category of the X-axis data.
724 @type x_data_type: str
725 @keyword y_data_type: The category of the Y-axis data.
726 @type y_data_type: str
727 @keyword spin_id: The spin identification string.
728 @type spin_id: str
729 @keyword plot_data: The type of the plotted data, one of 'value', 'error', or 'sim'.
730 @type plot_data: str
731 @keyword norm_type: The point to normalise to 1. This can be 'first' or 'last'.
732 @type norm_type: str
733 @keyword file: The name of the Grace file to create.
734 @type file: str
735 @keyword dir: The optional directory to place the file into.
736 @type dir: str
737 @param force: Boolean argument which if True causes the file to be overwritten if it already exists.
738 @type force: bool
739 @keyword norm: The normalisation flag which if set to True will cause all graphs to be normalised to a starting value of 1.
740 @type norm: bool
741 """
742
743
744 check_pipe()
745 check_mol_res_spin_data()
746
747
748 if plot_data not in ['value', 'error', 'sim']:
749 raise RelaxError("The plot data argument " + repr(plot_data) + " must be set to either 'value', 'error', 'sim'.")
750
751
752 if plot_data == 'sim' and not hasattr(cdp, 'sim_number'):
753 raise RelaxNoSimError
754
755
756 file_path = get_file_path(file, dir)
757 file = open_write_file(file, dir, force)
758
759
760 data, set_names, graph_type = assemble_data(spin_id, x_data_name=x_data_type, y_data_name=y_data_type, plot_data=plot_data)
761
762
763 if graph_type == 'X,Y':
764 graph_type = 'xy'
765 elif graph_type == 'X,Y,dX':
766 graph_type = 'xydx'
767 elif graph_type == 'X,Y,dY':
768 graph_type = 'xydy'
769 elif graph_type == 'X,Y,dX,dY':
770 graph_type = 'xydxdy'
771
772
773 if not len(data) or not len(data[0]) or not len(data[0][0]):
774 warn(RelaxWarning("No data could be found, creating an empty file."))
775 file.close()
776 return
777
778
779 data_type = [x_data_type, y_data_type]
780 seq_type, axis_labels = axis_setup(data_type=data_type, norm=norm)
781
782
783 write_xy_header(format=format, file=file, data_type=data_type, seq_type=seq_type, sets=[len(data[0])], set_names=[set_names], axis_labels=[axis_labels], norm=[norm])
784
785
786 write_xy_data(format=format, data=data, file=file, graph_type=graph_type, norm_type=norm_type, norm=[norm])
787
788
789 file.close()
790
791
792 label = None
793 if format == 'grace':
794 label = 'Grace'
795 add_result_file(type=format, label='Grace', file=file_path)
796