您是否应该循环遍历数据表或使用重新编码,或使用获取行位置来更改数据表中的值最近的社区讨论用于在整个表中查找数值并将其替换为缺失值的脚本提供了一个非常好的条目@SDF1被选为解决方案
names default to here(1);
dt=current data table();
For( i = 1, i <= N Rows( dt ), i++,
For( l = 1, l <= N Cols( dt ), l++,
If( Column( l )[i] == 999,
Column( l )[i] = .
)
)
);
当我查看解决方案时,我的脑海中突然浮现出一条古老的教导,粗略地说是:“不要循环遍历数据表。循环很慢。 使用 JMP 内置功能。”
于是我开始修改@SDF1代码。
我确定“Get RowsWhere()”的后备标准会更快。毕竟,在“非常快速”的 Get RowsWhere() 找到行之后,它允许使用一个赋值语句来设置所有找到的行。
我见过的另一个解决方案推社区推荐的是 JMP 中的 Recode 工具。
所以:
假设:Get RowsWhere 和 Recode 的内置 JMP 函数将比向下循环并遍历整个数据表更快。
我选择了 Semiconductor Capability 数据表,该表大小合适,有 132 列和 1455 行
我编写了一个小脚本,将每列中的 10 个随机值设置为 999。
names default to here(1);
dt=current data table();
for(i=5,i<=ncols(dt),i++,
if(col stddev(ascolumn(dt,i))!=0,
for(k=1,k<=10,k++,
column(dt,i)[randominteger(1,1455)]=999;
)
));
Names Default To Here( 1 );
dt = Current Data Table();
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
dt << Recode Column(
As Column( dt, i ),
{If( _rcNow == 999, ., _rcNow )},
Target Column( As Column( dt, i ) )
)
);
show(hptime()-start)
这是上面提到的讨论中要更改的原始值。999 将被脚本更改为缺失 (.)。
方法:
- 启动 JMP 16。
- 读入半导体能力数据表
- 运行脚本以设置每列中的 999 个值
- 对 3 种方法中的每一种重复上述 2 个步骤,并将时间重新编码为毫秒。
- 对 JMP15 重复上述步骤
这是重新编码的脚本
Names Default To Here( 1 );
dt = Current Data Table();
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
dt << Recode Column(
As Column( dt, i ),
{If( _rcNow == 999, ., _rcNow )},
Target Column( As Column( dt, i ) )
)
);
show(hptime()-start)
获取行的代码
names default to here(1);
dt=current data table();
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[dt<<GET rows="" where=""></GET>
这是令人惊讶的结果

For 循环轻而易举地获胜。从结果来看,应谨慎使用 Get RowsWhere。
欢迎大家批评指正......
数据混合和清理质量和工艺工程回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值
@jthi写道:
在这方面可能有点太深入了(但至少有了一些想法,如果我必须的话,以后如何进行这种类型的测试)。
我用不同的表达式编写了某种测试脚本来测试用缺失替换 999 的不同方法。
....
我刚用过@jthi的脚本:
https://community.jmp.com/t5/Discussions/Should-you-Loop-through-a-data-table-or-use-Recode-or-use-Get/mp/381766/highlight/true#M63232
使用 Jmp (16.2 & ) 17.1 在系统上重现不同方法的性能。
我强调了一些方法(包括洛克)现在必须在里面开始/结束数据更新*)
摆脱 999 百分比分数增加带来的严重时间增加。
因素 2 的差异可能是由不同的计算机造成的。
谁有 Jmp 15 和 17 的系统 - 可以直接比较?
*)尽管子集是“不可见”打开的。

