Skip to content

Commit 1bd8abc

Browse files
feat: move the relevance dependencies that can be moved today
Fifteen fields stated a relevance dependency in their description, in five languages, enforced nowhere. The useful question was not how many there are but which of them a rule can express today, so that got measured before any of it got written: - Five can, and now do. The seed-and-shuffle dependency in the five splitters that take both, and the covariance explorer's delta degrees of freedom. Their conditions read a plain boolean sibling, which is all the algebra needs. - Two cannot, because the condition is not in the schema at all. ColumnArithmetic and ColumnConcat say the constant is "only used (and required) when a single column is selected", and how many columns the user picked is session state. Those wait for the context channel, and a test now says so rather than leaving the omission looking like an oversight. - One could not because of the value model, which is the ExponentialSmoothing case from the previous commit. - Three were never dependencies. The sweep matched "at 0.0 the depth map has no effect" and "when keep_binary_mask is enabled, mask_i columns...", which describe what a value means and which columns get produced, not what the field depends on. Retracted rather than converted. The five splitters share one rule object instead of five copies of the same sentence. A rule is stateless, so one instance can be listed by several schemas, and each still has its field names checked against its own fields, so listing it somewhere without a shuffle field fails at import. Holdout's inline copy from the first commit is now that shared one too. The alternative was a shared base schema for five components, which is a structural change for one shared sentence. The covariance explorer is the interesting one: delta_degree_of_freedom is declared 30 lines above the numeric_only it depends on. A @field_validator there would read info.data, find its controller absent and quietly do nothing — the same silent no-op the RAG chunkers avoid only by luck of declaration order. A rule runs on the complete model, so the order cannot disable it, and there is a test asserting the order is in fact the bad one. 31 cases, including that the prose is gone from all ten descriptions in the five splitters, and that the two repeated fold splitters correctly declare no such rule: they have a seed but no shuffle flag, because sklearn always shuffles them.
1 parent 978cdd4 commit 1bd8abc

8 files changed

Lines changed: 345 additions & 199 deletions

File tree

DashAI/back/exploration/explorers/cov_matrix.py

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66
TableArtifact,
77
TablePayload,
88
)
9-
from DashAI.back.core.schema_fields import bool_field, int_field, schema_field
9+
from DashAI.back.core.schema_fields import (
10+
F,
11+
IsTrue,
12+
Relevance,
13+
bool_field,
14+
int_field,
15+
schema_field,
16+
)
1017
from DashAI.back.core.utils import MultilingualString
1118
from DashAI.back.dependencies.database.models import Explorer, Notebook
1219
from DashAI.back.exploration.base_explorer import (
@@ -69,23 +76,16 @@ class CovarianceMatrixExplorerSchema(BaseExplorerSchema):
6976
int_field(gt=0),
7077
1,
7178
description=MultilingualString(
79+
# The "only used if numeric_only is True" sentence is the Relevance
80+
# rule at the end of this class now.
7281
en=(
7382
"Delta degrees of freedom to use when calculating the covariance "
74-
"matrix. Only used if numeric_only is True."
75-
),
76-
es=(
77-
"Grados de libertad delta a usar al calcular la matriz de "
78-
"covarianza. Solo se usa si numeric_only es True."
79-
),
80-
pt=(
81-
"Graus de liberdade delta a usar ao calcular a matriz de "
82-
"covariância. Usado apenas se numeric_only for True."
83+
"matrix."
8384
),
84-
de=(
85-
"Delta-Freiheitsgrade zur Berechnung der Kovarianzmatrix. "
86-
"Wird nur verwendet, wenn numeric_only True ist."
87-
),
88-
zh="计算协方差矩阵时使用的自由度delta。仅在numeric_only为True时使用。",
85+
es=("Grados de libertad delta a usar al calcular la matriz de covarianza."),
86+
pt=("Graus de liberdade delta a usar ao calcular a matriz de covariância."),
87+
de="Delta-Freiheitsgrade zur Berechnung der Kovarianzmatrix.",
88+
zh="计算协方差矩阵时使用的自由度delta。",
8989
),
9090
alias=MultilingualString(
9191
en="Delta degrees of freedom",
@@ -144,6 +144,39 @@ class CovarianceMatrixExplorerSchema(BaseExplorerSchema):
144144
),
145145
) # type: ignore
146146

