關於從原始探測資料產生(提取)資料表的菜鳥問題大家好,這是我在這個社區的第一篇文章。我最近換了工作,將進行大量的探針台測試和數據分析。我們組使用JMP。當我需要弄清楚一些事情時,我發現谷歌總是把我指引到這裡,熱心的專家總是給出專業的答案。這就是我在這裡的原因:)。
現在,我的探測測試通常會產生一個大的 CSV 文件,其中測量數據與不感興趣的資訊(例如測試設定、條件等)隱藏在一起。我希望獲得一些幫助,以使用 JMP 腳本提取有用的信息,並從這裡開始我的高級腳本學習。
我在所附的螢幕截圖中解釋了我的任務。右上角是單一 IV 掃描資料點範例(用於測量 R),讓您了解我的巨大 CVS 的外觀(它包含數百個此類結構)。 「多行不感興趣的資訊」代表那些我不想匯入的無用行。除了複製和貼上 I/V/R 資料部分之外,我還喜歡從一些字串中提取晶圓批次 ID、晶圓 ID、標線(或稱步進欄位)索引和子站點編號。右上角的框顯示了我想要的 JMP 輸出。
作為可選閱讀,底部兩個框中有一些瑣事。左下角是我對資料結構的一般評論,右下角解釋了我需要提取某些資訊的字串的含義。
希望螢幕截圖能為您提供足夠的資訊來解決我的問題。我曾經使用Excel VBA巨集來做這樣的工作:掃描搜尋簽章字串,然後選擇性地導入該位置附近的某些字元/字串/行,然後繼續搜尋直到原始資料檔案的末尾。我想知道 JMP 腳本也會做同樣的事情嗎?
自動化和腳本編寫回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題你好 pmroz,感謝您的及時回覆。我第一次提交時確實忘記了附件,但在一分鐘內,我編輯了帖子並附上了螢幕截圖。你可以再檢查一下。
另外根據您的要求,我在這裡上傳了 Excel。我刪除了大部分無用的信息以使文件乾淨,但您可以複製並貼上左上角的框作為“原始資料”範例。
回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題沒有截圖。是否可以附加一個小型匿名資料集或 .csv 檔案?
回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題您顯示的格式不應該成為讀取的大問題。無趣的資訊行讓我擔心,因為其中的內容可能會幹擾資料的讀取。我的另一個問題是如何讓 JMP 識別包含項目名稱的行。它總是以 Proj 開頭嗎?
提供實際的文件將會非常有幫助。
回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題正如其他人所說,這可能高度依賴您的真實資料格式。以下是使用從 Excel 貼上到文字檔案的資料副本的範例
Names Default To Here(1);
filepath = "$DOWNLOADS/744027.txt";
txt = Load Text File(filepath);
lines = Words(txt, "\!N");
project_pattern = "^(.+)_(\d+\.\d*)(W\d{1,2})_(T\d+)_(.+):(.+)?";
subsite_pattern = "<SUBSITE>(\d+)</SUBSITE>";
datastart_pattern = "^DataName";
data_start = 0;
For Each({line, idx}, lines,
If(!IsMissing(Regex(line, project_pattern)),
matches = Regex Match(line, project_pattern);
projname = matches[2];
lotid = matches[3];
waferid = matches[4];
time = matches[5];
notes = matches[6];
reticles = Words(Trim Whitespace(matches[7]), " ");
, !IsMissing(Regex(line, subsite_pattern)),
subsite = Regex(line, subsite_pattern, "\1");
, !IsMissing(Regex(line, datastart_pattern)),
data_start = idx;
break();
);
);
data_str = Concat Items(lines[data_start::N Items(lines)], "\!N");
dt = Open(Char To Blob(data_str), "text");
dt << New Column("lot", Character, Nominal, Set Each Value(lotid));
dt << New Column("waferid", Character, Nominal, Set Each Value(waferid));
dt << New Column("x", Character, Nominal, Set Each Value(reticles[1]));
dt << New Column("y", Character, Nominal, Set Each Value(reticles[2]));
dt << New Column("subsite", Character, Nominal, Set Each Value(subsite));
dt << Move Selected Columns({:lot, :waferid, :x, :y, :subsite}, To First);
dt << Delete Columns("DataName");
它使用正規表示式來尋找特定行,然後解析它們。對於數據,它會找到它開始的行索引,然後透過讓 JMP 打開它來解析它(根據實際數據,這可能需要額外的步驟)。

