如何让一个数据过滤器镜像另一个数据过滤器?我有一个窗口,其中包含两个图形生成器,它们从两个不同的数据集中提取数据,我想用一个数据过滤器过滤这两个图形。我认为这是不可能的。我曾考虑过使用两个“同步”数据过滤器,但想不通。上周在 JMP Discovery, @danschikore向我指出事件处理程序是使用两个同步过滤器的可能解决方案。我想我快要让它工作了,但我被卡住了。
下面的脚本创建了两个过滤器和两个图表。我希望顶部过滤器中的选择自动反映在底部过滤器中。一旦成功,我将隐藏底部过滤器,用户将看到一个过滤器控制两个图表。
现在,当用户在顶部过滤器中进行选择时,脚本会使用“获取 Where 子句”将该 Where 子句打印到日志中,但我需要将此 where 子句应用于底部过滤器。我想要一个像“设置 Where 子句”这样的命令,让我将该 where 子句文本推送到底部过滤器中,但它似乎不存在。我真的希望避免出现必须“解压”文本字符串(如下)并将其转换为可以传递给下部过滤器的形式的情况。
选择其中(:name == \!“ALICE\!“ | :name == \!“BARBARA\!“)
为了说服自己,我可以让第一个过滤器以某种方式控制第二个过滤器,我让第一个过滤器清除第二个过滤器的选择。这意味着我可以与两个过滤器对话;我可以从第一个过滤器中提取 where 子句;我只是不能将它应用于第二个过滤器。
附加问题:一旦同步成功,我想隐藏第二个过滤器。脚本的最后一行隐藏了第一个过滤器。有人知道我可以如何修改它以隐藏第二个过滤器吗?
谢谢,
汤姆

自动化和脚本回复:如何让一个数据过滤器镜像另一个数据过滤器?Eval():运行代码*)
Eval Expr:预先评估用Expr() *)包装的所有内容
Expr():需要预先评估的代码的标记*)
*) 我的大脑:“E 字” ——3 次。
第四个是Expr Name() 
回复:如何让一个数据过滤器镜像另一个数据过滤器?同意 : )
对于 Big Class 来说可能有一些简单的解决方案,但这种解决方案可能对实际问题没有帮助。
回复:如何让一个数据过滤器镜像另一个数据过滤器?@hogi ,感谢这些链接。过去几年来,Eval/Eval Expr() 一直是 JSL 中最让我沮丧的部分。我曾通过复制和调整社区中的示例成功使用过几次,但从未觉得自己真正掌握了它。我期待着浏览您收集的资源列表,也许这将有助于我最终理解它。我知道这是我需要添加到我的工具包中的东西。
回复:如何让一个数据过滤器镜像另一个数据过滤器?我的意思是连接。通过执行更新/连接,您将身高数据(没有与之关联的年份)与体重数据(每年)关联起来。新图表看起来好像芭芭拉的身高在 2022、23 和 24 年测量为 60 英寸,但事实并非如此。
这里的诀窍是底部的数据与年份相关,但顶部的数据则不相关,我想要一个居中的条形图。也许我应该使用“学生出生时间和地点的人均 GDP”而不是身高——显然与 22/23/24 无关。
因为顶部和底部图表的底层数据具有不同的结构,所以我认为我需要两个单独的图表构建器来创建我想要的图片(上图)。
回复:如何让一个数据过滤器镜像另一个数据过滤器?感谢@TomF - 非常酷的技巧可以对抗标签设置的限制:使用 2 个表格:)
我想知道为什么不能生成这样的图表 - 无论是在单独的图表中,还是在一张图表中组合。

设置一个图表的标签会自动强制其他图表也显示标签:(
回复:如何让一个数据过滤器镜像另一个数据过滤器?了解不同的过滤技术使得在 JMP 中做各种好事成为可能(有时它们也是必要的,特别是当您有大量数据时)。
我假设通过连接你可能意味着更新/连接,我们可以有像这样的单个表(如果有唯一的标识符,可以使用虚拟连接)

由此可以创建如下图表

例如,您可以通过利用 Textlets(悬停标签)来实现
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]}"))})
)
);
如果需要,您可以自定义显示的文本, 使用 JMP > JMP 报告 > 自定义 JMP 图形中的悬停标签 > 在 JMP 图形中添加文本到悬停标签
回复:如何让一个数据过滤器镜像另一个数据过滤器?嗨@TomF ,我在代码中添加了一些注释来解释各个步骤。
一个技巧是Extract Expr() - 它扫描表达式并搜索特定的模式。
我刚刚看了一个旧脚本,其中我使用一个数据过滤器来控制同一个仪表板中的另外 4 个数据过滤器。
看起来就像
For( i = 1, i <= N Arg( filterScript ), i++,
If( Head( Arg( filterScript, i ) ) == Expr( Add Filter() ),
filterExpression = Arg( filterScript, i )
)
);
通过Extract Expr()实现的方法更加优雅 - 我去年刚刚从@jthi 的讨论中学到它:
:folded_hands: 表达式索引:读写访问
另一个技巧: Eval (Eval Expr( Expr())) - 一把瑞士军刀!
它将解决 JSL 中 95% 的问题:)
JMP 中的表达式处理:Tipps 和 Trapdoors
回复:如何让一个数据过滤器镜像另一个数据过滤器?恐怕很多代码我都看不懂,但我明白大概的意思。我会记住这一点,以应对更复杂的情况。谢谢!
回复:如何让一个数据过滤器镜像另一个数据过滤器?是的,我想要一个过滤器来过滤来自不同表格的两个报告。我认为这对于我上面分享的简单 Big Class 示例来说毫无意义,但我附上了一个更复杂的示例,该示例与我的实际数据集的结构非常相似。
假设您有一张表,其中包含每个学生的当前身高,另一张表包含过去三年每个学生的体重。这就是我想要查看这些数据的方式:

