您應該循環遍歷數據表還是使用重新編碼,或者使用 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);
還可以選擇使用列屬性值標籤和缺失值代碼,這在某些情況下可能會起作用。 我喜歡盡可能使用它們,因為這樣您就不會更改數據。