
具有缩放响应叠加图的多个 Y 轴
停止! 如果你只想要脚本,它附在帖子的底部。
这是一个专门的图表,它缩放所有 y 轴变量并覆盖它们,然后以变量的单位和范围提供 y 轴比例。 当您想要覆盖 3 或 4 个都在不同范围内的变量时,这很有用。 在上图中,流量在 0-40 范围内,而功率在 200-1000 和 100 到 500 范围内。 这种类型的图表通常用于科学和工程应用中,其中多个传感器同时记录数据,但每个传感器的范围有很大不同。 例如,在发酵示例中,葡萄糖为 1-5 g/dl,pH 为 6.8 - 7.8,乳酸为 10-50 mmol,细胞计数或浊度高出一个数量级。 一种简单的方法来绘制所有变量之间随时间变化的关系对于理解过程的动力学(或检测异常模式)非常有帮助。
很长一段时间以来,这个特定的图表一直是我的克星。 通常需要在 JMP 或 PowerPoint 中进行一些创造性的复制和粘贴,以获得看起来相当不错的任何内容。 那么为什么这张图这么难呢? 您必须以某种方式将数据缩放并组合成一个图形,然后您必须将所有未缩放的 y 轴组件与缩放的数据图形组合。 我见过和尝试过的所有方法都涉及太多的复制和粘贴步骤,而在 Powerpoint 中涉及取消分组和重新格式化图形元素的方法有时会产生糟糕的结果,因为尝试让所有元素对齐很乏味在我的幻灯片上。 JSL 自动化需要杂乱、手动、乏味和重复的流程。
在考虑编写解决方案脚本的策略时,需要考虑几个步骤
1. 为每个变量/列制作一个 y 轴框。
2.将每列的值缩放到相同的范围
3. 重塑数据,使其在叠加图中起作用。
4. 如果有一个启动对话框来选择 y 角色和 x 角色的列,那也很好,(或者可能只使用行号)
5. 最后但并非最不重要的一点是,图表需要包含轴框和原始比例中的每个变量。
我削尖了我的 JSL 铅笔,尝试自动制作这个数字。 我将尝试解释我在这里所做的事情,并希望进一步讨论如何使其更具功能性。 当前状态是一个脚本,它可以生成一种类型的固定图形,并且不需要清理它留下的中间表的尾迹。 (我会将完整的脚本附加到帖子中)
第一步:启动对话框
cd = Column Dialog(
ylist = ColList( "Y", Min Col( 2 ), Max Col( 8 ), Data Type( "Numeric" ), Modeling Type( {"Continuous"} ) ),
xlist = ColList( "X", Max Col( 1 ), Modeling Type( {"Continuous", "Multiple Response"} ) )
);
Eval( Eval Expr( cd[1] ) ); //eval expr, returns what is in cd[1], and the eval makes it execute
Eval( Eval Expr( cd[2] ) ); //separately run just cd[1], then run evalexpr)cd[1], this is how I get the ylist and xlist variables
每当我开始编写启动对话框时,我都会无耻地从脚本索引中复制它,删除我不需要的内容或根据需要添加更多内容。 目标是捕获 Y 和 X。 这里有一些规则,比如列必须是数字连续的,而且必须至少有 2 个 Y(原因后面会更清楚,而且,如果你只有一个 Y,那你为什么用这个?) Y 回复也有 8 个限制,请随意编辑,超过 8 个似乎太乱了。
eval(eval expr()) 的最后两行是我如何从列对话框的“cd”中返回的内容生成 ylist 和 xlist 变量。
数据表1:标准化数据表
dt1 = New Table( "multiple y-axis table" ); //this is the table where the standardized values go
If( N Items( xlist ) == 1, //note that if there is a column selected for xlist, then it doesn't get scaled
Insert Into( ylist, xlist, 1 ), //call me crazy, but the first value in ylist is the "X", if it exists
dt1 << New Column( "Row", formula( Row() ) )
);
DT1 是标准化后将包含数据的表。 如果在对话框中未选择 X,则第一列是“行”,其中包含行号用作图表中的 x 轴。 如果有 X,则将其添加到 ylist 的第一个位置。 是的,没错,变量 ylist 既有 X 又有 Y。
填充 dt1 并制作“轴框”
For(
<FONT color="#FF6600"> If( N Items( xlist ) == 1, //yep, that's an "if" argument inside a "for" loop
i = 2; //if an "x" is specified, then start with the second thing in the list
ycol = As Column( dt, ylist[1] ) << get values;
dt1 << New Column( Munger( ylist[1], 1, ":", "" ), set values( ycol ) );//this is soooo ugly
,i = 1), i <= N Items( ylist ), i++, //end of setup for the For loop</FONT>
<FONT color="#008000"> ycol = As Column( dt, ylist[i] ) << get values;
yycol = ((ycol - Min( ycol )) / (Max( ycol ) - Min( ycol )));
dt1 << New Column( Munger( ylist[i], 1, ":", "" ), set values( yycol ) ); //this is soooo ugly, very sorry</FONT>
<FONT color="#0000FF"> amin = Min( ycol ); //this second part of this script is getting values to make the y-axis boxes
amax = Max( ycol ); //I need the min and max values from each of the unscaled columns, along with their names
ymin = amin - ((amax - amin) * .1); //The y-axis box axis range is 10% higher and lower than the min and max.
ymax = amax + ((amax - amin) * .1); //It looks better and is more closely aligned with the scaled data in gb
obj1 = Graph Box( Framesize( 0, 230 ), yName( Char( ylist[i] ) ), Y Scale( ymin, ymax ) ); </FONT><BR /><FONT color="#0000FF"> //its not a true axis box, just a squished graph box. the axis box was free
Insert Into( objhlb1, Name Expr( obj1 ) ); </FONT><BR /><FONT color="#0000FF"> //there is a lot of inserting of name expr() here. its part of building expressions from the inside out.</FONT>
);
下一部分循环遍历为 Y 选择的列列表。 如果指定了 X,则从列表中的第二项开始
第一部分使用一些逻辑来设置循环并为结果制作表格,因此请进入。 表格变体取决于是否指定了 x 轴变量。
脚本的中间部分非常重要,尽管它很短。 它从原始表 (dt) 中的每一列中获取值,并将缩放后的值放入 dt1。我真的不为这条线感到骄傲:dt1 <<新列(芒格( ylist[i], 1 , “:” , “” ), 设置值(yycol));但事情是这样的,新列需要“colname”或colname,而:colname 会终止这一步,所以我只是把冒号删掉了。 (在评论中发送更好的方法来做到这一点。)
最后一部分查看每组列值并使用它们来缩放图形框。 图形框制作漂亮的轴框,如果图形框的宽度设置为 0,则仅显示 y 轴框。 发生的事情是这一步对于格式化图表很重要。 最后一行将图形框的表达式插入到最后计算的显示框中。 通过使用名称 expr 来引用 obj1(图形框),我得到了创建它的脚本,并且脚本中的所有变量都被评估,但脚本没有被评估。 每个图形框都插入到水平列表框 (objhlb1) 中。 后来这个盒子被插入另一个盒子。
建立一个表格来制作图表:
For(
If( N Items( xlist ) == 1,
i = 2,
i = 1
), i <= N Items( ylist ), i++,
Insert Into( dt2stackcols, ylist[i] )
);
Insert Into( dt2stack, Name Expr( dt2stackcols ), 1 );
Eval(
Substitute( Expr( dt2 = dt1 << ping ),
Expr( ping ), Eval Expr( dt2stack )
)
); //This is tricky!
dt2 << set name( "multiple y-axis stack" );
上面我提到了一个约束:必须选择至少两个 y 变量。 事实证明,你不能只堆叠一个变量(因为它已经堆叠了),如果只有一个变量,它会使脚本崩溃。 这里有趣的是处理是否指定 X 的逻辑。 如果有 X,我已经不想缩放它,现在我不想将它包含在堆栈中,因为我希望它作为 X 标签。 如果没有指定 X,那么我想使用 ylist 中的所有变量。
红色的最后一部分很难解释。 在一行中,构建并运行/评估了执行堆栈的脚本。 “Ping”是被替换掉的模式,当我考虑将这些表达式弹跳起来时,这是我想到的第一个词。
dt2stackcols 的表达式在哪里? 为了清楚起见,以及我在处理这个问题时的理智,我将所有定义的表达式移到了脚本的开头。 这很有帮助,因为当我改变脚本的顺序时,表达式在需要它们之前就已经存在。 如果我只是将它们分散在脚本中,我会经常尝试将它们插入到稍后在脚本中定义的表达式中。 看看完整的脚本,附上看看它是如何布局的。 这可能不是最佳实践,但我正在竭尽全力为我的克星图。
组装所有零件
Insert Into( objout, Name Expr( objhlb1 ) );
Insert Into( objvlb, Name Expr( objout ) );
Insert Into( objhlb2, Name Expr( objvlb ) );
Insert Into( objhlb2, Name Expr( obj ) );
Insert Into( objwin, Name Expr( objhlb2 ) );
Eval( Eval Expr( objwin ) ); //Evaluating all the assembled parts after all the expressions are evaluated
gb << size( 600, 285 ); //if you want to format the graph, send messages to gb
JSL 最好的一点是它是一种基于函数的语言。 这完成了两件重要的事情,首先它可以轻松地完成特定的事情,而无需重新发明轮子,其次,它可以轻松地思考如何构建复杂的结构。 (只要您将它们视为您从内到外构建的洋葱。 例如,典型的思维方式是这样的。 首先我买了很多土地,然后我在土地上盖了一栋楼,然后我在楼里放了房间,然后我把我的办公椅之类的东西放在房间里,然后我坐在椅子上,最后,我开始工作。 功能思维是这样工作的;首先有一把椅子,我把拜伦放在里面。 然后我把椅子(还有一把拜伦)放到办公室,然后我把它放到大楼里,我把它放到地段里,然后我买了它,最后,拜伦工作了。
第一步是将图形框放入objhlb1,然后有一个垂直列表框(objvlb),其中插入了一个轮廓框(objout)和objhlb1。 这是图表的左侧。 由于位于左侧,ovjvlb 被插入到第二个水平列表框 (objhlb2) 中,首先是图形构建器图表 (obj)。 最后 objhlb2 (现在包含所有内容)被插入到一个新的窗口框中,以便可以显示它。 然后为了使这一切正常工作,它会被评估。 如果我只是使用 Eval,那么.. 我不确定会发生什么,但它不起作用,所以我先执行了 Eval Expr,以便显示所有内容的整个评估脚本(尝试在脚本中仅运行 Eval Expr( objwin ) 以了解我的意思)然后我使用 Eval 运行该部分。
obj 有一点格式,即图形生成器图。 关闭标题,重新调整帧大小,关闭 y 轴。 这一切都是为了使其与 y 轴框的比例和形状相匹配。 使用 Graph Builder 绘图很方便,因为仍然可以编辑 x 轴,可以移动图例,并且所有 GB 选项都可用于进一步格式化。
当前终点
就脚本而言,在第 7 次重大修订时,它不是很整齐,而且它留下了一堆桌子。 这和其他几件事应该得到解决。 因此,如果您有添加的想法,请发送它们。 有些事情不会发生,例如图表中的数据不会动态链接到原始数据表,并且 Y 轴框不会链接到 Scaled Response Graph 大纲框,您必须调整大小每一个独立。 如果那行得通,那就太好了。 最后,我几乎 100% 确信有办法使脚本中断或返回不幸的结果,因此请注意特殊的列名和极端异常值,或大量缺失数据。
我很想就使这个脚本工作的更好方法发表评论。 (说到工作,我在 PC 和 Mac 上的 v13 和 v14 中对此进行了测试,它运行良好,可以发布。)
作者的结束语:
这是用最非正式的语气写的,就像我们在谈论这个一样。
这是故意的,因为编码中的正式术语,恕我直言,阻碍了沟通。 相反,我避免混淆。