# File-Name:       twitter_rec.R
# Date:            2012-02-10
# Author:          Drew Conway (drew.conway@nyu.edu)
# Purpose:         Trzeci plik kodu do rozdziału 11. Tutaj projektujemy prosty system rekomendacyjny,
#                   oparty na danych z Twittera. Na podstawie danych wygenerowanych przez dwa poprzednie
#                   skrypty próbujemy rekomendować użytkowników, których warto obserwować,
#                   na podstawie liczby obserwacji z węzłów już obserwowanych; spróbujemy też
#                   rekomendacji na podstawie przynależności do społeczności definiowanej przez
#                   odległości pomiędzy węzłami sieci.
# Data Used:       data/*.graphml
# Packages Used:   igraph

# All source code is copyright (c) 2012, under the Simplified BSD License.  
# For more information on FreeBSD see: http://www.opensource.org/licenses/bsd-license.php

# All images and materials produced by this code are licensed under the Creative Commons 
# Attribution-Share Alike 3.0 United States License: http://creativecommons.org/licenses/by-sa/3.0/us/

# All rights reserved.

######################################################
####                                              ####
####         OSTRZEŻENIE DLA CZYTELNIKÓW          ####
####                                              ####
#### W kwietniu 2012 roku usługa Google Social    ####
#### Graph została zarzucona, więc poniższy kod   ####
#### nie może być poprawnie wykonany; został      ####
#### zachowany dla ilustracji sposobu korzystania ####
#### z zewnętrznych źródeł danych z użyciem API.  ####
#### Właściwe analizy można przeprowadzić na      ####
#### podstawie danych dołączonych do kodu, pocho- ####
#### dzących sprzed wyłączenia usługi i dotyczą-  ####
#### cych jednego z Autorów.                      ####
######################################################

# UWAGA: w przypadku uruchamiania z konsoli R (w trybie interaktywnym) należy wykonać polecenie 'setwd'
# w celu zmiany katalogu roboczego na katalog zawierający plik skryptu.
# Inaczej może dojsć do błędów wczytywania danych i zapisu obrazków!

# Załadowanie bibliotek
library(igraph)

# Aby skorzystać z jednego z udostępnionych zbiorów danych, należy skorzystać z polecenia:
user.graph <- suppressWarnings(read.graph(paste("data/", user, "/", user, "_net.graphml", sep = ""), format = "graphml"))

# Prostą metodą sugerowania wartościowych twitterowców jest próba "zamykania trójkątów"
# pomiędzy użytkownikiem a jego 'znajomymi' (osobami już obserwowanymi). Nie wymaga to
# zgłębiania teorii grafów, a raczej efektywnego sposobu zliczania krawędzi w grafie:
# chcemy określić, którzy z użytkowników znajdują się w zbiorze użytkowników jeszcze nie
# obserwowanych, a są z kolei obserwowani przez tych, których już obserwujemy.

friends <- V(user.graph)$name[neighbors(user.graph, user, mode = "out") + 1]
user.el <- get.edgelist(user.graph)

# Kogo jeszcze nie śledzę, a kogo w większości śledzą moi 'znajomi'?
# Poniższy warunek implementuje w istocie większość czekającej nas pracy.
non.friends <- sapply(1:nrow(user.el), function(i) ifelse(any(user.el[i,] == user | 
    !user.el[i,1] %in% friends) | user.el[i,2] %in% friends, FALSE, TRUE))

non.friends.el <- user.el[which(non.friends == TRUE),]
friends.count <- table(non.friends.el[,2])

# Raportowanie wyników
friends.followers <- data.frame(list(Twitter.Users = names(friends.count), 
    Friends.Following=as.numeric(friends.count)), stringsAsFactors = FALSE)
    
friends.followers$Friends.Norm <- friends.followers$Friends.Following / length(friends)
friends.followers <- friends.followers[with(friends.followers, order(Twitter.Users)),]

head(friends.followers)

write.csv(friends.followers, paste("data/", user, "/", user, "_friends_rec.csv", sep=""), row.names=FALSE)

# A poza domykaniem trójkątów? Możemy rekomendować użytkowników na podstawie przynależności do
# wykrytych poprzednio społeczności.

user.ego <- suppressWarnings(read.graph(paste("data/", user, "/", user, "_ego.graphml", sep = ""), format = "graphml"))
friends.partitions <- cbind(V(user.ego)$HC8, V(user.ego)$name)

# Funkcja przyjmuje numer grupy w grafie i wyszukuje w niej użytkowników, których obserwuje
# największa liczba uczestników grupy, a których nie obserwuje użytkownik początkowy.
partition.follows <- function(i) {
    friends.in <- friends.partitions[which(friends.partitions[,1] == i),2]
    partition.non.follow <- non.friends.el[which(!is.na(match(non.friends.el[,1], friends.in))),]
    # Jeśli nie znaleziono, zwracamy wartość pustą NA
    if(nrow(partition.non.follow) < 2) {
        return(c(i, NA))
    }
    # Dla znalezionych węzłów zwracamy te, których obserwuje największa liczba uczestników grupy.
    else {
        partition.favorite <- table(partition.non.follow[,2])
        partition.favorite <- partition.favorite[order(-partition.favorite)]
        return(c(i, names(partition.favorite)[1]))
    }
}

# Uruchamiamy funkcję partition.follow dla wszystkich grup, i usuwamy z rekomendacji wartości puste i duplikaty
partition.recs <- t(sapply(unique(friends.partitions[,1]), partition.follows))
partition.recs <- partition.recs[!is.na(partition.recs[,2]) & !duplicated(partition.recs[,2]),]

# Pobranie indeksów węzłów wraz z rekomendowanymi użytkownikami
new.friends <- as.character(c(V(user.ego)$name, partition.recs[,2]))
new.index <- match(new.friends, V(user.graph)$name) - 1

# Wyznaczenie nowego podgrafu, ujmującego nowe rekomendacje
partition.graph <- subgraph(user.graph, new.index)

# Dodanie atrybutów wierzchołków potrzebnych do wizualizacji
all.partition <- rbind(cbind(get.vertex.attribute(user.ego, "HC8"), V(user.ego)$name), partition.recs)
all.index <- match(as.character(all.partition[,2]), V(partition.graph)$name) - 1
 
partition.graph <- set.vertex.attribute(partition.graph, "REC", index = all.index, value = all.partition[,1])

vertex.sizes <- c("3", rep("1", vcount(user.ego)-1), rep("2", nrow(partition.recs)))
partition.graph <- set.vertex.attribute(partition.graph, "SIZE", index = all.index, value = vertex.sizes)

# Zapisanie wynikowego grafu w formacie GraphML
write.graph(partition.graph, paste("data/", user, "/", user, "_rec.graphml",sep = ""), format = "graphml")

