flimanalyzer 0.6.0__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 (110) hide show
  1. flim/__init__.py +2 -0
  2. flim/_version.py +5 -0
  3. flim/analysis/__init__.py +1 -0
  4. flim/analysis/ml/__init__.py +0 -0
  5. flim/analysis/ml/autoencoder.py +188 -0
  6. flim/analyzerapp.py +131 -0
  7. flim/batch.py +235 -0
  8. flim/configs/__init__.py +77 -0
  9. flim/configs/analysis.py +554 -0
  10. flim/configs/data.py +278 -0
  11. flim/configs/plot.py +543 -0
  12. flim/configs/workflow.py +204 -0
  13. flim/core/__init__.py +0 -0
  14. flim/core/calculator.py +0 -0
  15. flim/core/config.py +305 -0
  16. flim/core/execution.py +110 -0
  17. flim/core/fields.py +226 -0
  18. flim/core/filter.py +179 -0
  19. flim/core/graph.py +111 -0
  20. flim/core/headers.py +72 -0
  21. flim/core/importer.py +269 -0
  22. flim/core/parser.py +305 -0
  23. flim/core/paths.py +66 -0
  24. flim/core/prefect_server.py +152 -0
  25. flim/core/preprocessor.py +287 -0
  26. flim/core/registry.py +108 -0
  27. flim/core/runner.py +703 -0
  28. flim/core/schema.py +114 -0
  29. flim/core/settings.py +419 -0
  30. flim/core/tool.py +284 -0
  31. flim/core/tools.py +76 -0
  32. flim/core/types.py +113 -0
  33. flim/core/validation.py +272 -0
  34. flim/core/workflow.py +148 -0
  35. flim/gui/__init__.py +12 -0
  36. flim/gui/app.py +953 -0
  37. flim/gui/configdialog.py +495 -0
  38. flim/gui/datapanel.py +841 -0
  39. flim/gui/delimpanel.py +56 -0
  40. flim/gui/dialogs/__init__.py +473 -0
  41. flim/gui/dialogs/autoencoder.py +89 -0
  42. flim/gui/dialogs/categoryorder.py +130 -0
  43. flim/gui/dialogs/filter.py +86 -0
  44. flim/gui/dialogs/unpivot.py +72 -0
  45. flim/gui/dicttablepanel.py +178 -0
  46. flim/gui/editors.py +1578 -0
  47. flim/gui/events.py +162 -0
  48. flim/gui/icons.py +16 -0
  49. flim/gui/importdlg.py +391 -0
  50. flim/gui/listcontrol.py +472 -0
  51. flim/gui/macos.py +77 -0
  52. flim/gui/plotframe.py +73 -0
  53. flim/gui/progressdlg.py +290 -0
  54. flim/resources/aeaugment.png +0 -0
  55. flim/resources/aefeatureworkflow.png +0 -0
  56. flim/resources/aerun.png +0 -0
  57. flim/resources/aetrain.png +0 -0
  58. flim/resources/aetuneworkflow.png +0 -0
  59. flim/resources/barplot.png +0 -0
  60. flim/resources/boxplot.png +0 -0
  61. flim/resources/categorize.png +0 -0
  62. flim/resources/concatenate.png +0 -0
  63. flim/resources/fileopen.png +0 -0
  64. flim/resources/filter.png +0 -0
  65. flim/resources/flimanalyzer.ico +0 -0
  66. flim/resources/flimanalyzer.png +0 -0
  67. flim/resources/flirrplot.png +0 -0
  68. flim/resources/flirrworkflow.png +0 -0
  69. flim/resources/gmm.png +0 -0
  70. flim/resources/heatmap.png +0 -0
  71. flim/resources/histogram.png +0 -0
  72. flim/resources/import.png +0 -0
  73. flim/resources/kde.png +0 -0
  74. flim/resources/kmeans.png +0 -0
  75. flim/resources/ks.png +0 -0
  76. flim/resources/lineplot.png +0 -0
  77. flim/resources/merge.png +0 -0
  78. flim/resources/order.png +0 -0
  79. flim/resources/pairplot.png +0 -0
  80. flim/resources/pca.png +0 -0
  81. flim/resources/pivot.png +0 -0
  82. flim/resources/randomforest.png +0 -0
  83. flim/resources/relchange.png +0 -0
  84. flim/resources/scatter.png +0 -0
  85. flim/resources/seriesanalysis.png +0 -0
  86. flim/resources/sort.png +0 -0
  87. flim/resources/split.png +0 -0
  88. flim/resources/summary.png +0 -0
  89. flim/resources/swarmplot.png +0 -0
  90. flim/resources/unpivot.png +0 -0
  91. flim/resources/violinplot.png +0 -0
  92. flim/resources/workflow.png +0 -0
  93. flim/results.py +70 -0
  94. flim/tools/__init__.py +7 -0
  95. flim/tools/autoencoder.py +662 -0
  96. flim/tools/common.py +62 -0
  97. flim/tools/data.py +301 -0
  98. flim/tools/ml.py +311 -0
  99. flim/tools/plot.py +1439 -0
  100. flim/tools/statistics.py +348 -0
  101. flim/utils.py +43 -0
  102. flim/workflow/__init__.py +3 -0
  103. flim/workflow/aefeature.py +145 -0
  104. flim/workflow/aetune.py +163 -0
  105. flim/workflow/basicflow.py +99 -0
  106. flimanalyzer-0.6.0.dist-info/METADATA +262 -0
  107. flimanalyzer-0.6.0.dist-info/RECORD +110 -0
  108. flimanalyzer-0.6.0.dist-info/WHEEL +4 -0
  109. flimanalyzer-0.6.0.dist-info/entry_points.txt +5 -0
  110. flimanalyzer-0.6.0.dist-info/licenses/LICENSE +19 -0
