Трамп сказал ЧТО?!?! (Анализ и поиск данных JSON)

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

  1. Создание файла JSON.
  2. Сохранение файла JSON онлайн в базе данных Mongo.
  3. Получение данных с помощью вызова API выборки.
  4. Поиск в данных для поиска элементов, соответствующих определенным условиям поиска.
  5. Возврат совпадающих данных.
  6. Создание простых визуализаций данных на основе возвращенных данных.

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

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

Мы можем сделать то же самое с любым типом документа или коллекцией документов, которые захотим, и это может быть очень полезным и мощным инструментом. Итак, приступим. В этом примере, чтобы объяснить концепции, я собираюсь использовать некоторые данные JSON, которые я организовал, с несколькими недавними выступлениями Дональда Трампа. Затем мы можем поискать его и узнать, как часто в его выступлениях встречаются определенные темы.

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

Https://trumpspeechdata.herokuapp.com/

ШАГ 1. ПОЛУЧЕНИЕ ЧИСТЫХ ДАННЫХ JSON ДЛЯ РАБОТЫ.

Ниже приведена ссылка на чистые данные JSON. Вы можете скачать его, если хотите, и можете просто использовать его в своем собственном проекте JavaScript. Но на следующих этапах я покажу, как подключить его к базе данных Mongo через Mlabs. Если вы предпочитаете работать с файлом локально, просто пропустите следующие шаги.



ШАГ 2: УСТАНОВИТЕ УЧЕТНУЮ ЗАПИСЬ MLABS.

  1. Создайте свою учетную запись здесь: https://mlab.com/login/
  2. Создайте новую базу данных.
  3. Создайте новую коллекцию.
  4. Добавить пользователя в базу данных.
  5. Используйте следующую команду, чтобы импортировать файл в коллекцию: mongoimport -h ds<database-number>.mlab.com:<database-number> -d signatures -c <collection> -u <user> -p <password> --file <input file>

Вы увидите более подробные инструкции на этом этапе после настройки учетной записи на вкладке «Инструменты».

ШАГ 3: НАЧНИТЕ НОВЫЙ ПРОЕКТ HTML / JAVASCRIPT.

Наш HTML будет довольно простым. Все, что нам нужно, это поле ввода, кнопка отправки и div для отображения результатов. Вот как выглядит мой:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>The Talk Maker</title>
  <link rel="stylesheet" href="/login.css">
  <link href="https://fonts.googleapis.com/css?family=Volkhov" rel="stylesheet">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<label>search for a word or phrase</label>
<input id="input" />
<buton id="button">search</button>
<div id="results"></div>
</body>
</html>

Я включил ссылку на jquery, потому что мы можем добавить туда и часть этого. Самая важная вещь здесь - это ‹input›, ‹button› & ‹div›, каждый из которых имеет идентификатор, который мы будем использовать, чтобы выбрать его в нашем JavaScript.

ШАГ 4: JAVASCRIPT

Лучше всего иметь в нашей программе отдельный JS-файл. Вы также можете поместить его в тег ‹script› в голове, если хотите, но это не всегда так чисто. Сначала мы вызовем наши переменные, используя идентификаторы из HTML.

let input = document.getElementById('input');
let button = document.getElementById('button');
button.onclick = searchAPI;

SearchAPI будет именем нашей функции, в которой мы вызываем нашу выборку. Это будет выглядеть примерно так:

function searchAPI() {
fetch('https://api.mlab.com/api/1/databases?apiKey=myAPIKey').then(function(response) {
          if (response.status != 200) {
            window.alert("Sorry, looks like there's been an error" + response.status);
            return;
          }
response.json().then(function(data) {
let api = data;
          })
        })
      }

Поскольку мы загрузили наши файлы, которые будем использовать, в Mlab, теперь нам нужно получить эти данные. Поскольку это все в одном файле, который мы импортировали, в одной коллекции, наш URL-адрес для выборки будет похож на указанный выше, но с именами коллекции и файла, специфичными для вашей конкретной базы данных. Если у вас возникли проблемы с настройкой URL-адреса, вы можете найти документацию mLab для получения данных здесь: http://docs.mlab.com/data-api/#base-url

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

  1. Я хочу найти слово и подсчитать его появление в документе. Например, в этих речах Трампа я мог бы поискать любые речи, в названии которых есть слово «экономия», а затем в этих речах подсчитать, сколько раз он ссылается на «средний класс».
  2. Во-вторых, я собираюсь составить набор общих слов, которые я смогу отслеживать в любом из документов. Например, мой массив может содержать:
