1

Scores in this datset in sheets are calculated using a weighted average of 4 variables in columns B:E. The weighted average is the same for each line.

enter image description here

Given several lines of scores and variables, how can the weights be discovered in excel or sheets?

Average weight function in sheets is:

AVERAGE.WEIGHTED(B2,a,C2,b,D2,c,E2,d)

Possible approaches:

  1. Solver in Excel or Sheets
  2. An approach similar to linear optimization function as in this StackOverflow discussion
  3. Essentially, the solution I am thinking of automates a process of guessing (through linear regression or another mechanism)

I attempted to set this up using Solver in Excel, but I am not sure that it can be set up to solve this or if my lack of familiarity with solver which is also available in sheets contributed.

Thank you in advance for any help. The dataset is shared in this sheet

TheMaster
  • 45,448
  • 6
  • 62
  • 85
MMsmithH
  • 323
  • 1
  • 8
  • 1
    I have updated my answer and provided you with 2 possible ways to solve the weights, using external libraries (e.g. nerdamer) and to solve using GAS's own Linear Optimization Service. See updated answer. Note that you need at least N rows of data where N is equal to the number of variables/headers/columns you have. (e.g. A, B, C, D needs at least 4 rows of data) – NightEye Jun 09 '22 at 07:27

1 Answers1

3

You can use Google Apps Script and import nerdamer library. See the setup below:

Setup:

  1. Create separate gs files containing these libraries.

NOTE: Follow the order or you will have errors

order

  1. Use the script below:
function solveWeights() {
 
  // Get data, process, then outputs on the actual column header.
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  const vars = sheet.getLastColumn() - 1;

  let data = sheet.getRange(1, 1, vars + 1, vars + 1).getValues();
  let headers = data.shift();

  let solution = [nerdamer.solveEquations(data.map(row => {
    return `${row[0]}=${row.slice(1).map((x, i) => x + headers[i + 1]).join('+')}`;
  })).toString().split(',').filter((_, i) => i % 2 == 1).map((x, i) => `${headers[i + 1]} = ${x}`)]

  sheet.getRange(1, 2, solution.length, solution[0].length).setValues(solution);
}

Sample data:

sample

Output:

output

Note:

  • You can add offset to your output range so that it won't overwrite your columns.
  • The preparation of multiple libraries slows the execution so it might take a while, but should take around 5 seconds to be done (using 4 column/variable data)

EDIT:

  • If you don't want to use external libraries, you can use LinearOptimizationService. See sample below:
function solveWeight() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  const vars = sheet.getLastColumn() - 1;

  // fetch N rows based on how many variables are present
  const data = sheet.getRange(1, 1, vars + 1, vars + 1).getValues();
  let headers = data.shift();
  headers = headers.slice(1);

  let engine = LinearOptimizationService.createEngine();
  // add variable
  headers.forEach(header => {
    // set the range of the weights' possible values (e.g. 0 - 100)
    engine.addVariable(header, 0, 100);
  });

  // create constraint
  data.forEach(row => {
    let sum = row.shift();
    let constraint = engine.addConstraint(sum, sum);
    // set all values as coefficients
    row.forEach((value, index) => {
      constraint.setCoefficient(headers[index], value);
    });
  });

  // get lowest possible values
  engine.setMinimization();

  let solution = engine.solve();
  if (!solution.isValid())
    Logger.log('No solution: ' + solution.getStatus());
  else 
    headers.forEach(header => {
      // workaround due to rounding issue of javascript
      console.log(header + ' value is: ' + solution.getVariableValue(header).toFixed(2))
    });
}

Output:

output

Note:

  • Downside to this approach is that you need to declare what the range of the weights, and other variables such as the coefficient per weight and etc. The above code assumes all weights are between 0 - 100.
  • Code on Linear Optimization service only logs the data, you can modify this to be a custom function instead or just write it directly to the sheet.

Reference:

NightEye
  • 10,634
  • 2
  • 5
  • 24
  • 1
    Thank you this a great answer! What happens if 1. weights of one of the variables is 0 (ex 3 of 4 variables make up the weighted average the 4th variable is actually not part of it). 2. The variables provided are insufficient to determine a weighted average (ex a 5th variable is missing)? – MMsmithH Jun 09 '22 at 13:02
  • @MMsmithH, you're welcome. Regarding your questions, 1. it should also show 0. 2. If a cell under a column is blank, then at least place a 0 on it, or the processing of data above should be modified and catch that case, then exclude it from the calculation of weights. – NightEye Jun 09 '22 at 14:47
  • @MMsmithH, before i forget. Even though this is obvious, it should be mentioned that this only works if the weights on all rows are the same (or at least the first N rows should be, where N is the number of variables/columns present) – NightEye Jun 09 '22 at 14:51