147+
# The delta degrees of freedom only apply to the numeric path. Declared
148+
# here rather than as a field_validator because the two fields are in the
149+
# wrong order for one: delta_degree_of_freedom is declared 30 lines above
150+
# numeric_only, and a field_validator reading info.data would find its
151+
# controller absent and silently do nothing. A rule runs on the complete
152+
# model, so declaration order cannot disable it.
153+
rules = [
154+
Relevance(
155+
"delta_degree_of_freedom",
156+
when=IsTrue(F("numeric_only")),
157+
effect="disable",
158+
reason=MultilingualString(
159+
en=(
160+
"The delta degrees of freedom are only used when the "
161+
"calculation is restricted to numeric columns."
162+
),
163+
es=(
164+
"Los grados de libertad delta solo se usan cuando el "
165+
"cálculo se restringe a columnas numéricas."
166+
),
167+
pt=(
168+
"Os graus de liberdade delta só são usados quando o "
169+
"cálculo é restrito a colunas numéricas."
170+
),
171+
de=(
172+
"Die Delta-Freiheitsgrade werden nur verwendet, wenn die "
173+
"Berechnung auf numerische Spalten beschränkt ist."
174+
),
175+
zh="自由度delta仅在计算限制为数值列时使用。",
176+
),
177+
),
178+
]
179+
147180

148181
class CovarianceMatrixExplorer(StatisticalExplorer):
149182
"""Explorer that computes and visualises the pairwise covariance matrix.

DashAI/back/splitters/group_k_fold.py

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
string_field,
1616
)
1717
from DashAI.back.core.utils import MultilingualString
18+
from DashAI.back.splitters.rules import SEED_ONLY_MATTERS_WHEN_SHUFFLING
1819

1920
from .fold_splitter import FoldSplitter, sklearn_random_state
2021

@@ -79,26 +80,15 @@ class GroupKFoldSplitterSchema(BaseSchema):
7980
bool_field(),
8081
placeholder=False,
8182
description=MultilingualString(
82-
en=(
83-
"Whether to shuffle the groups before assigning them to folds. When "
84-
"shuffling is disabled, the random state has no effect."
85-
),
83+
en=("Whether to shuffle the groups before assigning them to folds."),
8684
es=(
87-
"Si se deben mezclar los grupos antes de asignarlos a las "
88-
"particiones. Cuando la mezcla está desactivada, el estado aleatorio "
89-
"no tiene efecto."
85+
"Si se deben mezclar los grupos antes de asignarlos a las particiones."
9086
),
9187
pt=(
92-
"Se os grupos devem ser embaralhados antes de atribuí-los às "
93-
"partições. Quando o embaralhamento está desativado, o estado "
94-
"aleatório não tem efeito."
95-
),
96-
de=(
97-
"Ob die Gruppen vor der Zuweisung zu Folds gemischt werden sollen. "
98-
"Wenn das Mischen deaktiviert ist, hat der Zufallszustand keine "
99-
"Wirkung."
88+
"Se os grupos devem ser embaralhados antes de atribuí-los às partições."
10089
),
101-
zh="分配到各折之前是否打乱分组。关闭打乱时,随机状态不起作用。",
90+
de=("Ob die Gruppen vor der Zuweisung zu Folds gemischt werden sollen."),
91+
zh="分配到各折之前是否打乱分组。",
10292
),
10393
alias=MultilingualString(
10494
en="Shuffle", es="Mezclar", pt="Embaralhar", de="Mischen", zh="打乱"
@@ -108,24 +98,11 @@ class GroupKFoldSplitterSchema(BaseSchema):
10898
int_field(ge=0),
10999
placeholder=42,
110100
description=MultilingualString(
111-
en=(
112-
"Seed used to make the split reproducible when shuffle is enabled. It "
113-
"is ignored when shuffling is disabled."
114-
),
115-
es=(
116-
"Semilla utilizada para que la división sea reproducible cuando se "
117-
"activa la mezcla. Se ignora cuando la mezcla está desactivada."
118-
),
119-
pt=(
120-
"Semente usada para tornar a divisão reproduzível quando o "
121-
"embaralhamento está ativado. É ignorada quando o embaralhamento está "
122-
"desativado."
123-
),
124-
de=(
125-
"Seed, um die Aufteilung reproduzierbar zu machen, wenn Mischen "
126-
"aktiviert ist. Wird ignoriert, wenn das Mischen deaktiviert ist."
127-
),
128-
zh="启用打乱时,用于使划分可复现的随机种子。关闭打乱时将被忽略。",
101+
en=("Seed used to make the split reproducible."),
102+
es=("Semilla utilizada para que la división sea reproducible."),
103+
pt=("Semente usada para tornar a divisão reproduzível."),
104+
de=("Seed, um die Aufteilung reproduzierbar zu machen."),
105+
zh="用于使划分可复现的随机种子。",
129106
),
130107
alias=MultilingualString(
131108
en="Random state",
@@ -194,6 +171,10 @@ class GroupKFoldSplitterSchema(BaseSchema):
194171
),
195172
) # type: ignore
196173

174+
# The same dependency as every other splitter that takes a seed, declared
175+
# once in splitters/rules.py instead of copied here.
176+
rules = [SEED_ONLY_MATTERS_WHEN_SHUFFLING]
177+
197178

198179
class GroupKFoldSplitter(FoldSplitter):
199180
"""Splitter that generates folds while preserving the group structure of the data.