flim/configs/data.py ADDED
@@ -0,0 +1,278 @@
1
+ """Configs of the Data tools."""
2
+
3
+ from typing import Any, Literal, Optional, Union
4
+
5
+ from pydantic import Field, field_validator, model_validator
6
+
7
+ from flim.core.config import (
8
+ AllFeatures,
9
+ BaseConfig,
10
+ CategoryColumn,
11
+ FeatureColumn,
12
+ TaskConfig,
13
+ )
14
+
15
+
16
+ class DataTaskConfig(TaskConfig):
17
+ pass
18
+
19
+
20
+ class CategoryOrderConfig(DataTaskConfig):
21
+ """Order of the values of category columns; the columns are sorted by them."""
22
+
23
+ legacy_keys = {"categories": None}
24
+ legacy_dropped = frozenset({"inplace"})
25
+ hidden_fields = frozenset({"grouping", "features"})
26
+
27
+ categories: dict[CategoryColumn, list[Any]] = Field(
28
+ default={},
29
+ title="Value order",
30
+ description="For each category column, its values in the order to use.",
31
+ )
32
+
33
+ @classmethod
34
+ def _from_legacy(cls, params):
35
+ # legacy: {column: {"values": [...], "sort": ...}}; only the values are used
36
+ categories = params.pop("categories", {}) or {}
37
+ return {
38
+ "categories": {
39
+ col: list(spec.get("values", [])) for col, spec in categories.items()
40
+ }
41
+ }
42
+
43
+ def _to_legacy(self):
44
+ return {
45
+ "categories": {
46
+ col: {"values": list(values), "sort": "custom"}
47
+ for col, values in self.categories.items()
48
+ }
49
+ }
50
+
51
+
52
+ class ConcatConfig(DataTaskConfig):
53
+ legacy_keys = {"mode": None}
54
+ legacy_dropped = frozenset({"source_suffix"}) # unused
55
+ hidden_fields = frozenset({"grouping", "features"})
56
+
57
+ mode: Literal["vertical", "horizontal"] = Field(
58
+ default="vertical",
59
+ title="Direction",
60
+ description="Append the rows of the tables below each other, or their columns"
61
+ " side by side.",
62
+ json_schema_extra={
63
+ "labels": {
64
+ "vertical": "Rows (stack below)",
65
+ "horizontal": "Columns (side by side)",
66
+ }
67
+ },
68
+ )
69
+ numbers_only: bool = Field(
70
+ default=False,
71
+ title="Numeric columns only",
72
+ description="Columns: append only the numeric columns of the other tables.",
73
+ )
74
+
75
+ @classmethod
76
+ def _from_legacy(cls, params):
77
+ # legacy "type" is the "Horizontal Concatenate" checkbox
78
+ if "type" in params:
79
+ return {"mode": "horizontal" if params.pop("type") else "vertical"}
80
+ return {}
81
+
82
+ def _to_legacy(self):
83
+ return {"type": self.mode == "horizontal"}
84
+
85
+
86
+ def range_of_legacy(spec: dict) -> dict:
87
+ """low, high and dropna of a plugin's range filter; infinite: no limit."""
88
+ low, high = spec.get("rangelow"), spec.get("rangehigh")
89
+ return {
90
+ "low": None if low is None or low == float("-inf") else low,
91
+ "high": None if high is None or high == float("inf") else high,
92
+ "dropna": spec.get("dropna", True),
93
+ }
94
+
95
+
96
+ class RangeSettings(BaseConfig):
97
+ """Rows whose value lies outside [low, high] are dropped; empty: no limit."""
98
+
99
+ low: Optional[float] = Field(default=None, title="Low")
100
+ high: Optional[float] = Field(default=None, title="High")
101
+ dropna: bool = Field(
102
+ default=True,
103
+ title="Drop missing",
104
+ description="Also drop rows without a value.",
105
+ )
106
+
107
+ @model_validator(mode="after")
108
+ def _check_range(self):
109
+ if self.low is not None and self.high is not None and self.low > self.high:
110
+ raise ValueError(f"low ({self.low}) > high ({self.high})")
111
+ return self
112
+
113
+
114
+ class FilterConfig(DataTaskConfig):
115
+ legacy_keys = {"range_filters": None}
116
+ legacy_dropped = frozenset({"use", "inplace", "show_dropped"}) # unused
117
+ hidden_fields = frozenset({"grouping", "features"})
118
+
119
+ category_filters: dict[CategoryColumn, list[Any]] = Field(
120
+ default={},
121
+ title="Category values to keep",
122
+ description="For each category column, the values whose rows are kept.",
123
+ )
124
+ range_filters: dict[FeatureColumn, RangeSettings] = Field(
125
+ default={},
126
+ title="Range filters",
127
+ description=(
128
+ "Check the columns to filter: rows outside [low, high] are dropped (empty:"
129
+ " no limit)."
130
+ ),
131
+ )
132
+
133
+ @field_validator("range_filters", mode="before")
134
+ @classmethod
135
+ def _filters_from_list(cls, filters):
136
+ """A list of columns: filters without limits (dropping missing values)."""
137
+ if isinstance(filters, (list, tuple)):
138
+ return {f: {} for f in filters}
139
+ return filters
140
+
141
+ @classmethod
142
+ def _from_legacy(cls, params):
143
+ # the plugin: a list of {"name", "rangelow", "rangehigh", "selected", ...};
144
+ # it applied the selected ones
145
+ filters = {}
146
+ for spec in params.pop("range_filters", []) or []:
147
+ if spec.get("selected", True):
148
+ filters[spec["name"]] = RangeSettings(**range_of_legacy(spec))
149
+ return {"range_filters": filters}
150
+
151
+ def _to_legacy(self):
152
+ inf = float("inf")
153
+ return {
154
+ "range_filters": [
155
+ {
156
+ "name": column,
157
+ "rangelow": -inf if r.low is None else r.low,
158
+ "rangehigh": inf if r.high is None else r.high,
159
+ "allowedmin": None,
160
+ "allowedmax": None,
161
+ "selected": True,
162
+ "dropna": r.dropna,
163
+ }
164
+ for column, r in self.range_filters.items()
165
+ ]
166
+ }
167
+
168
+
169
+ class MergeConfig(DataTaskConfig):
170
+ legacy_keys = {"how": None, "on": None}
171
+ legacy_dropped = frozenset({"left_index", "right_index"})
172
+ hidden_fields = frozenset({"grouping", "features"})
173
+
174
+ how: Literal["inner", "outer", "left", "right"] = Field(
175
+ default="inner",
176
+ title="Join",
177
+ description=(
178
+ "inner: rows in both tables; outer: in either; left/right: all rows of"
179
+ " that table."
180
+ ),
181
+ )
182
+ on: Optional[list[CategoryColumn]] = Field(
183
+ default=None,
184
+ title="Merge on",
185
+ description="Columns to merge on; automatic: the category columns both have.",
186
+ )
187
+
188
+ @classmethod
189
+ def _from_legacy(cls, params):
190
+ how = params.pop("how", "")
191
+ return {"how": how} if how else {} # legacy default "" fails in pandas
192
+
193
+ def _to_legacy(self):
194
+ return {"how": self.how}
195
+
196
+
197
+ class PivotConfig(DataTaskConfig):
198
+ """Pivots the ``grouping`` columns."""
199
+
200
+ grouping: list[CategoryColumn] = Field(
201
+ default=["Treatment"],
202
+ title="Columns to pivot",
203
+ description="Their values become columns; the other category columns stay rows.",
204
+ )
205
+ features: Union[list[FeatureColumn], AllFeatures] = Field(
206
+ default="all", title="Features"
207
+ )
208
+
209
+
210
+ class SortConfig(DataTaskConfig):
211
+ """Sorts by the ``features`` columns."""
212
+
213
+ hidden_fields = frozenset({"grouping"})
214
+
215
+ features: Union[list[FeatureColumn], AllFeatures] = Field(
216
+ default=[], title="Sort by", description="The columns to sort by, in order."
217
+ )
218
+ ascending: bool = Field(default=True, title="Ascending")
219
+ ignore_index: bool = Field(
220
+ default=True,
221
+ title="Renumber rows",
222
+ description="Number the sorted rows 0, 1, ...",
223
+ )
224
+
225
+
226
+ class SplitConfig(DataTaskConfig):
227
+ """Splits a table by the values of the ``grouping`` columns."""
228
+
229
+ hidden_fields = frozenset({"features"})
230
+
231
+ grouping: list[CategoryColumn] = Field(
232
+ default=[],
233
+ title="Split by",
234
+ description="A table for each combination of the values of these columns.",
235
+ )
236
+
237
+ @field_validator("grouping")
238
+ @classmethod
239
+ def _some_columns(cls, grouping):
240
+ if not grouping:
241
+ raise ValueError("Select the columns to split by")
242
+ return grouping
243
+
244
+
245
+ class UnpivotConfig(DataTaskConfig):
246
+ """Moves one level (line) of the columns' headers into a category column: the
247
+ inverse of Pivot. E.g. the level Treatment of ``"FLIRR\\nctrl"``,
248
+ ``"FLIRR\\nDox15"``: a column Treatment (ctrl, Dox15) and a column FLIRR."""
249
+
250
+ legacy_keys = {"category_name": None}
251
+ legacy_dropped = frozenset({"feature_name"}) # now named by the other levels
252
+ hidden_fields = frozenset({"grouping"})
253
+
254
+ features: Union[list[FeatureColumn], AllFeatures] = Field(
255
+ default="all", title="Columns", description="The columns to unpivot."
256
+ )
257
+ level: str = Field(
258
+ default="",
259
+ title="Level",
260
+ description=(
261
+ "The header line that becomes a category column; empty: the last. The"
262
+ " other lines name the value columns."
263
+ ),
264
+ )
265
+ category_name: Optional[str] = Field(
266
+ default=None,
267
+ title="Category column",
268
+ description="Name of the new category column; empty: the level's name.",
269
+ )
270
+
271
+ @classmethod
272
+ def _from_legacy(cls, params):
273
+ # "Category" (the plugin's default name): the level's name
274
+ name = params.pop("category_name", None)
275
+ return {"category_name": None if name in (None, "", "Category") else name}
276
+
277
+ def _to_legacy(self):
278
+ return {"category_name": self.category_name or "Category"}