/chelyabinsk_NGO_net

exploration the network of NGO's audiences intersections in SNS VKontakte

Primary LanguageR

---
title: "Аудитории городских проектов: case Челябинск"
author: "Ilya Starikov"
date: "22 сентября 2017 г"
output:
  html_document:
    toc: yes
    toc_float: yes
---


```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, eval=FALSE, fig.width = 5, fig.height = 5)

```
```{r}
library(RCurl) # Библиотека для генерации запросов к API
library(jsonlite) # Библиотека для обработки JSON
library(igraph) # Библиотека для создание данных для построения графа
library(dplyr) # Библиотека для предобработки
library(tidyr) # Библиотека для предобработки
library(stringr) # Библиотека для работы со текстовыми данными
library(ggplot2) # Библиотека для визуализации
library(curl)
library(microbenchmark) # Библиотека для отслеживания времени исполнений кода
```

Мы используем `r R.Version()$version.string`)

Содержание:

1. Библиотеки 
2. Загрузка файла с БД проектов и предобработка
3. Выгрузка данных из ВК
    + функции для выгрузки
    + многопоточность
4. Создание файла для сетевого анализа
    + Матрица пересечений аудиторий
    + Применением меры сходства Жаккара
    + Применением собственной меры сходства
    + Созданием файла с данными для сетевого анализа (узлы ребра)

# 1 Библиотеки

`library("RCurl") # Библиотека для генерации запросов к API`
`library("jsonlite") # Библиотека для обработки JSON`
`library(igraph) # Библиотека для создание файла для построения графа`
`library(dplyr) # Библиотека для предобработки`
`library(tidyr) # Библиотека для предобработки`
`library(stringr) # Библиотека для работы со текстовыми данными`
`library(ggplot2) # Библиотека для визуализации`
`library(microbenchmark) # Библиотека для отслеживания времени исполнений кода`

# 2 Загрузка файла с БД проектов и предобработка

## 2.1 Загрузка файла

Мы составляли БД в *Google Disc* и потом сохраняли его на компьютере с расширением *.csv*

Далее загружаем файл в окружение с помощтю функции `read.cvs()` (рекомендуем установить параметр `stringsAsFactors = FALSE`)

```{r, include=FALSE, eval= TRUE}
# загрузка окружения
load("D:/2017/R net analysis/Челябинск/che74.RData")
```

```{r}
gr_che <- read.csv("Челябинск/Паблики Челябинск.csv", sep = ";", stringsAsFactors = F)
```

Преобразуем данные для дальнейшей обработки.

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

Для этого нужно оставить только id группы. Причем если в id группы были сформированны автоматически, они имеют вид *"club11001100"*, в таком случае нам необходимо оставить только цифры, а слово *"club"* удалит. Если же имя группы изменено ее владельцами и содержит слово *"club"*, например, *"club_super"*, тогда id группы будет считаться полностью *"club_super"*. 
Такие же манипуляции проделываем и с группами типа *public* и *event*.
```{r}
gr_che$url_VK <- gsub("http://vk.com/", "", gr_che$url_VK)
gr_che$url_VK <- gsub("https://vk.com/", "", gr_che$url_VK)
gr_che$url_VK <- str_trim(gr_che$url_VK)

x_club <- grep("club", gr_che$url_VK)
# проверяем какие из id следует преобразовать, а какие нет
gr_che$url_VK[x_club]
# оставляем только те id, которые следует преобразовать
x_club <- x_club[-13]
# теперь удаляем ненужные слова club
gr_che$url_VK[x_club] <- gsub("club", "", gr_che$url_VK[x_club])

x_club <- grep("event", gr_che$url_VK)
gr_che$url_VK[x_club] <- gsub("event", "", gr_che$url_VK[x_club])

x_club <- grep("public", gr_che$url_VK)
gr_che$url_VK[x_club] <- gsub("public", "", gr_che$url_VK[x_club])
```


# 3 Выгрузка данных из ВК

## 3.1 Устанавливаем соединение и создаем необходимые функции
Для получения списка подписчиков групп используем метод [groups.getMembers]<https://vk.com/dev/groups.getMembers> из [API VK]<https://vk.com/dev/methods>
```{r}
# базовый url для работы с API VK
url <- "https://api.vk.com/method/groups.getMembers?group_id=" 
```

Функция, которая возвращает количество участников группы
```{r}
get_count_memb <- function(id) {
  url_get <- paste0(url, id) # Составляем запрос к API
  resp <- getURL(url_get) # Запрашиваем и получаем ответ
  Sys.sleep(0.34) # Задержка для обхода ограничения на кол-во запросов в сек
  # Обрабатываем ответ и получаем id подписок
  fromJSON(resp)$response$count
}
```

