dcscope 2.22.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. dcscope/__init__.py +17 -0
  2. dcscope/__main__.py +44 -0
  3. dcscope/_version.py +21 -0
  4. dcscope/extensions.py +250 -0
  5. dcscope/gui/__init__.py +2 -0
  6. dcscope/gui/analysis/__init__.py +10 -0
  7. dcscope/gui/analysis/ana_basins.py +110 -0
  8. dcscope/gui/analysis/ana_basins.ui +143 -0
  9. dcscope/gui/analysis/ana_filter.py +341 -0
  10. dcscope/gui/analysis/ana_filter.ui +364 -0
  11. dcscope/gui/analysis/ana_log.py +122 -0
  12. dcscope/gui/analysis/ana_log.ui +100 -0
  13. dcscope/gui/analysis/ana_meta.py +207 -0
  14. dcscope/gui/analysis/ana_meta.ui +141 -0
  15. dcscope/gui/analysis/ana_plot.py +638 -0
  16. dcscope/gui/analysis/ana_plot.ui +1060 -0
  17. dcscope/gui/analysis/ana_slot.py +504 -0
  18. dcscope/gui/analysis/ana_slot.ui +684 -0
  19. dcscope/gui/analysis/ana_tables.py +227 -0
  20. dcscope/gui/analysis/ana_tables.ui +195 -0
  21. dcscope/gui/analysis/ana_view.py +69 -0
  22. dcscope/gui/analysis/ana_view.ui +269 -0
  23. dcscope/gui/analysis/dlg_slot_reorder.py +55 -0
  24. dcscope/gui/analysis/dlg_slot_reorder.ui +138 -0
  25. dcscope/gui/bulk/__init__.py +2 -0
  26. dcscope/gui/bulk/bulk_emodulus.py +200 -0
  27. dcscope/gui/bulk/bulk_emodulus.ui +245 -0
  28. dcscope/gui/compute/__init__.py +3 -0
  29. dcscope/gui/compute/comp_lme4.py +115 -0
  30. dcscope/gui/compute/comp_lme4.ui +194 -0
  31. dcscope/gui/compute/comp_lme4_dataset.py +51 -0
  32. dcscope/gui/compute/comp_lme4_dataset.ui +92 -0
  33. dcscope/gui/compute/comp_lme4_results.py +120 -0
  34. dcscope/gui/compute/comp_lme4_results.ui +268 -0
  35. dcscope/gui/compute/comp_stats.py +223 -0
  36. dcscope/gui/compute/comp_stats.ui +187 -0
  37. dcscope/gui/dcor/__init__.py +304 -0
  38. dcscope/gui/dcor/dcor.ui +118 -0
  39. dcscope/gui/export/__init__.py +3 -0
  40. dcscope/gui/export/e2data.py +394 -0
  41. dcscope/gui/export/e2data.ui +367 -0
  42. dcscope/gui/export/e2filter.py +76 -0
  43. dcscope/gui/export/e2filter.ui +111 -0
  44. dcscope/gui/export/e2plot.py +94 -0
  45. dcscope/gui/export/e2plot.ui +175 -0
  46. dcscope/gui/main.py +1089 -0
  47. dcscope/gui/main.ui +579 -0
  48. dcscope/gui/matrix/__init__.py +4 -0
  49. dcscope/gui/matrix/block_matrix.py +141 -0
  50. dcscope/gui/matrix/block_matrix.ui +207 -0
  51. dcscope/gui/matrix/data_matrix.py +574 -0
  52. dcscope/gui/matrix/dm_dataset.py +116 -0
  53. dcscope/gui/matrix/dm_dataset.ui +348 -0
  54. dcscope/gui/matrix/dm_element.py +111 -0
  55. dcscope/gui/matrix/dm_element.ui +169 -0
  56. dcscope/gui/matrix/dm_filter.py +115 -0
  57. dcscope/gui/matrix/dm_filter.ui +287 -0
  58. dcscope/gui/matrix/plot_matrix.py +316 -0
  59. dcscope/gui/matrix/pm_element.py +39 -0
  60. dcscope/gui/matrix/pm_plot.py +86 -0
  61. dcscope/gui/matrix/pm_plot.ui +249 -0
  62. dcscope/gui/pipeline_plot.py +797 -0
  63. dcscope/gui/pipeline_plot.ui +66 -0
  64. dcscope/gui/preferences.py +380 -0
  65. dcscope/gui/preferences.ui +554 -0
  66. dcscope/gui/quick_view/__init__.py +1 -0
  67. dcscope/gui/quick_view/qv_main.py +1072 -0
  68. dcscope/gui/quick_view/qv_main.ui +1297 -0
  69. dcscope/gui/quick_view/qv_scatter.py +284 -0
  70. dcscope/gui/quick_view/qv_style.css +10 -0
  71. dcscope/gui/update.py +90 -0
  72. dcscope/gui/widgets/__init__.py +15 -0
  73. dcscope/gui/widgets/bg_thread.py +29 -0
  74. dcscope/gui/widgets/bulk_list.py +79 -0
  75. dcscope/gui/widgets/bulk_list.ui +95 -0
  76. dcscope/gui/widgets/double_spin_box_nan.py +75 -0
  77. dcscope/gui/widgets/feature_combobox.py +140 -0
  78. dcscope/gui/widgets/get_path.py +42 -0
  79. dcscope/gui/widgets/key_value_table_widget.css +12 -0
  80. dcscope/gui/widgets/key_value_table_widget.py +75 -0
  81. dcscope/gui/widgets/mdi_subwindow_wo_close.py +12 -0
  82. dcscope/gui/widgets/qrangeslider.py +262 -0
  83. dcscope/gui/widgets/rangecontrol.py +308 -0
  84. dcscope/gui/widgets/rangecontrol.ui +136 -0
  85. dcscope/gui/widgets/simple_image_view.py +55 -0
  86. dcscope/gui/widgets/simple_plot_widget.py +113 -0
  87. dcscope/gui/widgets/so_colorbaritem.py +29 -0
  88. dcscope/gui/widgets/wait_cursor.py +33 -0
  89. dcscope/idiom.py +26 -0
  90. dcscope/img/__init__.py +2 -0
  91. dcscope/img/icon-theme/breeze/COPYING-ICONS +209 -0
  92. dcscope/img/icon-theme/breeze/COPYING.LIB +510 -0
  93. dcscope/img/icon-theme/breeze/README +5 -0
  94. dcscope/img/icon-theme/breeze/actions/16/application-exit.svg +17 -0
  95. dcscope/img/icon-theme/breeze/actions/16/code-context.svg +13 -0
  96. dcscope/img/icon-theme/breeze/actions/16/dialog-cancel.svg +13 -0
  97. dcscope/img/icon-theme/breeze/actions/16/dialog-close.svg +17 -0
  98. dcscope/img/icon-theme/breeze/actions/16/dialog-messages.svg +13 -0
  99. dcscope/img/icon-theme/breeze/actions/16/dialog-ok-apply.svg +13 -0
  100. dcscope/img/icon-theme/breeze/actions/16/dialog-ok.svg +13 -0
  101. dcscope/img/icon-theme/breeze/actions/16/document-open-folder.svg +13 -0
  102. dcscope/img/icon-theme/breeze/actions/16/document-open.svg +13 -0
  103. dcscope/img/icon-theme/breeze/actions/16/document-save.svg +13 -0
  104. dcscope/img/icon-theme/breeze/actions/16/documentinfo.svg +12 -0
  105. dcscope/img/icon-theme/breeze/actions/16/draw-watercolor.svg +13 -0
  106. dcscope/img/icon-theme/breeze/actions/16/edit-clear-all.svg +13 -0
  107. dcscope/img/icon-theme/breeze/actions/16/edit-clear.svg +13 -0
  108. dcscope/img/icon-theme/breeze/actions/16/edit-paste.svg +13 -0
  109. dcscope/img/icon-theme/breeze/actions/16/globe.svg +16 -0
  110. dcscope/img/icon-theme/breeze/actions/16/gtk-preferences.svg +13 -0
  111. dcscope/img/icon-theme/breeze/actions/16/list-add.svg +13 -0
  112. dcscope/img/icon-theme/breeze/actions/16/messagebox_warning.svg +20 -0
  113. dcscope/img/icon-theme/breeze/actions/16/object-columns.svg +13 -0
  114. dcscope/img/icon-theme/breeze/actions/16/object-order-lower.svg +13 -0
  115. dcscope/img/icon-theme/breeze/actions/16/object-rows.svg +13 -0
  116. dcscope/img/icon-theme/breeze/actions/16/office-chart-line-stacked.svg +13 -0
  117. dcscope/img/icon-theme/breeze/actions/16/office-chart-ring.svg +13 -0
  118. dcscope/img/icon-theme/breeze/actions/16/office-chart-scatter.svg +13 -0
  119. dcscope/img/icon-theme/breeze/actions/16/path-mode-polyline.svg +13 -0
  120. dcscope/img/icon-theme/breeze/actions/16/preferences-activities.svg +13 -0
  121. dcscope/img/icon-theme/breeze/actions/16/remove.svg +10 -0
  122. dcscope/img/icon-theme/breeze/actions/16/search.svg +13 -0
  123. dcscope/img/icon-theme/breeze/actions/16/show-grid.svg +13 -0
  124. dcscope/img/icon-theme/breeze/actions/16/special_paste.svg +13 -0
  125. dcscope/img/icon-theme/breeze/actions/16/tools-wizard.svg +13 -0
  126. dcscope/img/icon-theme/breeze/actions/16/view-calendar-list.svg +13 -0
  127. dcscope/img/icon-theme/breeze/actions/16/view-filter.svg +13 -0
  128. dcscope/img/icon-theme/breeze/actions/16/view-list-tree.svg +13 -0
  129. dcscope/img/icon-theme/breeze/actions/16/view-statistics.svg +13 -0
  130. dcscope/img/icon-theme/breeze/actions/16/visibility.svg +21 -0
  131. dcscope/img/icon-theme/breeze/actions/22/application-exit.svg +13 -0
  132. dcscope/img/icon-theme/breeze/actions/22/code-context.svg +14 -0
  133. dcscope/img/icon-theme/breeze/actions/22/dialog-cancel.svg +14 -0
  134. dcscope/img/icon-theme/breeze/actions/22/dialog-close.svg +10 -0
  135. dcscope/img/icon-theme/breeze/actions/22/dialog-messages.svg +14 -0
  136. dcscope/img/icon-theme/breeze/actions/22/dialog-ok-apply.svg +14 -0
  137. dcscope/img/icon-theme/breeze/actions/22/dialog-ok.svg +14 -0
  138. dcscope/img/icon-theme/breeze/actions/22/document-open-folder.svg +14 -0
  139. dcscope/img/icon-theme/breeze/actions/22/document-open.svg +14 -0
  140. dcscope/img/icon-theme/breeze/actions/22/document-save.svg +14 -0
  141. dcscope/img/icon-theme/breeze/actions/22/documentinfo.svg +14 -0
  142. dcscope/img/icon-theme/breeze/actions/22/draw-watercolor.svg +14 -0
  143. dcscope/img/icon-theme/breeze/actions/22/edit-clear-all.svg +14 -0
  144. dcscope/img/icon-theme/breeze/actions/22/edit-clear.svg +14 -0
  145. dcscope/img/icon-theme/breeze/actions/22/edit-paste.svg +14 -0
  146. dcscope/img/icon-theme/breeze/actions/22/globe.svg +12 -0
  147. dcscope/img/icon-theme/breeze/actions/22/gtk-preferences.svg +13 -0
  148. dcscope/img/icon-theme/breeze/actions/22/list-add.svg +14 -0
  149. dcscope/img/icon-theme/breeze/actions/22/messagebox_warning.svg +17 -0
  150. dcscope/img/icon-theme/breeze/actions/22/object-columns.svg +14 -0
  151. dcscope/img/icon-theme/breeze/actions/22/object-order-lower.svg +17 -0
  152. dcscope/img/icon-theme/breeze/actions/22/object-rows.svg +17 -0
  153. dcscope/img/icon-theme/breeze/actions/22/office-chart-line-stacked.svg +17 -0
  154. dcscope/img/icon-theme/breeze/actions/22/office-chart-ring.svg +17 -0
  155. dcscope/img/icon-theme/breeze/actions/22/office-chart-scatter.svg +17 -0
  156. dcscope/img/icon-theme/breeze/actions/22/path-mode-polyline.svg +17 -0
  157. dcscope/img/icon-theme/breeze/actions/22/preferences-activities.svg +14 -0
  158. dcscope/img/icon-theme/breeze/actions/22/remove.svg +10 -0
  159. dcscope/img/icon-theme/breeze/actions/22/special_paste.svg +14 -0
  160. dcscope/img/icon-theme/breeze/actions/22/tools-wizard.svg +14 -0
  161. dcscope/img/icon-theme/breeze/actions/22/view-calendar-list.svg +14 -0
  162. dcscope/img/icon-theme/breeze/actions/22/view-filter.svg +14 -0
  163. dcscope/img/icon-theme/breeze/actions/22/view-list-tree.svg +14 -0
  164. dcscope/img/icon-theme/breeze/actions/22/view-statistics.svg +14 -0
  165. dcscope/img/icon-theme/breeze/actions/22/visibility.svg +14 -0
  166. dcscope/img/icon-theme/breeze/actions/24/application-exit.svg +12 -0
  167. dcscope/img/icon-theme/breeze/actions/24/code-context.svg +12 -0
  168. dcscope/img/icon-theme/breeze/actions/24/dialog-cancel.svg +12 -0
  169. dcscope/img/icon-theme/breeze/actions/24/dialog-close.svg +10 -0
  170. dcscope/img/icon-theme/breeze/actions/24/dialog-messages.svg +12 -0
  171. dcscope/img/icon-theme/breeze/actions/24/dialog-ok-apply.svg +12 -0
  172. dcscope/img/icon-theme/breeze/actions/24/dialog-ok.svg +14 -0
  173. dcscope/img/icon-theme/breeze/actions/24/document-open-folder.svg +12 -0
  174. dcscope/img/icon-theme/breeze/actions/24/document-open.svg +12 -0
  175. dcscope/img/icon-theme/breeze/actions/24/document-save.svg +12 -0
  176. dcscope/img/icon-theme/breeze/actions/24/documentinfo.svg +14 -0
  177. dcscope/img/icon-theme/breeze/actions/24/draw-watercolor.svg +12 -0
  178. dcscope/img/icon-theme/breeze/actions/24/edit-clear-all.svg +14 -0
  179. dcscope/img/icon-theme/breeze/actions/24/edit-clear.svg +12 -0
  180. dcscope/img/icon-theme/breeze/actions/24/edit-paste.svg +12 -0
  181. dcscope/img/icon-theme/breeze/actions/24/globe.svg +14 -0
  182. dcscope/img/icon-theme/breeze/actions/24/gtk-preferences.svg +13 -0
  183. dcscope/img/icon-theme/breeze/actions/24/list-add.svg +12 -0
  184. dcscope/img/icon-theme/breeze/actions/24/messagebox_warning.svg +13 -0
  185. dcscope/img/icon-theme/breeze/actions/24/object-columns.svg +12 -0
  186. dcscope/img/icon-theme/breeze/actions/24/object-order-lower.svg +15 -0
  187. dcscope/img/icon-theme/breeze/actions/24/object-rows.svg +15 -0
  188. dcscope/img/icon-theme/breeze/actions/24/office-chart-line-stacked.svg +15 -0
  189. dcscope/img/icon-theme/breeze/actions/24/office-chart-ring.svg +15 -0
  190. dcscope/img/icon-theme/breeze/actions/24/office-chart-scatter.svg +15 -0
  191. dcscope/img/icon-theme/breeze/actions/24/path-mode-polyline.svg +15 -0
  192. dcscope/img/icon-theme/breeze/actions/24/preferences-activities.svg +13 -0
  193. dcscope/img/icon-theme/breeze/actions/24/remove.svg +10 -0
  194. dcscope/img/icon-theme/breeze/actions/24/special_paste.svg +12 -0
  195. dcscope/img/icon-theme/breeze/actions/24/tools-wizard.svg +12 -0
  196. dcscope/img/icon-theme/breeze/actions/24/view-calendar-list.svg +12 -0
  197. dcscope/img/icon-theme/breeze/actions/24/view-filter.svg +12 -0
  198. dcscope/img/icon-theme/breeze/actions/24/view-list-tree.svg +12 -0
  199. dcscope/img/icon-theme/breeze/actions/24/view-statistics.svg +12 -0
  200. dcscope/img/icon-theme/breeze/actions/24/visibility.svg +14 -0
  201. dcscope/img/icon-theme/breeze/actions/32/application-exit.svg +8 -0
  202. dcscope/img/icon-theme/breeze/actions/32/dialog-cancel.svg +13 -0
  203. dcscope/img/icon-theme/breeze/actions/32/dialog-messages.svg +13 -0
  204. dcscope/img/icon-theme/breeze/actions/32/dialog-ok-apply.svg +1 -0
  205. dcscope/img/icon-theme/breeze/actions/32/dialog-ok.svg +1 -0
  206. dcscope/img/icon-theme/breeze/actions/32/document-open-folder.svg +13 -0
  207. dcscope/img/icon-theme/breeze/actions/32/document-open.svg +13 -0
  208. dcscope/img/icon-theme/breeze/actions/32/document-save.svg +18 -0
  209. dcscope/img/icon-theme/breeze/actions/32/gtk-preferences.svg +17 -0
  210. dcscope/img/icon-theme/breeze/actions/32/object-order-lower.svg +18 -0
  211. dcscope/img/icon-theme/breeze/actions/32/view-filter.svg +18 -0
  212. dcscope/img/icon-theme/breeze/actions/32/view-list-tree.svg +18 -0
  213. dcscope/img/icon-theme/breeze/places/16/folder-cloud.svg +13 -0
  214. dcscope/img/icon-theme/breeze/places/16/folder.svg +13 -0
  215. dcscope/img/icon-theme/breeze/places/22/folder-cloud.svg +13 -0
  216. dcscope/img/icon-theme/breeze/places/22/folder.svg +13 -0
  217. dcscope/img/icon-theme/breeze/places/24/folder-cloud.svg +12 -0
  218. dcscope/img/icon-theme/breeze/places/24/folder.svg +12 -0
  219. dcscope/img/icon-theme/breeze/places/32/folder-cloud.svg +35 -0
  220. dcscope/img/icon-theme/breeze/places/32/folder.svg +30 -0
  221. dcscope/img/icon-theme/breeze/places/64/folder-cloud.svg +35 -0
  222. dcscope/img/icon-theme/breeze/places/64/folder.svg +30 -0
  223. dcscope/img/icon-theme/breeze/status/16/dialog-error.svg +9 -0
  224. dcscope/img/icon-theme/breeze/status/16/dialog-information.svg +9 -0
  225. dcscope/img/icon-theme/breeze/status/16/dialog-question.svg +7 -0
  226. dcscope/img/icon-theme/breeze/status/16/dialog-warning.svg +9 -0
  227. dcscope/img/icon-theme/breeze/status/22/dialog-error.svg +9 -0
  228. dcscope/img/icon-theme/breeze/status/22/dialog-information.svg +9 -0
  229. dcscope/img/icon-theme/breeze/status/22/dialog-question.svg +7 -0
  230. dcscope/img/icon-theme/breeze/status/22/dialog-warning.svg +9 -0
  231. dcscope/img/icon-theme/breeze/status/24/dialog-error.svg +13 -0
  232. dcscope/img/icon-theme/breeze/status/24/dialog-information.svg +13 -0
  233. dcscope/img/icon-theme/breeze/status/24/dialog-question.svg +9 -0
  234. dcscope/img/icon-theme/breeze/status/24/dialog-warning.svg +1 -0
  235. dcscope/img/icon-theme/breeze/status/64/dialog-error.svg +25 -0
  236. dcscope/img/icon-theme/breeze/status/64/dialog-information.svg +25 -0
  237. dcscope/img/icon-theme/breeze/status/64/dialog-question.svg +25 -0
  238. dcscope/img/icon-theme/breeze/status/64/dialog-warning.svg +25 -0
  239. dcscope/img/icon-theme/collect_icons.py +124 -0
  240. dcscope/img/icon-theme/dcscope/dcor.png +0 -0
  241. dcscope/img/icon-theme/dcscope/edit-copy-anew.svg +13 -0
  242. dcscope/img/icon-theme/dcscope/emodulus.svg +69 -0
  243. dcscope/img/icon-theme/dcscope/filter_ray.svg +54 -0
  244. dcscope/img/icon-theme/dcscope/region_channel.svg +86 -0
  245. dcscope/img/icon-theme/dcscope/region_reservoir.svg +89 -0
  246. dcscope/img/icon-theme/dcscope/rlang.svg +108 -0
  247. dcscope/img/icon-theme/dcscope/statistical_significance.svg +106 -0
  248. dcscope/img/icon-theme/index.theme +81 -0
  249. dcscope/img/icon.png +0 -0
  250. dcscope/img/icon.svg +73 -0
  251. dcscope/img/splash.png +0 -0
  252. dcscope/meta_tool.py +143 -0
  253. dcscope/pipeline/__init__.py +5 -0
  254. dcscope/pipeline/core.py +632 -0
  255. dcscope/pipeline/dataslot.py +270 -0
  256. dcscope/pipeline/filter.py +161 -0
  257. dcscope/pipeline/filter_ray.py +170 -0
  258. dcscope/pipeline/plot.py +187 -0
  259. dcscope/plot_cache.py +79 -0
  260. dcscope/session.py +379 -0
  261. dcscope/util.py +34 -0
  262. dcscope-2.22.1.dist-info/METADATA +95 -0
  263. dcscope-2.22.1.dist-info/RECORD +267 -0
  264. dcscope-2.22.1.dist-info/WHEEL +5 -0
  265. dcscope-2.22.1.dist-info/entry_points.txt +2 -0
  266. dcscope-2.22.1.dist-info/licenses/LICENSE +621 -0
  267. dcscope-2.22.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,632 @@
