你好,
我對 jmp(目前使用 jmp 16)相當陌生,並且對以下場景有疑問。 我有一個 csv 文件,每個缺陷都有以下列:
- 時間戳、序列號、x 位置、y 位置、缺陷大小(以毫米為單位)
我使用清潔過程,檢查工具會提供當天每一個缺陷的信息。 我需要跟踪幾件事:
1) 處理特定序列號的次數,並確定它是第一次被清除,還是第二次、第三次等。
2)如果序列號被多次清洗,那麼我需要知道每次清洗後(x,y)位置的任何組合是否仍然在同一位置
繪製 x 和 y 位置,然後按時間過濾我可以回答 2) 但這是一個非常手動的步驟。 我希望找到一個使用 jsl 的解決方案,可以自動化我需要跟踪的信息。
x,y 位置具有什麼樣的分辨率? 例如,它們可能總是從 1 到 100 的整數,或者它們可能是從 0 到 100 的浮點值,四捨五入到最接近的 0.01,或者它們可能是對分辨率沒有明顯限制的浮點數。 x,y 對必須多近才能重複? 缺陷的大小是否與 x,y 重疊有關? 或者尺寸總是小於 x,y 網格並被忽略?
如果只有 100 個 x 和 100 個 y 值,並且大小不感興趣,那麼我會將 x 和 y 值組合成一個鍵,也許 x*100+y,這可能會使重複測試更容易. 對於四捨五入的浮點數可能相同,最多約 6 位有效數字。
但如果大小很重要,或者必須測試距離,我可能會使用 KDTable。
還有,幾行?不到幾千可能不需要任何特定的優化。 超過一百萬可能需要一個深思熟慮的算法。 (少於一千行,每一行都可以考慮其他每一行,只有 1,000,000 次比較,但大小加倍,比較四倍。)
你好@SEG
1:嘗試使用Table <<匯總平台。在那裡,只需將序列號、x 位置和 y 位置放入組中。
這將為三者的每個獨特組合提供一個單獨的行。
繪製這個新的匯總表會很有用,只需使用 N 列作為顏色或大小
2:生成一次圖形後,在左上角的紅色三角形下,您可以選擇將腳本保存到數據表或腳本窗口中。 jmp 剛剛為您編寫了腳本。
如果您使用的是 jmp 16,請單擊查看>>日誌,您將獲得您在當前會話中所做的所有操作的腳本。
如果有幫助,請告訴我們。
朗
你好,
您能否通過分享一個示例來幫助我們更好地理解您的數據結構(模擬數據可以正常工作)? 根據您的描述,我推測每個清潔事件都可以通過時間戳和序列號的唯一組合來識別。 此外,您的數據是否包含任何復制信息(即具有相同時間戳和序列 ID 組合的行? 如果是這樣,它將使過程更加苛刻。 最後,在為一個序列 ID 識別多個清潔事件後,您是否只對捕獲所有事件的位置相同、任意兩個事件之間相同或其他情況感興趣?
最好的事物,
TS
你好克雷格
x 和 y 位置在小數點後 3 位。 只要它們在同一位置的 3% 以內,就會被視為重複缺陷。 大小與重疊沒有任何關係。 過去所做的是,一旦將其繪製成圖形,第一個循環就被指定為一種顏色的圓圈,而第二個循環則被指定為另一種顏色的“x”。 一旦它們重疊,它在視覺上很容易看出它是否在同一位置。
就行而言,它可能會達到數千,具體取決於一天的運行量。它從未超過數百萬,儘管如果我們一次看很長一段時間,它是有可能的。
你好,蒂埃里,
我附上了模擬數據的截圖。如您所見,每個 ID 將在同一時間戳記多次點擊,因為所有缺陷都被同時寫入,主要區別在於 x、y 位置和大小。 另請注意,此示例僅是第一次清潔。 第二次,相同的 ID 可能會出現更多或更少的行,這就是我想找到它是否重複並且還能夠識別它的第二次、第三次等清潔循環的時候。
在對序列號進行多次清理後,我想捕獲所有事件中的位置是否相同
這是一些入門代碼。 您需要研究它以確保它符合您的預期。 輸出在日誌窗口中。 (我會附上一個文件。下面的代碼可能會被弄亂,因為網站刪除了一些“無效的 html”。 )
// synthesize some data
radius = 1;
dt = New Table( "Untitled",
Add Rows( 1000 ), // <<<<<<<<<<<<<< change number of rows here
New Column( "time", Numeric, "Continuous", Format( "Best", 12 ) ),
New Column( "waferID", Numeric, "Continuous", Format( "Best", 12 ) ),
New Column( "xLocation", Numeric, "Continuous", Format( "Best", 12 ) ),
New Column( "yLocation", Numeric, "Continuous", Format( "Best", 12 ) )
);
here:time = 0; // for this synth data, time just increments by 1
here:waferID = 0; // and the waferID is random 0-9
For( row = 1, row <= N Rows( dt ), row += 1,
If( Random Uniform() > .8, // determines how many points in a cleaning, before time changes and wafer re-randomizes
here:time += 1;
here:waferID = Random Integer( 0, 9 );
);
dt:time[row] = here:time;
dt:waferID[row] = here:waferID;
dt:xLocation[row] = Round( Random Uniform( -radius, radius ), 2 );
dt:yLocation[row] = Round( Random Uniform( -radius, radius ), 2 );
);
// find the unique waferIDs using summary
dtWafer = dt << Summary( Group( :waferID ), Link to original data table( 1 ) );
dtWaferTime = dt << Summary( Group( :waferID, :time ) );
dtNWafer = dtWaferTime << Summary( Group( :waferID ) ); //number of distinct times for this wafer is number of cleanings
close = .03; // <<< this determines how close makes a hit
// produce a report for each waferID that shows up in the cleaning data...
For( iWafer = 1, iWafer <= N Rows( dtWafer ), iWafer += 1,
dtWafer << clear select << selectrows( iWafer ); // dtWafer selection also selects rows in dt because linked
rows = dt << get selected rows; // all the rows for this wafer, across all times
// rows is a 1-column matrix of row numbers in dt for this wafer. the argument to KDTable
// is all the locations over all cleanings for this wafer.
kdt = KDTable( dt[rows, {xLocation, yLocation}] );
// report header for each waferID... the report might have no lines below the header, or a bunch... Not indented.
Write( Eval Insert( "\!n\!nprocessing waferID=^dtWafer:waferID[iWafer]^, ^nrows(rows)^ points during ^dtNWafer:nrows[iWafer]^ cleanings" ) );
For( irow = 1, irow <= N Rows( rows ), irow += 1, // the rows should be ascending time
row = rows[irow]; // row (singular) is an index to dt
// find rows {up to this many,up to this far} near irow
{index, distance} = kdt << kNearestRows( {dtWafer:NRows[iWafer], close}, irow );
// above, index is into the 1-column matrix of row numbers. below, it becomes index into dt
index = rows[index];
printedHeader = 0; // don't print this header unless there are duplicates to go with it
// distance[] and index[] have same number of rows; index is indexes to near neighbors and distance is to the neighbor
For( idistance = 1, idistance <= N Items( distance ), idistance += 1,
If( dt:waferID[index[idistance]] != dt:waferID[row] | dt:waferID[row] != dtWafer:waferID[iWafer],
Throw( "wafer mismatch" )
);// sanity check
If( distance[idistance] <= close, // typically there is one item with a bigger distance than we care about, must be ignored by else-break below.
If( !printedHeader, // a little indented...
Write(
Eval Insert(
"\!n matches for row ^row^ time ^dt:time[row]^ loc [^char(dt:xLocation[row],7,3)^,^char(dt:yLocation[row],7,3)^]"
)
);
printedHeader = 1;
);
// there can be more than one subsequent cleaning, indented more...
Write( Eval Insert( "\!n row ^index[idistance]^ time ^dt:time[index[idistance]]^ " ) );
Write( Eval Insert( "[^char(dt:xLocation[index[idistance]],7,3)^,^char(dt:yLocation[index[idistance]],7,3)^]) " ) );
Write( Eval Insert( "dist(^distance[idistance]^)" ) );
, //else
Break()
);
);
kdt << removerows( irow ); // never look back at this row from future rows
);
);
// a graph to examine the rows found to be close.
dt << Graph Builder(
Size( 867, 787 ),
Show Control Panel( 0 ),
Variables( X( :xLocation ), Y( :yLocation ) ),
Elements( Points( X, Y, Legend( 5 ) ) ),
Local Data Filter( Add Filter( columns( :waferID ), Modeling Type( :waferID, Nominal ), Display( :waferID, N Items( 14 ) ) ) )
);