Функция, которая возвращает список id участников группы
```{r}
get_id_memb <- function(group_URL) {
  #url_get <- paste0(url, id) # Составляем запрос к API
  resp <- getURL(group_URL) # Запрашиваем и получаем ответ
  Sys.sleep(0.34) # Задержка для обхода ограничения на кол-во запросов в сек
  # Обрабатываем ответ и получаем id подписок
  fromJSON(resp)$response$users
}
```

Теперь строим супер функцию, для возвращения id участников по списку групп (которая включает предыдущие две функции с небольшим изминением).

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

```{r}
get_group_users <- function(group) {
  url1 <- "https://api.vk.com/method/groups.getMembers?group_id="
  print(paste0("Quering group ", group))
  # Узнаем количество участников
  resp <- getURL(paste0(url1, group)) # Составляем запрос к API
  Sys.sleep(0.34) # Задержка для обхода ограничения на кол-во запросов в сек
  group_members_count <- fromJSON(resp)$response$count
  print(group_members_count)
  
  # Получаем ид участников
  x <- character()
  for(i in 0:floor(group_members_count/1000)){
    print(paste0("Getting chunk ", i))
    group_URL <- paste0(url1, group, "&offset=", 1000*i)
    x <- append(x, get_id_memb(group_URL))
  }
  return(x)
}
```

## 3.2 Выкачка данных (работа с API VK)
Здесь несколько варинатов можно выполнить с помощью функций семейства `apply`, а можно использовать многопточность, что увеличит скорость. Когда у нас до 100 групп увеличение скорости не существенно, но если нам надо будет выгружать данные о каждом пользователе (более 100 тыс. пользователей), тогда примененеие многопточности просто незаменимо.

###  3.2.1 Настраиваем многопоточность (делаем быстро)

Для этого потребуется установка пакетов `foreach`, `doParallel`.

```{r}
library(foreach)
library(doParallel)    
```

```{r}
cl <- makeCluster(8)    # Создаем «кластер» на восемь потоков
registerDoParallel(cl)

#stopCluster(cl) # для того, чтобы остановить кластер, используем эту функцию
```

```{r}
group_url <- gr_che$url_VK

# получаем количество участников для каждой группы 
## можно использовать system.time() для того, чтобы впечатляться скоростью
system.time({
  member_count_dopar <- foreach(j=1:length(group_url), .packages = c("RCurl", "jsonlite")) %dopar% get_count_memb(group_url[j]) # в функции foreach необходимо явно указать какие пакеты не из базовой сборки R используются в применяемой функции
})

# функция foreach возвращает элемент типа list, который следует перевести в вектор
gr_che$group_size_VK_T2 <- unlist(member_count_dopar)

# список участников групп для каждой группы
system.time({
  users <- foreach(j=1:length(group_url), .packages = c("RCurl", "jsonlite")) %dopar% get_group_users(group_url[j]) 
})

```

Присваиваем имена элементам(векторам) списка
```{r}
names(users) <- gr_che$url_VK
```

```{r include= FALSE}
all.users <- c(users, recursive = T)
uniq.users <- unique(all.users)
```
Всего в группах, которые мы будем анализировать состоят `r length(uniq.users)` уникальных пользователей.

Нас в первую очередь интересуют аудитория групп, которые реализуют проекты направленные на изменение городской среды. При помощи следующего кода находим аудиторию только таких групп.
```{r}
urban_users <- users[gr_che$url_VK[gr_che$physic_environment %in% 
                                  c(4, 5) |
                                    gr_che$urban_improve == 1]]
all.urb_users <- c(urban_users, recursive = T)
uniq.urb_users <- unique(all.urb_users)
```

Таким образом, по нашм данным в Челябинске уникальных пользователей, которые состоят в группах, посвященных изменениеям в городской среде или в группах, которые занимаются проектами по изменнию городской среды состоит `r length(uniq.urb_users)` человек.


# 4 Создание файла для сетевого анализа


## 4.1 Матрица пересечений аудиторий

```{r}
intersect.mat <- matrix(0, nrow = length(users), ncol = length(users))
```

## 4.2 Применение меры сходства Жаккара
***нужно добавить формулу Жакара, описывающую вычисление пересечений среди групп используя Латех***


Для этого применям следующую формулу
```{r}
common <- length(intersect(users[[i]], users[[j]]))
all <- length(c(users[c(i,j)], recursive = T))
kj = common/(all - common)
```
Где 

## 4.3 Применение собственной меры сходства 