1
+ import copy
2
+ import warnings
3
+
4
+ import dclab
5
+ import numpy as np
6
+
7
+ from ..idiom import SLOPING_FEATURES
8
+
9
+ from .dataslot import Dataslot
10
+ from .filter import Filter
11
+ from .filter_ray import FilterRay
12
+ from .plot import Plot
13
+
14
+
15
+ class EmptyDatasetWarning(UserWarning):
16
+ pass
17
+
18
+
19
+ class MissingFeatureWarning(UserWarning):
20
+ pass
21
+
22
+
23
+ class Pipeline(object):
24
+ def __init__(self, state=None):
25
+ #: Filters are instances of :class:`dcscope.pipeline.Filter`
26
+ self.filters = []
27
+ #: Plots are instances of :class:`dcscope.pipeline.Plot`
28
+ self.plots = []
29
+ #: Filter rays of the current pipeline
30
+ self.rays = {}
31
+ #: Slots are instances of :class:`dcscope.pipeline.Dataslot`
32
+ self.slots = []
33
+ #: individual element states
34
+ self.element_states = {}
35
+
36
+ self.reset()
37
+ #: previous state (see __setstate__)
38
+ self._old_state = {}
39
+ if state is not None:
40
+ self.__setstate__(state)
41
+
42
+ def __setstate__(self, state):
43
+ if self._old_state == state:
44
+ # Nothing changed
45
+ return
46
+ self._old_state = state
47
+ self.reset()
48
+ for filt_state in state["filters"]:
49
+ self.add_filter(filt_state)
50
+ for plot_state in state["plots"]:
51
+ self.add_plot(plot_state)
52
+ self.check_contour_spacing(plot_state["identifier"])
53
+ for slot_state in state["slots"]:
54
+ self.add_slot(slot=slot_state)
55
+ # set element states at the end
56
+ self.element_states = state["elements"]
57
+
58
+ # sanity checks
59
+ if set(self.filters_used) != set(state["filters used"]):
60
+ raise ValueError("Bad pipeline state ('filters used' don't match)")
61
+ if set(self.slots_used) != set(state["slots used"]):
62
+ raise ValueError("Bad pipeline state ('slots used' don't match)")
63
+
64
+ def __getstate__(self):
65
+ state = {"elements": copy.deepcopy(self.element_states),
66
+ "filters": [filt.__getstate__() for filt in self.filters],
67
+ "filters used": self.filters_used,
68
+ "plots": [plot.__getstate__() for plot in self.plots],
69
+ "slots": [slot.__getstate__() for slot in self.slots],
70
+ "slots used": self.slots_used}
71
+ return state
72
+
73
+ @property
74
+ def filter_ids(self):
75
+ return [filt.identifier for filt in self.filters]
76
+
77
+ @property
78
+ def filters_used(self):
79
+ return [filt.identifier for filt in self.filters if filt.filter_used]
80
+
81
+ @property
82
+ def plot_ids(self):
83
+ return [plot.identifier for plot in self.plots]
84
+
85
+ @property
86
+ def slot_ids(self):
87
+ return [slot.identifier for slot in self.slots]
88
+
89
+ @property
90
+ def slots_used(self):
91
+ return [slot.identifier for slot in self.slots if slot.slot_used]
92
+
93
+ @property
94
+ def num_filters(self):
95
+ return len(self.filters)
96
+
97
+ @property
98
+ def num_plots(self):
99
+ return len(self.plots)
100
+
101
+ @property
102
+ def num_slots(self):
103
+ return len(self.slots)
104
+
105
+ @property
106
+ def paths(self):
107
+ return [ds.path for ds in self.slots]
108
+
109
+ def add_filter(self, filt=None, index=None):
110
+ """Add a filter to the pipeline
111
+
112
+ Parameters
113
+ ----------
114
+ filt: dcscope.pipeline.Filter or dict
115
+ Filter instance or its state from Filter.__getstate__()
116
+ index: int
117
+ Position in the filter list, defaults to `len(self.filters)`
118
+
119
+ Returns
120
+ -------
121
+ index: int
122
+ index of the filter in the pipeline;
123
+ indexing starts at "0".
124
+ """
125
+ if index is None:
126
+ index = self.num_filters
127
+ if filt is None:
128
+ filt = Filter()
129
+ elif isinstance(filt, Filter):
130
+ pass
131
+ elif isinstance(filt, dict):
132
+ state = filt
133
+ if state["identifier"] in Filter._instances:
134
+ filt = Filter._instances[filt["identifier"]]
135
+ else:
136
+ filt = Filter()
137
+ filt.__setstate__(state)
138
+ self.filters.insert(index, filt)
139
+ filt_id = filt.identifier
140
+ for slot_id in self.slot_ids:
141
+ self.element_states[slot_id][filt_id] = False
142
+ return filt_id
143
+
144
+ def add_plot(self, plot=None, index=None):
145
+ """Add a plot to the pipeline
146
+
147
+ Parameters
148
+ ----------
149
+ plot: dcscope.pipeline.Plot or dict
150
+ Plot instance or its state from Plot.__getstate__()
151
+ index: int
152
+ Position in the plot list, defaults to `len(self.plots)`
153
+
154
+ Returns
155
+ -------
156
+ index: int
157
+ index of the plot in the pipeline;
158
+ indexing starts at "0".
159
+ """
160
+ if index is None:
161
+ index = self.num_plots
162
+ if plot is None:
163
+ plot = Plot()
164
+ elif isinstance(plot, Plot):
165
+ pass
166
+ elif isinstance(plot, dict):
167
+ state = plot
168
+ if state["identifier"] in Plot._instances:
169
+ plot = Plot._instances[plot["identifier"]]
170
+ else:
171
+ plot = Plot()
172
+ plot.__setstate__(state)
173
+
174
+ self.plots.insert(index, plot)
175
+ plot_id = plot.identifier
176
+ for slot_id in self.slot_ids:
177
+ self.element_states[slot_id][plot_id] = False
178
+
179
+ return plot.identifier
180
+
181
+ def add_slot(self, slot=None, path=None, index=None):
182
+ """Add a slot (experiment) to the pipeline
183
+
184
+ Parameters
185
+ ----------
186
+ slot: dcscope.pipeline.Dataslot or dict
187
+ Dataslot representing an experimental dataset or its
188
+ state from Dataslot.__getstate__(); At least `slot`
189
+ or `path` need to be specified
190
+ path: str or pathlib.Path
191
+ Path to a measurement or DCOR URL
192
+ index: int
193
+ Position in the slot list, defaults to `len(self.slots)`
194
+
195
+ Returns
196
+ -------
197
+ index: int
198
+ index of the slot in the pipeline;
199
+ indexing starts at "0".
200
+ identifier: str
201
+ identifier of the slot
202
+ """
203
+ if index is None:
204
+ index = self.num_slots
205
+ if ((slot is None and path is None)
206
+ or (slot is not None and path is not None)):
207
+ raise ValueError("Please specify either `slot` or `path`.")
208
+ elif path is not None:
209
+ slot = Dataslot(path=path)
210
+ elif isinstance(slot, Dataslot):
211
+ pass
212
+ elif isinstance(slot, dict):
213
+ state = slot
214
+ if state["identifier"] in Dataslot._instances:
215
+ slot = Dataslot._instances[slot["identifier"]]
216
+ else:
217
+ slot = Dataslot()
218
+ slot.__setstate__(state)
219
+
220
+ self.slots.insert(index, slot)
221
+ slot_id = slot.identifier
222
+ self.element_states[slot_id] = {}
223
+ for filt_id in self.filter_ids:
224
+ self.element_states[slot_id][filt_id] = False
225
+ for plot_id in self.plot_ids:
226
+ self.element_states[slot_id][plot_id] = False
227
+ return slot.identifier
228
+
229
+ def apply_filter_ray(self, rtdc_ds, slot_id):
230
+ """Convenience function for applying filters to other data
231
+
232
+ The filters that are currently set for a specific slot with
233
+ `slot_id` are applied to a dataset.
234
+
235
+ Parameters
236
+ ----------
237
+ rtdc_ds: dclab.rtdc_dataset.RTDCBase
238
+ Dataset
239
+ slot_id: str
240
+ Identifier of the slot from which the filters are taken
241
+ """
242
+ # make sure the current ray is built correctly
243
+ self.get_dataset(self.slot_ids.index(slot_id), apply_filter=False)
244
+ # get the ray
245
+ ray = self.get_ray(slot_id)
246
+ ds = ray.get_final_child(rtdc_ds)
247
+ return ds
248
+
249
+ def check_contour_spacing(self, plot_id):
250
+ """Check the contour spacing for a specific plot against the data
251
+
252
+ This method was implemented to avoid tiny contour spacings for
253
+ plotting, which could lead to OOM events.
254
+ """
255
+ plot = self.plots[self.plot_ids.index(plot_id)]
256
+ plot_state = plot.__getstate__()
257
+ old_plot_state = copy.deepcopy(plot_state)
258
+ if plot_state["general"]["auto range"]:
259
+ for ax in ["x", "y"]:
260
+ feat = plot_state["general"][f"axis {ax}"]
261
+ spacing = plot_state["contour"][f"spacing {ax}"]
262
+ for ds, slot_state in zip(*self.get_plot_datasets(
263
+ plot_id, apply_filter=False)):
264
+ slot_id = slot_state["identifier"]
265
+ slot = self.get_slot(slot_id)
266
+ sp_min, sp_max = slot.get_sane_spacing_range(feat=feat)
267
+ if spacing < sp_min:
268
+ warnings.warn(f"Setting contour spacing for {slot_id} "
269
+ f"to minimum ({spacing}<{sp_min})")
270
+ spacing = sp_min
271
+ elif spacing > sp_max:
272
+ warnings.warn(f"Setting contour spacing for {slot_id} "
273
+ f"to maximum ({spacing}>{sp_max})")
274
+ spacing = sp_max
275
+ plot_state["contour"][f"spacing {ax}"] = spacing
276
+ if old_plot_state != plot_state:
277
+ plot.__setstate__(plot_state)
278
+
279
+ def get_dataset(self, slot_index, filt_index=-1, apply_filter=True):
280
+ """Return dataset with all filters updated (optionally applied)
281
+
282
+ Parameters
283
+ ----------
284
+ slot_index: int
285
+ index of measurement
286
+ filt_index: int or None
287
+ index of filter; if None, then the plain dataset is returned.
288
+ If negative (default), then the last dataset in the pipeline
289
+ is returned (all selected filters applied).
290
+ apply_filter: bool
291
+ whether to call `dataset.apply_filter` in the end;
292
+ if set to `False`, only the filtering configuration
293
+ of the dataset and its hierarchy parents are updated
294
+ """
295
+ if not isinstance(slot_index, int):
296
+ raise ValueError(
297
+ f"`slot_index` must be an integer, got '{slot_index}'")
298
+ slot = self.slots[slot_index]
299
+ if filt_index is None or (filt_index == -1 and len(self.slots) == 0):
300
+ # return the unfiltered dataset
301
+ ds = slot.get_dataset()
302
+ else:
303
+ slot_id = slot.identifier
304
+ # the filters used
305
+ filters = self.get_filters_for_slot(slot_id=slot_id,
306
+ max_filter_index=filt_index)
307
+ # filter ray magic
308
+ ray = self.get_ray(slot.identifier)
309
+ ds = ray.get_dataset(filters, apply_filter=apply_filter)
310
+ return ds
311
+
312
+ def get_datasets(self, filt_index=-1, apply_filter=True):
313
+ """Return all datasets with filters applied
314
+
315
+ The parameters are passed to :func:`Pipeline.get_dataset`.
316
+
317
+ Parameters
318
+ ----------
319
+ filt_index: int or None
320
+ index of filter; if None, then the plain dataset is returned.
321
+ If negative (default), then the last dataset in the pipeline
322
+ is returned (all selected filters applied).
323
+ apply_filter: bool
324
+ whether to call `dataset.apply_filter` in the end;
325
+ if set to `False`, only the filtering configuration
326
+ of the dataset and its hierarchy parents are updated
327
+ """
328
+ kw = {"filt_index": filt_index,
329
+ "apply_filter": apply_filter}
330
+ return [self.get_dataset(ii, **kw) for ii in range(len(self.slots))]
331
+
332
+ def get_features(self, scalar=False, label_sort=False, union=False,
333
+ plot_id=None, ret_labels=False):
334
+ """Return a list of features in the pipeline
335
+
336
+ Parameters
337
+ ----------
338
+ scalar: bool
339
+ If True, only return scalar features
340
+ label_sort: bool
341
+ If True, return the features sorted by label
342
+ instead of by feature name
343
+ union: bool
344
+ If True, return the union of features available in all
345
+ slots. If False (default), return only those features
346
+ that are shared by all slots.
347
+ plot_id: None or str
348
+ If set, only datasets that are part of the matching Plot
349
+ instance are used. If None, all datasets of the pipeline
350
+ are used.
351
+ ret_labels: bool
352
+ If True, return the labels as well
353
+
354
+ Notes
355
+ -----
356
+ This function returns an empty list if there are no features
357
+ available.
358
+ """
359
+ if union:
360
+ features = set()
361
+ else:
362
+ features = None
363
+ for slot_index in range(self.num_slots):
364
+ slot_id = self.slot_ids[slot_index]
365
+ if (plot_id is None
366
+ or (self.element_states[slot_id][plot_id]
367
+ and slot_id in self.slots_used)):
368
+ ds = self.get_dataset(slot_index=slot_index, filt_index=None)
369
+ if scalar:
370
+ ds_features = set(ds.features_scalar)
371
+ else:
372
+ ds_features = set(ds.features)
373
+ if union:
374
+ features |= ds_features
375
+ else:
376
+ if features is None:
377
+ features = ds_features
378
+ else:
379
+ features &= ds_features
380
+ if features is None:
381
+ # This means that the pipeline is empty
382
+ features = []
383
+ labs = [dclab.dfn.get_feature_label(f) for f in features]
384
+ if label_sort:
385
+ lf = sorted(zip(labs, features))
386
+ features = [it[1] for it in lf]
387
+ labs = [it[0] for it in lf]
388
+ else:
389
+ fl = sorted(zip(features, labs))
390
+ features = [it[0] for it in fl]
391
+ labs = [it[1] for it in fl]
392
+ if ret_labels:
393
+ return features, labs
394
+ else:
395
+ return features
396
+
397
+ def get_filter(self, filt_id):
398
+ """Return the Filter matching the identifier"""
399
+ if filt_id not in self.filter_ids:
400
+ raise ValueError(f"Filter '{filt_id}' not part of this pipeline!")
401
+ return self.filters[self.filter_ids.index(filt_id)]
402
+
403
+ def get_filters_for_slot(self, slot_id, max_filter_index=-1):
404
+ """Return list of filters for a slot
405
+
406
+ Parameters
407
+ ----------
408
+ slot_id: str
409
+ identifier of the slot
410
+ max_filter_index: int
411
+ maximum filter index to include in the list. Note that
412
+ some filters may not be applied to a slot, so the final
413
+ length of the returned list may be smaller than this
414
+ value. Set to a negative value (default) to include all
415
+ filters applied to a dataset.
416
+ """
417
+ filters = []
418
+ if max_filter_index < 0:
419
+ # include all filters that are used for this slot
420
+ max_filter_index = len(self.filters) - 1
421
+ for filt_id in self.filter_ids[:max_filter_index + 1]:
422
+ if (self.is_element_active(slot_id, filt_id)
423
+ and filt_id in self.filters_used):
424
+ filters.append(self.get_filter(filt_id))
425
+ return filters
426
+
427
+ def get_min_max(self, feat, plot_id=None, margin=0.0):
428
+ """Return minimum and maximum values for a feature
429
+
430
+ Parameters
431
+ ----------
432
+ feat: str
433
+ Feature name
434
+ plot_id: str
435
+ Plot identifier
436
+ margin: float
437
+ Fraction by which the minimum and maximum are
438
+ extended. E.g. for plotting with a 5% margin
439
+ use `margin=0.05`.
440
+
441
+ Returns
442
+ -------
443
+ [fmin, fmax]: list of float
444
+ Minimum and maximum values of the feature. If the feature
445
+ is empty or only-nan, an :class:`EmptyDatasetWarning` is
446
+ issued and both return values are set to zero.
447
+ """
448
+ if plot_id is not None:
449
+ dslist = self.get_plot_datasets(plot_id)[0]
450
+ else:
451
+ dslist = self.get_datasets(filt_index=None, apply_filter=False)
452
+ fmin = np.inf
453
+ fmax = -np.inf
454
+ for ds in dslist:
455
+ if np.any(ds.filter.all):
456
+ if feat in ds:
457
+ fdata = ds[feat][ds.filter.all]
458
+ invalid = np.isinf(fdata)
459
+ if np.any(invalid):
460
+ vdata = fdata[~invalid]
461
+ else:
462
+ vdata = fdata
463
+ if feat in SLOPING_FEATURES:
464
+ # We are a little faster here.
465
+ vmin = min(np.nanmin(vdata[:1000]),
466
+ np.nanmin(vdata[-1000:]))
467
+ vmax = max(np.nanmax(vdata[:1000]),
468
+ np.nanmax(vdata[-1000:]))
469
+ else:
470
+ vmin = np.nanmin(vdata)
471
+ vmax = np.nanmax(vdata)
472
+ fmin = min(fmin, vmin)
473
+ fmax = max(fmax, vmax)
474
+ else:
475
+ warnings.warn(f"Dataset {ds.identifier} does not "
476
+ f"contain the feature '{feat}'!",
477
+ MissingFeatureWarning)
478
+ else:
479
+ warnings.warn(f"Dataset {ds.identifier} does not "
480
+ f"contain any events when filtered!",
481
+ EmptyDatasetWarning)
482
+ if margin:
483
+ diff = fmax - fmin
484
+ fmin -= margin*diff
485
+ fmax += margin*diff
486
+
487
+ if np.any(np.isinf([fmin, fmax])):
488
+ # Set values to 0 if no
489
+ fmin = fmax = 0
490
+ return [fmin, fmax]
491
+
492
+ def get_plot(self, plot_id):
493
+ if plot_id not in self.plot_ids:
494
+ raise ValueError(f"Plot '{plot_id}' not part of this pipeline!")
495
+ self.check_contour_spacing(plot_id)
496
+ return self.plots[self.plot_ids.index(plot_id)]
497
+
498
+ def get_plot_datasets(self, plot_id, apply_filter=True):
499
+ """Return a list of datasets with slot states that belong to a plot"""
500
+ datasets = []
501
+ states = []
502
+ # keep the same order as in self.slots
503
+ for slot_index in range(len(self.slots)):
504
+ slot = self.slots[slot_index]
505
+ slot_id = slot.identifier
506
+ if (self.element_states[slot_id][plot_id]
507
+ and slot_id in self.slots_used):
508
+ ds = self.get_dataset(slot_index=slot_index,
509
+ apply_filter=apply_filter)
510
+ datasets.append(ds)
511
+ states.append(slot.__getstate__())
512
+ return datasets, states
513
+
514
+ def get_plot_col_row_count(self, plot_id, pipeline_state=None):
515
+ """Compute how many rows a plot layout requires
516
+
517
+ Parameters
518
+ ----------
519
+ plot_id: str
520
+ identifier of a plot in this pipeline
521
+ pipeline_state: dict
522
+ pipeline state to use; defaults to the current pipeline
523
+ state
524
+ """
525
+ if pipeline_state is None:
526
+ pipeline_state = self.__getstate__()
527
+
528
+ # plot state
529
+ for pstate in pipeline_state["plots"]:
530
+ if plot_id == pstate["identifier"]:
531
+ break
532
+ else:
533
+ raise KeyError(f"Plot '{plot_id}' not given in pipeline state!")
534
+
535
+ # number of datasets in that plot
536
+ num_scat = 0
537
+ for slot_id in pipeline_state["elements"]:
538
+ num_scat += pipeline_state["elements"][slot_id][plot_id]
539
+
540
+ # additional plots
541
+ div = pstate["layout"]["division"]
542
+ if div == "each":
543
+ num_plots = max(1, num_scat)
544
+ elif div == "merge":
545
+ num_plots = 1
546
+ elif div == "multiscatter+contour":
547
+ num_plots = num_scat + 1
548
+ else:
549
+ raise ValueError(f"Unrecognized division: '{div}'")
550
+
551
+ # column count
552
+ col_count = min(pstate["layout"]["column count"], num_plots)
553
+
554
+ # row count
555
+ row_count = int(np.ceil(num_plots/col_count))
556
+ return col_count, row_count
557
+
558
+ def get_ray(self, slot_id):
559
+ """Convenience function that creates and returns a filter ray"""
560
+ # cleanup (just in case)
561
+ for key in list(self.rays.keys()):
562
+ if key not in self.slot_ids:
563
+ self.rays.pop(key)
564
+ if slot_id not in self.rays:
565
+ # create filter ray if it does not exist
566
+ self.rays[slot_id] = FilterRay(self.get_slot(slot_id))
567
+ return self.rays[slot_id]
568
+
569
+ def get_slot(self, slot_id):
570
+ """Return the Dataslot matching the RTDCBase identifier"""
571
+ slot_id = slot_id.split("-")[0] # this is how FilterRay names children
572
+ if slot_id in self.slot_ids:
573
+ slot = self.slots[self.slot_ids.index(slot_id)]
574
+ else:
575
+ raise ValueError(f"Unknown dataset identifier: `{slot_id}`")
576
+ return slot
577
+
578
+ def is_element_active(self, slot_id, filt_plot_id):
579
+ return self.element_states[slot_id][filt_plot_id]
580
+
581
+ def remove_filter(self, filt_id):
582
+ """Remove a filter by filter identifier"""
583
+ index = self.filter_ids.index(filt_id)
584
+ self.filters.pop(index)
585
+ for slot_id in self.element_states:
586
+ if filt_id in self.element_states[slot_id]:
587
+ self.element_states[slot_id].pop(filt_id)
588
+
589
+ def remove_plot(self, plot_id):
590
+ """Remove a filter by plot identifier"""
591
+ index = self.plot_ids.index(plot_id)
592
+ self.plots.pop(index)
593
+ for slot_id in self.element_states:
594
+ if plot_id in self.element_states[slot_id]:
595
+ self.element_states[slot_id].pop(plot_id)
596
+
597
+ def remove_slot(self, slot_id):
598
+ """Remove a slot by slot identifier"""
599
+ index = self.slot_ids.index(slot_id)
600
+ self.slots.pop(index)
601
+ if slot_id in self.element_states:
602
+ self.element_states.pop(slot_id)
603
+
604
+ def reorder_slots(self, indices):
605
+ """Change the order of data slots
606
+
607
+ Parameters
608
+ ----------
609
+ indices: list of ints
610
+ New sequence of slots, i.e. the new slots
611
+ will be `self.slots[indices]`.
612
+ """
613
+ # sanity checks
614
+ if sorted(indices) != list(range(len(self.slots))):
615
+ raise ValueError(f"Cannot reorder slots with inconclusive "
616
+ f"ordering sequence '{indices}'!")
617
+ new_slots = []
618
+ for idx in indices:
619
+ new_slots.append(self.slots[idx])
620
+ self.slots = new_slots
621
+
622
+ def reset(self):
623
+ """Reset the pipeline"""
624
+ self.filters.clear()
625
+ self.plots.clear()
626
+ self.rays.clear()
627
+ self.slots.clear()
628
+ self.element_states.clear()
629
+
630
+ def set_element_active(self, slot_id, filt_plot_id, active=True):
631
+ """Activate an element in the block matrix"""
632
+ self.element_states[slot_id][filt_plot_id] = active