您应该循环遍历数据表还是使用重新编码,或者使用 Get Rows Where 来更改数据表中的值最近的社区讨论回复:在整个表中查找并用缺失值替换数值的脚本有一个非常好的条目@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 Rows Where()”的后备标准会更快。毕竟,它允许在“非常快”的 Get Rows Where() 找到行之后,使用一个赋值语句设置所有找到的行。
我见过的另一种解决方案推社区推荐的是 JMP 中的 Recode 工具。
所以:
假设:Get Rows Where 和 Recode 的 JMP 函数的构建将比循环遍历整个数据表更快。
我选择了半导体能力数据表作为一个大小合适的表,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;
)
));
这是上述讨论中要更改的原始值。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)
Get Rows Where 的代码
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 Rows Where。
欢迎评论和指正......
数据表脚本回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值是的我明白@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];
但我非常怀疑这会比你已经拥有的更快,尤其是因为元素明智的乘法。
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值你在这里的比较有点错误——“Get Rows Where”功能将评估它在每一行上的表达式,因此你需要在运行循环之前最小化任何多余的查找——特别是,你的迭代器是外部的,它必须进行一些范围步行才能在每一行上找到它。此外,它将评估每一行的 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 倍,这应该可以与您拥有的其他方法相媲美(如果不是更好的话)
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值是的......障碍在于,虽然可以使用单个索引(假定行主要布局)访问矩阵,但数据表不能。 因此,遗憾的是没有类似物
m[loc(m, 9999)] = .
其中 m 是一个 pxq 矩阵,在数据表世界中......因此在上面的代码中前面和后面的行。 虽然代码仍然很快,但如果表可以使用单索引访问,那将是非常快的。
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值为了 '创建矩阵的开销'我怀疑数据表下标可能有帮助。@Craige_Hales会知道。
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值这是一种避免循环的方法,尽管在创建矩阵并在之后分配回表时存在开销。 这种方法在这些数据上执行得很快,与 loc_2d 大致相同,但随着表变得巨大,我预计它的相对性能会受到影响,但我还没有验证这一点。
expr_mat = expr(
m = dt[0, 5 :: N Col( dt )];
m[Loc( m, 999 )] = .;
dt[0, 5 :: N Col( dt )] = m;
);
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值在这方面可能有点太深入了(但如果我必须的话,至少有一些想法如何在以后进行这种类型的测试)。
我用不同的表达式编写了某种测试脚本来测试用缺失替换 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)) 似乎也很重要@Ryan_Gilmore说(甚至没有费心测试 << 在没有它的情况下获取行,因为它会很慢),但我确实测试了使用和不使用 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 数据更新。
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值有趣的讨论! 我认为首先出现在 JMP 13 中的数据表矩阵表示法对于赋值非常有效。
对于这个特殊问题,它几乎比 for 循环快 500 倍(0,003 对 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 )] = .;
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值我可能在某些时候对使用循环持谨慎态度,但在学习了一门算法课程(我从未完成过......)之后,情况就不那么好了。 你只需要高效地使用它们,通常你循环太多(时间复杂度和大 O 表示法)。 使用循环也比尝试用纯数学解决问题要容易得多。
使用 JMP,我通常只使用我在某个时候学到的安全选项,因为速度很少成为问题。 但是当时间成为问题时(一个应用程序在工作中打开超过 6 分钟),我将开始寻找使其更快的方法(设法使其在不到 2 分钟的时间内启动)。 在这种情况下,我设法通过将 Loc() 与其他功能的不同组合(删除行、设置行状态)一起使用来使其更快。
除了 Loc() 之外,与我长期以来最喜欢的在列中获取唯一值的选项(关联数组() << 获取键)相比,使用 Summarize() 似乎“方式”更快。 当行数增加时,Associative array() << get keys 变得越来越慢, Summarize() 似乎没有遇到同样的问题,至少没有那么快。 所以我试图开始更多地使用 Summarize() (永远不知道数据表有多高)。
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值所以我们跳过 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);
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值我同意 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 语法@txnelson原始问题, For 循环仍然是最快的,但其他循环更具可比性。
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值你好@txnelson ,
非常有趣的比较,也不是我预期的结果。 我记得当我第一次开始在大学学习编程时,我也学到了类似的东西:小心循环,因为它们很慢。
当然,通过一些计算和在一些程序中,我发现 For 循环非常慢——比其他循环慢。 不过,我并不是一个出色的程序员,我倾向于依靠我知道如何做的事情,这并不总是快速或优雅的。 有时对我来说,循环的逻辑允许我编写一个功能代码来做我想做的事情,这让我可以继续下一步。
我发现有趣的是,JMP 16 的 For Loop 和 Recode 选项比 JMP 15 稍慢,但 Get Rows Where 通过将时间缩短近一半而得到改进,这是一个重大变化。
谢谢!,
DS
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值也许@Craige_Hales可以让我们更快的解决方案。 我从他的社区帖子/博客中得到了使用矩阵计算(尤其是 Loc())的想法。
我制作了一个更快的工具,它可以打开数据集,然后删除旧数据(基于用户选择)。 最初它使用 << Select Where 和 Delete rows。 我用一些 Loc() 魔法代替了它,它也变得更快了。 我最近非常喜欢 Loc()。
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值@jthi ,
您的基于矩阵的代码是最快的。
0.299795 秒…………
回复:您应该循环遍历数据表还是使用重新编码,还是使用 Get Rows Where 来更改数据表中的值我一直在对我们拥有的一些旧脚本进行一些优化。 我必须更改的一些内容与获取行的位置有关。 如果我需要快速的 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);
还可以选择使用列属性值标签和缺失值代码,这在某些情况下可能会起作用。 我喜欢尽可能使用它们,因为这样您就不会更改数据。