Рассчитать, сколько стандартных отклонений значения определенных ключей от среднего - PullRequest
0 голосов
/ 19 сентября 2018

Я работаю в Javascript / React с массивом объектов, содержащих спортивные данные.

Вот пример данных, с которыми я работаю:

const mydata = [
  { name: "Tom", year: 2018, statA: 23.2, statB: 12.3 },
  { name: "Bob", year: 2018, statA: 13.2, statB: 10.1 },
  { name: "Joe", year: 2018, statA: 18.2, statB: 19.3 },
  { name: "Tim", year: 2018, statA: 21.1, statB: 21.3 },
  { name: "Jim", year: 2018, statA: 12.5, statB: 32.4 },
  { name: "Nik", year: 2017, statA: 23.6, statB: 23.8 },
  { name: "Tre", year: 2017, statA: 37.8, statB: 18.3 },
  { name: "Ton", year: 2017, statA: 15.3, statB: 12.1 },
  { name: "Bil", year: 2017, statA: 32.2, statB: 41.3 },
  { name: "Geo", year: 2017, statA: 21.5, statB: 39.8 }
];

Моя проблема манипулирования данными здесь очень сложная, и я изо всех сил.Мне нужно масштабировать (чтобы обозначить 0, stdev 1) по годам каждый из нескольких ключей в моих данных (statA, statB).

Например, глядя на значения для year === 2018 в столбце statA, мы имеем [23.2, 13.2, 18.2, 21.1, 12.5].В качестве теста, подключение этого вектора к функции scale () R дает следующее:

scale(c(23.2, 13.2, 18.2, 21.1, 12.5))

           [,1]
[1,]  1.1765253
[2,] -0.9395274
[3,]  0.1184989
[4,]  0.7321542
[5,] -1.0876511
attr(,"scaled:center")
[1] 17.64
attr(,"scaled:scale")
[1] 4.72578 

... поэтому в моем исходном массиве объектов значение statA: 23.2 в первом объекте должно быть обновлено как1.1765, поскольку значение 23.2 на 1.1765 стандартных отклонений выше среднего для всех других значений statA, где Год == 2018. В моем полном наборе данных у меня ~ 8K объектов и ~ 50 ключей в каждом объекте, ~ 40 из которых мне нужно масштабироватьпо годам.

На высоком уровне, я думаю, мне нужно (1-е) вычислить среднее и st dev для каждого показателя за каждый год, и (2-е) использовать среднее и st dev для этого показателя для этогогод, и сопоставьте его с его масштабированным значением.Производительность / скорость важны для моего приложения, и я беспокоюсь, что обычный цикл for будет очень медленным, хотя сейчас я пытаюсь это сделать.

Любая помощь с этим приветствуется!

РЕДАКТИРОВАТЬ 2:

прежде чем я прочитал / закодировал на своем конце, хотел опубликовать то, что я закончил вчера:

    const scaleCols = ['statA', 'statB'];
    const allYears = [...new Set(rawData.map(ps => ps.Year))];

    // loop over each year of the data
    for(var i = 0; i < allYears.length; i++) {

        // compute sums and counts (for mean calc)
        thisYearsArray = rawData.filter(d => d.Year === allYears[i])
        sums = {}, counts = {};
        for(var j = 0; j < thisYearsArray.length; j++) {
            for(var k = 0; k < scaleCols.length; k++) {
                if(!(scaleCols[k] in sums)) {
                    sums[scaleCols[k]] = 0;
                    counts[scaleCols[k]] = 0;
                }

                sums[scaleCols[k]] += thisYearsArray[j][scaleCols[k]];
                counts[scaleCols[k]] += 1;
            }
        }

        console.log('sums', sums)
        console.log('counts', counts)
    }

... какя сказал не очень хорошо.

Редактировать: Помогут ли в этом функции масштабирования d3?

Ответы [ 3 ]

0 голосов
/ 19 сентября 2018

Как программист D3, я рад видеть другой ответ , использующий шкалу D3 (особенно потому, что вопрос изначально не был помечен ).Однако, как ответчик уже намекнул , вам здесь не нужна шкала D3, что является излишним.

Все, что вам нужно, это (value - mean) / deviation:

var result = arr.map(d => (d - mean) / deviation);

Вот демоверсия:

var arr = [23.2, 13.2, 18.2, 21.1, 12.5];
var deviation = d3.deviation(arr)
var mean = d3.mean(arr)

