dclab 0.67.0__cp314-cp314t-macosx_10_13_x86_64.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.

Potentially problematic release.


This version of dclab might be problematic. Click here for more details.

Files changed (142) hide show
  1. dclab/__init__.py +41 -0
  2. dclab/_version.py +34 -0
  3. dclab/cached.py +97 -0
  4. dclab/cli/__init__.py +10 -0
  5. dclab/cli/common.py +237 -0
  6. dclab/cli/task_compress.py +126 -0
  7. dclab/cli/task_condense.py +223 -0
  8. dclab/cli/task_join.py +229 -0
  9. dclab/cli/task_repack.py +98 -0
  10. dclab/cli/task_split.py +154 -0
  11. dclab/cli/task_tdms2rtdc.py +186 -0
  12. dclab/cli/task_verify_dataset.py +75 -0
  13. dclab/definitions/__init__.py +79 -0
  14. dclab/definitions/feat_const.py +202 -0
  15. dclab/definitions/feat_logic.py +182 -0
  16. dclab/definitions/meta_const.py +252 -0
  17. dclab/definitions/meta_logic.py +111 -0
  18. dclab/definitions/meta_parse.py +94 -0
  19. dclab/downsampling.cpython-314t-darwin.so +0 -0
  20. dclab/downsampling.pyx +230 -0
  21. dclab/external/__init__.py +4 -0
  22. dclab/external/packaging/LICENSE +3 -0
  23. dclab/external/packaging/LICENSE.APACHE +177 -0
  24. dclab/external/packaging/LICENSE.BSD +23 -0
  25. dclab/external/packaging/__init__.py +6 -0
  26. dclab/external/packaging/_structures.py +61 -0
  27. dclab/external/packaging/version.py +505 -0
  28. dclab/external/skimage/LICENSE +28 -0
  29. dclab/external/skimage/__init__.py +2 -0
  30. dclab/external/skimage/_find_contours.py +216 -0
  31. dclab/external/skimage/_find_contours_cy.cpython-314t-darwin.so +0 -0
  32. dclab/external/skimage/_find_contours_cy.pyx +188 -0
  33. dclab/external/skimage/_pnpoly.cpython-314t-darwin.so +0 -0
  34. dclab/external/skimage/_pnpoly.pyx +99 -0
  35. dclab/external/skimage/_shared/__init__.py +1 -0
  36. dclab/external/skimage/_shared/geometry.cpython-314t-darwin.so +0 -0
  37. dclab/external/skimage/_shared/geometry.pxd +6 -0
  38. dclab/external/skimage/_shared/geometry.pyx +55 -0
  39. dclab/external/skimage/measure.py +7 -0
  40. dclab/external/skimage/pnpoly.py +53 -0
  41. dclab/external/statsmodels/LICENSE +35 -0
  42. dclab/external/statsmodels/__init__.py +6 -0
  43. dclab/external/statsmodels/nonparametric/__init__.py +1 -0
  44. dclab/external/statsmodels/nonparametric/_kernel_base.py +203 -0
  45. dclab/external/statsmodels/nonparametric/kernel_density.py +165 -0
  46. dclab/external/statsmodels/nonparametric/kernels.py +36 -0
  47. dclab/features/__init__.py +9 -0
  48. dclab/features/bright.py +81 -0
  49. dclab/features/bright_bc.py +93 -0
  50. dclab/features/bright_perc.py +63 -0
  51. dclab/features/contour.py +161 -0
  52. dclab/features/emodulus/__init__.py +339 -0
  53. dclab/features/emodulus/load.py +252 -0
  54. dclab/features/emodulus/lut_HE-2D-FEM-22.txt +16432 -0
  55. dclab/features/emodulus/lut_HE-3D-FEM-22.txt +1276 -0
  56. dclab/features/emodulus/lut_LE-2D-FEM-19.txt +13082 -0
  57. dclab/features/emodulus/pxcorr.py +135 -0
  58. dclab/features/emodulus/scale_linear.py +247 -0
  59. dclab/features/emodulus/viscosity.py +260 -0
  60. dclab/features/fl_crosstalk.py +95 -0
  61. dclab/features/inert_ratio.py +377 -0
  62. dclab/features/volume.py +242 -0
  63. dclab/http_utils.py +322 -0
  64. dclab/isoelastics/__init__.py +468 -0
  65. dclab/isoelastics/iso_HE-2D-FEM-22-area_um-deform.txt +2440 -0
  66. dclab/isoelastics/iso_HE-2D-FEM-22-volume-deform.txt +2635 -0
  67. dclab/isoelastics/iso_HE-3D-FEM-22-area_um-deform.txt +1930 -0
  68. dclab/isoelastics/iso_HE-3D-FEM-22-volume-deform.txt +2221 -0
  69. dclab/isoelastics/iso_LE-2D-FEM-19-area_um-deform.txt +2151 -0
  70. dclab/isoelastics/iso_LE-2D-FEM-19-volume-deform.txt +2250 -0
  71. dclab/isoelastics/iso_LE-2D-ana-18-area_um-deform.txt +1266 -0
  72. dclab/kde/__init__.py +1 -0
  73. dclab/kde/base.py +459 -0
  74. dclab/kde/contours.py +222 -0
  75. dclab/kde/methods.py +313 -0
  76. dclab/kde_contours.py +10 -0
  77. dclab/kde_methods.py +11 -0
  78. dclab/lme4/__init__.py +5 -0
  79. dclab/lme4/lme4_template.R +94 -0
  80. dclab/lme4/rsetup.py +204 -0
  81. dclab/lme4/wrapr.py +386 -0
  82. dclab/polygon_filter.py +398 -0
  83. dclab/rtdc_dataset/__init__.py +15 -0
  84. dclab/rtdc_dataset/check.py +902 -0
  85. dclab/rtdc_dataset/config.py +533 -0
  86. dclab/rtdc_dataset/copier.py +353 -0
  87. dclab/rtdc_dataset/core.py +896 -0
  88. dclab/rtdc_dataset/export.py +867 -0
  89. dclab/rtdc_dataset/feat_anc_core/__init__.py +24 -0
  90. dclab/rtdc_dataset/feat_anc_core/af_basic.py +75 -0
  91. dclab/rtdc_dataset/feat_anc_core/af_emodulus.py +160 -0
  92. dclab/rtdc_dataset/feat_anc_core/af_fl_max_ctc.py +133 -0
  93. dclab/rtdc_dataset/feat_anc_core/af_image_contour.py +113 -0
  94. dclab/rtdc_dataset/feat_anc_core/af_ml_class.py +102 -0
  95. dclab/rtdc_dataset/feat_anc_core/ancillary_feature.py +320 -0
  96. dclab/rtdc_dataset/feat_anc_ml/__init__.py +32 -0
  97. dclab/rtdc_dataset/feat_anc_plugin/__init__.py +3 -0
  98. dclab/rtdc_dataset/feat_anc_plugin/plugin_feature.py +329 -0
  99. dclab/rtdc_dataset/feat_basin.py +762 -0
  100. dclab/rtdc_dataset/feat_temp.py +102 -0
  101. dclab/rtdc_dataset/filter.py +263 -0
  102. dclab/rtdc_dataset/fmt_dcor/__init__.py +7 -0
  103. dclab/rtdc_dataset/fmt_dcor/access_token.py +52 -0
  104. dclab/rtdc_dataset/fmt_dcor/api.py +173 -0
  105. dclab/rtdc_dataset/fmt_dcor/base.py +299 -0
  106. dclab/rtdc_dataset/fmt_dcor/basin.py +73 -0
  107. dclab/rtdc_dataset/fmt_dcor/logs.py +26 -0
  108. dclab/rtdc_dataset/fmt_dcor/tables.py +66 -0
  109. dclab/rtdc_dataset/fmt_dict.py +103 -0
  110. dclab/rtdc_dataset/fmt_hdf5/__init__.py +6 -0
  111. dclab/rtdc_dataset/fmt_hdf5/base.py +192 -0
  112. dclab/rtdc_dataset/fmt_hdf5/basin.py +30 -0
  113. dclab/rtdc_dataset/fmt_hdf5/events.py +276 -0
  114. dclab/rtdc_dataset/fmt_hdf5/feat_defect.py +164 -0
  115. dclab/rtdc_dataset/fmt_hdf5/logs.py +33 -0
  116. dclab/rtdc_dataset/fmt_hdf5/tables.py +60 -0
  117. dclab/rtdc_dataset/fmt_hierarchy/__init__.py +11 -0
  118. dclab/rtdc_dataset/fmt_hierarchy/base.py +278 -0
  119. dclab/rtdc_dataset/fmt_hierarchy/events.py +146 -0
  120. dclab/rtdc_dataset/fmt_hierarchy/hfilter.py +140 -0
  121. dclab/rtdc_dataset/fmt_hierarchy/mapper.py +134 -0
  122. dclab/rtdc_dataset/fmt_http.py +102 -0
  123. dclab/rtdc_dataset/fmt_s3.py +354 -0
  124. dclab/rtdc_dataset/fmt_tdms/__init__.py +476 -0
  125. dclab/rtdc_dataset/fmt_tdms/event_contour.py +264 -0
  126. dclab/rtdc_dataset/fmt_tdms/event_image.py +220 -0
  127. dclab/rtdc_dataset/fmt_tdms/event_mask.py +62 -0
  128. dclab/rtdc_dataset/fmt_tdms/event_trace.py +146 -0
  129. dclab/rtdc_dataset/fmt_tdms/exc.py +37 -0
  130. dclab/rtdc_dataset/fmt_tdms/naming.py +151 -0
  131. dclab/rtdc_dataset/load.py +77 -0
  132. dclab/rtdc_dataset/meta_table.py +25 -0
  133. dclab/rtdc_dataset/writer.py +1019 -0
  134. dclab/statistics.py +226 -0
  135. dclab/util.py +176 -0
  136. dclab/warn.py +15 -0
  137. dclab-0.67.0.dist-info/METADATA +153 -0
  138. dclab-0.67.0.dist-info/RECORD +142 -0
  139. dclab-0.67.0.dist-info/WHEEL +6 -0
  140. dclab-0.67.0.dist-info/entry_points.txt +8 -0
  141. dclab-0.67.0.dist-info/licenses/LICENSE +283 -0
  142. dclab-0.67.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,24 @@