回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題- 我忽略了子網站,抱歉。我已將其添加到腳本中。
- 討論論壇的目的是幫助 JMP 使用者解決執行 JMP 時遇到的問題。它不是免費的腳本編寫服務。我為社群成員編寫腳本的原因是幫助他們教導如何使用 JSL。我希望我提供的腳本的接收者能夠花時間向他們學習,這樣他們就能夠解決他們的整個問題。
- 我已將動態列(部分列屬性)變更為數字
- 該腳本現在將每個創建的表附加在一起
- 關於程式碼壓縮......為什麼?如果壓縮的目的使它運行得更快,我可以同意。但是,如果壓縮一段程式碼會使下一個需要查看它的人更難閱讀,那麼程式碼就不是好程式碼。因此,對於您的問題是否可以將程式碼壓縮為更簡潔的格式,我的回答是我確信可以。
names default to here(1);
clear symbols( dtOut );
file = pick file();
dt = Open(
File,
Import Settings(
End Of Line( CRLF, CR, LF ),
End Of Field( Comma, CSV( 0 ) ),
Treat Leading Zeros as Character( 1 ),
Strip Quotes( 1 ),
Use Apostrophe as Quotation Mark( 0 ),
Use Regional Settings( 0 ),
Scan Whole File( 1 ),
Treat empty columns as numeric( 0 ),
CompressNumericColumns( 0 ),
CompressCharacterColumns( 0 ),
CompressAllowListCheck( 0 ),
Labels( 0 ),
Column Names Start( 1 ),
First Named Column( 1 ),
Data Starts( 1 ),
Lines To Read( "All" ),
Year Rule( "20xx" )
)
);
// Change column names to simplify coding
for( i=1,i<=ncols(dt), i++, column(i)<< set name( "C" || char(i)));
// Create a base output table
dtFinal = New Table( "Final" );
// Loop through the data
for(theRow = 1, theRow <= NRows(dt), theRow++,
If( dt:C1[theRow] == "SetupTitle",
memoryProjectName = "";
memoryLotName = "";
memoryWaferID = "";
memoryT0 = "";
memory1strun = "";
memoryIndexX = .;
memoryIndexY = .;
memorySubsite = .;
setupFlag = 1;
projFlag = 0;
// If this is not the first read in table, then append the table
// to the Final table, and close the current dtOut table
If( nrows(dtFinal) != 0 | isEmpty( dtOut ) == 0,
dtFinal << concatenate( dtOut, append to first table(1));
close( dtOut, nosave )
)
,
dt:C1[theRow] == "" & setupFlag == 1,
projFlag = 1;
setupFlag = 0;
,
projFlag == 1 & dt:C1[theRow] != "",
dt:C1[theRow] = trim(dt:C1[theRow]);
memoryProjectName = word(1, dt:C1[theRow], "_");
memoryLotName = word(2, dt:C1[theRow], "_W");
memoryWaferID = word(3, dt:C1[theRow], "_W");
memoryT0 = word(4, dt:C1[theRow], "_W");
memory1strun = word(5, dt:C1[theRow], "_W:");
memoryIndexX = num(word(2, dt:C1[theRow], ": "));
memoryIndexY = num(word(3, dt:C1[theRow], ": "));
memorySubsite = num(word(2, trim(dt:C1[theRow + 2 ]),"<>"));
projFlag = 0;
,
dt:C1[theRow] == "DataName",
colList = {};
col=2;
while( column(dt,col)[theRow] != "",
insert into(colList, column(dt,col)[theRow]);
col++
);
dtOut = New Table( "Lot " || memoryLotName || " WaferID " || memoryWaferID,
new column( "Project Name", character),
new column( "Lot Name", character),
new column( "WaferID", character),
new column( "Time Readout", character),
new column( "Personal Note", character),
new column( "SubSite", numeric, ordinal),
new column( "Index X", numeric, ordinal),
new column( "Index Y", numeric, ordinal)
);
for each( {colName}, colList,
dtOut << new column( eval(colName), numeric, continuous);
);
,
dt:C1[theRow] == "DataValue",
dtOut << Add Rows(1);
dtOut:Project Name[nrows(dtOut)] = memoryProjectName;
dtOut:Lot Name[nrows(dtOut)] = memoryLotName;
dtOut:WaferID[nrows(dtOut)] = memoryWaferID;
dtOut:Time Readout[nrows(dtOut)] = memoryT0;
dtOut:Personal Note[nrows(dtOut)] = memory1strun;
dtOut:SubSite[nrows(dtOut)] = memorysubsite;
dtOut:Index X[nrows(dtOut)] = memoryIndexX;
dtOut:Index Y[nrows(dtOut)] = memoryIndexY;
for each( {colName, index}, colList,
column( dtOut, colName )[nrows(dtOut)] = num(column(dt,index+1)[theRow])
)
)
);
// Close the last work file
close( dtOut, nosave );
// Uncomment the below line if you want the raw table deleted
// close( dt, nosave );
回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題繼續我之前的想法,將文字檔案分成幾個部分,然後解析這些部分(本錄音部分涵蓋了Scripters Club 2024:第 2 節 - 準備用於分析的非結構化傳入資料)。
Names Default To Here(1);
// Test sections seem to always start with a line starting with "SetupTitle"
// and end to same UNLESS it is last section when it ends with
// line of ",,,,,,,,,,,,,,,,,,,,,,,"
TEST_SECTION_START = "SetupTitle, ";
EMPTY_LINE = ",,,,,,,,,,,,,,,,,,,,,,,";
// Section patterns for regex
PROJECT_PATTERN = "^(.+)_(\d+\.\d*)(W\d{1,2})_(T\d+)_(.+): (\d+ \d+)\,+?";
SUBSITE_PATTERN = "<SUBSITE>(\d+)</SUBSITE>";
DATASTART_PATTERN = "^DataName";
find_test_section_lines = function({lines}, {Default Local},
start_idx = 0;
end_idx = 0;
TEST_SECTION_START = TEST_SECTION_START; // JMP "feature", 00075244
TEST_SECTION_END = TEST_SECTION_END; // JMP "feature", 00075244
For Each({line, idx}, lines,
If(Starts With(line, TEST_SECTION_START),
If(start_idx == 0,
start_idx = idx;
,
end_idx = idx;
break();
);
);
);
If(start_idx != 0 & end_idx == 0,
end_idx = N Items(lines);
);
return(Eval List({start_idx, end_idx}));
);
parse_test_section = function({lines}, {Default Local},
data_start = 0;
PROJECT_PATTERN = PROJECT_PATTERN; //JMP "feature", 00075244
SUBSITE_PATTERN = SUBSITE_PATTERN; //JMP "feature", 00075244
DATASTART_PATTERN = DATASTART_PATTERN; //JMP "feature", 00075244
For Each({line, idx}, lines,
If(!IsMissing(Regex(line, PROJECT_PATTERN)),
matches = Regex Match(line, PROJECT_PATTERN);
projname = matches[2];
lotid = matches[3];
waferid = matches[4];
time = matches[5];
notes = matches[6];
reticles = Words(Trim Whitespace(matches[7]), " ");
, !IsMissing(Regex(line, SUBSITE_PATTERN)),
subsite = Regex(line, SUBSITE_PATTERN, "\1");
, !IsMissing(Regex(line, DATASTART_PATTERN)),
data_start = idx;
break();
);
);
data_str = Concat Items(lines[data_start::N Items(lines)], "\!N");
dt = Open(Char To Blob(data_str), "text", invisible);
For Each({colname}, Reverse(dt << Get Column Names("String")), // drop empty columns
If(Col Number(Column(dt, colname)) == 0,
dt << Delete Column(colname);
,
break(); // break on first "ok" column
);
);
// define order and names here
dt << New Column("project", Character, Nominal, Set Each Value(projname));
dt << New Column("lot", Character, Nominal, Set Each Value(lotid));
dt << New Column("waferid", Character, Nominal, Set Each Value(waferid));
dt << New Column("time", Character, Nominal, Set Each Value(time));
dt << New Column("notes", Character, Nominal, Set Each Value(notes));
dt << New Column("x", Character, Nominal, Set Each Value(reticles[1]));
dt << New Column("y", Character, Nominal, Set Each Value(reticles[2]));
dt << New Column("subsite", Character, Nominal, Set Each Value(subsite));
dt << Move Selected Columns({:project, :lot, :waferid, :time, :notes, :x, :y, :subsite}, To First);
dt << Delete Columns("DataName");
return(dt);
);
// Start parsing
filepath = "$DOWNLOADS/Raw data examples_3DP.csv";
txt = Load Text File(filepath);
lines = Words(txt, "\!N");
lines = Filter Each({line}, lines, line != EMPTY_LINE); // drop empty lines
{start, end} = find_test_section_lines(lines);
dt_result = Empty();
While(All(start, end),
cur_testset = Remove From(lines, start, end - 1);
dt = parse_test_section(cur_testset);
If(Is Empty(dt_result),
dt_result = dt;
,
dt_result << Concatenate(
dt,
"Append to first table"
);
Close(dt, no save);
);
{start, end} = find_test_section_lines(lines);
);
dt_result << Show Window(1);

