Comment puis-je faire en sorte qu'un filtre de données reflète un autre ?J'ai une fenêtre contenant deux générateurs de graphiques, provenant de deux ensembles de données différents, et je souhaite filtrer les deux graphiques avec un seul filtre de données. Je ne pense pas que ce soit possible. J'ai pensé à utiliser à la place deux filtres de données « synchronisés », mais je n'ai pas réussi à le comprendre. La semaine dernière, lors de JMP Discovery, @danschikore m'a indiqué les gestionnaires d'événements comme solution possible pour utiliser deux filtres synchronisés. Je pense que je suis sur le point de le faire fonctionner, mais je suis bloqué.
Le script ci-dessous crée les deux filtres et les deux graphiques. Je souhaite que les sélections effectuées dans le filtre supérieur soient automatiquement reflétées dans le filtre inférieur. Une fois que cela fonctionne, je masquerai le filtre inférieur et l'utilisateur verra un filtre contrôlant les deux graphiques.
Dans l'état actuel des choses, lorsque l'utilisateur fait une sélection dans le filtre supérieur, le script imprime cette clause Where dans le journal à l'aide de « Get Where Clause », mais je dois appliquer cette clause Where au filtre inférieur. Je veux une commande comme « Set Where Clause » qui me permettrait de placer ce texte de clause Where dans le filtre inférieur, mais il ne semble pas exister. J'espère vraiment éviter le scénario dans lequel je dois « décompresser » la chaîne de texte (ci-dessous) et la transformer en un formulaire que je peux transmettre au filtre inférieur.
Sélectionner Où( :name == \!"ALICE\!" | :name == \!"BARBARA\!" )
Juste pour me convaincre que je peux faire en sorte que le premier filtre contrôle le second d'une certaine manière, je fais en sorte que le premier filtre efface la sélection du second filtre. Cela signifie que je peux communiquer avec les deux filtres ; je peux extraire la clause where du premier ; je ne peux simplement pas l'appliquer au second.
Question bonus : une fois cette synchronisation effectuée, je souhaite masquer le deuxième filtre. La dernière ligne du script masque le premier filtre. Est-ce que quelqu'un sait comment je pourrais modifier cela pour masquer le deuxième filtre à la place ?
Merci,
Tom

Automatisation et scriptsRe : Comment faire en sorte qu'un filtre de données reflète un autre ?Eval() : exécuter le code *)
Eval Expr : pré-évaluer tout ce qui est enveloppé avec Expr() *)
Expr() : marqueur pour le code qui doit être pré-évalué *)
*) mon cerveau : "le mot en E" - 3 fois.
et le 4ème est Expr Name() ; -)
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Accepter : )
Pour les grandes classes, il existe peut-être une solution simple, mais cette solution pourrait ne pas aider à résoudre le problème réel.
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?@hogi , merci pour ces liens. Eval/Eval Expr() a été la partie la plus frustrante de JSL pour moi au cours des dernières années. Je l'ai utilisé avec succès plusieurs fois en copiant et en modifiant des exemples de la communauté, mais je n'ai jamais eu l'impression de bien le comprendre. J'ai hâte de parcourir la liste des ressources que vous avez collectées et peut-être que cela vous aidera à enfin l'assimiler. Je sais que c'est quelque chose que je dois ajouter à ma boîte à outils.
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Je voulais dire concaténer. En faisant update/join, vous associez les données de taille, qui n'ont pas d'année associée, aux données de poids, qui sont annuelles. Le nouveau graphique donne l'impression que la taille de Barbara a été mesurée à 60 pouces en 2022, 23 et 24, ce qui n'est pas le cas.
L'astuce ici est que les données du bas sont associées à l'année, mais pas celles du haut, et je veux une barre centrée pour cela. Peut-être qu'au lieu de la taille, j'aurais dû utiliser « PIB par habitant au moment et à l'endroit où l'étudiant est né » -- clairement pas associé aux 22/23/24.
Étant donné que les données sous-jacentes des graphiques du haut et du bas ont une structure différente, je pense que j’ai besoin de deux générateurs de graphiques distincts pour créer l’image que je souhaite (ci-dessus).
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Merci @TomF - astuce très sympa pour lutter contre la restriction des paramètres d'étiquettes : utiliser 2 tables : )
Je me demande pourquoi il n'est pas possible de générer des tracés comme celui-ci - ni dans des graphiques séparés, ni combinés dans un seul graphique.

