可接受语法不一致AllCols = dt << Get Column Names();
AllColNames = dt << Get Column Names(string);
for(i=3, i<=n items(AllCols), i++,
colcol = AllCols[i];
cname = AllColNames[i];
nnm1 = Col Number(colcol);
Fstr = "num(:\!""||cname||"\!"n)";
Fexpr = parse(Fstr);
newcol = dt << new column("Temp", numeric, formula(eval(evalexpr(Fexpr))));
nnm2 = Col Number(newcol);
if(nnm2 == nnm1,
newcol << delete formula();
dt << Delete Columns(colcol);
newcol << set name(cname), //else
dt << Delete Columns(newcol);
);
);我在 Windows 上使用 JMP18。当我编写经常使用的代码时,我喜欢为其制作一个工具栏按钮。但有时我发现从打开的脚本窗口运行可接受的代码在从工具栏图标运行时不可接受。这里有一个示例,我循环遍历列并将它们从字符更改为数字(如果所有条目都是数字)。从工具栏运行时,反对意见是针对以下行:
newcol = dt << new column("Temp", numeric, formula(eval(evalexpr(Fexpr))));
当我从脚本窗口运行时,此语法是可以接受的!
为什么 jsl 解释器在每种情况下都会不同?
什么样的语法才能被普遍接受?
对于非常大的数据集和列,这可能会很慢。有没有更好的方法来实现这一点?
自动化和脚本数据混合和清理视窗回复:不一致的可接受语法您收到什么错误消息?我认为脚本窗口有当前数据表,但工具栏脚本可能没有。也许只需添加
currentDatatable(dt);
在脚本的开头就足够了。
你可以
print(currentDatatable());
在脚本的开始处找出工具栏脚本知道什么。
回复:不一致的可接受语法工具栏可能会将名称默认添加到此处(1)(我认为你应该始终这样做)添加到你的脚本中