1
+ from .ancillary_feature import AncillaryFeature # noqa: F401
2
+ from . import af_basic
3
+ from . import af_emodulus
4
+ from . import af_fl_max_ctc
5
+ from . import af_image_contour
6
+ from . import af_ml_class
7
+
8
+
9
+ #: features whose computation is fast
10
+ FEATURES_RAPID = [
11
+ "area_ratio",
12
+ "area_um",
13
+ "aspect",
14
+ "deform",
15
+ "index",
16
+ "time",
17
+ ]
18
+
19
+
20
+ af_basic.register()
21
+ af_emodulus.register()
22
+ af_fl_max_ctc.register()
23
+ af_image_contour.register()
24
+ af_ml_class.register()
@@ -0,0 +1,75 @@
1
+ import numpy as np
2
+
3
+ from .ancillary_feature import AncillaryFeature
4
+
5
+
6
+ def compute_area_ratio(mm):
7
+ valid = mm["area_msd"] != 0
8
+ out = np.nan * np.ones(len(mm), dtype=float)
9
+ return np.divide(mm["area_cvx"], mm["area_msd"], where=valid, out=out)
10
+
11
+
12
+ def compute_area_um(mm):
13
+ pxs = mm.config["imaging"]["pixel size"]
14
+ return mm["area_cvx"] * pxs**2
15
+
16
+
17
+ def compute_aspect(mm):
18
+ """Compute the aspect ratio of the bounding box
19
+
20
+ Notes
21
+ -----
22
+ If the cell is elongated along the channel, i.e.
23
+ `size_x` is larger than `size_y`, then the aspect
24
+ ratio is larger than 1.
25
+ """
26
+ out = np.nan * np.ones(len(mm), dtype=float)
27
+ valid = mm["size_y"] != 0
28
+ # parallel to flow, perpendicular to flow
29
+ return np.divide(mm["size_x"], mm["size_y"], where=valid, out=out)
30
+
31
+
32
+ def compute_deform(mm):
33
+ return 1 - mm["circ"]
34
+
35
+
36
+ def compute_index(mm):
37
+ return np.arange(1, len(mm)+1)
38
+
39
+
40
+ def compute_time(mm):
41
+ fr = mm.config["imaging"]["frame rate"]
42
+ # Since version 0.47.8, we don't "normalize" the time anymore
43
+ # with the information from mm["frame"][0]. This is important
44
+ # for cases where it is important to know the time elapsed before
45
+ # the first event was recorded (issue #207).
46
+ return np.array(mm["frame"], dtype=float) / fr
47
+
48
+
49
+ AncillaryFeature(feature_name="time",
50
+ method=compute_time,
51
+ req_config=[["imaging", ["frame rate"]]],
52
+ req_features=["frame"])
53
+
54
+
55
+ AncillaryFeature(feature_name="index",
56
+ method=compute_index)
57
+
58
+
59
+ def register():
60
+ AncillaryFeature(feature_name="area_ratio",
61
+ method=compute_area_ratio,
62
+ req_features=["area_cvx", "area_msd"])
63
+
64
+ AncillaryFeature(feature_name="area_um",
65
+ method=compute_area_um,
66
+ req_config=[["imaging", ["pixel size"]]],
67
+ req_features=["area_cvx"])
68
+
69
+ AncillaryFeature(feature_name="aspect",
70
+ method=compute_aspect,
71
+ req_features=["size_x", "size_y"])
72
+
73
+ AncillaryFeature(feature_name="deform",
74
+ method=compute_deform,
75
+ req_features=["circ"])
@@ -0,0 +1,160 @@
1
+ import warnings
2
+
3
+ from ... import features
4
+
5
+ from .ancillary_feature import AncillaryFeature
6
+
7
+
8
+ def compute_emodulus(mm):
9
+ """Wrapper function for computing the Young's modulus
10
+
11
+ Please take a look at the docs :ref:`sec_emodulus_usage`
12
+ for more details on the three cases A, B, and C.
13
+ There are also some sanity checks taking place here.
14
+ """
15
+ calccfg = mm.config["calculation"]
16
+
17
+ medium = calccfg.get("emodulus medium", "other").lower()
18
+ temperature = calccfg.get("emodulus temperature", None)
19
+ viscosity = calccfg.get("emodulus viscosity", None)
20
+
21
+ if viscosity is not None and medium == "other":
22
+ # sanity checks
23
+ if temperature is not None:
24
+ warnings.warn("The 'emodulus temperature' configuration key is "
25
+ "ignored if the 'emodulus viscosity' key is set!")
26
+ # Case B from the docs
27
+ return compute_emodulus_visc_only(mm)
28
+ else:
29
+ # sanity checks
30
+ if not isinstance(medium, str):
31
+ raise ValueError(
32
+ f"'emodulus medium' must be a string, got '{medium}'!")
33
+ if medium not in features.emodulus.viscosity.KNOWN_MEDIA:
34
+ raise ValueError(
35
+ f"Only the following media are supported: "
36
+ f"{features.emodulus.viscosity.KNOWN_MEDIA}, got '{medium}'!")
37
+ if viscosity is not None:
38
+ raise ValueError("You must not set the 'emodulus viscosity' "
39
+ "configuration keyword for known media!")
40
+ # warnings
41
+ if "emodulus viscosity model" not in calccfg:
42
+ warnings.warn("Please specify the 'emodulus viscosity model' "
43
+ "key in the 'calculation' config segion, falling "
44
+ "back to 'herold-2017'!",
45
+ DeprecationWarning)
46
+ # actual function calls
47
+ if temperature is not None:
48
+ # case C from the docs
49
+ temperature = mm.config["calculation"]["emodulus temperature"]
50
+ return compute_emodulus_known_media(mm, temperature=temperature)
51
+ elif "temp" in mm:
52
+ # case A from the docs
53
+ return compute_emodulus_known_media(mm, temperature=mm["temp"])
54
+
55
+
56
+ def compute_emodulus_known_media(mm, temperature):
57
+ """Only use known media and one temperature for all"""
58
+ calccfg = mm.config["calculation"]
59
+ # compute elastic modulus
60
+ emod = features.emodulus.get_emodulus(
61
+ area_um=mm["area_um"],
62
+ deform=mm["deform"],
63
+ medium=calccfg["emodulus medium"],
64
+ channel_width=mm.config["setup"]["channel width"],
65
+ flow_rate=mm.config["setup"]["flow rate"],
66
+ px_um=mm.config["imaging"]["pixel size"],
67
+ temperature=temperature,
68
+ lut_data=calccfg["emodulus lut"],
69
+ visc_model=calccfg.get("emodulus viscosity model", "herold-2017"),
70
+ )
71
+ return emod
72
+
73
+
74
+ def compute_emodulus_visc_only(mm):
75
+ """The user entered the viscosity directly"""
76
+ calccfg = mm.config["calculation"]
77
+ # compute elastic modulus
78
+ emod = features.emodulus.get_emodulus(
79
+ area_um=mm["area_um"],
80
+ deform=mm["deform"],
81
+ medium=calccfg["emodulus viscosity"],
82
+ channel_width=mm.config["setup"]["channel width"],
83
+ flow_rate=mm.config["setup"]["flow rate"],
84
+ px_um=mm.config["imaging"]["pixel size"],
85
+ temperature=None,
86
+ visc_model=None,
87
+ lut_data=calccfg["emodulus lut"],
88
+ )
89
+ return emod
90
+
91
+
92
+ def is_channel(mm):
93
+ """Check whether the measurement was performed in the channel
94
+
95
+ If the chip region is not set, then it is assumed to be a
96
+ channel measurement (for backwards compatibility and user-
97
+ friendliness).
98
+ """
99
+ if "setup" in mm.config and "chip region" in mm.config["setup"]:
100
+ region = mm.config["setup"]["chip region"]
101
+ if region == "channel":
102
+ # measured in the channel
103
+ return True
104
+ else:
105
+ # measured in the reservoir
106
+ return False
107
+ else:
108
+ # This might be a testing dictionary or someone who is
109
+ # playing around with data. Avoid disappointments here.
110
+ return True
111
+
112
+
113
+ def register():
114
+ # Please note that registering these things is a delicate business,
115
+ # because the priority has to be chosen carefully.
116
+ # Note that here we have not included the "emodulus viscosity model"
117
+ # configuration keyword. This is checked in the `compute_emodulus`
118
+ # method above and a deprecation warning is issued, so old code
119
+ # does not break immediately.
120
+ for pr, vm in [(1, ["emodulus viscosity model"]),
121
+ (0, []) # this is deprecated and should be removed!
122
+ ]:
123
+ AncillaryFeature(feature_name="emodulus",
124
+ method=compute_emodulus,
125
+ data="case C",
126
+ req_features=["area_um", "deform"],
127
+ req_config=[["calculation", vm + [
128
+ "emodulus lut",
129
+ "emodulus medium",
130
+ "emodulus temperature"]],
131
+ ["imaging", ["pixel size"]],
132
+ ["setup", ["flow rate", "channel width"]]
133
+ ],
134
+ req_func=is_channel,
135
+ priority=4 + pr)
136
+ AncillaryFeature(feature_name="emodulus",
137
+ data="case A",
138
+ method=compute_emodulus,
139
+ req_features=["area_um", "deform", "temp"],
140
+ req_config=[["calculation", vm + [
141
+ "emodulus lut",
142
+ "emodulus medium"]],
143
+ ["imaging", ["pixel size"]],
144
+ ["setup", ["flow rate", "channel width"]]
145
+ ],
146
+ req_func=is_channel,
147
+ priority=0 + pr)
148
+
149
+ AncillaryFeature(feature_name="emodulus",
150
+ data="case B",
151
+ method=compute_emodulus,
152
+ req_features=["area_um", "deform"],
153
+ req_config=[["calculation", vm + [
154
+ "emodulus lut",
155
+ "emodulus viscosity"]],
156
+ ["imaging", ["pixel size"]],
157
+ ["setup", ["flow rate", "channel width"]]
158
+ ],
159
+ req_func=is_channel,
160
+ priority=2)
@@ -0,0 +1,133 @@
1
+
2
+
3
+ from ... import features
4
+ from .ancillary_feature import AncillaryFeature
5
+
6
+
7
+ class MissingCrosstalkMatrixElementsError(BaseException):
8
+ pass
9
+
10
+
11
+ def compute_ctc(mm, fl_channel):
12
+ if "fl1_max" in mm:
13
+ fl1 = mm["fl1_max"]
14
+ else:
15
+ fl1 = 0
16
+
17
+ if "fl2_max" in mm:
18
+ fl2 = mm["fl2_max"]
19
+ else:
20
+ fl2 = 0
21
+
22
+ if "fl3_max" in mm:
23
+ fl3 = mm["fl3_max"]
24
+ else:
25
+ fl3 = 0
26
+
27
+ ctdict = {}
28
+
29
+ for i in [1, 2, 3]:
30
+ for j in [1, 2, 3]:
31
+ if i == j:
32
+ continue
33
+ key = "crosstalk fl{}{}".format(i, j)
34
+ par = "ct{}{}".format(i, j)
35
+ if key in mm.config["calculation"]:
36
+ ctdict[par] = mm.config["calculation"][key]
37
+
38
+ if ("fl1_max" in mm and
39
+ "fl2_max" in mm and
40
+ "fl3_max" in mm and
41
+ ("ct12" not in ctdict or
42
+ "ct13" not in ctdict or
43
+ "ct21" not in ctdict or
44
+ "ct23" not in ctdict or
45
+ "ct31" not in ctdict or
46
+ "ct32" not in ctdict)):
47
+ msg = "{}, has fl1_max, fl2_max, and fl3_max,".format(mm) \
48
+ + " but not all crosstalk matrix elements are" \
49
+ + " defined in the 'calculation' configuration section."
50
+ raise MissingCrosstalkMatrixElementsError(msg)
51
+
52
+ return features.fl_crosstalk.correct_crosstalk(
53
+ fl1=fl1,
54
+ fl2=fl2,
55
+ fl3=fl3,
56
+ fl_channel=fl_channel,
57
+ **ctdict)
58
+
59
+
60
+ def compute_ctc1(mm):
61
+ return compute_ctc(mm, fl_channel=1)
62
+
63
+
64
+ def compute_ctc2(mm):
65
+ return compute_ctc(mm, fl_channel=2)
66
+
67
+
68
+ def compute_ctc3(mm):
69
+ return compute_ctc(mm, fl_channel=3)
70
+
71
+
72
+ def get_method(fl_channel):
73
+ if fl_channel == 1:
74
+ return compute_ctc1
75
+ elif fl_channel == 2:
76
+ return compute_ctc2
77
+ elif fl_channel == 3:
78
+ return compute_ctc3
79
+
80
+
81
+ def register():
82
+ opts_all = (["fl1_max",
83
+ "fl2_max",
84
+ "fl3_max"],
85
+ ["crosstalk fl21",
86
+ "crosstalk fl31",
87
+ "crosstalk fl12",
88
+ "crosstalk fl32",
89
+ "crosstalk fl13",
90
+ "crosstalk fl23"])
91
+
92
+ opts_12 = (["fl1_max",
93
+ "fl2_max"],
94
+ ["crosstalk fl21",
95
+ "crosstalk fl12"])
96
+
97
+ opts_13 = (["fl1_max",
98
+ "fl3_max"],
99
+ ["crosstalk fl31",
100
+ "crosstalk fl13"])
101
+
102
+ opts_23 = (["fl2_max",
103
+ "fl3_max"],
104
+ ["crosstalk fl32",
105
+ "crosstalk fl23"])
106
+
107
+ for flch in [1, 2, 3]:
108
+ AncillaryFeature(feature_name="fl{}_max_ctc".format(flch),
109
+ method=get_method(flch),
110
+ req_features=opts_all[0],
111
+ req_config=[["calculation", opts_all[1]]],
112
+ priority=1)
113
+
114
+ for flch in [1, 2]:
115
+ AncillaryFeature(feature_name="fl{}_max_ctc".format(flch),
116
+ method=get_method(flch),
117
+ req_features=opts_12[0],
118
+ req_config=[["calculation", opts_12[1]]],
119
+ priority=0)
120
+
121
+ for flch in [1, 3]:
122
+ AncillaryFeature(feature_name="fl{}_max_ctc".format(flch),
123
+ method=get_method(flch),
124
+ req_features=opts_13[0],
125
+ req_config=[["calculation", opts_13[1]]],
126
+ priority=0)
127
+
128
+ for flch in [2, 3]:
129
+ AncillaryFeature(feature_name="fl{}_max_ctc".format(flch),
130
+ method=get_method(flch),
131
+ req_features=opts_23[0],
132
+ req_config=[["calculation", opts_23[1]]],
133
+ priority=0)
@@ -0,0 +1,113 @@
1
+ from ... import features
2
+ from .ancillary_feature import AncillaryFeature
3
+
4
+
5
+ def compute_contour(mm):
6
+ cont = features.contour.get_contour_lazily(mask=mm["mask"])
7
+ return cont
8
+
9
+
10
+ def compute_bright(mm):
11
+ bavg, bsd = features.bright.get_bright(
12
+ mask=mm["mask"],
13
+ image=mm["image"],
14
+ ret_data="avg,sd",
15
+ )
16
+ return {"bright_avg": bavg, "bright_sd": bsd}
17
+
18
+
19
+ def compute_bright_bc(mm):
20
+ bavg, bsd = features.bright_bc.get_bright_bc(
21
+ mask=mm["mask"],
22
+ image=mm["image"],
23
+ image_bg=mm["image_bg"],
24
+ bg_off=mm["bg_off"] if "bg_off" in mm else None,
25
+ ret_data="avg,sd",
26
+ )
27
+ return {"bright_bc_avg": bavg, "bright_bc_sd": bsd}
28
+
29
+
30
+ def compute_bright_perc(mm):
31
+ p10, p90 = features.bright_perc.get_bright_perc(
32
+ mask=mm["mask"],
33
+ image=mm["image"],
34
+ image_bg=mm["image_bg"],
35
+ bg_off=mm["bg_off"] if "bg_off" in mm else None,
36
+ )
37
+ return {"bright_perc_10": p10, "bright_perc_90": p90}
38
+
39
+
40
+ def compute_inert_ratio_cvx(mm):
41
+ return features.inert_ratio.get_inert_ratio_cvx(cont=mm["contour"])
42
+
43
+
44
+ def compute_inert_ratio_prnc(mm):
45
+ return features.inert_ratio.get_inert_ratio_prnc(cont=mm["contour"])
46
+
47
+
48
+ def compute_inert_ratio_raw(mm):
49
+ return features.inert_ratio.get_inert_ratio_raw(cont=mm["contour"])
50
+
51
+
52
+ def compute_tilt(mm):
53
+ return features.inert_ratio.get_tilt(cont=mm["contour"])
54
+
55
+
56
+ def compute_volume(mm):
57
+ vol = features.volume.get_volume(
58
+ cont=mm["contour"],
59
+ pos_x=mm["pos_x"],
60
+ pos_y=mm["pos_y"],
61
+ pix=mm.config["imaging"]["pixel size"])
62
+ return vol
63
+
64
+
65
+ def register():
66
+ AncillaryFeature(feature_name="contour",
67
+ method=compute_contour,
68
+ req_features=["mask"])
69
+
70
+ AncillaryFeature(feature_name="bright_avg",
71
+ method=compute_bright,
72
+ req_features=["image", "mask"])
73
+
74
+ AncillaryFeature(feature_name="bright_sd",
75
+ method=compute_bright,
76
+ req_features=["image", "mask"])
77
+
78
+ AncillaryFeature(feature_name="bright_bc_avg",
79
+ method=compute_bright_bc,
80
+ req_features=["image", "image_bg", "mask"])
81
+
82
+ AncillaryFeature(feature_name="bright_bc_sd",
83
+ method=compute_bright_bc,
84
+ req_features=["image", "image_bg", "mask"])
85
+
86
+ AncillaryFeature(feature_name="bright_perc_10",
87
+ method=compute_bright_perc,
88
+ req_features=["image", "image_bg", "mask"])
89
+
90
+ AncillaryFeature(feature_name="bright_perc_90",
91
+ method=compute_bright_perc,
92
+ req_features=["image", "image_bg", "mask"])
93
+
94
+ AncillaryFeature(feature_name="inert_ratio_cvx",
95
+ method=compute_inert_ratio_cvx,
96
+ req_features=["contour"])
97
+
98
+ AncillaryFeature(feature_name="inert_ratio_prnc",
99
+ method=compute_inert_ratio_prnc,
100
+ req_features=["contour"])
101
+
102
+ AncillaryFeature(feature_name="inert_ratio_raw",
103
+ method=compute_inert_ratio_raw,
104
+ req_features=["contour"])
105
+
106
+ AncillaryFeature(feature_name="tilt",
107
+ method=compute_tilt,
108
+ req_features=["contour"])
109
+
110
+ AncillaryFeature(feature_name="volume",
111
+ method=compute_volume,
112
+ req_features=["contour", "pos_x", "pos_y"],
113
+ req_config=[["imaging", ["pixel size"]]])
@@ -0,0 +1,102 @@
1
+
2
+ import numpy as np
3
+ from .ancillary_feature import AncillaryFeature
4
+
5
+
6
+ def get_ml_score_names(mm):
7
+ """Return a list of all ml_score_??? features"""
8
+ feats = []
9
+ # We cannot loop over mm.features because of infinite recursions
10
+ for ft in mm._feature_candidates:
11
+ if ft.startswith("ml_score_") and ft in mm:
12
+ feats.append(ft)
13
+ return sorted(feats)
14
+
15
+
16
+ def compute_ml_class(mm, sanity_checks=True):
17
+ """Compute the most-probable class membership for all events
18
+
19
+ Parameters
20
+ ----------
21
+ mm: .RTDCBase
22
+ instance with the `ml_score_???` features
23
+ sanity_checks: bool
24
+ set to `False` to not perform sanity checks (checks whether
25
+ the scores are between 0 and 1)
26
+
27
+ Returns
28
+ -------
29
+ ml_class: 1D ndarray
30
+ The most-probable class for each event in `mm`. If no class
31
+ can be attributed to an event (because the scores are all
32
+ `np.nan` or `0` for that event), the class `-1` is used.
33
+
34
+ Notes
35
+ -----
36
+ I initially thought about also checking whether each feature
37
+ sums to one, but discarded the idea. Let's assume that a classifier
38
+ does an awful classification and classifies all events in
39
+ the same way. If the dataset is cropped at some point (e.g.
40
+ debris or other events), then this bad classifier has an
41
+ increased probability compared to another classifier which is
42
+ perfect at picking out one population. The ml_score values
43
+ should be just in the range of [0, 1]. This also simplifies
44
+ export to hdf5 and the work with hierarchy children.
45
+ """
46
+ feats = get_ml_score_names(mm)
47
+
48
+ # the score matrix
49
+ score_matrix = np.zeros((len(mm), len(feats)), dtype=float)
50
+
51
+ for ii, ft in enumerate(feats):
52
+ if sanity_checks:
53
+ if np.nanmax(mm[ft]) > 1:
54
+ raise ValueError("Feature '{}' has values > 1!".format(ft))
55
+ elif np.nanmin(mm[ft]) < 0:
56
+ raise ValueError("Feature '{}' has values < 0!".format(ft))
57
+ score_matrix[:, ii] = mm[ft]
58
+
59
+ # Now compute the maximum for each event. The initial idea was to just
60
+ # use `ml_class = np.nanargmax(score_matrix, axis=1)`. However, here we
61
+ # run into these problems:
62
+ # 1. This does not handle All-NaN slices, e.g. all features are `np.nan`
63
+ # for an event.
64
+ # 2. This does not properly handle manually-rated, zero-valued features,
65
+ # e.g. in a situation where we have two features, one with `np.nan`
66
+ # and one with `0`, we cannot assign the event to either of the two
67
+ # classes.
68
+ # 3. There is no "unclassified" class (this also becomes apparent in
69
+ # point 2). We will set all events that cannot be attributed to a
70
+ # class to `-1` in `ml_class`.
71
+
72
+ # Define unusable entries:
73
+ unusable = np.logical_or(np.isnan(score_matrix), (score_matrix == 0))
74
+ where_idx_nan = np.sum(~unusable, axis=1) == 0
75
+ score_matrix[where_idx_nan, :] = -1
76
+ ml_class = np.nanargmax(score_matrix, axis=1)
77
+ ml_class[where_idx_nan] = -1
78
+ return ml_class
79
+
80
+
81
+ def has_ml_scores(mm):
82
+ """Check whether the dataset has ml_scores defined"""
83
+ # Return the sorted score names plus Ancillary feature hashes.
84
+ # This will be used to determine the hash of the ml_class feature,
85
+ # which is important in case the user replaces an ML feature
86
+ # with a new one.
87
+ features = get_ml_score_names(mm)
88
+ idlist = []
89
+ for feat in features:
90
+ # We also hash any other AncillaryFeature that might implement
91
+ # this ML score. But this use case is basically non-existent and
92
+ # the performance impact is probably negligible.
93
+ candidates = AncillaryFeature.get_instances(feat)
94
+ idlist.append((feat, [c.hash(mm) for c in candidates]))
95
+ return idlist
96
+
97
+
98
+ def register():
99
+ AncillaryFeature(feature_name="ml_class",
100
+ method=compute_ml_class,
101
+ req_func=has_ml_scores,
102
+ )