回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題以下腳本讀取範例 CSV 檔案中的所有 3 個項目資料並建立 3 個單獨的資料表。
names default to here(1);
file = pick file();
dt = Open(
File,
Import Settings(
End Of Line( CRLF, CR, LF ),
End Of Field( Comma, CSV( 0 ) ),
Treat Leading Zeros as Character( 1 ),
Strip Quotes( 1 ),
Use Apostrophe as Quotation Mark( 0 ),
Use Regional Settings( 0 ),
Scan Whole File( 1 ),
Treat empty columns as numeric( 0 ),
CompressNumericColumns( 0 ),
CompressCharacterColumns( 0 ),
CompressAllowListCheck( 0 ),
Labels( 0 ),
Column Names Start( 1 ),
First Named Column( 1 ),
Data Starts( 1 ),
Lines To Read( "All" ),
Year Rule( "20xx" )
)
);
// Change column names to simplify coding
for( i=1,i<=ncols(dt), i++, column(i)<< set name( "C" || char(i)));
// Loop through the data
for(theRow = 1, theRow <= NRows(dt), theRow++,
If( dt:C1[theRow] == "SetupTitle",
memoryProjectName = "";
memoryLotName = "";
memoryWaferID = "";
memoryT0 = "";
memory1strun = "";
memoryIndexX = .;
memoryIndexY = .;
setupFlag = 1;
projFlag = 0;
,
dt:C1[theRow] == "" & setupFlag == 1,
projFlag = 1;
setupFlag = 0;
,
projFlag == 1 & dt:C1[theRow] != "",
dt:C1[theRow] = trim(dt:C1[theRow]);
memoryProjectName = word(1, dt:C1[theRow], "_");
memoryLotName = word(2, dt:C1[theRow], "_W");
memoryWaferID = word(3, dt:C1[theRow], "_W");
memoryT0 = word(4, dt:C1[theRow], "_W");
memory1strun = word(5, dt:C1[theRow], "_W:");
memoryIndexX = num(word(2, dt:C1[theRow], ": "));
memoryIndexY = num(word(3, dt:C1[theRow], ": "));
projFlag = 0;
,
dt:C1[theRow] == "DataName",
colList = {};
col=2;
while( column(dt,col)[theRow] != "",
insert into(colList, column(dt,col)[theRow]);
col++
);
dtOut = New Table( "Lot " || memoryLotName || " WaferID " || memoryWaferID,
new column( "Project Name", character),
new column( "Lot Name", character),
new column( "WaferID", character),
new column( "Time Readout", character),
new column( "Personal Note", character),
new column( "Index X", numeric, ordinal),
new column( "Index Y", numeric, ordinal)
);
for each( {colName}, colList,
dtOut << new column( eval(colName), numeric, continuous);
);
,
dt:C1[theRow] == "DataValue",
dtOut << Add Rows(1);
dtOut:Project Name[nrows(dtOut)] = memoryProjectName;
dtOut:Lot Name[nrows(dtOut)] = memoryLotName;
dtOut:WaferID[nrows(dtOut)] = memoryWaferID;
dtOut:Time Readout[nrows(dtOut)] = memoryT0;
dtOut:Personal Note[nrows(dtOut)] = memory1strun;
dtOut:Index X[nrows(dtOut)] = memoryIndexX;
dtOut:Index Y[nrows(dtOut)] = memoryIndexY;
for each( {colName, index}, colList,
column( dtOut, colName )[nrows(dtOut)] = column(dt,index+1)[theRow]
)
)
);
回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題嗨, txnelson ,說得好。為我的懶惰道歉。我知道如果我花時間我可以自己找出答案(從長遠來看這對我有好處),但我感到有壓力盡快得到正確的答案。
當我成為經驗豐富的人時,我會像你們一樣回來幫助新人。我認為這是一個真正的用戶社區,每個人都互相幫助。每個答案都是根據個人需求量身訂製的。 JMP 確實應該激勵這裡的活躍用戶。希望您在該帖子中的回答也能讓其他工程師用戶受益。
回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題你好 特克斯尼爾森,它有效!我確實需要這個腳本來製作下週的報告。謝謝!
同時如果不是太複雜的話可以做一些改進嗎?
1. 子站點資訊(欄)遺失。
//為了幫助未來的觀眾,一個晶圓有多個遮罩版(又稱步進場或晶片);每個十字線都有自己獨特的(索引X,索引Y);每個標線都有許多稱為“子站點”(又稱 subdie)的設備。測量是針對每個子網罩晶圓進行的。

