Социальные сети (Twitter, Facebook, LinkedIn) — пожалуй, самая популярная бесплатная доступная широкой общественности площадка для высказывания мыслей по разным поводам. Миллионы твитов (постов) ежедневно — там кроется огромное количество информации. В частности, Twitter широко используется компаниями и обычными людьми для описания состояния дел, продвижения продуктов или услуг. Twitter также является прекрасным источником данных для проведения интеллектуального анализа текстов: начиная с логики поведения, событий, тональности высказываний и заканчивая предсказанием трендов на рынке ценных бумаг. Там кроется огромный массив информации для интеллектуального и контекстуального анализа текстов.
В этой статье я покажу, как проводить простой анализ тональности высказываний. Мы загрузим twitter-сообщения по определенной теме и сравним их с базой данных позитивных и негативных слов. Отношение найденных позитивных и негативных слов называют отношением тональности. Мы также создадим функции для нахождения наиболее часто встречающихся слов. Эти слова могут дать полезную контекстуальную информацию об общественном мнении и тональности высказываний. Массив данных для позитивных и негативных слов, выражающих мнение (тональных слов) взят из Хью и Лью, KDD-2004.
Реализация на R с применением
Первый шаг — зарегистрироваться на портале разработчиков для Twitter и пройти авторизацию. Вам понадобятся:
После получения этих данных авторизируемся для получения доступа к Twitter API:
Следующий шаг — загрузить массив позитивных и негативных тональных слов (словарь) в рабочую папку R. Слова можно будет достать из переменных,
Всего 2006 позитивных и 4783 негативных слова. Раздел выше также показывает некоторые примеры слов из этих словарей.
Можно добавлять в словари новые слова или удалять существующие. С помощью кода ниже мы добавляем слово
Следующий шаг — задать строку поиска по twitter-сообщениям и присвоить ее значение переменной,
В коде выше используется строка «CyberSecurity» и 5000 твитов. Код для поиска по twitter:
У твитов множество дополнительных полей и системной информации. Мы используем функцию
На этом шаге напишем функцию, которая выполнит ряд команд и очистит текст: удалит знаки пунктуации, специальные символы, ссылки, дополнительные пробелы, цифры. Эта функция также приводит символы в верхнем регистре к нижнему, используя
Функция clean() очищает твиты и разбивает строки на векторы слов.
На этом шаге мы применим функцию
Мы добрались до фактической задачи анализа твитов. Сравниваем тексты твитов со словарями и находим совпадающие слова. Для того, чтобы это сделать, сначала зададим функцию для подсчета позитивных и негативных слов, совпадающих со словами из нашей базы. Вот код функции
Теперь применим функцию к списку
Следующий шаг — задать цикл для подсчета общего количества позитивных слов в твитах.
Как видно выше, в твитах 1569 позитивных слов. Аналогично можно найти количество негативных. Код ниже считает позитивные и негативные вхождения.
Эта функция применяется к списку
Аналогично создается ряд функций и циклов для подсчета негативных тональных слов. Эта информация записывается в другой data frame,
В этом разделе мы создадим некоторые графики, чтобы показать распределение часто встречающихся негативных и позитивных слов. Используем функцию
Используя пакет
Выведем основные позитивные слова и их частоту с пмощью пакета
Аналогично, выведем негативные слова и их частоту. В 5000 твитов, содержащих поисковую строку «CyberSecurity», содержится 2063 негативных слова.
Превратим
Теперь создадим для твитов облако слов, используя пакет
На этом последнем шаге мы превращаем блок слов в матрицу документов функцией
Наконец, приводим матрицу к data frame, фильтруем по
Как видим, тональность CyberSecurity негативная с отношением 1,3: 1. Этот анализ можно расширить для нескольких временных промежутков с целью выделения трендов. Также можно осуществлять его итеративно, по близким темам, чтобы сравнить и проанализировать относительный рейтинг тональности.
В этой статье я покажу, как проводить простой анализ тональности высказываний. Мы загрузим twitter-сообщения по определенной теме и сравним их с базой данных позитивных и негативных слов. Отношение найденных позитивных и негативных слов называют отношением тональности. Мы также создадим функции для нахождения наиболее часто встречающихся слов. Эти слова могут дать полезную контекстуальную информацию об общественном мнении и тональности высказываний. Массив данных для позитивных и негативных слов, выражающих мнение (тональных слов) взят из Хью и Лью, KDD-2004.
Реализация на R с применением
twitteR, dplyr, stringr, ggplot2, tm, SnowballC, qdap
и wordcloud
. Перед применением нужно установить и загрузить эти пакеты, используя команды install.packages()
и library()
.Загрузка Twitter API
Первый шаг — зарегистрироваться на портале разработчиков для Twitter и пройти авторизацию. Вам понадобятся:
api_key = "Ваш ключ API"
api_secret = "Ваш api_secret пароль"
access_token = "Ваш токен доступа"
access_token_secret = "Ваш пароль токена доступа"
После получения этих данных авторизируемся для получения доступа к Twitter API:
setup_twitter_oauth(api_key,api_secret,access_token,access_token_secret)
Загрузка словарей
Следующий шаг — загрузить массив позитивных и негативных тональных слов (словарь) в рабочую папку R. Слова можно будет достать из переменных,
positive
и negative
, как показано ниже.positive=scan('positive-words.txt',what='character',comment.char=';')
negative=scan('negative-words.txt',what='character',comment.char=';')
positive[20:30]
## [1] "accurately" "achievable" "achievement" "achievements"
## [5] "achievible" "acumen" "adaptable" "adaptive"
## [9] "adequate" "adjustable" "admirable"
negative[500:510]
## [1] "byzantine" "cackle" "calamities" "calamitous"
## [5] "calamitously" "calamity" "callous" "calumniate"
## [9] "calumniation" "calumnies" "calumnious"
Всего 2006 позитивных и 4783 негативных слова. Раздел выше также показывает некоторые примеры слов из этих словарей.
Можно добавлять в словари новые слова или удалять существующие. С помощью кода ниже мы добавляем слово
cloud
в словарь positive
и удаляем его из словаря negative
.positive=c(positive,"cloud")
negative=negative[negative!="cloud"]
Поиск по twitter-сообщениям
Следующий шаг — задать строку поиска по twitter-сообщениям и присвоить ее значение переменной,
findfd
. Количество твитов, которые будут использованы для анализа, присваивается другой переменной, number
. Время на поиск по сообщениям и извлечение информации зависит от этого числа. Медленное соединение с Интернет или сложный поисковый запрос могут привести к задержкам. findfd= "CyberSecurity"
number= 5000
В коде выше используется строка «CyberSecurity» и 5000 твитов. Код для поиска по twitter:
tweet=searchTwitter(findfd,number)
## Time difference of 1.301408 mins
Получение текста твитов
У твитов множество дополнительных полей и системной информации. Мы используем функцию
gettext()
для получения текстовых полей и присвоим получившийся список переменной tweetT
. Функция применяется ко всем 5000 твитов. Код ниже также показывает результат выборки для первых пяти сообщений.tweetT=lapply(tweet,function(t)t$getText())
head(tweetT,5)
## [[1]]
## [1] "RT @PCIAA: \"You must have realtime technology\" how do you defend against #Cyberattacks? @FireEye #cybersecurity http://t.co/Eg5H9UmVlY"
##
## [[2]]
## [1] "@MPBorman: #Cybersecurity on agenda for 80% corporate boards http://t.co/eLfxkgi2FT @CS
http://t.co/h9tjop0ete http://t.co/qiyfP94FlQ"
##
## [[3]]
## [1] "The FDA takes steps to strengthen cybersecurity of medical devices | @scoopit via @60601Testing http://t.co/9eC5LhGgBa"
##
## [[4]]
## [1] "Senior Solutions Architect, Cybersecurity, NYC-Long Island region, Virtual offic... http://t.co/68aOUMNgqy #job#cybersecurity"
##
## [[5]]
## [1] "RT @Cyveillance: http://t.co/Ym8WZXX55t #cybersecurity #infosec - The #DarkWeb As You Know It Is A Myth via @Wired http://t.co/R67Nh6Ck70"
Функция очищения текста
На этом шаге напишем функцию, которая выполнит ряд команд и очистит текст: удалит знаки пунктуации, специальные символы, ссылки, дополнительные пробелы, цифры. Эта функция также приводит символы в верхнем регистре к нижнему, используя
tolower()
. Функция tolower()
часто выдает ошибку, если встречает специальные символы, и выполнение кода останавливается. Чтобы этого не допустить, напишем функцию для перехвата ошибок, tryTolower
, и используем ее в коде функции очищения текста.tryTolower = function(x)
{
y = NA
# tryCatch error
try_error = tryCatch(tolower(x), error = function(e) e)
# if not an error
if (!inherits(try_error, "error"))
y = tolower(x)
return(y)
}
Функция clean() очищает твиты и разбивает строки на векторы слов.
clean=function(t){
t=gsub('[[:punct:]]','',t)
t=gsub('[[:cntrl:]]','',t)
t=gsub('\\d+','',t)
t=gsub('[[:digit:]]','',t)
t=gsub('@\\w+','',t)
t=gsub('http\\w+','',t)
t=gsub("^\\s+|\\s+$", "", t)
t=sapply(t,function(x) tryTolower(x))
t=str_split(t," ")
t=unlist(t)
return(t)
}
Очищение и разбиение твитов на слова
На этом шаге мы применим функцию
clean()
, чтобы очистить 5000 твитов. Результат будет храниться в переменной-списке tweetclean
. Нижеследующий код также показывает первые пять твитов, очищенных и разбитых на слова с помощью этой функции.tweetclean=lapply(tweetT,function(x) clean(x))
head(tweetclean,5)
## [[1]]
## [1] "rt" "pciaa" "you" "must"
## [5] "have" "realtime" "technology" "how"
## [9] "do" "you" "defend" "against"
## [13] "cyberattacks" "fireeye" "cybersecurity"
##
## [[2]]
## [1] "mpborman" "cybersecurity" "on" "agenda"
## [5] "for" "" "corporate" "boards"
## [9] " " "cs"
##
## [[3]]
## [1] "the" "fda" "takes" "steps"
## [5] "to" "strengthen" "cybersecurity" "of"
## [9] "medical" "devices" "" "scoopit"
## [13] "via" "testing"
##
## [[4]]
## [1] "senior" "solutions" "architect"
## [4] "cybersecurity" "nyclong" "island"
## [7] "region" "virtual" "offic"
## [10] "" "jobcybersecurity"
##
## [[5]]
## [1] "rt" "cyveillance" "" "cybersecurity"
## [5] "infosec" "" "the" "darkweb"
## [9] "as" "you" "know" "it"
## [13] "is" "a" "myth" "via"
## [17] "wired"
Анализ твитов
Мы добрались до фактической задачи анализа твитов. Сравниваем тексты твитов со словарями и находим совпадающие слова. Для того, чтобы это сделать, сначала зададим функцию для подсчета позитивных и негативных слов, совпадающих со словами из нашей базы. Вот код функции
returnpscore
для подсчета позитивных совпадений.returnpscore=function(tweet) {
pos.match=match(tweet,positive)
pos.match=!is.na(pos.match)
pos.score=sum(pos.match)
return(pos.score)
}
Теперь применим функцию к списку
tweetclean
.positive.score=lapply(tweetclean,function(x) returnpscore(x))
Следующий шаг — задать цикл для подсчета общего количества позитивных слов в твитах.
pcount=0
for (i in 1:length(positive.score)) {
pcount=pcount+positive.score[[i]]
}
pcount
## [1] 1569
Как видно выше, в твитах 1569 позитивных слов. Аналогично можно найти количество негативных. Код ниже считает позитивные и негативные вхождения.
poswords=function(tweets){
pmatch=match(t,positive)
posw=positive[pmatch]
posw=posw[!is.na(posw)]
return(posw)
}
Эта функция применяется к списку
tweetclean
, и в цикле слова добавляются в data frame, pdatamart
. Код ниже показывает первые 10 вхождений позитивных слов.words=NULL
pdatamart=data.frame(words)
for (t in tweetclean) {
pdatamart=c(poswords(t),pdatamart)
}
head(pdatamart,10)
## [[1]]
## [1] "best"
##
## [[2]]
## [1] "safe"
##
## [[3]]
## [1] "capable"
##
## [[4]]
## [1] "tough"
##
## [[5]]
## [1] "fortune"
##
## [[6]]
## [1] "excited"
##
## [[7]]
## [1] "kudos"
##
## [[8]]
## [1] "appropriate"
##
## [[9]]
## [1] "humour"
##
## [[10]]
## [1] "worth"
Аналогично создается ряд функций и циклов для подсчета негативных тональных слов. Эта информация записывается в другой data frame,
ndatamart
. Вот список первых десяти негативных слов в твитах.head(ndatamart,10)
## [[1]]
## [1] "attacks"
##
## [[2]]
## [1] "breach"
##
## [[3]]
## [1] "issues"
##
## [[4]]
## [1] "attacks"
##
## [[5]]
## [1] "poverty"
##
## [[6]]
## [1] "attacks"
##
## [[7]]
## [1] "dead"
##
## [[8]]
## [1] "dead"
##
## [[9]]
## [1] "dead"
##
## [[10]]
## [1] "dead"
Графики часто встречающихся негативных и позитивных слов
В этом разделе мы создадим некоторые графики, чтобы показать распределение часто встречающихся негативных и позитивных слов. Используем функцию
unlist()
, чтобы превратить списки в векторы. Векторные переменные pwords
и nwords
приводятся к data frame-объектам.dpwords=data.frame(table(pwords))
dnwords=data.frame(table(nwords))
Используя пакет
dplyr
, нужно привести слова к переменным типа character и затем отфильтровать позитивные и негативные по частоте встречаемости (frequency > 15
).dpwords=dpwords%>%
mutate(pwords=as.character(pwords))%>%
filter(Freq>15)
Выведем основные позитивные слова и их частоту с пмощью пакета
ggplot2
. Как видим, позитивных слов всего 1569. Функция распределения показывает степень позитивной тональности.ggplot(dpwords,aes(pwords,Freq))+geom_bar(stat="identity",fill="lightblue")+theme_bw()+
geom_text(aes(pwords,Freq,label=Freq),size=4)+
labs(x="Major Positive Words", y="Frequency of Occurence",title=paste("Major Positive Words and Occurence in \n '",findfd,"' twitter feeds, n =",number))+
geom_text(aes(1,5,label=paste("Total Positive Words :",pcount)),size=4,hjust=0)+theme(axis.text.x=element_text(angle=45))
Аналогично, выведем негативные слова и их частоту. В 5000 твитов, содержащих поисковую строку «CyberSecurity», содержится 2063 негативных слова.
Удаление общих слов и создание облака слов
Превратим
tweetclean
в блок слов, используя функцию VectorSource
. Представление в виде блока позволит удалить избыточные общие слова с помощью пакета интеллектуального анализа текста tm
. Удаление общих слов, так называемых стоп-слов, поможет нам сосредоточиться на важных и выделить контекст. Код ниже выводит несколько примеров стоп-слов:tweetscorpus=Corpus(VectorSource(tweetclean))
tweetscorpus=tm_map(tweetscorpus,removeWords,stopwords("english"))
stopwords("english")[30:50]
## [1] "what" "which" "who" "whom" "this" "that" "these"
## [8] "those" "am" "is" "are" "was" "were" "be"
## [15] "been" "being" "have" "has" "had" "having" "do"
Теперь создадим для твитов облако слов, используя пакет
wordcloud
. Обратите внимание, мы ограничиваем максимальное количество — 300.wordcloud(tweetscorpus,scale=c(5,0.5),random.order = TRUE,rot.per = 0.20,use.r.layout = FALSE,colors = brewer.pal(6,"Dark2"),max.words = 300)
Анализ и построение графика часто встречающихся слов
На этом последнем шаге мы превращаем блок слов в матрицу документов функцией
DocumentTermMatrix
. Матрицу документов можно анализировать на предмет часто встречающихся нетипичных слов. Затем убираем из блока редкие слова (со слишком низкой частотой встречаемости). Код ниже выводит наиболее часто встречающиеся (с частотой 50 и выше).dtm=DocumentTermMatrix(tweetscorpus)
# #removing sparse terms
dtms=removeSparseTerms(dtm,.99)
freq=sort(colSums(as.matrix(dtm)),decreasing=TRUE)
#get some more frequent terms
findFreqTerms(dtm,lowfreq=100)
## [1] "amp" "atf" "better" "breach"
## [5] "china" "cyber" "cybercrime" "cybersecurity"
## [9] "data" "experts" "federal" "firm"
## [13] "government" "hackers" "hack" "healthcare"
## [17] "help" "heres" "http
" "icit"
## [21] "infosec" "investigation" "iot" "learn"
## [25] "look" "love" "lunch" "new"
## [29] "news" "next" "official" "opm"
## [33] "passwords" "possible" "post" "privacy"
## [37] "reportedly" "securing" "security" "senior"
## [41] "share" "site" "startups" "talk"
## [45] "thehill" "tips" "took" "top"
## [49] "via" "wanted" "wed" "whats"
Наконец, приводим матрицу к data frame, фильтруем по
Minimum frequency > 75
и строим график с помощью ggplot2
:wf=data.frame(word=names(freq),freq=freq)
wfh=wf%>%
filter(freq>=75,!word==tolower(findfd))
ggplot(wfh,aes(word,freq))+geom_bar(stat="identity",fill='lightblue')+theme_bw()+
theme(axis.text.x=element_text(angle=45,hjust=1))+
geom_text(aes(word,freq,label=freq),size=4)+labs(x="High Frequency Words ",y="Number of Occurences", title=paste("High Frequency Words and Occurence in \n '",findfd,"' twitter feeds, n =",number))+
geom_text(aes(1,max(freq)-100,label=paste("# Positive Words:",pcount,"\n","# Negative Words:",ncount,"\n",result(ncount,pcount))),size=5, hjust=0)
Выводы
Как видим, тональность CyberSecurity негативная с отношением 1,3: 1. Этот анализ можно расширить для нескольких временных промежутков с целью выделения трендов. Также можно осуществлять его итеративно, по близким темам, чтобы сравнить и проанализировать относительный рейтинг тональности.