["middle class", "poverty", "economy", "taxes", "deficit"]

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

Вот еще один способ сделать это, который тоже может быть полезен. У нас могло быть два массива. Один массив, который выглядит так:

["good", "happy", "prosperity"]

и второй массив, состоящий из противоположных слов, например:

["sad", "bad", "poverty"]

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

Чтобы выполнить все это, я собираюсь выполнить цикл for после выборки. Затем у меня будет ряд массивов внутри цикла for для проверки. Один для слов, которые я хочу проверить, как часто они появляются, второй массив для «хороших» слов и третий массив для «плохих» слов. Затем, вне цикла for, у меня будет несколько пустых массивов, в которые я позже отправлю совпадающие данные, чтобы потом использовать их. Теперь, когда у нас есть четкое представление о том, что мы хотим сделать, давайте напишем псевдокод, а затем разбиваем его на управляемые части. Вот как выглядит мой псевдокод:

function searchAPI() {
fetch('https://api.mlab.com/api/1/databases?apiKey=myAPIKey').then(function(response) {
          if (response.status != 200) {
            window.alert("Sorry, looks like there's been an error" + response.status);
            return;
          }
response.json().then(function(data) {
let api = data;
array A = [empty array to hold matched words pushed from for loop];
array B = [empty array to hold "good" words pushed from for loop];
array C = [empty array to hold "bad" words pushed from for loop];
for ( for loop to map through api) {
array 1 = [array that holds the words we want to find]
array 2 = [array to hold good words to test for]
array 3 = [array that holds bad words to test for]
//--We'll need to create some "if" statements here
if(the document contains the word we searched for){
search the document for additional words from array 1 
push matching words to array A
search the document for words in array 2
push matching words to array B
search the document for words in array 3
push matching words to array C
}
function A = function that compares the results of an array and give us a count
run array A through function A.
run array B through function A.
run array C through function A.
compare results of array B and C, and see which one is bigger.
see results from array A, and see which word appears most.
}
          })
        })
      }

Хорошо, теперь, когда у нас есть хорошее начало для нашего псевдокода, давайте начнем вставлять его в некоторый реальный код. Вот что я сделал, но вы можете придумать лучшее решение.

let matchedFrequentWords = [];
            let totalBadWords = [];
            let totalGoodWords = [];
            for (var i = 0; i < api.length; i++) {
              let inputValue = input.value; 
//we'll include this in our initial if statement.
              let frequentWords = [ "america", "great", "republican", "democrat", "fight", "wall", "terrorism"]; 
              let goodWords = ["good", "happy", "great", "happiness", "love", "win", "success"];
              let badWords = ["bad", "sad", "terrible", "sadness", "hate", "fail", "unsuccessful"];
            }

В цикле for я собираюсь начать с функции, которая сравнивает слова в документе со словами в нашем плохом массиве, а затем с хорошим массивом. Я считаю, что мне понадобится оператор if, чтобы проверить, содержит ли документ даже то, что я ищу. Затем внутри оператора if я выполню 2 цикла for, один из которых вложен в другой. Один будет перебирать документ, второй - «хороший» массив, любые совпадающие элементы, я вставляю в массив «totalGoodWords» за пределами начального цикла for. Для этого, поскольку текст речи представляет собой одну длинную строку, я хочу разбить ее на отдельные слова и поместить каждое слово в массив, который мне будет проще перебирать. Я могу сделать это методом «разделения» и использовать пробел в качестве разделения. Это в основном превратит строку в значения, разделенные запятыми, причем каждый пробел между словами будет заменен запятой. Затем, вместо того, чтобы перебирать исходный текст, я буду перебирать этот массив. Тогда, если есть совпадение, что мы будем нажимать? Наши данные JSON включают заголовки речи, дату выступления и местоположение речи, а также текст речи. Если есть совпадение, мы помещаем каждый элемент данных JSON в наш массив, чтобы позже мы могли не только сравнивать частоту появления слова, но и сравнивать, являются ли определенные темы более распространенными в определенных местах или в определенные даты. Имейте в виду, что это всего лишь небольшой тест с 4 выступлениями, и наши результаты были бы намного лучше и точнее, если бы мы работали с большим количеством данных. Вот что я придумал:

