The function below calculates a single respondent-level weight variable such that when applied as a weight, the marginal distribution of multiple elements yields the desired proportions. This can be useful to ensure that the results of a survey sample reflect the population distribution.
Add this function within a Protobi data process:
/**
* Calculate a single respondent-level weight such that when weighting is applied
* multiple variables yield the desired marginal distribution
* @param protobi Protobi instance with elements and data preset
* @param name Name of weight variable to calculate within protobi.data()
* @param targets Object specifying target marginal distributions for one or more elements
* { <key> : { <val>: <proportion>, <val>:<proportion> } }
*/
Protobi.calculate_rim_weights = function(protobi, name, targets) {
var rows = protobi.data() // assume rows have already been set
var weights = {}
rows.forEach(function(row) {
row.weight = 1;
})
protobi.setGlobalWeight(name)
// iterate 10 times. We could get fancier about convergence but good enough
for (var i=0; i<10; i++) {
// iterate over each attribute with weighting criteria
_.each(targets, function(vals, key) {
var marginal = protobi.getMarginal(key);
weights[key] = weights[key] || {}
for (var val in vals) {
var tgt = vals[val]
var wgt = _.get(weights, [key, val]) || 1
_.set(weights, [key, val], wgt * tgt / marginal.getPercent(val));
}
// now apply these weights so available for the next variable
rows.forEach(function(row, idx) {
row[name] = 1;
_.each(targets, function(vals, key) {
var dim = protobi.getDimension(key)
var val = dim.getValue(row)
if (dim.groupFn) val = dim.groupFn(val)
var factor = _.get(weights, [key, val]) || 1
row[name] *= factor
})
})
})
}
// normalize final weights
var sum_weight = rows.reduce(function(sum, row) {return sum + row[name]}, 0)
rows.forEach(function(row) {row[name] =rows.length * row[name] / sum_weight; })
return weights;
}
The below example demonstrates how to apply the above function in data process. This program retrieves the data and elements for the project, calculates the weights and saves the data.
Protobi.get_tables(["main", "OE"], function(err, data) {
if (err) return callback(err);
Protobi.get_elements(function(err, protobi) {
if (err) return callback(err);
var rows= data["main"] //primary data file
protobi.setData(rows);
Protobi.calculate_rim_weights(
protobi,
'weight', {
"s3":{
"1":0.49,
"2":0.51
},
"s2":{
"0":0.84,
"1":0.16
},
"region":{
"Northeast":0.17,
"Midwest":0.21,
"West":0.24,
"South":0.38,
}
})
return callback (null, rows)
})
})