身高数据集显然是每个学生一行。体重数据以每个学生一行(三列体重)开始,但我不知道如何在 Graph Builder 中制作底部图表而不堆叠体重。我的体重表现在有身高表的 3 倍行数。
由于数据过滤器无法过滤两个不同的表格,到目前为止,我的解决方案是将表格连接起来,这样才能制作出这张图表。虽然不是很优雅,但效果很好。
我真正遇到麻烦的地方是,当我将鼠标悬停在身高上时,我希望弹出窗口显示他们在测量时是否穿着鞋子,而当我将鼠标悬停在体重上时,我想知道所用秤的序列号。当所有数据都在一个表中时,我为两列都制作了标签 (1),当您将鼠标悬停时,您最终会得到缺失值(因为体重测量上没有秤序列号)……这也可以解决,但它开始变得非常混乱。
我认为将数据保留在两个表格中并使用两个同步过滤器(一个隐藏)的解决方案效果会更好。但如果有更好的方法来创建此图表,我愿意听取建议。
只是对 Graph Builder 和使用 JSL 控制它的能力表示感谢。这种无缝融合两种不同图表的能力之所以成为可能,是因为我们能够关闭顶部图表底部和底部图表顶部的所有内容,并将它们直接放在一起。这使我们能够根据特定情况创建高度定制且数据密集的图表。现在我们也可以优雅地过滤它:)也许其他人以后会发现这很有用。
回复:如何让一个数据过滤器镜像另一个数据过滤器?对于这样的事情,有很多不同的选择,所有这些都取决于实际要做什么(数据看起来如何,是否可以操纵行状态,正在过滤什么(以及为什么),......)。这是一个在许多情况下(通常)工作得很好的选项,但它对处理程序来说具有相当复杂的功能(为这样的事情添加固定大小(借助 Spacer Box())通常是一个好主意,可以避免窗口令人讨厌的闪烁)。
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();回复:如何让一个数据过滤器镜像另一个数据过滤器?@hogi我不完全理解你的代码的作用,但我把它放进去后,它运行得很好。谢谢!
说实话,我并没有真正从陷阱门里逃出来。我掉进去了,然后又爬出来了。我跟着这社区帖子,但我认为没有理由将事件处理程序存储为变量,所以我没有这样做。它不起作用,我更仔细地按照帖子操作,将其存储为变量,它开始起作用,我继续操作,却不明白发生了什么。我用你的两个陷阱门在我的代码中加入了注释,希望将来能避免某人受伤。
在这种情况下,我的过滤很简单 - 总是一列 - 并且我的两个表都包含相同的 x 值,因此无需担心。
我已在上面发布了更新后的代码。
回复:如何让一个数据过滤器镜像另一个数据过滤器?
@jthi写道:
如果必须是本地的,那么除了使用两个本地数据过滤器之外还有其他选择(这也是可能的)。
过滤器状态处理程序不仅仅由数据过滤器的改变触发。
您的条形图已保存 - 但对于点图,只要鼠标触摸数据点,就会触发状态处理程序!
因此,根据绘图类型,将当前过滤器设置与之前的设置进行比较可能是一个好主意 - 当检测到变化时,只需将设置应用于第二个绘图。
也许在这里投票:
改进 JMP 的状态处理程序(悬停标签、行状态处理程序、过滤器状态处理程序、调度程序……)
回复:如何让一个数据过滤器镜像另一个数据过滤器?确保两个表包含相同的值!
如果数据过滤器找不到匹配的行,它将重置并显示所有行:

此问题将在 JMP19 中通过:folded_hands: 列属性:包容/强制值进行修复
作为 <=JMP18 的解决方法,您可以在剩余列中添加包含缺失数据的虚拟行
-> 不会出现在图中,但会阻止数据过滤器重置。
查看更多...
//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)))))
);
回复:如何让一个数据过滤器镜像另一个数据过滤器?您可以使用数据过滤器上下文/源框通过单个过滤器过滤两个(或更多)图表,但它们必须具有相同的源表
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)
)
)
)
);
现在我的问题是你想做什么?你希望有一个过滤器来过滤来自不同表格的两个报告?我会使用一个本地数据过滤器,然后使用它来隐藏/排除第二个表格中的行(如果你允许它修改表格而不是只是本地的)。如果它必须是本地的,那么除了使用两个本地数据过滤器(这也是可能的)之外还有其他选择。
回复:如何让一个数据过滤器镜像另一个数据过滤器?好的 : )
当我创建我的第一个状态处理程序时,我忘记将过滤器状态处理程序存储在变量中 - 它立即被销毁了。
然后它就不起作用了,因为我使用了一个没有参数的函数。-> 干得好,你逃脱了两个活板门:)
没有Set where 子句- Where 子句不是数据过滤器能够理解的消息,您可以将其发送到表以选择行。除此之外<< Get Where Clause ()返回一个字符串。您必须使用Parse将其转换为表达式。
解包的工作量比您想象的要少 - 只需向数据过滤器询问脚本,通过Extract Expr()提取Add Filter()部分并将其应用于第二个过滤器 - 适用于简单选择*)
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
)
)
);
)
);
*) 当您通过 OR() 组合多个过滤器时,脚本将包含多个添加 Filter() - 并且Extract Expr()将只返回第一个。
为了获得最终的克隆函数,您可以解析 DF 脚本,收集所有AddFilter()并将它们一个接一个地应用于第二个数据过滤器。