2. 將所有 DP 輸出到單一 JMP 表中
//沒有想太多,我將你的腳本應用到我的完整原始文件(數百個DP...),你可以想像發生了什麼:與無數永遠彈出的JMP窗口戰鬥,並最終通過轉向任務管理器投降。
3. 將資料表部分列屬性從字元變更為數字。
4. 是否可以將編碼壓縮成更簡潔的格式?
//目前的非常直觀,非常適合我作為初學者。但是,將來,更短的程式碼可能會更加用戶友好。如果它很複雜或不再適合初次觀看者,請忘記它。
回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題你好 jthi ,謝謝你!稍後我肯定會花時間學習這個正規表示式函數。
希望這個帖子不僅能為我提供所需的答案(非常感謝),而且還可以作為類似掃描和提取 JMP 任務的起始模板。將保存我未來工作的所有腳本!
為了進一步促進這個主題,我使用此類腳本時最可能出現的問題是Job id 格式一致性。請密切注意如何建構作業ID,不要隨意更改它,否則將導致程式設計失真。舉兩個例子,我的job id標準格式是:Projname_Lot id_wafer id_personal 注意:index X index Y
但是,由於這是一個基準測試,並且我手動命名了所有測試,因此在某些情況下我自己違反了規則並且沒有完全遵循上面的樣式:
1. 遺漏了lot id和wafer id之間的“_”,例如,在某些情況下我輸入了“753367.1W11”,而不是“753367.1_W11”;
2.新增了兩個個人註解而不是一個:例如,當第一次執行後重新測試時,我使用「_1st_redo_」而不是「_2nd_」。由於我使用“_”作為資訊部分分隔符,因此當計數混亂時,它會導致麻煩以及該部分後面的任何內容。
再次感謝 jthi 和 txnelson 幫助我邁出了高級 JMP 腳本編寫的第一步!
回覆:關於從原始探測資料產生(提取)資料表的菜鳥問題根據您的應用程序,您可以透過尋找重複模式而不是依賴於匹配特定行的正則表達式模式來使解析器更加健壯。例如,您的專案名稱看起來位於元資料行之間

