你好,
我是 jmp 的新手(目前使用 jmp 16)並且對以下場景有疑問。 我有一個 csv 文件,每個缺陷都有以下列:
- 時間戳、序列號、x 位置、y 位置、缺陷大小(以毫米為單位)
我在清潔過程中工作,檢查工具會提供當天每一個缺陷的信息。 我需要跟踪幾件事:
1) 處理特定序列 ID 的次數,並確定它是第一次被清理,還是第二次、第三次等。
2) 如果序列 ID 被清洗了不止一次,那麼我需要知道每次清洗後 (x,y) 位置的任何組合是否仍然在同一個位置
繪製 x 和 y 位置圖,然後按時間過濾我可以得到答案 2),但這是一個非常手動的步驟。 我希望找到一個使用 jsl 的解決方案,它可以自動化我需要跟踪的信息。
我喜歡克雷格黑爾斯解決方案,但我也想提供我的解決方案,因為我過去曾面臨過類似的任務。
我的方法是使用可用數據計算附加信息,將其添加到數據表中,然後我能夠生成顯示最終結果的報告(圖形生成器、表格等)。
在您的請求中,您希望能夠計算特定序列號的處理次數。我還發現創建一個指示器列非常有用,該指示器列可識別來自晶圓的最後/最終處理的數據條目。
基於來自“mock data.PNG”的示例數據,我將執行以下操作:
- 計算每次處理晶圓的順序號
- 計算每個晶圓的最終處理時間
- 根據大致位置按名稱識別缺陷位置
- 計算每個晶圓的每個位置名稱被識別的次數
為此,我使用製表法生成了幾個臨時數據表,添加了一些公式以獲得我想要的答案,然後將它們合併回原始數據表。
JSL 代碼看起來像這樣
names default to here(1);
/////////// make example data table /////////////////////
dt = New Table( "example data set",
Add Rows( 13 ),
New Column( "Time",
Numeric,
"Nominal",
Format( "Best", 12 ),
Set Values( [1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4] )
),
New Column( "WaferID",
Numeric,
"Nominal",
Format( "Best", 12 ),
Set Values( [1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 1, 1, 1] )
),
New Column( "X location",
Numeric,
continuous,
Format( "Best", 12 ),
Set Values(
[2.745, 1.566, -28.143, -4.793, -29.251, 19.527, 6.581, 29.37, 4.907,
21.699, 2.745, -4.793, 6.581]
)
),
New Column( "Y location",
Numeric,
continuous,
Format( "Best", 12 ),
Set Values(
[44.164, 61.915, -42.954, -38.091, -46.659, 32.201, -24.715, 41.533,
60.217, -60.26, 44.164, -38.091, -24.715]
)
),
New Column( "Size",
Numeric,
"Continuous",
Format( "Best", 12 ),
Set Values(
[0.164, 0.136, 0.125, 0.122, 0.122, 0.036, 0.035, 0.035, 0.035, 0.034,
0.164, 0.122, 0.035]
)
)
);
//dt = current data table();
/////////// create temporary data table to determine Clean Count and Final Clean /////////////////
tab = dt << Tabulate(
Show Control Panel( 0 ),
Full Path Column Name( 1 ),
Add Table( Row Table( Grouping Columns( :WaferID, :Time ) ) )
);
dtx = tab << make into data table();
tab << close window();
dtx << New Column("Wafer Clean Count", Numeric, "Nominal", Formula(Col Rank(:Time, :WaferID)));
dtx << New Column("Final Wafer Clean", Numeric, "Nominal", Formula(If(:Time == Col Maximum(:Time, :WaferID), 1, 0)));
dtx << delete Column("N");
///////// move Clean Count and Final Clean information back to original data table //////////////
dt << Update(
With( dtx ),
Match Columns( :WaferID = :WaferID, :Time = :Time ),
Add Columns from Update Table( :Wafer Clean Count, :Final Wafer Clean ),
Replace Columns in Main Table( :Wafer Clean Count, :Final Wafer Clean )
);
//////// close data table //////////////////////////////////////////////////////
close(dtx, no save);
//////// modify X & Y values to allow variation in measurements////////////////
dt << new column("X_Temp", numeric, continuous, formula(round(:X location, 1)));
dt << new column("Y_Temp", numeric, continuous, formula(round(:Y location, 1)));
/////// create temporary data table to name defect locations ///////////////
tab = dt << Tabulate(
Show Control Panel( 0 ),
Include missing for grouping columns( 1 ),
Full Path Column Name( 1 ),
Add Table( Row Table( Grouping Columns( :X_Temp, :Y_Temp ) ) )
);
dtx = tab << make into data table();
tab << close window();
dtx << new column("Location", character, nominal, formula("L" || char(row())));
dtx << delete column("N");
//////// move Location information back to original data table ////////////////
dt << Update(
With( dtx ),
Match Columns( :X_Temp = :X_Temp, :Y_Temp = :Y_Temp ),
Add Columns from Update Table( :Location ),
Replace Columns in Main Table( :Location )
);
/////// clean up /////////////////////////
close(dtx, no save);
dt << delete columns(
{
"X_Temp",
"Y_Temp"
}
);
/////// calculate the number of times a defect location occured on a given wafer ////////
dt << new column("Wafer Location Count", numeric, nominal, formula(Col Rank( :Time, :Location, :WaferID )));
/////// make some reports /////////////
nw = new window("results",
h list box(
gb = dt << Graph Builder(
Show Control Panel( 0 ),
Variables(
X( :X location ),
Y( :Y location ),
Group X( :Wafer Clean Count ),
Group Y( :WaferID ),
Color( :Wafer Location Count )
),
Elements( Points( X, Y, Legend( 7 ) ) )
),
tab = dt << Tabulate(
Show Control Panel( 0 ),
Include missing for grouping columns( 1 ),
Full Path Column Name( 1 ),
Add Table(
Column Table( Statistics( Max ), Analysis Columns( :Wafer Location Count ) ),
Row Table( Grouping Columns( :WaferID, :X location, :Y location ) )
),
Local Data Filter(
Add Filter(
columns( :Final Wafer Clean ),
Where( :Final Wafer Clean == 1 )
)
)
)
)
);在命名缺陷位置時,我將 x 坐標和 y 坐標四捨五入到小數點後一位,以允許位置測量值發生一些變化。 這可能會或可能不會滿足您的需求,但這種方法允許您修改 X_Temp 和 Y_Temp 的公式,其餘的仍將按預期工作。
生成的數據表包含以下附加列:
- 晶圓清潔計數
- 最終晶圓清潔
- 如果條目不是最後一次處理晶圓,則指示器列為零,如果是最後一次處理晶圓,則為一
- 地點
- 晶圓位置計數
使用生成的數據表,可以解決您提出的問題等等。 可以調查的一些可能問題包括但不限於:
- 多個清潔週期後哪些缺陷位置仍然存在
- 第一次晶圓清洗週期後的整體缺陷率是多少
- 執行所有清潔週期後的總體缺陷率是多少
- 每個晶圓的平均清潔週期數是多少
- 是否有區域始終存在需要清潔的缺陷區域
我希望這有幫助。
這是一些入門代碼。 您需要對其進行研究以確保它符合您的預期。 輸出在日誌窗口中。 (我會附上一個文件。下面的代碼可能會搞砸,因為該網站刪除了一些“無效的 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 ) ) ) )
);
x,y 位置有什麼樣的分辨率? 例如,它們可能始終是從 1 到 100 的整數,或者它們可能是從 0 到 100 的浮點值,四捨五入到最接近的 .01,或者它們可能是沒有明顯分辨率限制的浮點數。 x,y 對必須有多接近才能重複? 缺陷的大小與 x,y 重疊有什麼關係嗎? 或者尺寸總是小於 x,y 網格並被忽略?
如果只有 100 個 x 和 100 個 y 值,並且大小不感興趣,那麼我會將 x 和 y 值組合成一個鍵,也許是 x*100+y,這樣可以更容易地測試重複. 圓形浮點數可能相同,最多約 6 位有效數字。
但如果大小很重要,或者必須測試距離,我可能會使用 KDTable。
還有,多少行?少於幾千個可能不需要任何特定的優化。 超過一百萬可能需要一個深思熟慮的算法。 (在一千行以下,每一行都可以考慮每隔一行,只有 1,000,000 次比較,但大小加倍,比較四倍。)