回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值@hogi写道:
...
如果用户意识到问题,一个非常便宜的解决方案是添加一个评估()
便宜的关于 JSL 代码的复杂性与时间损失。
但对于较长的桌子评估(i)再次大幅损失替换(...,NameExpr(作为列()))) 。
所以:仍然相当快(与原始版本相比:),但没有我预期的那么快。
为什么? 根本的区别是什么?
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值嗯,也许可以将 JSL 切换到用户友好模式(如果这会带来更好的性能,则会预先评估变量)?
希望:获取行其中:速度问题
如果用户意识到这个问题,便宜的解决方案是添加一个评估():
start = HP Time();<BR />for(i=5,i<= ncols(dt),i++,<BR />dt << get rows where( As Column( dt, Eval(i) ) == 999 );<BR />);<BR />Show( (HP Time() - start) / 1000000 );start = HP Time();
i = 5;
dt << get rows where( As Column( dt, Eval(i) ) == 999 );
Show( (HP Time() - start) / 1000000 );
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值你好@ron_horne ,问题在于获取行,其中,所以速度应该/独立于这些设置。
另一方面,如果列索引是显式的或者它是一个变量(即使该变量具有指定的值),则会产生巨大的差异。
这对我来说并不清楚——我发现这次谈话真是太好了!
我不知道有多少 jsl 代码可以比 ~ N(数据表行)更快...
这个问题可以通过 JSL 后台发生的事情来解决吗?
或者 JSL 架构中是否有一个基本的东西指定这样的行为是有意的并且不能改变?
如果是这样:警告消息可以帮助编码人员不要陷入这个问题......
Names Default To Here( 1 );
dt = Open( "$SAMPLE_DATA/Semiconductor Capability.jmp" );
For( k = 1, k <= 4, k++,
dt << Concatenate( dt, dt, dt, Append to first table )
);
nuke = Function( {},
For( k = 1, k <= 10, k++,
Column( dt, 5 )[Random Integer( 1, 1455 )] = 999
)
);
//dt << show window(0);
//dt<< Begin Data Update();
nuke();
start = HP Time();
i = 5;
dt << get rows where( As Column( dt, i ) == 999 );
Show( (HP Time() - start) / 1000000 );
nuke();
start = HP Time();
dt << get rows where( As Column( dt, 5 ) == 999 );
Show( (HP Time() - start) / 1000000 );
//dt << End Data Update();
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值谢谢@tx尼尔森
您能估计一下在不同方法上使用不可见和私有表的效果吗?它们必须是修改任何脚本以提高性能的最快方法。
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值哇,多棒的把戏啊:)
我担心出现问题缺失值代码一旦您尝试,就会开始:
Col Maximum(:NPN3)
[据我们所知排除行必须是排除在 Col 中手动聚合(或者更好:通过通过...分组争论)。]
但令人惊讶的是:上校聚合尊重缺失值代码
挑剔一点: OP问题原帖的内容相当含蓄:
并且从结果来看,获取行的位置应谨慎使用。
欢迎大家批评指正......
译为——我的肚子里有一种不安的感觉——为: “到底为什么?!?”
谢谢@ErraticAttack为了解决这个难题:)
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值这是一次非常有趣且内容丰富的讨论! 精彩的答案。 我确实没有资格在这里添加任何有价值的东西,所以我将诉诸“作弊”。
OP 问题是关于用缺失值替换数据中的 999 值,因为 999 是缺失值代码,我们不希望将它们包含在计算中。 这根本不符合这个非常有趣的问题的精神,但我的贡献是,利用缺失值代码列属性比对数据进行任何替换更快,并且可以为*大多数*目的实现相同的效果:)
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
Column(dt,i) << Set Property( "Missing Value Codes", {999} )
);
Show( HP Time() - start);
在我的 m1 Mac 上约为 0.007 秒。
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值啊,我不知道:
:姓名()行为就像作为列() ?
从脚本索引中不太清楚:)
所以,与代替和作为列()代替Eval(解析(Eval Insert(JSL Quote(name("^names[i]^"))这是:
start=hptime();
for(i=5,i<= ncols(dt),i++,
Eval(Substitute(Expr(column(dt,i)[dt<<GET rows="" where=""></GET>
之后这个令人惊讶的发现我几乎感到惊讶的是获取行,其中如果将数字比较替换为字符比较,速度也会很快:)
start=hptime();
for(i=5,i<= ncols(dt),i++,
Eval(Substitute(Expr(column(dt,i)[dt<<GET rows="" where=""></GET>
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值是的我明白@brady_brady 。 然而,人们可以这样做:
NamesDefaultToHere(1);
// Make an example table with some rogue '999' values
nr = 10; // Number of rows
nc = 10; // Number of columns
p = 0.1; // Fraction of rogue values
val = 999; // Rogue value
m = J(nr, nc, RandomInteger(1, 10));
m = Shape(m, nr*nc, 1);
m[RandomIndex(nr*nc, Floor(nr*nc*p))] = val;
m = Shape(m, nr, nc);
dt = AsTable(m);
dt << setName("Rogue Values");
Wait(3);
// Set rogue values to '.'
mask = J(nr*nc, 1, 1);
mask[Loc(dt[0,0] == val)] = .;
mask = Shape(mask, nr, nc);
dt[0,0] = mask :*</img> dt[0,0];
但我非常怀疑这会比你已经拥有的更快,尤其是因为元素明智的乘法。
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值这里的比较有点错误 - “获取行位置”功能将评估每一行的表达式,因此您需要在运行循环之前最大限度地减少任何多余的查找 - 特别是,您的迭代器是外部的,并且它必须进行一些范围步行才能在每一行找到它。此外,它将评估每行的 AsColumn 函数,每次查找“dt”和“i”。这_非常_贵。正确的方法如下:
names default to here(1);
dt=current data table();
start=hptime();
names = dt << Get Column Names( "string" );
for(i=5,i<= ncols(dt),i++,
Eval( Parse( Eval InserT( JSL Quote(
column(dt,i)[dt<<GET rows="" where=""></GET>
您会注意到,它的执行速度快了 3000 倍,这应该可以与您拥有的其他方法相媲美(如果不是更好的话)
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值是的......障碍是,虽然可以使用单个索引访问矩阵(假定行主布局),但数据表不能。 因此,遗憾的是没有类似的
m[loc(m, 9999)] = .
其中 m 是数据表世界中的 pxq 矩阵...因此上面的代码中这一行之前和之后的行。 虽然代码仍然很快,但如果表可以使用单索引访问,那么速度会非常快。
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值为了 '创建矩阵的开销'我怀疑数据表下标可能有帮助。@Craige_Hales会知道。
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值这是一种避免循环的方法,尽管创建矩阵并在之后分配回表会产生开销。 这种方法对此数据的执行速度很快,与 loc_2d 大致相同,但随着表变得巨大,我预计其相对性能会受到影响,但我尚未验证这一点。
expr_mat = expr(
m = dt[0, 5 :: N Col( dt )];
m[Loc( m, 999 )] = .;
dt[0, 5 :: N Col( dt )] = m;
);
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值在这方面可能有点太深入了(但至少有了一些想法,如果我必须的话,以后如何进行这种类型的测试)。
我用不同的表达式编写了某种测试脚本来测试用缺失替换 999 的不同方法。 我使用两个数据集(Semiconductor Capability.jmp 和 Probe.jmp)进行了测试,并更改了列中缺失值的数量。
Loc() 替换值的速度最快(这并不奇怪)。 Missing Value Codes()“总是”很快,但与 Loc() 相比,它的用例更有限,并且如果需要替换多个值,则使用起来会更麻烦。

最快的最终结果是 loc_2d (不必费心更改变量名称:将 Loc 与 2D 矩阵结合使用)。 仍然需要循环来获取值集(至少据我所知),所以最有可能比 Loc() 更快的原因是它会跳过一些列。 下面是expr_loc2d和expr_loc:
expr_loc = Expr(
for(i=5,i<= ncols(testData),i++,
column(testData,i)[Loc(testData[0,i], 999)]=.;
);
);
expr_loc_2d = Expr(
mat = testData[0,5::NCols(testData)];
nr = N Rows(mat);
nc = N Cols(mat);
blacklocs = Loc(mat, 999) - 1;
rowlocs = Floor((blacklocs) / nc ) + 1;
collocs = Mod((blacklocs), nc ) + 1 + 4;
uniqCols = Associative Array(collocs) << get keys;
For(i_2d = 1, i_2d <= N Items(uniqCols), i_2d++,
testData[rowlocs[Loc(collocs, uniqCols[i_2d])], uniqCols[i_2d]] = .;
);
);
此外,使用 AsColumn(Column(dt, i)) 似乎很重要@瑞安吉尔摩说(甚至没有费心测试 << get rows where without it,因为它会非常慢),但我确实测试了使用和不使用 Column() 的重新编码。 我还注意到我在重新编码中错过了一个 Column() ,因此运行几个快速测试(未附加在数据或脚本中):
这三者之间的区别相当大:
expr_recode_ascolumn = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(dt, i),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(testData, i))
)
);
);
expr_recode_ascolumn_column = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(dt, i),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(Column(testData, i)))
)
);
);<BR />
expr_recode_ascolumn_column_column = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(Column(dt, i)),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(Column(testData, i)))
)
);
);

附上测试脚本和一些结果数据。 脚本很可能会出现一些错误。 如果有人愿意的话,添加新的表达式应该相当容易。 我根本没有测试的一件事是使用 << Begin/End 数据更新。
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值有趣的讨论! 我认为数据表矩阵表示法首次出现在 JMP 13 中,对于赋值来说非常有效。
对于这个特定问题,它几乎比 for 循环快 500 倍(0,003 秒 vs 1.5 秒,JMP 15 Mac 笔记本电脑)。
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
Try( dt[Loc( dt[0, i], 999 ), i] = . )
);
Show( HP Time() - start);
需要 try() 以避免没有“999”的列出现错误。
循环列仍然是必要的,因为对整个矩阵的直接分配(我一度认为这会更快)不起作用。 错误:“多个下标在访问或评估的每个内部级别都需要单个下标......”,仔细想想,这是有道理的。
// The left Data Table matrix syntax is OK on its own, but not for assignment.
dt[0, 5 :: N Col( dt )][Loc( dt[0, 5 :: N Col( dt )], 999 )] = .;
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值我可能在某些时候对使用循环持谨慎态度,但在学习了一门算法课程(我从未完成......)之后,我的警惕性有所降低。 你只需要高效地使用它们,通常你会循环太多(时间复杂度和 Big O 表示法)。 此外,使用循环比尝试用纯数学解决问题要容易得多。
对于 JMP,我通常只使用我在某些时候学到的安全选项,因为速度很少是问题。 但当时间成为问题时(一个应用程序在工作中打开需要 6 分钟以上),我将开始寻找使其更快的方法(设法使其在不到 2 分钟内启动)。 在本例中,我通过将 Loc() 与其他函数的不同组合(删除行、设置行状态)结合使用,设法使其更快。
除了 Loc() 之外,与我长期以来最喜欢的获取列中唯一值的选项(关联数组() << 获取键)相比,使用 Summarize() 似乎“方式”更快。 当行数增加时,Associative array() << get keys 变得越来越慢,而 Summarize() 似乎没有遇到同样的问题,至少没有那么快。 所以我尝试开始更多地使用 Summarize() (永远不知道数据表有多高)。
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值因此,我们跳过 AsColumn() (更多地凭直觉进行,我还没有测试这将替换所有正确的值):
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[Loc(dt[0,i], 999)]=.;
if(mod(i,10)==0,show(i));
);
show(hptime()-start);
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值我同意 Loc() 是最好的方法。
对我来说,速度变慢似乎是因为 As Column 的原因。 我看到之间存在显着差异,
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( column(dt, i) );
);
Show( HP Time() - start );
和
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( dt, i );
);
Show( HP Time() - start );
更改代码中的 As Column 语法@tx尼尔森原来的问题,For 循环仍然是最快的,但其他循环更具可比性。
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值你好@tx尼尔森,
非常有趣的比较,但结果也不是我所期望的。 我记得当我第一次在大学学习编程时,也被教导了类似的东西:要警惕循环,因为它们很慢。
当然,通过一些计算和一些程序,我发现 For 循环非常慢——比其他循环慢。 然而,我并不是一个多么优秀的程序员,我倾向于依靠我知道如何做的事情,这并不总是快速或优雅的。 有时对我来说,循环的逻辑允许我编写一个功能代码来完成我想要它做的事情,这允许我继续下一步。
我发现有趣的是,JMP 16 的 For Loop 和 Recode 选项比 15 稍慢,但 Get Rows 的改进之处在于将时间几乎减少了一半,这是一个重大变化。
谢谢!,
DS
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值或许@Craige_Hales可以为我们提供更快的解决方案。 我从他的社区帖子/博客中得到了使用矩阵计算(尤其是 Loc())的想法。
我制作了一种工作速度更快的工具,它可以打开数据集,然后删除旧数据(基于用户选择)。 最初它使用 << 选择位置并删除行。 我用一些 Loc() 魔法替换了它,它也变得更快了。 我最近非常喜欢 Loc() 。
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值@jthi ,
您的基于矩阵的代码是最快的。
0.299795秒........
回复:您是否应该循环遍历数据表或使用重新编码,或使用获取行在何处更改数据表中的值我在工作中一直在对我们拥有的一些旧脚本进行一些优化。 我必须更改的一些内容与获取行的位置有关。 如果我需要快速的 JSL 代码,我已经开始使用越来越多的矩阵运算(主要是 Loc())。
例如,您可以将获取行更改为以下位置(未测试它是否替换正确的值或全部值):
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[Loc(ascolumn(dt,i) << get as matrix, 999)]=.;
if(mod(i,10)==0,show(i));
);
show(hptime()-start);
还可以选择使用列属性值标签和缺失值代码,这在某些情况下可能有效。 我喜欢尽可能使用它们,因为这样您就不会更改数据。