根據您可以做出的假設,您可以根據這些元資料和「空」行來提取它,而不是依賴「行以 Proj 開頭」或「行與此正規表示式模式相符」。
回覆:關於從原始探測資料產生(提取)資料表的新手問題你好,Jthi,
不知何故,我在運行此腳本時遇到了這個錯誤。你能指導一下嗎?

回覆:關於從原始探測資料產生(提取)資料表的新手問題有趣的觀點,jthi。會考慮一下。
請注意,該資訊嵌入在作業 ID 行中,主要問題是如何提取訊息,而不是如何定位它。我的教訓是讓「以 Proj 開頭的行」保持一致。非常非常重要。為了再次確認這一點,我們可以新增一個模式驗證腳本並確保提取的資訊在預期之內。 Regex 是一個用於此目的的直覺工具。
回覆:關於從原始探測資料產生(提取)資料表的新手問題您是否定義了 TEST_SECTION_END?
回覆:關於從原始探測資料產生(提取)資料表的新手問題是的,我使用的是您之前發布的相同腳本。

回覆:關於從原始探測資料產生(提取)資料表的新手問題啊.. 無論出於什麼原因,它們都從我的腳本中刪除了,您必須確定 TEST_SECTION_END。快速查看它可能與 EMPTY_LINE 相同。