A procedure for adjusting a table of data cells such that
they add up to selected totals for both the columns and rows of the table
Aaron D. Schroeder
Starting table and target margins
seedTable = matrix(c(6,6,3,8,10,10,9,10,9,3,14,8),ncol=3,byrow=TRUE)
goalRowMargs = c(20,30,35,15)
goalColMargs = c(35,40,25)
Support Functions
Equal Margins
Function to check for equality of margins
equalMarginals = function(rowMs, colMs){sum(rowMs) == sum(colMs)}
No Zeros
Function to fill cells with vlaue 0 with 0.001
noZeros = function(aMatrix){replace(aMatrix, aMatrix == 0, .0001)}
Cell Functions
Functions to calculate cell values during an iteration
rowCellAdj = function(prevCellVal, prevRowSum, goalRowSum){prevCellVal/prevRowSum*goalRowSum}
colCellAdj = function(prevCellVal, prevColSum, goalColSum){prevCellVal/prevColSum*goalColSum}
Function for a single iteration of IPF
Returns a list of two tables, a row adjusted table and a column adjusted table
iterTable = function(inTable, goalRowMargs, goalColMargs){
# adjust rows
rowAdjTable = matrix(nrow = nrow(inTable), ncol = ncol(inTable))
for (r in 1:nrow(inTable)){
prevRowSum = sum(inTable[r,])
goalRowSum = goalRowMargs[r]
for (c in 1:ncol(inTable)){
prevCellVal = inTable[r,c]
newCellVal = rowCellAdj(prevCellVal, prevRowSum, goalRowSum)
rowAdjTable[r,c] = newCellVal
}
}
# adjust columns
colAdjTable = matrix(nrow = nrow(rowAdjTable), ncol = ncol(rowAdjTable))
for (c in 1:ncol(rowAdjTable)){
prevColSum = sum(rowAdjTable[,c])
goalColSum = goalColMargs[c]
for (r in 1:nrow(rowAdjTable)){
prevCellVal = rowAdjTable[r,c]
newCellVal = rowCellAdj(prevCellVal, prevColSum, goalColSum)
colAdjTable[r,c] = newCellVal
}
}
resultTables = list()
resultTables[[1]] = rowAdjTable
resultTables[[2]] = colAdjTable
resultTables
}
IPF function
Sets intial values and iteration parameters - returns a proportionally-fitted table
ipf = function(seedTable, goalRowMargs, goalColMargs, accuracy = .0001, maxiter = 50){
if (equalMarginals(goalRowMargs, goalColMargs)){
seedTable = noZeros(seedTable)
iter = 0
checkDif = 1
i = iterTable(seedTable, goalRowMargs, goalColMargs)
while((checkDif > accuracy) && (iter < maxiter)){
i = iterTable(i[[2]], goalRowMargs, goalColMargs)
rowMaxDif = max(abs(rowSums(i[[2]]) - goalRowMargs))
colMaxDif = max(abs(colSums(i[[1]]) - goalColMargs))
checkDif = max(rowMaxDif, colMaxDif)
iter = iter + 1
}
round(addmargins(i[[2]]), 2)
}
}
ipf(seedTable, goalRowMargs, goalColMargs)
9.14 | 7.75 | 3.11 | 20.00 |
10.30 | 10.92 | 8.77 | 30.00 |
13.34 | 12.57 | 9.09 | 35.00 |
2.21 | 8.76 | 4.02 | 15.00 |
35 | 40 | 25 | 100 |