然后可能会显示一些错误,如果所有内容都在全局范围内,则这些错误将不可见(但它们通常仍然是错误)。我使用 Eval(EvalExpr()) 或 Eval(Substitute()) 构建公式
Eval(EvalExpr(
newcol = dt << New Column("Temp", numeric, formula(Num(Expr(NameExpr(AsColumn(dt, i))))));
));
对于大型数据集,您可能必须考虑进行类似这样的采样。还有非公式解决方案可能更快,但也可能取决于您的数据。
由于某种原因,我尚未将其发布到 JMP 社区(或者至少我找不到它), 字符列到数字(github.com) ,您可以查看代码以了解我如何从https://github.com/jthi-jmp-tools/character-columns-to-numeric/blob/main/bin/bin/character_columns_to_numeric.jsl执行此任务。从那时起,我已经重做了这个脚本,如果我没记错的话,我现在确实使用了稍微不同的方法。
回复:不一致的可接受语法工具栏图标了解当前数据表():
问题恰恰相反。
请在之前定义“dt=当前数据表()”
dt << Get Column Names();
回复:不一致的可接受语法谢谢克雷格。
错误信息是:
名称未解析:访问或评估“2PCS_X(um)”时第 1 行的 2PCS_X(um),“2PCS_X(um)”n /*###*/
“2PCS_X(um)”是要评估的第一列的名称。
因此第一列的表达式应该是:
newcol = dtTOSA << 新列("Temp", 数字, 公式(Num( :"2PCS_X(um)"n )));
回复:不一致的可接受语法此外,在实际脚本中,我不使用 dt 作为变量。我使用更具描述性的变量,如 dtTOSA。(见上文)每次我想处理特定数据表时,我总是明确说明它以避免混淆,就像我在这个语句中所做的那样。我不明白解释器怎么会认为我指的是不同的表。
回复:不一致的可接受语法可能,但错误消息表明 JMP 没有在正确的位置查找“2PCS_X(um)”。我不确定工具栏脚本可以使用什么上下文。
回复:不一致的可接受语法克雷格,
我采用了您建议的使用打印语句,但发现它从未到达我认为有问题的那一行。
newcol = dt << new column("Temp", numeric, formula(eval(evalexpr(Fexpr))));问题就出在这行。
nnm1 = Col Number(colcol);
所以我将其改为这样,并且成功了。
nnm1 = dtTOSA << Col Number(colcol);
我发现我还需要再次提醒 JMP 当前表在代码中是什么,并且我必须在删除列中使用 {} 才能正常工作。出于某种原因,如果从脚本窗口运行,JMP 并不关心这些花括号是否存在。
代码片段现在是这样的。顺便说一下,这是一个较长代码的片段,它还创建了这个和另一个表。
AllCols = dtTOSA << Get Column Names();
AllColNames = dtTOSA << Get Column Names(string);
currentDatatable(dtTOSA);
for(i=3, i<=n items(AllCols), i++,
colcol = AllCols[i];
cname = AllColNames[i];
nnm1 = dtTOSA << Col Number(colcol);
Fstr = "num(:\!""||cname||"\!"n)";
Fexpr = parse(Fstr);
newcol = dtTOSA << new column("Temp", numeric, set formula(eval(evalexpr(Fexpr))));
nnm2 = dtTOSA << Col Number(newcol);
if(nnm2 == nnm1,
currentDatatable(dtTOSA);
newcol << delete formula();
dtTOSA << Delete Columns({colcol});
wait(0);
newcol << set name(cname), //else
dtTOSA << Delete Columns({newcol});
);
);但是,现在出现了一个不同的问题。尽管我在重命名新列之前删除了旧列,但 JMP 还是在列名后面添加了一个不需要的 2!同样,它不会在从脚本窗口运行时执行此操作。我尝试添加一个 wait(0) 语句,但没有帮助。
如何修复此问题?
回复:不一致的可接受语法实际上它并没有执行删除列操作。
dtTOSA << Delete Columns({colcol});因此它保留了原始的字符列和新的数字列。
回复:不一致的可接受语法奇怪的是,如果转到“else”条件,则删除语句可以正常工作。
回复:不一致的可接受语法尽管该列有两种不同的引用方式(一种是文本字符串名称,另一种是列类型),但最终还是成功了。我已经用 jsl 编写代码 18 年了,但仍然发现在 jsl 中将列引用为变量不一致且令人困惑。
AllCols = dtTOSA << Get Column Names();
AllColNames = dtTOSA << Get Column Names(string);
currentDatatable(dtTOSA);
for(i=3, i<=n items(AllCols), i++,
colcol = AllCols[i];
cname = AllColNames[i];
nnm1 = dtTOSA << Col Number(colcol);
Fstr = "num(:\!""||cname||"\!"n)";
Fexpr = parse(Fstr);
newcol = dtTOSA << new column("Temp", numeric, set formula(eval(evalexpr(Fexpr))));
nnm2 = dtTOSA << Col Number(newcol);
if(nnm2 == nnm1,
currentDatatable(dtTOSA);
newcol << delete formula();
dtTOSA << Delete Columns(eval(cname));
currentDatatable(dtTOSA);
newcol << set name(cname), //else
dtTOSA << Delete Columns({newcol});
);
);回复:不一致的可接受语法我总是使用字符串作为列基础,然后根据需要从中构建引用。我发现这是处理不同类型引用的最灵活方式。
例如,如果我采用您的脚本并对其目的做出一些假设(从第三列开始遍历所有脚本,创建一个新的公式列,在其中将值更改为数字,比较缺失值并根据结果执行操作)我会在 JMP18 中像这样编写它:
Names Default To Here(1);
dtTOSA = New Table("Untitled",
Add Rows(4),
Compress File When Saved(1),
New Column("Column 1",
Numeric,
"Continuous",
Format("Best", 12),
Set Values([1, 2, 3, 4])
),
New Column("Column 2", Character, "Nominal", Set Values({"A", "1", "2", "3"})),
New Column("Column 3", Character, "Nominal", Set Values({"1", "2", "3", "4"})),
New Column("Column 4", Character, "Nominal", Set Values({"A", "B", "C", "D"}))
);
wait(1); // demo purposes
cols = dtTOSA << Get Column Names("String");
// Remove From(cols, 1, 2); // skipping this for demo purposes
For Each({colname}, cols,
// colname = cols[3]; // for testing
Eval(EvalExpr(
newcol = dtTOSA << New Column("Temp", numeric, Formula(
Num(Expr(Name Expr(As Column(dtTOSA, colname))))
));
));
newcol << Run Formulas;
newcol << delete formula();
orig_vals = Col Number(Column(dtTOSA, colname));
new_vals = Col Number(Column(dtTOSA, (newcol << get name)));
If(orig_vals == new_vals,
dtTOSA << Delete Columns(colname);
newcol << set name(colname);
,
dtTOSA << Delete Columns(newcol)
);
);
如果我从一开始就这样做,但仍然使用与你类似的想法,我会(至少在这一秒)做这样的事情
Names Default To Here(1);
dt = New Table("Untitled",
Add Rows(4),
Compress File When Saved(1),
New Column("Column 1",
Numeric,
"Continuous",
Format("Best", 12),
Set Values([1, 2, 3, 4])
),
New Column("Column 2", Character, "Nominal", Set Values({"A", "1", "2", "3"})),
New Column("Column 3", Character, "Nominal", Set Values({"1", "2", "3", "4"})),
New Column("Column 4", Character, "Nominal", Set Values({"A", "B", "C", "D"}))
);
wait(1); // demo purposes
cols = dt << Get Column Names("Character", "String"); // We only need to check character columns
For Each({colname}, cols,
dt_summary = dt << Summary(
Group(Eval(colname)),
Freq("None"),
Weight("None"),
Link to original data table(0),
output table name("Summar of " || colname),
Private
);
orig_vals = Col Number(Column(dt_summary, 1));
Column(dt_summary, 1) << Set Data Type("Numeric");
new_vals = Col Number(Column(dt_summary, 1));
Close(dt_summary, no save);
If(orig_vals == new_vals, // we can change
Column(dt, colname) << Set Data Type("Numeric");
Column(dt, colname) << Set Modeling Type("Continuous");
);
);
我倾向于使用 Transform Each 和 Num 来完成这些操作,但有时让 JMP 进行转换可能就足够了(但有些情况下您可能不想这样做...,请参见脚本索引中的 Num() 以及它如何与 Num("5%") 一起运行。<< Restrict 可避免这种行为)
回复:不一致的可接受语法雅尔莫,
谢谢你的想法。我有时会有数千列和数万行,速度可能是一个问题。我现在的做法实际上将时间缩短到了我以前做法的 1/3 左右,但仍然需要几分钟。我可以尝试一下你的方法,但我想知道你认为每次制作汇总表会更慢还是更快。
关于处理列引用,我尝试使用 Column(dt, colname),但并不总是有效。但在您的脚本中,您还使用了 As Column()。一般来说,As Column() 是否用于公式?
回复:不一致的可接受语法
@pcarroll1写道:
通常来说,As Column() 是否用于公式?
是的,要么作为 column(columnname) ,要么作为:column(dt, columnname)[]
回复:不一致的可接受语法对于第一个“问题”:如果您尝试针对特定用例优化速度,则只需尝试哪种方法最有效,因为存在许多不同的方法,最快的方法可能在很大程度上取决于您的数据。在这种情况下,我使用汇总表(或汇总)来避免检查重复值,但就像我提到的那样,这取决于您的数据、您的 JMP 版本,以及可能还有其他因素……
对于列引用;我的规则是:始终使用列名和表引用作为起点,并从它们构建最终引用。我认为引用取决于我在何处以及如何使用它。例如,如果我正在构建一个公式,我希望我的公式以 :mycol 的形式引用列(而不是 AsColumn(mycol) 或 Column(mycol)[],这通常也可以)。我可以使用相当长的 NameExpr(AsColumn(dt,colname)) 样式和表达式求值来获取 :mycol 格式。通常,AsColumn(dt, colname)(或 dt:colname)与 Column(dt, colname)[Row()](或 Column(dt, colname)[])相同,后者将获取当前行的值(在我看来并非总是这样。例如,在平台中,我认为 :mycol 是一个引用,而不是某一行上的特定值...)。
举例来说,这将返回缺失值,因为 mycol 将被立即评估,并且 JMP 中的初始 Row() 值为 0(这不是公式中的问题,因为 Row() 将一直更新)
Names Default To Here(1);
dt = open("$SAMPLE_DATA/Big Class.jmp");
colname = "age";
mycol = NameExpr(AsColumn(dt,colname));
show(mycol)
这是列引用最难理解的区别,例如 JMP 中的 Column()(当前行的值)与Column()(对列的引用) 。很难理解它们的工作原理……我基本上是通过多次失败才学会的。现在,我很清楚在构建表达式时应该首先使用哪个(我有时可能仍然会出错)。我希望在某个时候写一篇博客文章来介绍这个主题,因为我的观点(目前)可能与其他人不同,而且我认为自己是相当优秀的 JMP 脚本编写者,尽管不是 JMP(JSL)开发人员。
这里需要注意一点:我不是 JMP 开发人员,我甚至不关心开发人员如何看待 JSL 的工作方式(我确实不时与开发人员意见不合)。我的所有答案始终基于我使用过的 JMP 版本的经验。
如果这篇文章有点令人困惑,我很抱歉(我今晚可能喝了几杯啤酒:))。如果您希望进一步了解我对这些主题的看法,您可以发帖至此主题(但请用@标记我,这样我会更容易注意到)。如有必要,您也可以给我发送私信。