DashAI/back/splitters/holdout.py

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
BaseSchema,
88
Check,
99
F,
10-
IsTrue,
11-
Relevance,
1210
Sum,
1311
bool_field,
1412
float_field,
@@ -17,6 +15,7 @@
1715
)
1816
from DashAI.back.core.utils import MultilingualString
1917
from DashAI.back.dataloaders.classes.dashai_dataset import split_dataset
18+
from DashAI.back.splitters.rules import SEED_ONLY_MATTERS_WHEN_SHUFFLING
2019

2120
from .base_splitter import BaseSplitter
2221

@@ -95,23 +94,11 @@ class HoldoutSplitterSchema(BaseSchema):
9594
bool_field(),
9695
placeholder=True,
9796
description=MultilingualString(
98-
en=(
99-
"Whether to shuffle the data before splitting it. When shuffling is "
100-
"disabled, the random state has no effect."
101-
),
102-
es=(
103-
"Si se deben mezclar los datos antes de dividirlos. Cuando la mezcla "
104-
"está desactivada, el estado aleatorio no tiene efecto."
105-
),
106-
pt=(
107-
"Se os dados devem ser embaralhados antes de dividi-los. Quando o "
108-
"embaralhamento está desativado, o estado aleatório não tem efeito."
109-
),
110-
de=(
111-
"Ob die Daten vor der Aufteilung gemischt werden sollen. Wenn das "
112-
"Mischen deaktiviert ist, hat der Zufallszustand keine Wirkung."
113-
),
114-
zh="划分前是否打乱数据。关闭打乱时,随机状态不起作用。",
97+
en="Whether to shuffle the data before splitting it.",
98+
es="Si se deben mezclar los datos antes de dividirlos.",
99+
pt="Se os dados devem ser embaralhados antes de dividi-los.",
100+
de="Ob die Daten vor der Aufteilung gemischt werden sollen.",
101+
zh="划分前是否打乱数据。",
115102
),
116103
alias=MultilingualString(
117104
en="Shuffle", es="Mezclar", pt="Embaralhar", de="Mischen", zh="打乱"
@@ -183,27 +170,8 @@ class HoldoutSplitterSchema(BaseSchema):
183170
zh="训练集比例必须大于 0。",
184171
),
185172
),
186-
Relevance(
187-
"random_state",
188-
when=IsTrue(F("shuffle")),
189-
effect="disable",
190-
reason=MultilingualString(
191-
en="The random state has no effect while shuffling is disabled.",
192-
es=(
193-
"El estado aleatorio no tiene efecto mientras la mezcla "
194-
"está desactivada."
195-
),
196-
pt=(
197-
"O estado aleatório não tem efeito enquanto o "
198-
"embaralhamento está desativado."
199-
),
200-
de=(
201-
"Der Zufallszustand hat keine Wirkung, solange das Mischen "
202-
"deaktiviert ist."
203-
),
204-
zh="关闭打乱时,随机状态不起作用。",
205-
),
206-
),
173+
# Shared with the four fold splitters, which carry the same dependency.
174+
SEED_ONLY_MATTERS_WHEN_SHUFFLING,
207175
]
208176

209177

DashAI/back/splitters/k_fold.py

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
schema_field,
1414
)
1515
from DashAI.back.core.utils import MultilingualString
16+
from DashAI.back.splitters.rules import SEED_ONLY_MATTERS_WHEN_SHUFFLING
1617

1718
from .fold_splitter import FoldSplitter, sklearn_random_state
1819