if(api[i].text.indexOf(inputValue) > -1) {
                    let stringX = api[i].text.split(" ");
              for (var j = 0; j < badWords.length; j++) {
                for (var k = 0; k < stringX.length; k++) {
                  if (badWords[j] == stringX[k]) {
                    totalBadWords.push([
                      api[i].speechtitle,
                      api[i].speechdate,
                      api[i].speechlocation,
                    ])
                            }
                          }
                        }
                      }

Итак, давайте резюмируем, что здесь происходит. Мы будем искать термин в нашем HTML. Может быть, мы будем искать, например, «экономия». Затем мы просматриваем все документы в нашем API с помощью [i]. Если [i] содержит строку «economy», мы собираемся разбить весь текст на массив слов, пройти по этому массиву [k], а затем пройти через badWords [j] и найти любые совпадения. Если есть совпадения, мы возвращаемся к нашему исходному циклу [i] и нажимаем заголовок речи, дату выступления и место выступления. Помните, что мы пытаемся найти «настроение». Насколько позитивно или негативно настроение выступления. Поскольку у нас есть дата, название и информация о местоположении, мы должны иметь возможность определить настроение выступлений, а затем увидеть, в какие даты «тон» этих выступлений был хорошим или плохим и в каких местах был более позитивный или отрицательный тон. В основном мы сделаем то же самое для нашего «хорошего массива»:

if(api[i].text.indexOf(inputValue) > -1) {
                    let stringX = api[i].text.split(" ");
              for (var j = 0; j < goodWords.length; j++) {
                for (var k = 0; k < stringX.length; k++) {
                  if (goodWords[j] == stringX[k]) {
                    totalGoodWords.push([
                      api[i].speechtitle,
                      api[i].speechdate,
                      api[i].speechlocation,
])
                            }
                          }
                        }
                      }

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

if(api[i].text.indexOf(inputValue) > -1) {
let stringX = api[i].text.split(" ");
   for (var j = 0; j < frequentWords.length; j++) {
     for (var k = 0; k < stringX.length; k++) {
        if (frequentWords[j] == stringX[k]) {
            matchedFrequentWords.push([
                 frequentWords[j]
                    ])
                   }
                 }
               }
             }

Все эти функции будут внутри нашего исходного цикла for. Теперь выйдем на улицу, ниже нашего цикла for, и создадим функцию. Что нам нужно сделать, так это создать что-то, что сравнивает все элементы и возвращает нам элемент, его частоту в массиве или частоту появления. Я нашел эту замечательную функцию в StackOverflow, которая делает именно это. Вот оно ниже:

Array.prototype.byCount= function(){
              var itm, a= [], L= this.length, o= {};
              for(var i= 0; i<L; i++){
                itm= this[i];
                if(!itm) continue;
                if(o[itm]== undefined) o[itm]= 1;
                else ++o[itm];
              }
        for(var p in o) a[a.length]= {item: p, frequency: o[p]};
              return a.sort(function(a, b){
                return o[b.item]-o[a.item];
              });
            }

Теперь нам нужно передать массивы, в которые мы поместили объекты, через эту функцию, чтобы получить результат. Мы сохраним результат как переменную, чтобы мы могли получить к нему доступ позже. Вот как мы это сделаем:

let frequentWordCount = matchedFrequentWords.byCount();
let badWordCount = totalBadWords.byCount();
let goodWordCount = totalGoodWords.byCount();

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

В своем поиске я проверяю это, ищу любые речи, содержащие слово «экономика».

Если все работает и у вас те же массивы и данные, которые я использовал, ваш вывод для partialWordCount должен выглядеть следующим образом:

  1. 0: {item: «отлично», частота: 42}
  2. 1: {item: «драка», частота: 4}
  3. 2: {item: «терроризм», частота: 4}
  4. 3: {item: «стена», частота: 1}

Это говорит нам о том, что из всех речей «Великий» появляется 42 раза, «драка» - 4 раза, «терроризм» - 4 раза и «стена» - один раз. Ни один из других предметов, которые мы искали, вообще не появился.

