可接受的語法不一致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());
在腳本的開頭尋找工具列腳本所知道的內容。
回覆:可接受的語法不一致工具列可能會將 Names Default To Here(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", numeric, 公式(Num( :"2PCS_X(um)"n )));
回覆:可接受的語法不一致另外,在實際腳本中我不會使用 dt 作為變數。我使用更具描述性的變量,例如 dtTOSA。 (見上文)每次我想要處理特定的資料表時,我總是明確地聲明它以避免混淆,就像我在本聲明中所做的那樣。我不明白口譯員怎麼會認為我指的是另一張桌子。
回覆:可接受的語法不一致也許吧,但錯誤訊息表明 JMP 沒有在正確的位置查找“2PCS_X(um)”n。我不確定工具列腳本可以使用什麼上下文。
回覆:可接受的語法不一致克雷格,
我使用了您使用 print 語句的建議,發現它從未到達我認為有問題的行。
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() 是否用於公式?
是的,作為列(列名)或:列(dt,列名)[]
回覆:可接受的語法不一致對於第一個「問題」:如果您嘗試針對特定用例最佳化速度,您只需嘗試哪種方法效果最好,因為有許多不同的方法,而最快的方法可能在很大程度上取決於您的數據。在這種情況下,我使用匯總表(或匯總)來避免檢查重複值,但就像我提到的那樣,它取決於您的資料、JMP 版本以及可能的其他因素...
對於專欄參考;我的規則是:始終使用列名稱和表引用作為起點,並從它們建立最終引用。我認為什麼是參考取決於我在哪裡以及如何使用它。例如,如果我正在建立一個公式,我希望我的公式將列引用為 :mycol (而不是 AsColumn(mycol) 或 Column(mycol)[],它們通常也很好)。我可以使用相當長的 NameExpr(AsColumn(dt,colname)) 樣式和表達式求值來取得 :mycol 格式。一般來說,AsColumn(dt, colname) (OR dt:colname) 與 Column(dt, colname)[Row()] (或 Column(dt, colname)[])相同,它將取得目前行的值(在我認為這並不總是這樣。
例如,這將傳回缺失值,因為 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 版本的經驗。
如果這篇文章有點令人困惑,我很抱歉(我今晚可能喝了幾杯啤酒:))。如果您想對我對這個主題的看法有更多解釋,您可以發佈到這個主題(但用@標記我,這樣我會更容易注意到它)。有需要的也可以私訊我。