journal.push({
events: events,
squirreclass="underline" didITurnIntoASquirrel
});
}
Каждый вечер, часов в десять – а иногда и назавтра утром, спускаясь с верхней полки шкафа – он записывает свой день.
addEntry(["работа", "тронул дерево", "пицца", "пробежка", "телевизор"], false);
addEntry(["работа", "мороженое", "цветная капуста", "лазанья", "тронул дерево", "почистил зубы"], false);
addEntry(["выходной", "велик", "перерыв", "арахис", "пивасик"], true);
Как только у него будет достаточно данных, он собирается вычислить корреляцию между его оборачиваниями и событиями каждого из дней, и в идеале узнать из их корреляций что-то полезное.
Корреляция – это мера зависимости между переменными величинами (переменными в статистическом смысле, а не в смысле JavaScript). Она обычно выражается в виде коэффициента, принимающего значения от -1 до 1. Нулевая корреляция обозначает, что переменные вообще не связаны, а корреляция 1 означает, что они полностью связаны – если вы знаете одну, вы автоматически знаете другую. Минус один также означает прочную связь переменных, но и их противоположность – когда одна true, вторая всегда false.
Для измерения корреляции булевских переменных хорошо подходит коэффициент фи (ϕ), к тому же, его сравнительно легко подсчитать. Для этого нам нужна таблица, содержащая количество раз, когда наблюдались различные комбинации двух переменных. К примеру, мы можем взять события «поел пиццы» и «обращение» и представить их в следующей таблице:
ϕ можно вычислить по следующей формуле, где n относится к ячейкам таблицы:
n01 обозначает количество измерений, когда первое событие (пицца) – false (0), а второе событие (обращение) – true (1). В нашем примере n01 = 4.
Запись n1• обозначает сумму всех измерений, где первое событие было true, что для нашего примера равно 10. Соответственно, n•0 – сумма всех измерений, где событие «обращение» было false.
Значит, для таблицы с пиццей числитель формулы будет 1×76 - 9×4 = 40, а знаменатель – корень из 10×80×5×85, или √340000. Получается, что ϕ ≈ 0,069, что довольно мало. Непохоже, чтобы пицца влияла на обращения в белку.
Вычисляем корреляцию
Таблицу 2×2 можно представить массивом из четырёх элементов ([76, 9, 4, 1]
), массивом из двух элементов, каждый из которых является также двухэлементным массивом ([[76, 9], [4, 1]]
), или же объектом со свойствами под именами "11"
или "01"
. Но для нас одномерный массив проще, и выражение для доступа к нему будет короче. Мы будем обрабатывать индексы массива как двузначные двоичные числа, где левый знак обозначает переменную оборачиваемости, а правый – события. К примеру, 10
обозначает случай, когда Жак обратился в белку, но событие (к примеру, «пицца») не имело места. Так случилось 4 раза. И поскольку двоичное 10 – это десятичное 2, мы будем хранить это в массиве по индексу 2.
Функция, вычисляющая коэффициент ϕ из такого массива:
function phi(table) {
return (table[3] * table[0] - table[2] * table[1]) /
Math.sqrt((table[2] + table[3]) *
(table[0] + table[1]) *
(table[1] + table[3]) *
(table[0] + table[2]));
}
console.log(phi([76, 9, 4, 1]));
// → 0.068599434
Это просто прямая реализация формулы ϕ на языке JavaScript. Math.sqrt
– это функция извлечения квадратного корня объекта Math
из стандартного окружения JavaScript. Нам нужно сложить два поля таблицы для получения полей типа n1•, потому что мы не храним в явном виде суммы столбцов или строк.
Жак вёл журнал три месяца. Результат доступен на сайте книги eloquentjavascript.net/code/jacques_journal.js.
Чтобы извлечь переменную 2×2 для конкретного события, нам нужно в цикле пройтись по всем записям и посчитать, сколько раз оно случается по отношению к обращению в белку.
function hasEvent(event, entry) {
return entry.events.indexOf(event) != -1;
}
function tableFor(event, journal) {
var table = [0, 0, 0, 0];
for (var i = 0; i < journal.length; i++) {
var entry = journal[i], index = 0;
if (hasEvent(event, entry)) index += 1;
if (entry.squirrel) index += 2;
table[index] += 1;
}
return table;
}
console.log(tableFor("pizza", JOURNAL));
// → [76, 9, 4, 1]
Функция hasEvent
проверяет, содержит ли запись нужный элемент. У массивов есть метод indexOf
, который ищет заданное значение (в нашем случае – имя события) в массиве и возвращает индекс его положения в массиве (-1, если его в массиве нет). Значит, если вызов indexOf
не вернул -1, то событие в записи есть.