var result = arr.map(d => (d - mean) / deviation);

console.log(result)
<script src="https://d3js.org/d3.v5.min.js"></script>

Кроме того, два соображения:

  1. "На высоком уровне, я думаю, что я должен (1-й)вычислите среднее значение и стандартное отклонение для каждого показателя за каждый год, и (2-е) используйте среднее значение и стандартное отклонение для этого показателя за этот год ": это верно, вы не можете рассчитать, сколько стандартных отклонений составляет значение от среднего значенияпрежде чем знать стандартное отклонение и среднее значение, которое вы можете знать только зацикливание всего массива в первую очередь.Поэтому вы не можете делать то, что хотите, итерируя по массиву данных менее 2 раз.
  2. "Производительность / скорость важна для моего приложения, и я беспокоюсь, что обычный цикл for будет оченьslow ": сейчас все немного по-другому, но до недавнего времени ничто не могло сравниться с циклом for в отношении производительности.Итак, то, что вы называете обычным циклом, обычно является самым быстрым решением.
0 голосов
/ 19 сентября 2018

Хотя я считаю себя поклонником d3, я думаю, что добавление тега к этому вопросу было скорее красной селедкой.Два других ответа совершенно хороши тем, что они дают правильные результаты, но отстают, когда дело доходит до производительности.Поскольку это был основной аспект вашего вопроса, я бы хотел добавить к этому свои два цента.Я думаю, что было бы полезно реализовать вычисления, придерживаясь Vanilla-JS.

Глядя на реализацию из d3.deviation(), можно заметить, что это простотонкая обертка вокруг d3.variance(), вычисляющая квадратный корень из дисперсии.Изучение реализации последней напоминает о двух вещах:

  1. Код использует защиту для защиты от undefined и NaN значения:

    Этот метод игнорирует неопределенные значения и значения NaN;это полезно для игнорирования пропущенных данных.

    Если вы можете быть уверены, что в ваших данных нет пропущенных значений, вы можете безопасно избавиться от этих дорогих проверок.

  2. Пока вычисляет дисперсию, среднее значение рассчитывается как побочный эффект:

    delta = value - mean;
    mean += delta / ++m;
    sum += delta * (value - mean);
    

    Вы можете использовать это для возврата как дисперсии, так и среднего значения после одного циклаваши данные.

Кроме того, d3.mean() также использует те же меры защиты против NaN или undefined значений, что и d3.variance(),Последовательный вызов обоих методов, конечно, означает, что эти проверки также будут выполняться дважды для каждого значения.

Заимствуя из собственной реализации d3, решение этой проблемы может быть реализовано по следующим направлениям:

function meanAndDeviation(values) {
  const len = values.length;
  let i = 0;
  let value;
  let mean = 0;
  let sum = 0;
  while (i<len) {
    delta = (value = values[i]) - mean;
    mean += delta / ++i;
    sum += delta * (value - mean);
  }

  return { mean, deviation: Math.sqrt(sum / (i - 1))};
}

Взгляните на следующую демонстрацию:

function meanAndDeviation(values) {
  const len = values.length;
  let i = 0;
  let value;
  let mean = 0;
  let sum = 0;
  while (i<len) {
    delta = (value = values[i]) - mean;
    mean += delta / ++i;
    sum += delta * (value - mean);
  }
  
  return { mean, deviation: Math.sqrt(sum / (i - 1))};
}

const arr = [23.2, 13.2, 18.2, 21.1, 12.5];
const {mean, deviation} = meanAndDeviation(arr);

const result = arr.map(d => (d - mean) / deviation);

console.log(result);

Согласен, деструктуризация возвращаемого объекта - не самая производительная часть кода, но так как он вызывается только один раз, когда мне нравится его читаемость.

0 голосов
/ 19 сентября 2018

Вы можете достичь того же результата (что и шкала R), создавая непрерывную шкалу d3 .См фрагмент ниже.

var arr = [23.2, 13.2, 18.2, 21.1, 12.5];
var deviation = d3.deviation(arr)
var mean = d3.mean(arr)

var scale = d3.scaleLinear()
   .domain([mean-deviation, mean+deviation])
   .range([-1, 1]);
   
var result = arr.map(el => scale(el));

console.log(result)
   <script src="https://d3js.org/d3.v5.min.js"></script>
...