Трамп сказал ЧТО?!?! (Анализ и поиск данных JSON)
Возможность синтаксического анализа и поиска в данных JSON может быть невероятно мощной, независимо от того, в какой отрасли вы работаете. В следующем примере я рассмотрю следующие концепции, связанные с данными JSON:
- Создание файла JSON.
- Сохранение файла JSON онлайн в базе данных Mongo.
- Получение данных с помощью вызова API выборки.
- Поиск в данных для поиска элементов, соответствующих определенным условиям поиска.
- Возврат совпадающих данных.
- Создание простых визуализаций данных на основе возвращенных данных.
Что мы хотим сделать, так это, учитывая набор документов, мы хотим отсортировать их все и определить, встречается ли определенный символ в каком-либо из документов. Если это происходит в документе, мы хотим выполнить поиск в этом документе, подсчитать количество раз, когда это произойдет, и вернуть счет.
Причина, по которой это был бы такой полезный инструмент, заключается в том, что представьте, если бы у вас был сборник речей, произнесенных политиками. Вы можете быстро просмотреть их и посмотреть, как часто они произносят определенные слова. Или, что еще лучше, мы могли бы сортировать несколько слов одновременно и видеть, какие слова встречаются чаще всего.
Мы можем сделать то же самое с любым типом документа или коллекцией документов, которые захотим, и это может быть очень полезным и мощным инструментом. Итак, приступим. В этом примере, чтобы объяснить концепции, я собираюсь использовать некоторые данные JSON, которые я организовал, с несколькими недавними выступлениями Дональда Трампа. Затем мы можем поискать его и узнать, как часто в его выступлениях встречаются определенные темы.
У меня готов рабочий прототип, и вы можете проверить, какова моя основная конечная цель. Вы можете проверить это здесь:
Https://trumpspeechdata.herokuapp.com/
ШАГ 1. ПОЛУЧЕНИЕ ЧИСТЫХ ДАННЫХ JSON ДЛЯ РАБОТЫ.
Ниже приведена ссылка на чистые данные JSON. Вы можете скачать его, если хотите, и можете просто использовать его в своем собственном проекте JavaScript. Но на следующих этапах я покажу, как подключить его к базе данных Mongo через Mlabs. Если вы предпочитаете работать с файлом локально, просто пропустите следующие шаги.
ШАГ 2: УСТАНОВИТЕ УЧЕТНУЮ ЗАПИСЬ MLABS.
- Создайте свою учетную запись здесь: https://mlab.com/login/
- Создайте новую базу данных.
- Создайте новую коллекцию.
- Добавить пользователя в базу данных.
- Используйте следующую команду, чтобы импортировать файл в коллекцию:
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, равную данным. Затем нам нужно будет получить доступ к нужным нам данным. А пока вот несколько вещей, которые я хочу сделать.
- Я хочу найти слово и подсчитать его появление в документе. Например, в этих речах Трампа я мог бы поискать любые речи, в названии которых есть слово «экономия», а затем в этих речах подсчитать, сколько раз он ссылается на «средний класс».
- Во-вторых, я собираюсь составить набор общих слов, которые я смогу отслеживать в любом из документов. Например, мой массив может содержать:
["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 должен выглядеть следующим образом:
- 0: {item: «отлично», частота: 42}
- 1: {item: «драка», частота: 4}
- 2: {item: «терроризм», частота: 4}
- 3: {item: «стена», частота: 1}
Это говорит нам о том, что из всех речей «Великий» появляется 42 раза, «драка» - 4 раза, «терроризм» - 4 раза и «стена» - один раз. Ни один из других предметов, которые мы искали, вообще не появился.
Результат для badWordCount должен выглядеть примерно так:
- 0: {item: «Выступление президента Трампа на совместном заседании Конгресса, 28 февраля 2017 г., на совместном заседании Конгресса», частота: 3}
- 1: {item: «Замечания президента Трампа о налоговой реформе, 5 сентября 2017 г., Спрингфилд, штат Миссури», частота: 2}
- 2: {item: «Президент Трамп о Парижском климатическом соглашении, 1 июня 2017 г., Розовый сад», частота: 2}
Это говорит нам о том, что в первой речи из всех плохих слов, которые мы использовали, они встречаются только 3 раза и по 2 раза в каждой из двух других речей. Давайте сравним это с нашим выводом для goodWordCount, который должен выглядеть примерно так:
- 0: {item: «Выступление президента Трампа на мероприятии по налоговой реформе, 28 сентября 2017 г., здание Indiana Farm Bureau Building», частота: 26}
- 1: {item: «Выступление президента Трампа на совместном заседании Конгресса, 28 февраля 2017 г., на совместном заседании Конгресса», частота: 25}
- 2: {item: «Замечания президента Трампа о налоговой реформе, 5 сентября 2017 г., Спрингфилд, штат Миссури», частота: 14}
- 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, а затем поместить заголовок речи в массив. Тогда мы могли бы увидеть аналогичные данные о том, когда и где президент чаще всего обращается к этой конкретной теме. В любом случае это довольно простой проект, но может быть интересно проанализировать данные и увидеть закономерности, которых вы, возможно, не ожидаете. Если у вас есть какие-либо вопросы или комментарии, не стесняйтесь обращаться к нам. Спасибо!