Wcześniej pobrałem graf znajomych z Facebooka (robi się to bardzo łatwo korzystając z jednej z facebook’owych aplikacji). Szczegóły jak to zrobić wkrótce w oddzielnym wpisie.
Na podstawie tych danych przygotowałem sobie dwa pliki CSV. Jeden zawiera facebook’owe id, Imie Nazwisko; Drugi to zbiór par oznaczających relację bycia ze sobą znajomymi osób a i b. Zawiera po prostu: id_a, id_b.
Dane wczytujemy domyślnie do data frameów. (linie: 2-3)
(OPCJONALNE, niekonieczne linie 5-6) Zamieniamy zapis z notacji naukowej (ID są długie, więc domyślnie zobaczymy w R-ze je w notacji naukowej) na zwykłą.
Tworzymy sobie dodatkową kolumnę na inicjały (linia 7, inicjujemy 0), chociaż tak na prawdę wrzucimy tam całe imię oraz 3 pierwsze litery nazwiska bez spacji.(linie 8-14)
Biblioteka igraph z której będziemy korzystać nie poradzi sobie z tak dużymi liczbami jak ID, więc dokonamy przenumerowania, zamienimy unikalne ID na unikalne, ale bardzo krótkie reprezentacje. (linia 16 nadanie krótkich wersji)
Teraz dla data framu z danymi o parach również potrzebujemy dokonać podmiany długiego ID na wcześniej ustalone krótkie id. Piszemy w tym celu funkcję getShort().
Od 19-40 linii znajduje się funkcja realizująca wyszukiwanie binarne z bardzo satysfakcjonującą złożonością O(lgn). R posiada wiele bardzo dobrze zoptymalizowanych własnych metod, dlatego zamiast kodu w liniach 19-40 swobodnie można zaimplementować to jak w liniach: 58-62.
Ze stworzonej pętli skorzystamy w liniach 66-68 iterując po poszczególnych parach.
W liniach 70-78 piszemy sobie funkcję która zwróci nam informacje o relacjach znajomości w formie listy. Funkcja graph z biblioteki igraph oczekuje na wejściu właśnie listy wierzchołków, które są połączone krawędzią.
Lista a_0,a_1,a_2,a_3…. gdzie wierzchołki połączone krawędzią to pary: (a_0,a_1), (a_2,a_3)…
W linii 84 wreszcie tworzymy graf. Graf jest nieskierowany (ang. undirected) dlatego tworzymy go z parametrem directed=FALSE.
W lini 85 wiążemy z odpowiednimi wierzchołkami odpowiednie etykiety.
W lini 89 deklarujemy plik png będący naszym płótnem o celowo zadanych przez nas dużych wymiarach, na którym narysujemy graf: polecenie w linii 90. Precyzujemy kolor linii i wielkość wierzchołka.
setwd("~/Desktop/fb_graph")
ludzie<-read.csv("id_people.csv", header=FALSE)
relacje<-read.csv("relations.csv", header=FALSE)
for (i in 1:nrow(ludzie)){ ludzie$V1[i] <-format(ludzie$V1[i], scientific=FALSE)}
for (i in 1:nrow(relacje)){ relacje$V1[i]<-format(relacje$V1[i], scientific=FALSE); relacje$V2[i]<-format(relacje$V2[i], scientific=FALSE);}
ludzie$inicjaly<-0
for (i in 1:nrow(ludzie)) {
ludzie$inicjaly[i] <- paste(
(strsplit(as.character(ludzie$V2[i]), ' ')[[1]])[1] ,
substring(((strsplit(as.character(ludzie$V2[i]), ' ')[[1]])[2]),1,3)
,sep="")}
ludzie$short<-0
for (i in 1:nrow(ludzie)) {ludzie$short[i]=i}
getShort <-function(long) {
short<-0
L<- 1
R<- nrow(ludzie)
M<- floor((L+R)/2)
#for (i in L:R){
while (L<=R){
if(ludzie$V1[M]==long){
short<-ludzie$short[M]
break
} else
if(long < ludzie$V1[M] ){# szukany element < od wyznaczonego przez M
R<-(M-1)
M<-floor((L+R)/2)
} else { #szukany element > od wyznaczonego przez M
L<-(M+1)
M<-floor((L+R)/2)
}
}
return (short)
}
getShort <-function(long) {
short<-0
short<-ludzie$V1[long]
return (short)
}
getShort <-function(long) {
short<-0
short<-ludzie$short[ludzie$V1 == long]
return (short)
}
rl<-relacje
for (i in 1:nrow(rl)){
rl$V1[i]<-getShort(rl$V1[i]); rl$V2[i]<-getShort(rl$V2[i]);
}
getList<-function(){
l <-vector()
for (i in 1:nrow(rl)){
l<-c(l,rl$V1[i])
l<-c(l,rl$V2[i])
}
return(l)
}
v<-getList()
library(igraph)
g<-graph(v, directed=FALSE)
V(g)$label <- ludzie$inicjaly
p2<-png(filename="plocik.png",width=6500,height=6500,unit="px")
p2<-plot(g, vertex.size=1, edge.color="red")
Szukanie kliki.
Klika w grafie nieskierowanym G=(V,E) to podzbiór wierzchołków
w którym każda para wierzchołków jest połączona krawędzią należącą do E. Inaczej mówiąc klka to pełny podgraf grafu G. Rozmiar kliki to liczba wierzchołków, kótre zawiera. Problemy kliki to optymalizacyjny problem znalezieni w grafie kliki maksymalnego rozmiaru. W wersji decyzyjnej pytamy po prostu czy istnieje w grafie klika danego rzomiaru k. Formalna definicja to:
CLIQUE = { <G,k> : G jest grafem zawierającym klikę rozmiaru k }
Problem kliki jest NP-zupełny. Algorytm „naiwny” stwierdza, czy graf G=(V,E) o |V| wierzchołkach zawiera klikę rozmiary k, polega naprzemierzeniu wszystkich k-podzbiorów zbioru V i sprawdzeniu każdego z nich, czy aby nie tworzy kliki. Czas działania algorytmy jest WIELOMIANOWY jeśli k jest stałą. Ogólnie jednak k może być bliskie |V|/2 a wówczas czas działania jest większy niż wielomianowy.
T. Cormen, Ch. Leiserson, R. Rivest, C. Stein,. Wprowadzenie do algorytmów.
a) wersja ~decyzyjna:
Szukanie kiliki o rozmiarze conajmniej k:
cliques(g, min=5)
b) wersja optymalizacyjna:
Szukanie największej kliki:
largest.cliques(g)
wiele więcej tutaj:
http://igraph.sourceforge.net/doc/R/cliques.html