définir l'étiquette d'un graphique force automatiquement les autres graphiques à afficher également les étiquettes : (
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Connaître différentes techniques de filtrage permet de faire toutes sortes de choses intéressantes dans JMP (elles sont parfois aussi nécessaires surtout si vous avez beaucoup de données).
Je suppose que par concaténer, vous pourriez vouloir dire mettre à jour/joindre et nous pourrions avoir une seule table comme celle-ci (une jointure virtuelle peut être utilisée pour cela s'il existe un identifiant unique)

À partir de cela, il est possible de créer un graphique comme celui-ci

Vous pouvez le faire par exemple en utilisant des Textlets (étiquette de survol)
Graph Builder(
Size(1085, 688),
Show Control Panel(0),
Show Legend(0),
Variables(X(:Name), X(:Year, Position(1)), Y(:Height), Y(:Weight), Overlay(:Name)),
Elements(Position(1, 1), Bar(X(1), X(2), Y, Overlay(0), Legend(13))),
Elements(Position(1, 2), Line(X(1), X(2), Y, Legend(15)), Points(X(1), X(2), Y, Legend(16))),
Local Data Filter(
Add Filter(
columns(:Name),
Where(
:Name == {"AMY", "BARBARA", "CAROL", "CHRIS", "CLAY", "DANNY", "DAVID", "EDWARD", "ELIZABETH", "FREDERICK", "HENRY", "JACLYN"}
),
Display(:Name, N Items(15), Find(Set Text("")))
)
),
SendToReport(
Dispatch({}, "Name", ScaleBox, {Label Row(2, Show Major Grid(1))}),
Dispatch({}, "Graph Builder", FrameBox, {Set Textlet(Markup("Serial Number: {:Serial No.[local:_firstRow]}"))}),
Dispatch({}, "Graph Builder", FrameBox(2), {Set Textlet(Markup("Serial Number: {:Serial No.[local:_firstRow]}"))})
)
);
Vous pouvez personnaliser davantage le texte affiché si nécessaire, en utilisant JMP > Rapports JMP > Personnaliser les étiquettes de survol dans les graphiques JMP > Ajouter du texte aux étiquettes de survol dans les graphiques JMP
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Salut @TomF , j'ai ajouté quelques commentaires au code pour expliquer les différentes étapes.
Une astuce consiste à extraire l'expression () : elle analyse l'expression et recherche un modèle spécifique.
Je viens de jeter un œil à un ancien script dans lequel j'utilisais un filtre de données pour contrôler 4 filtres de données supplémentaires dans le même tableau de bord.
Et il semblait que
For( i = 1, i <= N Arg( filterScript ), i++,
If( Head( Arg( filterScript, i ) ) == Expr( Add Filter() ),
filterExpression = Arg( filterScript, i )
)
);
L'approche via Extract Expr() est beaucoup plus élégante - je l'ai apprise l'année dernière grâce à @jthi dans cette discussion :
:folded_hands: Indexation des expressions : accès en lecture et en écriture
L'autre astuce : Eval (Eval Expr( Expr())) - un couteau suisse !
Il résoudra 95 % de vos problèmes en JSL:)
Gestion des expressions dans JMP : astuces et pièges
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Je crains qu'une grande partie de ce code me dépasse, mais j'ai compris l'idée générale. Je garderai cela à l'esprit pour les situations plus compliquées. Merci !
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Oui, je veux un filtre qui filtre deux rapports de tables différentes. J'imagine que cela n'a pas de sens avec l'exemple simple de Big Class que j'ai partagé ci-dessus, mais je joins un exemple plus compliqué qui imite étroitement la structure de mon ensemble de données réel.
Imaginez que vous avez un tableau avec la taille actuelle de chaque élève et un autre tableau avec le poids de chaque élève au cours des trois dernières années. Voici comment je souhaite visualiser ces données :

L'ensemble de données de taille comporte évidemment une ligne par élève. Les données de poids commencent avec une ligne par élève (trois colonnes de poids), mais je ne connais aucun moyen dans Graph Builder de créer ce graphique inférieur sans empiler les poids. Mon tableau de poids comporte maintenant 3 fois plus de lignes que le tableau de taille.
Comme un filtre de données ne peut pas filtrer deux tables différentes, ma solution jusqu'à présent a été de concaténer les tables, juste pour pouvoir créer ce graphique. Ce n'est pas élégant, mais ça fonctionne bien.
Là où j'ai vraiment eu des problèmes, c'est que lorsque je passe la souris sur la taille, je veux que la fenêtre contextuelle indique si la personne portait des chaussures pendant la mesure, et lorsque je passe la souris sur le poids, je veux connaître le numéro de série de la balance qui a été utilisée. Lorsque toutes les données sont dans une table et que je crée Label(1) pour les deux colonnes, vous vous retrouvez avec des valeurs manquantes lorsque vous passez la souris sur (car il n'y a pas de numéro de série de balance sur une mesure de poids)... ce qui peut également être résolu, mais cela a commencé à devenir très compliqué.
Je pense que la solution consistant à laisser les données dans deux tables avec deux filtres synchronisés (dont un masqué) fonctionnera beaucoup mieux. Mais s'il existe une meilleure façon de créer ce graphique, je suis ouvert aux suggestions.
Juste un petit mot sur Graph Builder et la possibilité de le contrôler avec JSL. Cette fusion transparente de deux graphiques différents n'est possible que parce que nous avons la possibilité de désactiver tout ce qui se trouve en bas du graphique supérieur et en haut du graphique inférieur et de les placer l'un contre l'autre. Cela nous a permis de créer un graphique très personnalisé et très riche en données dans notre situation spécifique. Et maintenant, nous pouvons également le filtrer de manière élégante
Peut-être que d'autres trouveront cela utile à l'avenir.
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Il existe de nombreuses options différentes pour quelque chose comme cela et tout dépend de ce qui est réellement fait (à quoi ressemblent les données, les états des lignes peuvent-ils être manipulés, ce qui est filtré (et pourquoi), ...). Voici une option qui fonctionne (généralement) assez bien dans de nombreux cas, mais elle a une fonction assez compliquée pour le gestionnaire (ajouter une taille fixe (avec l'aide de Spacer Box()) pour quelque chose comme cela est généralement une bonne idée pour éviter le clignotement ennuyeux de la fenêtre).
Names Default To Here(1);
dt1 = Open("$SAMPLE_DATA/Probe.jmp");
dt2 = dt1 << Subset(All Rows, Selected Columns(0));
dt2 << Delete Rows(dt2 << get rows where(:Process == "Old"));
nw = New Window("",
Data Filter Context Box(
H List Box(
Data Filter Source Box(
df1 = dt1 << Data Filter(
Local,
Show Controls(0),
Show Modes(0),
Mode(Include(1)),
Add Filter(Columns(:Site, :Process)), Display(:Site, N Items(5))
),
),
V List Box(
gb1 = dt1 << Graph Builder(
Size(525, 458),
Show Control Panel(0),
Variables(X(:Lot ID), Y(:DELL_RPNBR)),
Elements(Bar(X, Y, Legend(4)))
),
gb2_collector = V List Box()
)
)
)
);
create_gb2 = function({a},
c = df1 << Get where clause;
If(Is Missing(c), // could also just use dt2 instead of dt2_private
dt2_privatenew = dt2 << Subset(All Rows, Selected columns only(0), Link to original data table(1), private);
,
where = Arg(Parse(df1 << Get where clause));
dt2_privatenew = Eval(EvalExpr(dt2 << Subset(
Filtered Rows(Expr(Name Expr(where))),
Selected columns only(0),
Link to original data table(1),
private
)));
If(Is Empty(dt2_privatenew), // no rows left -> pick first row and remove it, no linking
dt2_privatenew = dt2 << Subset(Rows(1), Selected columns only(0), Link to original data table(0), private);
dt2_privatenew << Delete Rows(1);
);
);
nw << inval;
Try(Close(dt2_private, no save)); // will also remove old graph builder
dt2_private = dt2_privatenew;
gb2_collector << Append(
gb2 = dt2_private << Graph Builder(
Size(528, 458),
Show Control Panel(0),
Variables(X(:Lot ID), Y(:DELL_RPNBR)),
Elements(Bar(X, Y, Legend(20)))
)
);
wait(0);
nw << Update Window;
);
create_gb2(.);
nw:handler = df1 << Make Filter Change Handler(
create_gb2();
);
Eval(EvalExpr(
nw << On Close(
Try(Close(Expr(dt1), no save));
Try(Close(Expr(dt2), no save));
Try(Close(Expr(dt2_private), no save));
);
));
Write();Re : Comment faire en sorte qu'un filtre de données reflète un autre ?@hogi Je ne comprends pas complètement ce que fait ton code, mais je l'ai mis et ça a parfaitement fonctionné. Merci !
Pour être honnête, je n'ai pas vraiment échappé à la trappe. Je suis tombé dedans et je me suis ensuite relevé. Je suivaiscepost communautaire, mais je n'ai vu aucune raison de stocker le gestionnaire d'événements en tant que variable, donc je ne l'ai pas fait. Cela n'a pas fonctionné, j'ai suivi le post plus attentivement, en le stockant en tant que variable, cela a commencé à fonctionner et j'ai continué sans comprendre ce qui s'est passé. J'ai mis des commentaires dans mon code avec vos deux trappes, en espérant épargner des bleus à quelqu'un à l'avenir.
Dans ce cas, mon filtrage est simple (toujours une colonne) et mes deux tables contiennent les mêmes valeurs x, donc pas de soucis à ce niveau.
J'ai posté le code mis à jour ci-dessus.
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?
@jthi a écrit :
S'il doit s'agir d'un filtre local, il existe d'autres options que d'utiliser deux filtres de données locaux (ce qui est également possible).
Le gestionnaire d’état du filtre n’est pas seulement déclenché par les modifications du filtre de données.
Vos graphiques à barres sont sauvegardés, mais pour les tracés de points, le gestionnaire d'état sera déclenché chaque fois que la souris touchera un point de données !
Ainsi, en fonction du type de tracé, il peut être judicieux de comparer les paramètres de filtre actuels avec les précédents, et d'appliquer simplement les paramètres au deuxième tracé lorsque vous détectez un changement.
peut-être voter ici :
Améliorer les gestionnaires d'état de JMP (étiquette de survol, gestionnaire d'état de ligne, gestionnaire d'état de filtre, planificateur, ...)
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Assurez-vous que les deux tables contiennent les mêmes valeurs !
Si le filtre de données ne trouve pas de ligne correspondante, il se réinitialise et affiche toutes les lignes :

Ce problème sera résolu dans JMP19 via :folded_hands: Propriété de colonne : valeurs inclusives/forcées
Pour contourner ce problème, vous pouvez ajouter des lignes factices avec des données manquantes dans les colonnes restantes.
-> n'apparaîtra pas dans le graphique mais empêchera la réinitialisation du filtre de données.
Voir plus...
//Data Filter Event Handler Test.jsl
//2024-10-31
dtHeight = Open( "$SAMPLE_DATA/Big Class.jmp" );
dtHeight << delete columns({"age", "sex", "weight"});
dtHeight << Set Name ("Height");
dtWeight = Open( "$SAMPLE_DATA/Big Class.jmp" );
dtWeight << delete columns({"age", "sex", "height"});
dtWeight << Set Name ("Weight");
dtHeight << select where(:Name == "ALFRED") << delete rows(); // <- dangerous !!!!
Try(win << Close Window);
win = New Window("Height and Weight",
Data Filter Context Box(
H List Box (
V List Box(
df1_Wt = dtWeight << Data Filter( Local,
Show Controls( 0 ), Show Modes( 0 ), Mode( Include( 1 ) ),
Add Filter(Columns(:name)),
Display( :name, N Items( 4 ))
),
df2_Ht = dtHeight << Data Filter( Local,
Show Controls( 0 ), Show Modes( 0 ), Mode( Include( 1 ) ),
Add Filter(Columns(:name)),
Display( :name, N Items( 4 ))
)
),
V List Box (
gbWt = dtWeight << Graph Builder(
Size( 612, 250 ),
Show Control Panel( 0 ), Show Legend (0), Show Title (0),
Show X Axis (0), Show X Axis Title (0), Show Footer( 0 ),
Variables( X( :name ), Y( :weight ) ),
Elements( Bar( X, Y, Legend( 6 ) ) ),
//Dispatch( {}, "Graph Builder", OutlineBox, {Set Title( "" )} ),
Dispatch({},"weight",ScaleBox,{ Max( 200 )})
),
H List Box(
Spacer Box(Size(25,200)),
gbHt = dtHeight << Graph Builder(
Size( 600, 300 ),
Show Control Panel( 0 ), Show Legend (0), Show Title (0),
Variables( X( :name ), Y( :Height ) ),
Elements( Bar( X, Y, Legend( 6 ) ) ),
Dispatch( {}, "Graph Builder", OutlineBox, {Set Title( "" )} )
)
)
)
)
)
);
eh = df1_Wt << MakeFilterChangeHandler(
function({x},
dfscript = df1_Wt << get script();
filter = Extract Expr(dfscript, Add Filter(Wild List()));
Eval(Eval Expr(df2_Ht << delete all() << Expr (Name Expr(filter)))))
);
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Vous pouvez filtrer deux (ou plusieurs) graphiques avec un seul filtre en utilisant la zone Contexte/Source du filtre de données MAIS ils doivent avoir la même table source
Names Default To Here(1);
dt = Open("$SAMPLE_DATA/Big Class.jmp");
nw = New Window("Selection Filter",
Data Filter Context Box(
H List Box(
dfsb = Data Filter Source Box(
dt << Data Filter(
Local,
Add Filter(columns(:age), Display(:age, N Items(6)))
),
),
Graph Builder(
Size(208, 207),
Show Control Panel(0),
Show Legend(0),
Variables(X(:age)),
Elements(Bar(X, Legend(3)))
),
Bubble Plot(
X(:weight),
Y(:height),
Sizes(:age),
Title Position(0, 0)
)
)
)
);
Maintenant, ma question est : qu'est-ce que vous essayez de faire ? Vous souhaitez avoir un filtre qui filtre deux rapports DE tables différentes ? J'utiliserais un filtre de données local, puis je l'utiliserais pour masquer/exclure des lignes de la deuxième table (si vous l'autorisez à modifier la table au lieu d'être simplement local). S'il doit être local, il existe encore d'autres options que d'utiliser deux filtres de données locaux (ce qui est également possible).
Re : Comment faire en sorte qu'un filtre de données reflète un autre ?Bon : )
Lorsque j'ai créé mon premier gestionnaire d'état, j'ai oublié de stocker le gestionnaire d'état du filtre dans une variable - et il a été détruit immédiatement.
Et puis ça n'a pas marché car j'ai utilisé une fonction avec 0 argument. -> bravo, tu as échappé aux deux trappes : )
Il n'y a pas de clause Set where - la clause Where n'est pas un message qu'un filtre de données comprendra, vous pouvez l'envoyer à la table pour sélectionner les lignes. En plus de cela, << Get Where Clause () renvoie une chaîne. Vous devez utiliser Parse pour la convertir en expression.
Le déballage demande moins d'efforts que vous ne le pensez : il suffit de demander au filtre de données le script, d'extraire la partie Ajouter un filtre () via Extraire l'expr () et de l'appliquer au deuxième filtre - cela fonctionnera pour des sélections simples *)
eh = df1_Wt << MakeFilterChangeHandler(
Function( {x},
dfscript = df1_Wt << get script(); // store the code to create DF1 - with all the settings
filter = Extract Expr(
dfscript, // take the code which is stored in dfscript and ...
Add Filter( Wild List() ) // search for the part with Add Filter(something). Wild List() - like magic :)
);
Eval( // run the code
Eval Expr( // search for any Expr() and execute it (*)
df2_Ht << delete all() // remove all previous settings
<< Expr( // (*) find this Expr() and execute it
Name Expr( filter ) // take the code which is stored in "filter" and replace filter with the actual code
)
)
);
)
);
*) Lorsque vous combinez plusieurs filtres via OR(), le script contiendra plusieurs add Filter() - et Extract Expr() renverra simplement le premier.
Pour obtenir une fonction de clonage ultime, vous pouvez analyser le script DF, collecter tous les AddFilter() et les appliquer l'un après l'autre au deuxième filtre de données.