```{r}
for(i in 1:nrow(intersect.mat))
  for(j in 1:ncol(intersect.mat))
    intersect.mat[i, j] <- length(intersect(users[[i]], users[[j]]))/length(users[[i]])
# диагональ должна быть равна 0
diag(intersect.mat) <- 0
```
такая формула подсчитывает все возможные пересечения даже случайные. Ясно, что если в одной группе несколько тысяч человек и в другой тоже, то какая-то доля пересечений пользователей будет неинформативной. При этом такие слабые пересечения будут влиять на общий анализ взаимосвязей групп. В данном случае будет разумным считать несущественными слабые пересечений до определенного уровня и приравнять их к нулу. В нашем случае мы приходим к выводу, что доля ниже которой мы будем считать пересечния несущественными состаляет `r cut`.

```{r}
cut <- 0.05
intersect.mat.cut <- apply(intersect.mat, 1, function(x){
  x[x < cut] <- 0
  x})
```

## 4.4 Созданием файла для построение графа и добавление параметров в данный файл

Для начала создаем файл, который потом можно будет использовать для визуализации графа и собственно сетевого анализа. *(сетевой анализ мы будем осуществлять в программе Gephi)*
```{r}
int.graph <- graph_from_adjacency_matrix(intersect.mat.cut,
                                         mode = "directed",
                                         weighted = T)
```

Немного перобразуем наш дата фрейм. Сейчас у нас характеристки представлены в виде кодов (1, 2, 0...). Для понятности и необходимо отразить значения данных кодов.
```{r}
# параметры отражающие участие в конкурсах на гранты
gr_che$grant_part <- ifelse(gr_che$grant_part %in% 1, "grant_part", "not_grant_part")
gr_che$grant_win <- ifelse(gr_che$grant_win %in% 1, "grant_win", "not_grant_win")

# имеет ли статус официального НКО или нет
gr_che$is.NGO <- ifelse(gr_che$is.NGO %in% 1, "NGO", "not_NGO")

# созданы ли проекты или НКО органами валасти. 
gr_che$gov <- ifelse(gr_che$gov %in% 1, "gov", "not_gov")

# цели в сфере изменнеия городской физической среды
gr_che$physic_environment <- as.factor(gr_che$physic_environment)
levels(gr_che$physic_environment) <- c("not", "prob_not", "prob_yes", "yes")

# опыт в изменнеии городской физ среды
gr_che$urban_improve <- ifelse(gr_che$urban_improve %in% 1, "urban_project", "urban_project")

# протестная деятельность
gr_che$revolt <- ifelse(gr_che$revolt %in% 1, "revolt", "not_revolt")

gr_che$eco <- ifelse(gr_che$eco %in% 1, "eco", "not_eco")
gr_che$commerce <- ifelse(gr_che$commerce %in% 1, "for_$", "not_for_$")
```


Теперь добавляем параметры, которые будем использовать для анализа графа. В данном исследовании нам нужные параметры, которые отражают направление деятельности активистских проектов или НКО, их опыт участия в гос грантах, являются ли они официально зарегистрированными или нет.

```{r}
# для удобства сохраняем необходимые характеристики в отдельные переменные
is.NGO <- gr_che$is.NGO
grant_win <- gr_che$grant_win
grant_part <- gr_che$grant_part
prof_eco <- gr_che$eco
prof_commerce <- gr_che$commerce
prof_physic_target <- as.character(gr_che$physic_environment)
prof_urban_project <- gr_che$urban_improve
prof_gov <- as.character(gr_che$gov)
prof_revolt <- gr_che$revolt
```

Назначаем необходимые параметры узлов в файл с данными для построения графа.
```{r}
int.graph <- set_vertex_attr(int.graph, "group_size", value = unlist(member_count_dopar))
int.graph <- set_vertex_attr(int.graph, "gov", value = prof_gov)
int.graph <- set_vertex_attr(int.graph, "urban_project", value = prof_urban_project)
int.graph <- set_vertex_attr(int.graph, "grant_win", value = grant_win)
int.graph <- set_vertex_attr(int.graph, "grant_part", value = grant_part)
int.graph <- set_vertex_attr(int.graph, "physic_target", value = prof_physic_target)
int.graph <- set_vertex_attr(int.graph, "revolt", value = prof_revolt)
int.graph <- set_vertex_attr(int.graph, "eco", value = prof_eco)
int.graph <- set_vertex_attr(int.graph, "commerce", value = prof_commerce)
int.graph <- set_vertex_attr(int.graph, "lable_NGO", value = gr_che$short_name)
int.graph <- set_vertex_attr(int.graph, "is_NGO", value = is.NGO)

```

Сохраняем файл с данным для сетеовго анализа, который потом можно будет загрузить и использовать в *Gephi*.

```{r}
write_graph(int.graph, "Челябинск/che_graph_0.5.graphml", format = "graphml")
```