こんにちは、
私は jmp (現在 jmp 16 を使用) を初めて使用し、次のシナリオについて質問があります。 各欠陥に対して次の列を持つ csv ファイルがあります。
- タイムスタンプ、シリアル ID、x 位置、y 位置、欠陥のサイズ (mm)
私はクリーニング プロセスに取り組んでおり、検査ツールは 1 日のすべての欠陥に関する情報を提供します。 追跡する必要があることがいくつかあります。
1) 特定のシリアル ID が処理された回数と、それが最初にクリーニングされたのか、2 回目、3 回目などかを調べます。
2) シリアル ID が複数回クリーニングされている場合、各クリーニング後に (x,y) 位置の組み合わせのいずれかが同じ場所にあるかどうかを知る必要があります。
x と y の位置をグラフ化し、時間でフィルタリングすると、2) に答えることができましたが、これは非常に手動の手順です。 追跡する必要がある情報を自動化できるjslを使用したソリューションを見つけたいと思っています。
好きクレイジ・ヘイルズソリューションですが、過去に同様のタスクに直面したため、ソリューションも提供したいと考えています。
私のアプローチは、利用可能なデータを使用して追加情報を計算し、これをデータ テーブルに追加することでした。その後、最終結果を示すレポート (グラフ ビルダー、集計など) を生成することができました。
リクエストでは、特定のシリアル ID が処理された回数を計算できるようにしたいと考えています。また、ウェーハの最終/最終処理からのデータ エントリを識別するインジケータ列を作成すると非常に便利であることがわかりました。
「mock data.PNG」のサンプル データに基づいて、次のようにします。
- ウェーハが処理されるたびに連続番号を計算する
- 各ウェーハが処理された最終時間を計算する
- おおよその位置に基づいて名前で欠陥位置を特定する
- 各ウェーハについて、各場所の名前が特定された回数を計算する
これを実現するために、tabulate を使用していくつかの一時データ テーブルを生成し、数式を追加して必要な回答を得てから、それらを元のデータ テーブルにマージします。
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 座標を小数点以下 1 桁に丸めました。 これはニーズに十分である場合とそうでない場合がありますが、このアプローチにより、X_Temp と Y_Temp の式を変更でき、残りは期待どおりに機能します。
結果のデータ テーブルには、次の追加の列が含まれます。
- ウェーハ クリーン カウント
- ウェーハ上で実行された洗浄回数の連続番号が含まれています
- 最終ウェーハ洗浄
- エントリーがウエハーが処理された最後の時間でない場合はゼロであり、ウエハーが処理された最後の時間である場合は 1 のインジケーター列
- 位置
- ウェーハ位置カウント
- この欠陥位置名が特定のウェーハで発生した連続回数が含まれています
結果として得られるデータ テーブルを使用すると、質問したことなどに対処することができます。 調査できる可能性のある質問には、次のようなものがありますが、これらに限定されません。
- 複数回のクリーニングサイクル後も残っている欠陥の場所
- 最初のウェーハ洗浄サイクル後の全体的な欠陥率は?
- すべての洗浄サイクルが実行された後の全体的な不良率は?
- ウェーハあたりの平均洗浄サイクル数は?
- クリーニングが必要な欠陥領域が一貫して存在する領域がありますか?
これが助けになることを願っています。
ここにいくつかのスターターコードがあります。 期待どおりに動作することを確認するには、それを研究する必要があります。 出力はログ ウィンドウにあります。 (ファイルを添付します。Web サイトが「無効な 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 までの整数であるか、最も近い .01 に丸められた 0 から 100 までの浮動小数点値である場合や、解像度に明確な制限がない浮動小数点数である場合があります。 x,y のペアがどれだけ近いと繰り返しになりますか? 不完全さのサイズは、x、y のオーバーラップと関係がありますか? または、サイズは常に x、y グリッドよりも小さく、無視されますか?
x 値と y 値が 100 個しかなく、サイズが重要でない場合は、x 値と y 値を単一のキー (x*100+y) に結合すると、繰り返しのテストが容易になる可能性があります。 . 有効数字が約 6 桁までの丸められた浮動小数点数についても同じかもしれません。
しかし、サイズが重要な場合、または距離をテストする必要がある場合は、KDTable を使用することがあります。
また、何列ですか?数千未満はおそらく特別な最適化を必要としません。 100 万を超えると、よく考え抜かれたアルゴリズムが必要になるでしょう。 (1,000 行未満では、すべての行が 1 行おきに考えることができ、比較は 1,000,000 しかありませんが、サイズは 2 倍になり、比較は 4 倍になります。)