/* Tsong 1996.jsl 07Apr2017 Copyright (c) 2017 by SAS Institute Inc., Cary, NC 27513, USA. All rights reserved. Note: please read the disclaimer at the end of this script. Purpose This script implements the comparison procedure in Tsong, et al, 1996, "Statistical Assessment of Mean Differences Between Two Dissolution Data Sets," Drug Information Journal, Vol. 30, pp. 1105--1112. Author Diane K. Michelson (SAS Institute) Contact di.michelson@sas.com Usage Simply run this script by any one of these methods: Edit > Run Script Control-R Click "Run Script" button in tool bar Future Improvement Ideas Calculate confidence region limits. */ //choose data table numTables = N Table(); lstTables = List(); lstTables[1] = "None of these, launch the OPEN TABLE dialog."; For( iTable = 1, iTable <= numTables, iTable++, lstTables[iTable + 1] = Data Table( iTable ) << get name ); nwTable = New Window( "Choose a data table", Text Box( "Choose a data table containing columns for analysis." ), cbTable = Combo Box( lstTables ), bbGo = Button Box( "OK", exprGo ) ); exprGo = Expr( If( (cbTable << get selected) == "None of these, launch the OPEN TABLE dialog.", dt = Open(), dt = Data Table( cbTable << get selected ) ); //pick grouping column, pick analysis columns nw1 = New Window( "Select Columns", Text Box( "Select grouping column. Select analysis columns." ), H List Box( Panel Box( "Select Columns", colListData = Col List Box( all ) ), Panel Box( "Cast Selected Columns into Roles", Lineup Box( N Col( 2 ), Button Box( "Grouping Column", colListGroup << append( colListData << get selected ); groupCol = colListGroup << get items; ), colListGroup = Col List Box( minitems( 1 ), maxitems( 1 ), nlines( 1 ) ), Button Box( "Analysis Columns", colListY << append( colListData << get selected ); yCol = colListY << get items; ), colListY = Col List Box( numeric, minitems( 2 ), nlines( 5 ) ) ) ), Panel Box( "Action", Button Box( "OK", exprOK ), Button Box( "Cancel", nw1 << close window() ), Text Box( " " ), Button Box( "Remove", colListGroup << remove selected; colListY << remove selected; ), Button Box( "Help", exprHelp ) ) ) ); nwTable << close window(); ); exprOK = Expr( //pick reference and group lstGroups = Associative Array( Column( dt, groupCol[1] ) << get values ) << get keys; nw1 << close window(); nw2 = New Window( "Select groups", Text Box( "Select reference and test groups in column " || (Column( dt, groupCol[1] ) << get name) || "." ), H List Box( Panel Box( "Select Groups", lbGroups = List Box( lstGroups ) ), Panel Box( "Identify Reference and Test Groups", Lineup Box( N Col( 2 ), Button Box( "Reference Group", lbRef << append( lbGroups << get selected ); refGroup = lbRef << get items; ), lbRef = List Box( {}, max selected( 1 ), nlines( 1 ) ), Button Box( "Test Group", lbTest << append( lbGroups << get selected ); testGroup = lbTest << get items; ), lbTest = List Box( {}, max selected( 1 ), nlines( 1 ) ) ) ), Panel Box( "Action", Button Box( "OK", exprOK2 ), Button Box( "Cancel", nw2 << close window() ), Text Box( " " ), Button Box( "Remove", lbRef << remove selected; lbTest << remove selected; ) ) ) ); ); exprOK2 = Expr( //setup ref = (lbRef << get items)[1]; test = (lbTest << get items)[1]; colGroup = Parse( ":" || (Column( dt, groupCol[1] ) << get name) ); dt << clear select; dt << select where( colGroup == ref ); dtREF = dt << subset( selected rows ); dt << clear select; dt << select where( colGroup == test ); dtTEST = dt << subset( selected rows ); dt << clear select; matREF = J( N Row( dtREF ), N Items( yCol ), . ); For( iCol = 1, iCol <= N Items( yCol ), iCol++, matREF[0, iCol] = (Column( dtREF, yCol[iCol] ) << get values) ); matTEST = J( N Row( dtTEST ), N Items( yCol ), . ); For( iCol = 1, iCol <= N Items( yCol ), iCol++, matTEST[0, iCol] = (Column( dtTEST, yCol[iCol] ) << get values) ); //find mean and cov of reference group meanREF = J( 1, N Items( yCol ), 0 ); For( iRow = 1, iRow <= N Row( matREF ), iRow++, meanREF = meanREF + matREF[iRow, 0] ); meanREF = Transpose(meanREF / N Row( matREF )); sREF = Covariance( matREF ); //find mean and cov of test group meanTEST = J( 1, N Items( yCol ), 0 ); For( iRow = 1, iRow <= N Row( matTEST ), iRow++, meanTEST = meanTEST + matTEST[iRow, 0] ); meanTEST = Transpose(meanTEST / N Row( matTEST )); sTEST = Covariance( matTEST ); sPOOL = (sREF + sTEST) / 2; //find distance of test group to reference group //dm = sqrt(round((meanTEST-meanREF),2)*round(inverse(sPOOL),2)*round((meanTEST-meanREF),2)`); meanDIFF = meanTEST - meanREF; p = N Items( yCol ); dm = Sqrt( Transpose(meanDIFF) * Inverse( sPOOL ) * meanDIFF ); //calculate f2 mu_inprod = Transpose(meanDIFF)*meanDIFF; f2 = 50*log10(100/sqrt(1+mu_inprod/p)); //find confidence region, lamda = 0 n1 = N Row( matREF ); n2 = N Row( matTEST ); k = n1 * n2 * (n1 + n2 - p - 1) / ((n1 + n2) * (n1 + n2 - 2) * p); df2 = n1 + n2 - p - 1; Fp = F Quantile(0.90,p,df2,0); y1p = meanDIFF*(1 + sqrt(Fp/(k*dm*dm))); y2p = meanDIFF*(1 - sqrt(Fp/(k*dm*dm))); Dupper = max(sqrt(Transpose(y1p)*Inverse(sPOOL)*y1p),sqrt(Transpose(y2p)*Inverse(sPOOL)*y2p)); Dlower = min(sqrt(Transpose(y1p)*Inverse(sPOOL)*y1p),sqrt(Transpose(y2p)*Inverse(sPOOL)*y2p)); //find critical distance for shift of 10% relative to reference group critDIFF = J( N Items( yCol ), 1, 10 ); dmc = Sqrt( Transpose(critDIFF) * Inverse( sPOOL ) * critDIFF ); //find confidence region, lambda > 0 ncp = n1 * n2 / (n1 + n2); y = k*dm*dm; root = F noncentrality(ncp, p, df2, 0.10); d = sqrt(root/(k*sum(Inverse(sPOOL)))); //display results nw2 << close window(); Close( dtREF, nosave ); Close( dtTEST, nosave ); nw3 = New Window( "Results", Text Box( "Results of comparison of " || test || " to " || ref || "." ), Table Box( String Col Box( "Quantity", {"n1", "n2", "p", "k", "Dm", "Dlower", "Dupper", "Dmc", "d", "f2"} ), Number Col Box( "Value", {n1, n2, p, k, dm[1], Dlower, Dupper, dmc[1], d, f2[1]} ) ) ); ); /* Revision History (date, change, person) 07Apr2017, created, Di Michelson 09Feb2018, extended, Piet & Sven */ /* Disclaimer by SAS Institute Inc. License Agreement for Corrective Code or Additional Functionality SAS INSTITUTE INC. IS PROVIDING YOU WITH THE COMPUTER SOFTWARE CODE INCLUDED WITH THIS AGREEMENT ("CODE") ON AN "AS IS" BASIS, AND AUTHORIZES YOU TO USE THE CODE SUBJECT TO THE TERMS HEREOF. BY USING THE CODE, YOU AGREE TO THESE TERMS. YOUR USE OF THE CODE IS AT YOUR OWN RISK. SAS INSTITUTE INC. MAKES NO REPRESENTATION OR WARRANTY, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE, WITH RESPECT TO THE CODE. The Code is intended to be used solely as part of a product ("Software") you currently have licensed from SAS or one of its subsidiaries or authorized agents ("SAS"). The Code is designed to either correct an error in the Software or to add functionality to the Software, but has not necessarily been tested. Accordingly, SAS makes no representation or warranty that the Code will operate error-free. SAS is under no obligation to maintain or support the Code. Neither SAS nor its licensors shall be liable to you or any third party for any general, special, direct, indirect, consequential, incidental or other damages whatsoever arising out of or related to your use or inability to use the Code, even if SAS has been advised of the possibility of such damages. Except as otherwise provided above, the Code is governed by the same agreement that governs the Software. If you do not have an existing agreement with SAS governing the Software, you may not use the Code. (SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. ® indicates USA registration. Other brand and product names are registered trademarks or trademarks of their respective companies.) */ ;