На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: ElcnU, ANDLL, fatalist
  
> Белка-оборотень , Сказ о том как Жак решил свою проблему с помощью JavaScript
    Все слышали историю про белку? :D

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

    С одной стороны, Жак рад, что он не превращается в классического волка. Превращение в белку влечёт меньше проблем. Вместо того, чтобы волноваться о том, не съешь ли ты соседа (это было бы неловко), он волнуется, как бы его не съел соседский кот. После того, как он дважды просыпался на очень тонкой ветке в кроне дуба, голый и дезориентированный, он приучился запирать окна и двери в своей комнате на ночь, и класть несколько орешков на пол, чтобы чем-то занять себя.

    Так решаются проблемы с котом и дубом. Но Жак всё ещё страдает от своего заболевания. Нерегулярные обращения наводят его на мысль, что они должны быть чем-то вызваны. Сначала он думал, что это происходит только в те дни, когда он прикасался к деревьям. Он перестал это делать, и даже стал избегать подходить к ним. Но проблема не исчезла. Перейдя к более научному подходу, Жак решил вести ежедневный дневник всего, чем он занимался, записывая туда, обращался ли он в белку. Так он надеется сузить круг вещей, приводящих к трансформации.

    Сперва он решил разработать структуру данных для хранения этой информации :D. Набор журнальных записей можно представить в виде массива. Но записи не состоят только лишь из номеров или строк – каждая должна хранить список того, что сделал наш герой, и булевское значение, показывающее, превратился ли Жак в белку. В идеале нам бы хотелось группировать каждую из записей в какую-то одну переменную, и потом добавлять их в массив :D.

    Итак, Жак запускает свой любимый интерпретатор JavaScript Node.js :D и создаёт окружение, необходимое для хранения журнала.

    ExpandedWrap disabled
      var journal = [];
       
      function addEntry(events, didITurnIntoASquirrel) {
        journal.push({
          events: events,
          squirrel: didITurnIntoASquirrel
        });
      }


    Каждый вечер, часов в десять - а иногда и назавтра утром, спускаясь с верхней полки шкафа – он записывает свой день.

    ExpandedWrap disabled
      addEntry(['carrot','exercise','weekend'], false);
      addEntry(['bread','pudding','brushed teeth','weekend','touched tree'], false);
      addEntry(['carrot','nachos','brushed teeth','cycling','weekend'], true);


    Как только у него будет достаточно данных, он собирается вычислить корреляцию :lool: между его обращениями и событиями каждого из дней, и в идеале узнать из их корреляций что-то полезное :D.

    Для измерения корреляции булевских переменных хорошо подходит коэффициент φ, к тому же, его сравнительно легко подсчитать. Для этого нам нужна таблица, содержащая количество раз, когда наблюдались различные комбинации двух переменных. К примеру, мы можем взять события "pizza" и значения squirrel и представить их в следующей таблице:

    No squirrel, no pizza: 76No squirrel, pizza: 9
    Squirrel, no pizza: 4Squirrel, pizza: 1


    φ можно вычислить по следующей формуле, где n относится к ячейкам таблицы:

    ExpandedWrap disabled
      φ = (n11 * n00 - n10 * n01)/ √ (n1• * n0• * n•1 * n•0)


    n01 обозначает количество измерений, когда первая переменная (squirrelness) - false (0), а вторая переменная (pizza) - true (1). В нашем примере n01 = 9. Запись n1• обозначает сумму всех измерений, где первая переменная - true, что для нашего примера равно 5. Соответственно, n•0 – сумма всех измерений, где вторая переменная - false, в нашем случае равно 80.

    Значит, для таблицы с пиццей числитель формулы будет 1*76 - 9*4 = 40, а знаменатель – корень из 5* 85*10*80, или √340000. Получается, что φ ≈ 0.069, что довольно мало. Непохоже, чтобы пицца влияла на обращения в белку :jokingly:.

    Функция, вычисляющая коэффициент φ из такого массива, просто прямая реализация формулы φ на языке JavaScript:

    ExpandedWrap disabled
      function phi([n00, n01, n10, n11]) {
        return (n11 * n00 - n10 * n01) /
          Math.sqrt((n10 + n11) * (n00 + n01) * (n01 + n11) * (n00 + n10));
      }
       
      console.log(phi([76, 9, 4, 1])); // 0.06859943405700354


    Жак вёл журнал три месяца...

    ExpandedWrap disabled
      {
        {"events":["carrot","exercise","weekend"],"squirrel":false},
        {"events":["bread","pudding","brushed teeth","weekend","touched tree"],"squirrel":false},
        {"events":["carrot","nachos","brushed teeth","cycling","weekend"],"squirrel":false},
        {"events":["brussel sprouts","ice cream","brushed teeth","computer","weekend"],"squirrel":false},
        {"events":["potatoes","candy","brushed teeth","exercise","weekend","dentist"],"squirrel":false},
        {"events":["brussel sprouts","pudding","brushed teeth","running","weekend"],"squirrel":false},
        {"events":["pizza","brushed teeth","computer","work","touched tree"],"squirrel":false},
        {"events":["bread","beer","brushed teeth","cycling","work"],"squirrel":false},
        {"events":["cauliflower","brushed teeth","work"],"squirrel":false},
        {"events":["pizza","brushed teeth","cycling","work"],"squirrel":false},
        {"events":["lasagna","nachos","brushed teeth","work"],"squirrel":false},
        {"events":["brushed teeth","weekend","touched tree"],"squirrel":false},
        {"events":["lettuce","brushed teeth","television","weekend"],"squirrel":false},
        {"events":["spaghetti","brushed teeth","work"],"squirrel":false},
        {"events":["brushed teeth","computer","work"],"squirrel":false},
        {"events":["lettuce","nachos","brushed teeth","work"],"squirrel":false},
        {"events":["carrot","brushed teeth","running","work"],"squirrel":false},
        {"events":["brushed teeth","work"],"squirrel":false},
        {"events":["cauliflower","reading","weekend"],"squirrel":false},
        {"events":["bread","brushed teeth","weekend"],"squirrel":false},
        {"events":["lasagna","brushed teeth","exercise","work"],"squirrel":false},
        {"events":["spaghetti","brushed teeth","reading","work"],"squirrel":false},
        {"events":["carrot","ice cream","brushed teeth","television","work"],"squirrel":false},
        {"events":["spaghetti","nachos","work"],"squirrel":false},
        {"events":["cauliflower","ice cream","brushed teeth","cycling","work"],"squirrel":false},
        {"events":["spaghetti","peanuts","computer","weekend"],"squirrel":true},
        {"events":["potatoes","ice cream","brushed teeth","computer","weekend"],"squirrel":false},
        {"events":["potatoes","ice cream","brushed teeth","work"],"squirrel":false},
        {"events":["peanuts","brushed teeth","running","work"],"squirrel":false},
        {"events":["potatoes","exercise","work"],"squirrel":false},
        {"events":["pizza","ice cream","computer","work"],"squirrel":false},
        {"events":["lasagna","ice cream","work"],"squirrel":false},
        {"events":["cauliflower","candy","reading","weekend"],"squirrel":false},
        {"events":["lasagna","nachos","brushed teeth","running","weekend"],"squirrel":false},
        {"events":["potatoes","brushed teeth","work"],"squirrel":false},
        {"events":["carrot","work"],"squirrel":false},
        {"events":["pizza","beer","work","dentist"],"squirrel":false},
        {"events":["lasagna","pudding","cycling","work"],"squirrel":false},
        {"events":["spaghetti","brushed teeth","reading","work"],"squirrel":false},
        {"events":["spaghetti","pudding","television","weekend"],"squirrel":false},
        {"events":["bread","brushed teeth","exercise","weekend"],"squirrel":false},
        {"events":["lasagna","peanuts","work"],"squirrel":true},
        {"events":["pizza","work"],"squirrel":false},
        {"events":["potatoes","exercise","work"],"squirrel":false},
        {"events":["brushed teeth","exercise","work"],"squirrel":false},
        {"events":["spaghetti","brushed teeth","television","work"],"squirrel":false},
        {"events":["pizza","cycling","weekend"],"squirrel":false},
        {"events":["carrot","brushed teeth","weekend"],"squirrel":false},
        {"events":["carrot","beer","brushed teeth","work"],"squirrel":false},
        {"events":["pizza","peanuts","candy","work"],"squirrel":true},
        {"events":["carrot","peanuts","brushed teeth","reading","work"],"squirrel":false},
        {"events":["potatoes","peanuts","brushed teeth","work"],"squirrel":false},
        {"events":["carrot","nachos","brushed teeth","exercise","work"],"squirrel":false},
        {"events":["pizza","peanuts","brushed teeth","television","weekend"],"squirrel":false},
        {"events":["lasagna","brushed teeth","cycling","weekend"],"squirrel":false},
        {"events":["cauliflower","peanuts","brushed teeth","computer","work","touched tree"],"squirrel":false},
        {"events":["lettuce","brushed teeth","television","work"],"squirrel":false},
        {"events":["potatoes","brushed teeth","computer","work"],"squirrel":false},
        {"events":["bread","candy","work"],"squirrel":false},
        {"events":["potatoes","nachos","work"],"squirrel":false},
        {"events":["carrot","pudding","brushed teeth","weekend"],"squirrel":false},
        {"events":["carrot","brushed teeth","exercise","weekend","touched tree"],"squirrel":false},
        {"events":["brussel sprouts","running","work"],"squirrel":false},
        {"events":["brushed teeth","work"],"squirrel":false},
        {"events":["lettuce","brushed teeth","running","work"],"squirrel":false},
        {"events":["candy","brushed teeth","work"],"squirrel":false},
        {"events":["brussel sprouts","brushed teeth","computer","work"],"squirrel":false},
        {"events":["bread","brushed teeth","weekend"],"squirrel":false},
        {"events":["cauliflower","brushed teeth","weekend"],"squirrel":false},
        {"events":["spaghetti","candy","television","work","touched tree"],"squirrel":false},
        {"events":["carrot","pudding","brushed teeth","work"],"squirrel":false},
        {"events":["lettuce","brushed teeth","work"],"squirrel":false},
        {"events":["carrot","ice cream","brushed teeth","cycling","work"],"squirrel":false},
        {"events":["pizza","brushed teeth","work"],"squirrel":false},
        {"events":["spaghetti","peanuts","exercise","weekend"],"squirrel":true},
        {"events":["bread","beer","computer","weekend","touched tree"],"squirrel":false},
        {"events":["brushed teeth","running","work"],"squirrel":false},
        {"events":["lettuce","peanuts","brushed teeth","work","touched tree"],"squirrel":false},
        {"events":["lasagna","brushed teeth","television","work"],"squirrel":false},
        {"events":["cauliflower","brushed teeth","running","work"],"squirrel":false},
        {"events":["carrot","brushed teeth","running","work"],"squirrel":false},
        {"events":["carrot","reading","weekend"],"squirrel":false},
        {"events":["carrot","peanuts","reading","weekend"],"squirrel":true},
        {"events":["potatoes","brushed teeth","running","work"],"squirrel":false},
        {"events":["lasagna","ice cream","work","touched tree"],"squirrel":false},
        {"events":["cauliflower","peanuts","brushed teeth","cycling","work"],"squirrel":false},
        {"events":["pizza","brushed teeth","running","work"],"squirrel":false},
        {"events":["lettuce","brushed teeth","work"],"squirrel":false},
        {"events":["bread","brushed teeth","television","weekend"],"squirrel":false},
        {"events":["cauliflower","peanuts","brushed teeth","weekend"],"squirrel":false}
      }


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

    ExpandedWrap disabled
      function tableFor(event, journal) {
        let table = [0, 0, 0, 0];
        for (let i = 0; i < journal.length; i++) {
          let entry = journal[i], index = 0;
          if (entry.events.includes(event)) {
            index += 1;
          }
          if (entry.squirrel) {
            index += 2;
          }
          table[index] += 1;
        }
        return table;
      }
       
      console.log(tableFor('pizza', JOURNAL)); // [76, 9, 4, 1]


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

    Продолжение следует...
    Сообщение отредактировано: Cfon -
      Итак Жак готов сделать анализ данных, их корреляцию! :D

      Нам нужно вычислить корреляцию для каждого типа события, которое встречается в наборе данных. Для этого сначала нужно найти все типы событий.

      ExpandedWrap disabled
        function journalEvents(journal) {
          let events = [];
          for (let entry of journal) {
            for (let event of entry.events) {
              if (!events.includes(event)) {
                events.push(event);
              }
            }
          }
          return events;
        }


      Обойдя все события и добавив те, которые еще не находятся в массиве events, функция journalEvents собирает каждый тип событий в массиве. Используя его, мы можем видеть все корреляции. Она принимает значения от -1 до 1. Нулевая корреляция обозначает, что переменные вообще не связаны, а корреляция 1 означает, что они полностью связаны – если вы знаете одну, вы автоматически знаете другую. -1 также означает прочную связь переменных, но обозначает их противоположность, т.е когда одна - true, вторая всегда false.

      Смотрим, что получилось:

      ExpandedWrap disabled
        for (let event of journalEvents(journal)) {
          console.log(event + ':', phi(tableFor(event, journal)));
        }
        // carrot: 0.0140970969
        // exercise: 0.0685994341
        // weekend: 0.1371988681
        // bread: -0.0757554019
        // pudding: -0.0648203724
        // ...


      Большинство корреляций лежат близко к нулю. Морковка, хлеб и пудинг, очевидно, не связаны с обращением в белку. Но обращение более часто происходит на выходных. Давайте отфильтруем результаты, чтобы выводить только корреляции больше 0.1 или меньше -0.1.

      ExpandedWrap disabled
        for (let event of journalEvents(journal)) {
          let correlation = phi(tableFor(event, journal));
          if (correlation > 0.1 || correlation < -0.1) {
            console.log(event + ':', correlation);
          }
        }
        // weekend: 0.1371988681
        // brushed teeth: -0.3805211953
        // candy: 0.1296407447
        // work: -0.1371988681
        // spaghetti: 0.2425356250
        // reading: 0.1106828054
        // peanuts: 0.5902679812


      Ага! У двух факторов корреляции заметно больше остальных. Арахис (peanuts) сильно влияет на вероятность превращения в белку, тогда как чистка зубов (brushed teeth) наоборот, препятствует этому :D. Интересно. Попробуем вот что:

      ExpandedWrap disabled
        for (let entry of journal) {
          if (entry.events.includes('peanuts') && !entry.events.includes('brushed teeth')) {
            entry.events.push('peanut teeth');
          }
        }
         
        console.log(phi(tableFor('peanut teeth', journal))); //--> 1 !!!


      Ошибки быть не может! Феномен случается именно тогда, когда Жак ест арахис и не чистит зубы :D. Если б он только не был таким неряхой относительно оральной гигиены :lool:, он бы вообще не заметил своего несчастья. Зная это, Жак просто перестаёт есть арахис (чистить зубы сложнее :D) и обнаруживает, что трансформации прекратились.

      У Жака какое-то время всё хорошо. Но через несколько лет он теряет работу, и в конце концов ему приходится наняться в цирк, где он выступает как Удивительный Человек-белка, набирая полный рот арахисового масла перед шоу :D. Однажды, устав от столь жалкого существования, Жак не обращается обратно в человека, пробирается через дыру в цирковом тенте и исчезает в лесу. Больше его никто не видел :'(

      Вот такая история :writer:
      ВСЕ! :whistle:
      Сообщение отредактировано: Cfon -
        Жак - тупой. Ему выпал шанс, а он его профукал.
          Надо переделать корреляцию на функциональный JS, а потом на Haskell для разминки моска белки :D
          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
          0 пользователей:


          Рейтинг@Mail.ru
          [ Script execution time: 0,0371 ]   [ 15 queries used ]   [ Generated: 18.04.24, 16:05 GMT ]