Результат для badWordCount должен выглядеть примерно так:

  1. 0: {item: «Выступление президента Трампа на совместном заседании Конгресса, 28 февраля 2017 г., на совместном заседании Конгресса», частота: 3}
  2. 1: {item: «Замечания президента Трампа о налоговой реформе, 5 сентября 2017 г., Спрингфилд, штат Миссури», частота: 2}
  3. 2: {item: «Президент Трамп о Парижском климатическом соглашении, 1 июня 2017 г., Розовый сад», частота: 2}

Это говорит нам о том, что в первой речи из всех плохих слов, которые мы использовали, они встречаются только 3 раза и по 2 раза в каждой из двух других речей. Давайте сравним это с нашим выводом для goodWordCount, который должен выглядеть примерно так:

  1. 0: {item: «Выступление президента Трампа на мероприятии по налоговой реформе, 28 сентября 2017 г., здание Indiana Farm Bureau Building», частота: 26}
  2. 1: {item: «Выступление президента Трампа на совместном заседании Конгресса, 28 февраля 2017 г., на совместном заседании Конгресса», частота: 25}
  3. 2: {item: «Замечания президента Трампа о налоговой реформе, 5 сентября 2017 г., Спрингфилд, штат Миссури», частота: 14}
  4. 3: {item: «Президент Трамп о Парижском климатическом соглашении, 1 июня 2017 г., Розовый сад», частота: 12}

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

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

let goodNumbers = [];
    for (var i = 0; i < goodWordCount.length; i++) {
      goodNumbers.push(goodWordCount[i].frequency);
    }
let badNumbers = [];
    for (var i = 0; i < badWordCount.length; i++) {
      badNumbers.push(badWordCount[i].frequency);
    }
function getSum(total, num) {
    return total + num;
    }
let goodSum = goodNumbers.reduce(getSum);
    let badSum = badNumbers.reduce(getSum);
    let totalSum = goodSum + badSum;
let goodPercent = (Math.ceil((goodSum / totalSum) * 100));
let badPercent = (Math.ceil((badSum / totalSum) * 100));

Теперь у нас должен быть процент хороших слов и процент плохих слов. Мой вывод был таким:

goodPercent = 92%

badPercent = 9%;

(Я использую Math.ceil, поэтому они округляют до ближайшего целого числа по причинам).

Опять же, больше данных могло бы помочь, но, поскольку мы изначально ищем экономику, мы могли бы сказать: «В выступлениях, посвященных экономике, Трамп оптимистичен примерно на 92%». Это могло бы быть действительно полезно, если бы у нас было больше выступлений, затем мы могли бы искать, скажем, «равенство», «права женщин», «аборт» и т. Д. И смотреть, изменится ли его оптимизм или настроение выступления лучше или хуже по определенным темам. Еще одна интересная вещь, которую мы можем сделать, - это представить эти данные в виде гистограммы. Для этого мы создадим переменную, используя обратные кавычки, а затем вставим ее в наш div с идентификатором результатов innerHTML.

По сути, мы создадим новый элемент div. Мы будем использовать встроенный стиль, поскольку этот элемент создается динамически в JavaScript. Мы определим его цвет и высоту. Затем мы определим ширину с помощью переменной goodPercent, которая создаст красивое визуальное представление данных, которые мы только что проанализировали. Вот как это будет выглядеть в JavaScript:

let goodBarGraph = `
        <div class="dataSubLabel" style="color:green">Good:</div>
        <div style="padding-left:50px;background-color:green;color:white;display:flex;flex-direction: row;justify-content:flex-end;align-items:center;height:15px;width:${goodPercent}%;">${goodPercent}% </div>
    `;
    let badBarGraph = `
        <div class="dataSubLabel" style="color:red">Bad:</div>
        <div style="padding-left:50px;background-color:red;color:white;display:flex;flex-direction: row;justify-content:flex-end;align-items:center;height:15px;width:${badPercent}%;">${badPercent}% </div>
    `
    document.getElementById('results').innerHTML += goodBarGraph;
    document.getElementById('results').innerHTML += badBarGraph;

