Оптимизация скрипта листов Google, который принимает массив в качестве входных данных и выполняется в течение ок. 200 ячеек - PullRequest
0 голосов
/ 02 июля 2019

Я настраиваю довольно сложную таблицу Google и пытаюсь автоматизировать некоторую рутинную интерполяцию с помощью скрипта.Теперь у меня есть скрипт, который работает, но я хочу его оптимизировать.

Позвольте мне кратко описать установку с некоторыми (простыми) примерами данных:

          A             B         C           D              E
1    Lookup date       Result           Recorded date     Value
2    17/8/2018           -               31/12/2018         210
3    31/12/2018         210              31/3/2019          273
4    14/2/2019          241.5            12/6/2019          411
5    31/3/2019          273
6    12/6/2019          411
7    1/7/2019           411

В этом примере яимеют небольшое количество записанных значений (столбцы D и E), и я хочу вычислить значение для любой даты (столбец A). Столбец B является выводом скрипта.Проблема в том, что мой сценарий очень медленный, занимает много времени на моем ноутбуке (иногда мне приходится обновлять страницу) и никогда не выполняется полностью на моем iPad.

Я думаю, что частью этого может быть объемзапросы: я запускаю этот скрипт для примерно 200 ячеек в моем листе.

Я кратко объясню сценарий (полный код JavaScript ниже).

Он создает пользовательскую функцию getvalue (x, y, lookupdate) , который для заданного x-диапазона (столбец D) y-диапазона (столбец E) и «даты поиска» (например, A4) вернет правильный результат (например, B4).Этот результат является либо:

  • пустым, если дата поиска происходит до первой записанной даты
  • точное значение, если дата поиска равна записанной дате
  • с интерполяциейзначение, если дата поиска находится между двумя записанными датами
  • окончательное записанное значение, если дата поиска выходит за пределы диапазона записанных дат

Теперь я несколько оптимизировал это.В моей реализации я фактически запускаю его как массив для 100 ячеек в столбце A (только некоторые из которых действительно должны запускать сценарий).У меня есть другая простая система, которая в основном автоматически заполняет дату в столбце A в виде двоичного флага, чтобы сказать, что скрипт должен быть запущен.Поэтому, используя ISBLANK () в качестве переключателя, моя формула массива для ячеек B3: B103:

=ArrayFormula(IF(ISBLANK(A3:A103),"",getvalue(D:D,E:E,A3:A103)))

Несмотря на то, что массив охватывает 100 ячеек, только около 50 из них «активируются» с датой вСтолбец, поэтому только около 50 из них действительно должны запускать функцию getvalue.Однако, как последнее усложнение, я фактически делаю это, чтобы вычислить четыре разных значения для каждой "даты поиска", запустив четыре разных массива в четырех столбцах, так что я говорю, что скрипт запускается прибл.200 раз.

Вот мой настоящий сценарий:

function getvalue(x, y, lookupdate) {

/// LOOKUP AN ARRAY  
  if (lookupdate.map) {
    return lookupdate.map(function(v) {
      return getvalue(x, y, v);
    });
  }


/// GET RID OF EMPTY CELLS IN COLUMN
  var xf = x.filter(function(el) {
    return el != ""; 
  });

  var yf = y.filter(function(el) {
    return el != "";
  });

/// GET RID OF HEADER ROW
  xf.shift()
  yf.shift()

/// SAVE THE FIRST AND LAST VALUES
  var firstx = xf[0][0]
  var firsty = yf[0][0]
  var lastx = xf[xf.length - 1][0]
  var lasty = yf[yf.length - 1][0]

/// FIGURE OUT WHAT TO RETURN
  if (lookupdate < firstx) {
      return "";
  } else if (lookupdate.valueOf() == firstx.valueOf()) {
      return firsty;
  } else if (lookupdate > lastx) {
      return lasty;
  } else {
      var check = 0, index;
      for(var i = 0, iLen = xf.length; i < iLen; i++) {
          if(xf[i][0] == lookupdate) {
              return yf[i][0];
              } else {      
              if(xf[i][0] < lookupdate && ((xf[i][0] - check) < (lookupdate - check))) {
              check = xf[i][0];
              index = i;
               }
           }
       }
       var xValue, yValue, xDiff, yDiff, xInt;
       yValue = yf[index][0];
       xDiff = xf[index+1][0] - check;
       yDiff = yf[index+1][0] - yValue;
       xInt = lookupdate - check; 

       return (xInt * (yDiff / xDiff)) + yValue;
    }
}

Сообщение об ошибке на iPad - просто ячейки никогда не проходят мимо «Загрузка ...», а на ноутбуке это занимаетнамного дольше, чем ожидалось.

Самое запутанное, что я думаю, что стало еще хуже, так как я настроил его как массив.Ранее у меня было это, где все 400 ячеек выполняли бы проверку ISBLANK (), а затем для приблизительно 200 сработавших ячеек они индивидуально запускали сценарий.Это по крайней мере будет загружать на iPad.Я читал здесь и в Общей поддержке Google , что сценарии будут выполняться намного быстрее, если вы выполняете пакетные операции, но, похоже, он стал медленнее после перехода от 200 отдельных ячеек к 4 массивам.

Нужно ли оптимизировать это дальше, или есть какая-то другая причина, по которой он может зависнуть на моем iPad?Можно ли даже оптимизировать его и сделать это за один вызов, а не за 4 массива?

1 Ответ

0 голосов
/ 04 июля 2019
  1. Учет дела иначе если (lookupdate.valueOf() == firstx.valueOf()) return firsty; лишнее, потому что оно уже покрыто if(xf[i][0] == lookupdate)
  2. (xf[i][0] - check) < (lookupdate - check) можно упростить до xf[i][0] < lookupdate
  3. Вы используете чистый код JavaScript, но имейте в виду, что в App Script есть много дополнительных функций, которые удобны при работе с электронной таблицей. https://developers.google.com/apps-script/reference/spreadsheet/ Так, например, для запуска вашей функции только для заполненных функций диапазона, таких как getDataRange() или getRange() в сочетании с getNextDataCell() и getLastRow(), будет очень полезно для вас.
  4. Главный важный момент - функциональность вашего скрипта. Предполагаете ли вы, что между Recorded date и value существует приблизительно линейная зависимость и, таким образом, интерполируете значение для незаписанных дат? В этом случае наиболее статистически точным способом (и программно самым простым) будет вычисление вашего наклона между первым и последним x и y соответственно. То есть: Result=first_y+((y_last-y_first)/(x_last-x_first)*(Lookup_Date-first_x))

Если этот подход вам подходит, ваш код упростится и будет выглядеть в App Script примерно так:

function myFunction() {
  var ss=SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var Result_Range=ss.getRange("A2:B")
  var limit=Result_Range.getNextDataCell(SpreadsheetApp.Direction.DOWN).getLastRow()
  var Result_values=Result_Range.getValues();
  var valueRange=ss.getRange("D1:E");
  var values=valueRange.getValues();
  var last_Index=valueRange.getNextDataCell(SpreadsheetApp.Direction.DOWN).getLastRow()
  var last_y=values[last_Index-1][1];
  var last_x=values[last_Index-1][0].valueOf();
  var first_y=values[1][1];
  var first_x=values[1][0].valueOf();
  var slope=(last_y-first_y)/(last_x-first_x);

  for(var i=1;i<limit;i++)
  {  
    Result_Range.getCell(i,2).setValue(first_y+(slope*(Result_values[i-1][0].valueOf()-first_x)))
    Logger.log(i)
    Logger.log(Result_values[i][0].valueOf()-first_x)
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...