@@ -43,26 +44,11 @@ class KFoldSplitterSchema(BaseSchema):
4344
bool_field(),
4445
placeholder=True,
4546
description=MultilingualString(
46-
en=(
47-
"Whether to shuffle the data before splitting it into folds. When "
48-
"shuffling is disabled, the random state has no effect."
49-
),
50-
es=(
51-
"Si se deben mezclar los datos antes de dividirlos en particiones. "
52-
"Cuando la mezcla está desactivada, el estado aleatorio no tiene "
53-
"efecto."
54-
),
55-
pt=(
56-
"Se os dados devem ser embaralhados antes de dividi-los em partições. "
57-
"Quando o embaralhamento está desativado, o estado aleatório não tem "
58-
"efeito."
59-
),
60-
de=(
61-
"Ob die Daten vor der Aufteilung in Folds gemischt werden sollen. "
62-
"Wenn das Mischen deaktiviert ist, hat der Zufallszustand keine "
63-
"Wirkung."
64-
),
65-
zh="划分为折之前是否打乱数据。关闭打乱时,随机状态不起作用。",
47+
en=("Whether to shuffle the data before splitting it into folds."),
48+
es=("Si se deben mezclar los datos antes de dividirlos en particiones."),
49+
pt=("Se os dados devem ser embaralhados antes de dividi-los em partições."),
50+
de=("Ob die Daten vor der Aufteilung in Folds gemischt werden sollen."),
51+
zh="划分为折之前是否打乱数据。",
6652
),
6753
alias=MultilingualString(
6854
en="Shuffle", es="Mezclar", pt="Embaralhar", de="Mischen", zh="打乱"
@@ -72,24 +58,11 @@ class KFoldSplitterSchema(BaseSchema):
7258
int_field(ge=0),
7359
placeholder=42,
7460
description=MultilingualString(
75-
en=(
76-
"Seed used to make the split reproducible when shuffle is enabled. It "
77-
"is ignored when shuffling is disabled."
78-
),
79-
es=(
80-
"Semilla utilizada para que la división sea reproducible cuando se "
81-
"activa la mezcla. Se ignora cuando la mezcla está desactivada."
82-
),
83-
pt=(
84-
"Semente usada para tornar a divisão reproduzível quando o "
85-
"embaralhamento está ativado. É ignorada quando o embaralhamento está "
86-
"desativado."
87-
),
88-
de=(
89-
"Seed, um die Aufteilung reproduzierbar zu machen, wenn Mischen "
90-
"aktiviert ist. Wird ignoriert, wenn das Mischen deaktiviert ist."
91-
),
92-
zh="启用打乱时,用于使划分可复现的随机种子。关闭打乱时将被忽略。",
61+
en=("Seed used to make the split reproducible."),
62+
es=("Semilla utilizada para que la división sea reproducible."),
63+
pt=("Semente usada para tornar a divisão reproduzível."),
64+
de=("Seed, um die Aufteilung reproduzierbar zu machen."),
65+
zh="用于使划分可复现的随机种子。",
9366
),
9467
alias=MultilingualString(
9568
en="Random state",
@@ -158,6 +131,10 @@ class KFoldSplitterSchema(BaseSchema):
158131
),
159132
) # type: ignore
160133

134+
# The same dependency as every other splitter that takes a seed, declared
135+
# once in splitters/rules.py instead of copied here.
136+
rules = [SEED_ONLY_MATTERS_WHEN_SHUFFLING]
137+
161138

162139
class KFoldSplitter(FoldSplitter):
163140
"""Splitter that generates K folds for cross-validation.

DashAI/back/splitters/rules.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""Rules shared by more than one splitter.
2+
3+
The seed-and-shuffle dependency is the same fact in five splitters, so it is
4+
declared once here rather than copied into each of them. A rule is a stateless
5+
object, so the same instance can appear in several schemas' ``rules`` lists;
6+
each schema still has its field names checked against its own fields at class
7+
definition, so listing it somewhere without a ``shuffle`` field fails loudly.
8+
9+
The alternative would have been a shared base schema, which is a structural
10+
change to five components for one shared sentence. This gets the deduplication
11+
without it.
12+
"""
13+
14+
from DashAI.back.core.schema_fields import F, IsTrue, Relevance
15+
from DashAI.back.core.utils import MultilingualString
16+
17+
__all__ = ["SEED_ONLY_MATTERS_WHEN_SHUFFLING"]
18+
19+
#: ``random_state`` has no effect unless the data is shuffled first.
20+
#:
21+
#: Every splitter that takes both said so in its descriptions, in five
22+
#: languages, and none of them enforced it: the field stayed editable and the
23+
#: value was quietly ignored. As a rule the renderer disables the control and
24+
#: says why, and the sentence comes out of the prose.
25+
SEED_ONLY_MATTERS_WHEN_SHUFFLING = Relevance(
26+
"random_state",
27+
when=IsTrue(F("shuffle")),
28+
effect="disable",
29+
reason=MultilingualString(
30+
en="The random state has no effect while shuffling is disabled.",
31+
es=("El estado aleatorio no tiene efecto mientras la mezcla está desactivada."),
32+
pt=(
33+
"O estado aleatório não tem efeito enquanto o embaralhamento está "
34+
"desativado."
35+
),
36+
de=(
37+
"Der Zufallszustand hat keine Wirkung, solange das Mischen deaktiviert ist."
38+
),
39+
zh="关闭打乱时,随机状态不起作用。",
40+
),
41+
)

0 commit comments

Comments
 (0)