В документе приведен короткий анализ активности пользователей соцсети “Вконтакте”, состоящих в группе Stepik.org. Под активностью понимаются: со стороны участников группы - лайки и репосты записей на стене группы, со стороны администраторов - частота записей.
Предупреждение:
Основные гипотезы, которые подвергнутся проверке в ходе данного мини-исследования: 1) С течением времени число лайков и репостов растет 2) Количество лайков и репостов прямо пропорциональны друг другу 3) Активность зависит от содержимого записи
Исходными данными для исследования является массив данных, скаченный с помощью VK Api 1. Скачивание данных происходило с помощью отдельного парсера, написанного не на R, поэтому его описание выходит за рамки данного документа. Данные представлены таблицей в файле формата *.txt. Скачать файл можно по ссылке: https://yadi.sk/i/HhaFP3iSvbJs7. Дата сбора данных: 19.09.2016.
Краткое описание колонок таблицы исходных данных:
Загрузим набор данных:
#подключаем нужные пакеты
require(tidyr)
require(ggplot2)
require(dplyr)
require(stringr)
#не забудьте сменить рабочую директорию!
df <- read.csv("Stepik_data.txt", sep="\t", encoding = "UTF-8", stringsAsFactors = F)
#заменим Да/Нет на TRUE/FALSE
df[df$Attachments=="Да",]$Attachments <- TRUE
df[df$Attachments=="Нет",]$Attachments <- FALSE
#Приведем дату к правильному формату
df$Date <- strptime(df$Date, format="%d.%m.%Y %H:%M:%S")
df$Date <- as.POSIXct(df$Date)
#Числовые показатели приведем к числовому типу
df$Likes <- as.numeric(df$Likes)
df$Reposts <- as.numeric(df$Reposts)
#Добавим время дня для каждой записи (в часах)
df$Time <- (as.numeric(format(df$Date, "%H"))*3600+
as.numeric(format(df$Date, "%M"))*60+
as.numeric(format(df$Date, "%S")))/3600
#и день недели, в который была опубликована запись
#не забудем при этом привести новую переменную к фактору с правильным порядком уровней
df$WeekDay <- weekdays(df$Date)
df$WeekDay <- factor(df$WeekDay,levels=c("понедельник","вторник","среда", "четверг","пятница","суббота","воскресенье"))
Посмотрим на общие характеристики нашего набора данных:
Всего записей в массиве: 371. Среднее количество лайков: 36.74. Репостов:7.76.
Прежде всего посмотрим на то, как ведут себя лайки и репосты в динамике:
#приведем датафрейм к длинному формату для того, чтобы удобнее было строить график
df_long <- gather(df[c("Date","Likes","Reposts")],Action,Value, Likes, Reposts)
LikesDatePlot <- function(df){
ggplot(df_long,aes(Date, Value, color=Action))+
geom_line()+
geom_smooth( se=F)+
xlab("Даты (год-месяц)")+
ylab("Число действий")+
theme_bw()+
theme(axis.text.x = element_text(angle=-45))+
scale_color_manual(name="Тип действия",values=c("lightgreen","blue"),
labels=c("Лайки","Репосты"))
}
LikesDatePlot(df_long)
На графике четко видны пики, появляющиеся в разных местах. Вероятно, это какие-то исключительные важные записи, которые по какой-от причине были “репостнуты” многими другими сообществами и пользователями и набрали много лайков. Такие записи, несомненно, являются ключевыми для любого сообщества, но они затрудняют дальнейший анализ. Поэтому будем считать такие записи выбросами и очистим набор данных от выбросов сверху:
#Удаление выбросов
hlimL <- quantile(df$Likes,0.75, na.rm = T)+
1.5*(quantile(df$Likes,0.75, na.rm = T)-quantile(df$Likes,0.25, na.rm = T))
hlimR <- quantile(df$Reposts,0.75, na.rm = T)+
1.5*(quantile(df$Reposts,0.75, na.rm = T)-quantile(df$Reposts,0.25, na.rm = T))
df_clear <- filter(df, Likes<=hlimL & Reposts<=hlimR)
df_long <- gather(df_clear[c("Date","Likes","Reposts")],Action,Value, Likes, Reposts)
Теперь график выглядит немного более ровно:
LikesDatePlot(df_long)
corL <- cor.test(df_clear$Likes, as.numeric(df_clear$Date))
corR <- cor.test(df_clear$Reposts, as.numeric(df_clear$Date))
Видно, что количество лайков растет в среднем с течением времени, в то время как количество репостов изменяется значительно меньше. Вероятно, с течением времени растет и число участников группы, что обуславливает и рост лайков. Однако информации о численности группы нет в исходных данных, поэтому однозначно судить об этом нельзя. При этом графики и лайков, и репостов имеют довольно сильный разброс по вертикали: записи с низким количество лайков/репостов встречаются как в 2014 году, так и в 2016. Это говорит о том, что нельзя говорить о сильной связи между датой и активностью пользователей. Это же подтверждают и невысокие коэффициенты корреляции между лайками и датой 0.28(значимый, p-value 0). И особенно между репостами и датой: 0.07 (не значим, , p-value 0.25). Обратите так же внимание на увеличение частоты записей примерно с начала 2016 года.
Таким образом, гипотеза о росте активности в группе с течением времени подтверждается лишь частично.
ggplot(df_clear,aes(Likes, Reposts))+
geom_line(color = "lightgreen")+
xlab("Количество лайков")+
ylab("Количество репостов")+
geom_smooth(se=F)+
theme_bw()
corLR <- cor.test(df_clear$Likes, df_clear$Reposts)
Между собой эти величины связаны более тесно: коэф. корреляции 0.73, p-value 0. Это вполне логично: чем больше репостов, тем больше людей видят запись, а значит и больше лайков.
Как и ожидалось, гипотеза о тесной связе лайков и репостов подтверждена.
Для начала посмотрим на количество записей по дням недели:
#введем новый датафрейм, сгруппировав старый
df_WD <- df_clear %>% group_by(WeekDay) %>% summarise(LikesMean = mean(Likes),
RepostsMean = mean(Reposts),
Posts = n())
#применим тот же прием, что и для графика с датами
df_WD <- gather(df_WD,Action,ActionCount,LikesMean,RepostsMean)
ggplot(df_WD,aes(WeekDay, Posts))+
geom_bar(stat = "identity", fill = "lightgreen")+
xlab("Дни недели")+
ylab("Количество записей")+
theme_bw()
Видимо, по выходным администраторы группы отдыхают, так как число записей резко уменьшается в нерабочие дни. Есть некоторое увеличение во вторник - возможно, это связано с уведомлениями о дедлайнах? Рассмотрим, как выходные влияют на лайки и репосты:
ggplot(df_WD,aes(WeekDay, ActionCount, fill = Action))+
geom_bar(stat = "identity", position = "dodge")+
xlab("Дни недели")+
ylab("Среднее количество действий")+
scale_fill_manual(name="Тип действия",values=c("lightgreen","blue"),
labels=c("Лайки","Репосты"))+
theme_bw()+
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Лайки и репосты так же уменьшаются. Либо в выходные выкладывается не очень интересный контент, либо в выходные все заняты и проводят мало времени в соцсетях.
Тот факт, что администраторы группы предпочитают выкладывать записи в рабочее время, подтверждается и графиком зависимости лайков и репостов от времени суток. Обратите внимание, как много значений сгруппировано в диапазоне от 12 до 17 часов дня:
ggplot(df_clear,aes(Time, Likes))+
geom_line(color = "lightgreen")+
xlab("Время дня")+
ylab("Количество лайков")+
geom_smooth(se=F)+
scale_x_continuous(breaks = c(0:24))+
theme_bw()
ggplot(df_clear,aes(Time, Reposts))+
geom_line(color = "lightgreen")+
xlab("Время дня")+
ylab("Количество репостов")+
geom_smooth(se=F)+
scale_x_continuous(breaks = c(0:24))+
theme_bw()
И последний признак, по которому мы будем анализировать активность записей - ее содержание. Поскольку объем документа ограничен, рассмотрим только один вариант разделения записей по их содержанию - содержит ли запись ссылку на сайт http://stepik.org или нет.
Ссылка может содержаться как в тексте записи, так и непосредственно в ссылке, прикрепленной к записи:
#добавим новую переменную в датафрейм, указывающую на наличие ссылки на Stepik
df_clear$Stepic <- grepl("stepi(c|k).org", df_clear$Text)|grepl("stepi(c|k).org", df_clear$Link)
#% процент записей, содержащих ссылку
ratio <- round(100*nrow(df_clear[df_clear$Stepic==T,])/nrow(df_clear),2)
#средния значения лайков и репостов для записей, содержащих и не содержащих ссылки на сайт
mLS <- round(mean(df_clear[df_clear$Stepic==T,]$Likes),2)
mLnS <- round(mean(df_clear[df_clear$Stepic==F,]$Likes),2)
mRS <- round(mean(df_clear[df_clear$Stepic==T,]$Reposts),2)
mRnS <-round(mean(df_clear[df_clear$Stepic==F,]$Reposts),2)
Записей, содержащих ссылку на портал 75%.
| Среднее число лайков | Среднее число репостов | |
|---|---|---|
| С ссылкой | 33.09 | 4.97 |
| Без ссылки | 30.37 | 3.37 |
Воспользовавшись непараметрическим критерием Манна-Уитни2, увидим, что различия в среднем количестве лайков незначимы, а вот для репостов - вполне:
Ltest <- wilcox.test(df_clear[df_clear$Stepic==T,]$Likes, df_clear[df_clear$Stepic==F,]$Likes)
Rtest <- wilcox.test(df_clear[df_clear$Stepic==T,]$Reposts, df_clear[df_clear$Stepic==F,]$Reposts)
| Значение статистики | p-уровень | |
|---|---|---|
| Лайки | 5491.5 | 0.5528 |
| Репосты | 6557.5 | 0.003 |
Наша гипотеза о зависимости активности от содержимого записи частично подтвердилась (для репостов).
С течением времени (и, вероятно, с возрастанием числа участников группы) количество лайков, а так же частота записей в группе увеличиваются. Однако такой тенденции не наблюдается у репостов, хотя в целом репосты и лайки связаны относительно тесно. Наибольшая активность наблюдается в рабочее время как со стороны пользователей, так и со стороны администраторов группы. Большее количество лайков и репостов собирают записи, содержащие ссылки на сам портал. Причем для репостов эта разница статистически значима. Исходя из вышесказанного, можно сделать следующие предварительные рекомендации для администраторов сообщества: возможно, не стоит публиковать важную информацию во внерабочее время, так как активность в группе в это время спадает. Если среднее количество репостов является важным показателем, то следует обратить внимание на то, что с течением времени он растет слабо.
Подробное описание Api приведено тут: https://vk.com/dev/main↩
Применить обычный t-test нельзя, так как величины распределены не нормально. Доказательство этого не приводится в целях экономии места, но в этом можно убедиться с помощью любого теста на нормальность, например, Шапиро-Уилка. Подробнее о непараметрических критериях и их преимуществах рассказывается в курсе “Основы статистики. Часть 2”↩