Вся наша переменная goodBarGraph заключена в обратные кавычки, за исключением двух вещей. Мы поместили значение witdth внутри $ {}, а также ту же переменную внутри второго div. Мы проделали то же самое для badBarGraph, а затем поместили эти переменные в innerHTML нашего div "results". Если мы все сделали правильно, то увидим что-то вроде этого:

Увидев, что это работает, мы могли бы сделать то же самое, но разбить его по выступлениям, годам или месту проведения. В зависимости от того, насколько сложен наш объект JSON, мы можем приблизительно вычислить «хороший» или «плохой» и разбить его множеством интересных способов. Довольно круто, правда? Хорошо, давай сделаем шаг назад. У нас есть прекрасные данные, которые говорят нам, как часто используются определенные слова. Давайте сделаем что-нибудь интересное и с этими данными. Принцип будет более или менее таким же, как и то, что мы уже сделали, но опять же, мы можем получить действительно захватывающие результаты, чем больше данных мы скармливаем нашим функциям. Чтобы наша визуализация работала, нам также необходимо преобразовать эти данные в проценты, чтобы мы могли отображать их с большей точностью. Вот как мы могли это сделать:

function getSum(total, num) {
    return total + num;
    }
let frequentWordTotal = [];
    for (var i = 0; i < frequentWordCount.length; i++) {
      frequentWordTotal.push(frequentWordCount[i].frequency);
    }
let frequentSum = frequentWordTotal.reduce(getSum);
let word1Percent = (Math.ceil((frequentWordCount[0].frequency / frequentSum) * 100));
    let word2Percent = (Math.ceil((frequentWordCount[1].frequency / frequentSum) * 100));
    let word3Percent = (Math.ceil((frequentWordCount[2].frequency / frequentSum) * 100));

Теперь, если все работает, наш word1Percent будет около 83%, а наш word2Percent должен быть около 8%, что, как мы знаем, является примерно точным, учитывая данные, которые мы получили ранее, поскольку мы знаем, что word1, слово «great» появляется 42 раза, а word2 , «Драка» появляется 4 раза. Таким образом, мы также можем визуализировать эти данные с помощью другой простой гистограммы. Вот как это может выглядеть в JavaScript:

let wordGraph = `
    <div class="dataSubLabel" style="color:#0056e0">${frequentWordCount[0].item} - appears  ${frequentWordCount[0].frequency} times.</div>
    <div style="padding-left:50px;background-color:#0056e0;color:white;display:flex;flex-direction: row;justify-content:flex-end;align-items:center;height:15px;width:${word1Percent}%;">${word1Percent}% </div>
<div class="dataSubLabel" style="color:#3460a8">${frequentWordCount[1].item} - appears  ${frequentWordCount[1].frequency} times.</div>
    <div style="padding-left:50px;background-color:#3460a8;color:white;display:flex;flex-direction: row;justify-content:flex-end;align-items:center;height:15px;width:${word2Percent}%;">${word2Percent}% </div>
<div class="dataSubLabel" style="color:#5874a3">${frequentWordCount[2].item} - appears  ${frequentWordCount[2].frequency} times.</div>
    <div style="padding-left:50px;background-color:#5874a3;color:white;display:flex;flex-direction: row;justify-content:flex-end;align-items:center;height:15px;width:${word3Percent}%;">${word3Percent}% </div>
    `
document.getElementById('results').innerHTML += wordGraph;

Здесь, как и раньше, мы определяем ширину нашей гистограммы с помощью процентной переменной, которую мы определили ранее. Затем мы также используем другие переменные, содержащиеся в нашем объекте wordFrequency, чтобы показать не только процентное соотношение, но и фактическое слово, а также количество слов. Вот как это может выглядеть в нашем HTML.

Итак, один фрагмент данных, который мы нечасто использовали, - это слово, которое мы на самом деле ищем. Используя этот формат, мы могли теперь вернуться и подсчитать, сколько раз это встречается в нашем цикле for, а затем поместить заголовок речи в массив. Тогда мы могли бы увидеть аналогичные данные о том, когда и где президент чаще всего обращается к этой конкретной теме. В любом случае это довольно простой проект, но может быть интересно проанализировать данные и увидеть закономерности, которых вы, возможно, не ожидаете. Если у вас есть какие-либо вопросы или комментарии, не стесняйтесь обращаться к нам. Спасибо!