Bu sayfadaki içerik, R-Ladies Istanbul’un R ile Sosyal Ag Analizi başlıklı Kasım 2019 etkinliğinde sunduğum giriş seviyesindeki dersin taslak notlarından oluşmaktadır. Daha kapsamlı ve geniş açıklamaların yer aldığı yeni bir versiyonu yine bu sayfada yayınlayacağım. Soru, yorum ve önerileriniz için her zaman yazabilirsiniz: twitter, eposta.

Giriş

Ağ, en genel tanımıyla, birbirlerine bağlanmış şeylerden oluşan sistemlere denir. Bir ağ, noktalar ve bu noktaları birbirine bağlayan çizgilerle görselleştirilebilir. Bu noktalar düğüm (İng. node, vertex) veya aktör (İng. aktör) olarak, çizgiler ise bağ (İng. link, tie) veya ayrıt (İng. edge) olarak adlandırılır.

İnsanların fiziksel veya dijital hayattaki sosyal ilişkilerinin bir sosyal ağ oluşturması gibi, hayvanların kendi aralarındaki benzer ilişkiler de sosyal ağ oluşturabilir. Firmaların birbirleri arasındaki finansal ve ticari ilişkiler, ülkelerin ithalat/ihracat ilişkileri, enerji aktarım şebekeleri, besin zincirleri, protein etkileşimleri, internet altyapısı da ağlarla temsil edilebilecek sistemlere örnektir. Bu sistemlerin bir çoğu genellikle kabul edilen anlamda bir sosyallik içermeyebilir. Dolayısıyla, sosyal ağ analizi terimi yerini ağ analizi ve ağ bilimi gibi terimlere bırakmaya başlamıştır. Ancak, aynı temel ağ analiz teknikleri çok farklı tiplerdeki ağlarda (örn. sosyal, ekonomik, biyolojik, fiziksel) benzer şekilde kullanılır.

Ağ analizi, temellerini Sosyoloji ve Matematikten alır. Sosyologlar, ağlar vasıtasıyla insanların diğer insanlarla olan ilişkilerini temsil ederek, insanların davranış ve özelliklerinin bu ilişkileri nasıl değiştirdiğini ve aynı zamanda bu ilişkilerin insanların davranış ve özelliklerini nasıl değiştirdiğini anlamaya çalışmışlardır. Öte yandan, Matematikçiler de ağlar üzerinde çeşitli problemler (örn. en kısa yol problemi) tanımlayarak bunlarla ilgilenmişlerdir. Zamanla, bilgisayar bilimi, istatistiksel fizik, yaşam bilimi gibi bir çok alandaki araştırmacılar ağ analizi tekniklerini kullanmaya başlamış, her bir alan kendi perspektifiyle bu alanı daha da geliştirmiş ve zenginleştirmiştir.

Bu açık derste, uygulamalı bir yöntem benimseyerek konu açıklamalarının ve R diliyle uygulamalarının eşzamanlı gitmesi için çaba göstereceğiz. igraph kütüphanesini ve diğer kütüphaneleri aşağıdaki şekilde yükleyelim.

library(igraph)
library(visNetwork)
library(igraphdata)

Bir ağın düğümler ve bu düğümler arasındaki bağlardan oluştuğunu belirtmiştik. Boş bir ağ oluşturarak buna önce düğümler, sonra bağlar ekleyelim. Bir ağdaki bağlar yönlü veya yönsüz olabilir. Bu örnekte yönsüz bağlara sahip bir ağ oluşturacağız.

g <- make_empty_graph(directed = FALSE)
g <- g + vertices("Eda", "Can", "Oya", "Ali", "Nur", "Cem")
plot(g)

g <- g + edges("Eda", "Oya", "Eda", "Cem", "Can", "Cem", "Ali", "Nur", "Nur", "Eda")
plot(g)

g <- g + edge("Ali", "Oya") + edge("Nur", "Cem")
plot(g)

Ağda bulunan düğüm ve bağları listeleyelim, düğüm ve bağ adetlerini bulalım.

V(g)
+ 6/6 vertices, named, from b40dd52:
[1] Eda Can Oya Ali Nur Cem
E(g)
+ 7/7 edges from b40dd52 (vertex names):
[1] Eda--Oya Eda--Cem Can--Cem Ali--Nur Eda--Nur Oya--Ali Nur--Cem
vcount(g)
[1] 6
ecount(g)
[1] 7

Düğümlerin komşu sayısı derece (İng. degree) olarak isimlendirilir. Düğümlerin komşu sayısını, ve komşularının kim olduğunu bulalım.

degree(g)
Eda Can Oya Ali Nur Cem 
  3   1   2   2   3   3 
degree(g, "Ali")
Ali 
  2 
degree(g, c("Ali", "Oya"))
Ali Oya 
  2   2 
neighbors(g, "Ali")
+ 2/6 vertices, named, from b40dd52:
[1] Oya Nur
neighbors(g, "Oya")
+ 2/6 vertices, named, from b40dd52:
[1] Eda Ali

Verilen iki düğümün komşu olup olmadığını kontrol edelim.

are_adjacent(g, "Ali", "Oya")
[1] TRUE
are_adjacent(g, "Nur", "Oya")
[1] FALSE

Ağlardaki bağların yönlü de olabileceğini belirtmiştik. Elimizdeki ağın yönsüz olduğunu teyit edelim ve yönlü bir ağa çevirelim. Çevirirken, var olan bağların yerine yönlü iki bağlarla değiştirilmesini sağlayalım. Ardından bu bağlardan bazılarını silelim.

is.directed(g)
[1] FALSE
g2 <- as.directed(g, mode ="mutual")
plot(g2)


g2 <- delete.edges(g2, c("Can|Cem", "Eda|Cem", "Cem|Eda", "Eda|Oya", "Nur|Ali", "Ali|Oya"))
plot(g2)

Yönlü ağlarda, bir düğümün derecesi sahip olduğu bağların yönüne göre iç-derece ve dış-derece olmak üzere iki şekilde hesaplanabilir. Aşağıda bunu hesaplayalım.

degree(g2)
Eda Can Oya Ali Nur Cem 
  3   1   2   2   5   3 
degree(g2, mode="in")
Eda Can Oya Ali Nur Cem 
  2   1   0   1   3   1 
degree(g2, mode="out")
Eda Can Oya Ali Nur Cem 
  1   0   2   1   2   2 

Yönlü bir ağda, verilen bir düğümden diğerine bir bağ olup olmadığını kontrol edelim.

are_adjacent(g2, "Oya", "Eda")
[1] TRUE
are_adjacent(g2, "Eda", "Oya")
[1] FALSE

igraph kütüphanesi düğümlerin ve bağların adlarını ve id’lerini farklı şekilde tutmaktadır. her bir düğümün 1’den başlayan ve sıralı giden tam sayı id’leri vardır. Aynı durum bağlar için de geçerlidir. Bu durumda, \(n\) düğüm ve \(m\) bağdan oluşan bir ağ için düğüm id’leri \({1, 2, .., n}\), bağ id’leri \({1, 2, .., m}\) olarak oluşacaktır. Bunlarla ilgili bir kaç örnek yapalım.

V(g2)
+ 6/6 vertices, named, from b5450ad:
[1] Eda Can Oya Ali Nur Cem
as.numeric(V(g2))
[1] 1 2 3 4 5 6
as.numeric(V(g2)["Oya"])
[1] 3
E(g2)
+ 8/8 edges from b5450ad (vertex names):
[1] Ali->Nur Eda->Nur Oya->Ali Nur->Cem Oya->Eda Cem->Can Nur->Eda Cem->Nur
as.numeric(E(g2))
[1] 1 2 3 4 5 6 7 8
as.numeric(E(g2)["Oya|Ali"])
[1] 3
get.edge.ids(g2, c("Oya", "Ali"))
[1] 3

Düğüm ve bağların id’leri düğüm ve bağların silinmesi ve benzeri durumlarda yeniden numaralandırılmaktadır. Aşağıda kod parçası buna bir örnek gösterir.

g3 <- delete_edges(g2, "Ali|Nur")
get.edge.ids(g3, c("Oya", "Ali"))
[1] 2

Düğümlerin isimleri name isminde bir özellik üzerinde tutulmaktadır.

V(g3)$name
[1] "Eda" "Can" "Oya" "Ali" "Nur" "Cem"
vertex_attr(g3)
$name
[1] "Eda" "Can" "Oya" "Ali" "Nur" "Cem"
edge_attr(g3)
list()

Dilersek, kendimiz de düğümlere farklı özellikler ekleyebiliriz.

V(g3)$surname <- c("Som", "Yön", "Kum", "Gün", "Gün", "Gün")
V(g3)["Ali"]$age <- 24

vertex_attr(g3)
$name
[1] "Eda" "Can" "Oya" "Ali" "Nur" "Cem"

$surname
[1] "Som" "Yön" "Kum" "Gün" "Gün" "Gün"

$age
[1] NA NA NA 24 NA NA

Benzer şekilde bağlara da özellikler ekleyebilir, bu özelliklere göre filtrelemeler yapabiliriz.


E(g3)$weight <- c(0.5, 1., 2.5, 1., 0.5, 1.5, 2.)
edge_attr(g3)
$weight
[1] 0.5 1.0 2.5 1.0 0.5 1.5 2.0
E(g3)[weight > 1]
+ 3/7 edges from b680844 (vertex names):
[1] Nur->Cem Nur->Eda Cem->Nur

Görselleri de özelleştirebiliriz. Ancak bu derste ağ görselleştirme üzerinde durmayacağız.

plot(g3, edge.arrow.size=.5, vertex.color="gold", vertex.size=25, 

     vertex.frame.color="gray", vertex.label.color="black", 

     vertex.label.cex=0.8, vertex.label.dist=2, edge.curved=0.2) 


plot(g3, vertex.color="gold", vertex.size=40, layout=layout_in_circle)

NA
NA

Aşağıda dersin geri kalanında kullanacağımız bir görselleştirme fonksiyonu tanımlıyoruz.

myViz <- function (myGraph){
  visIgraph(myGraph, randomSeed = 11)  %>%
  visIgraphLayout(randomSeed = 11) %>%
  visNodes(
    font = list(size = 59),
    shape = "dot",
    color = list(
      background = "#0085AF",
      border = "#013848",
      highlight = "#FF8000"
    ),
    shadow = list(enabled = TRUE, size = 10)
  ) %>%
  visEdges(
    shadow = FALSE,
    color = list(color = "#0085AF", highlight = "#C62F4B")
  ) %>%
  visLayout(randomSeed = 11)
}

Bazı Temel Ağ İstatistikleri ve Düğüm Merkeziliği

Floransa’daki Evlilik Ağları

Aşağıda, 1426-1434 yılları için Floransa’daki aileler arasındaki evlilik bağlarını gösteren bir ağ oluşturuyoruz. Her bir düğüm bir aileyi, her bağ bu aileler arasında bir evlilik bağı olduğunu göstermektedir.

Orijinal veri kaynağı: Kent, D. V. (1978). The rise of the Medici: Faction in Florence, 1426-1434. Oxford University Press, USA.

florentineRelships <- read.csv("http://raw.githubusercontent.com/lhehnke/renaissance-florence-network-data/master/florentine_families_relations_matrix_sample.csv")

head(florentineRelships)

florentineMarriages <- florentineRelships[florentineRelships$marriage ==1, 1:2]

is.data.frame(florentineRelships)
[1] TRUE
florentine <- graph_from_data_frame(florentineRelships, directed = FALSE)

plot(florentine, layout=layout_with_lgl)


myViz(florentine)

NA

Temel istatistikler

V(florentine)
+ 88/88 vertices, named, from b94a16e:
 [1] Acciaiuoli    Adimari       Albizzi       Altoviti      Ardinghelli   Arrighi      
 [7] Arrigucci     Baldovinetti  Barbadori     Bardi         Bartoli       Belfradelli  
[13] Bencivenni    Benizzi       Berlinghieri  Bischeri      Brancacci     Capponi      
[19] Carducci      Carnesecchi   Castellani    Cavalcanti    Corbinelli    Corsi        
[25] Da Uzzano     Dall'Antella  Davanzati     Degli Agli    Del Forese    Del Palagio  
[31] Della Casa    Dietisalvi    Doffi         Donati        Fagni         Fortini      
[37] Frescobaldi   Gianfigliazzi Ginori        Giugni        Guadagni      Guasconi     
[43] Guicciardini  Lamberteschi  Lapi          Manelli       Manovelli     Martelli     
[49] Medici        Orlandini     Panciatichi   Pandolfini    Pazzi         Pepi         
[55] Peruzzi       Pitti         Ricasoli      Ridolfi       Rondinelli    Rossi        
+ ... omitted several vertices
E(florentine)
+ 449/449 edges from b94a16e (vertex names):
 [1] Acciaiuoli --Medici        Adimari    --Rossi         Adimari    --Ardinghelli  
 [4] Albizzi    --Guadagni      Albizzi    --Medici        Albizzi    --Della Casa   
 [7] Albizzi    --Salviati      Albizzi    --Fortini       Albizzi    --Guicciardini 
[10] Albizzi    --Scolari       Albizzi    --Benizzi       Albizzi    --Panciatichi  
[13] Albizzi    --Medici        Albizzi    --Guadagni      Albizzi    --Frescobaldi  
[16] Altoviti   --Gianfigliazzi Altoviti   --Spini         Altoviti   --Barbadori    
[19] Altoviti   --Ardinghelli   Albizzi    --Altoviti      Altoviti   --Guasconi     
[22] Albizzi    --Altoviti      Altoviti   --Bencivenni    Ardinghelli--Spini        
[25] Ardinghelli--Strozzi       Ardinghelli--Peruzzi       Ardinghelli--Lamberteschi 
[28] Ardinghelli--Strozzi       Ardinghelli--Gianfigliazzi Ardinghelli--Velluti      
+ ... omitted several edges
is.directed(florentine)
[1] FALSE

Aileler arasındaki en kısa yolları ve bu yolların uzunluğuna bakalım. Aynı zamanda tüm aileler arasındaki ortalama uzunluğa (ortalama en kısa yol) ve en uzak iki düğüm arasındaki en kısa uzaklığı (çap) hesaplıyoruz. Sonuçları karşılaştırdığımızda, Medici ailesinin diğer ailelere görece daha yakın olduğunu görebiliriz.


shortest_paths(florentine, 'Sertini', 'Medici')
$vpath
$vpath[[1]]
+ 4/88 vertices, named, from b94a16e:
[1] Sertini   Spini     Brancacci Medici   


$epath
NULL

$predecessors
NULL

$inbound_edges
NULL
distances(florentine, 'Medici', 'Sertini')
       Sertini
Medici       3
distances(florentine,'Medici')
       Acciaiuoli Adimari Albizzi Altoviti Ardinghelli Arrighi Arrigucci Baldovinetti Barbadori
Medici          1       1       1        2           2       3         2            2         1
       Bardi Bartoli Belfradelli Bencivenni Benizzi Berlinghieri Bischeri Brancacci Capponi
Medici     1       2           2          1       2            1        2         1       1
       Carducci Carnesecchi Castellani Cavalcanti Corbinelli Corsi Da Uzzano Dall'Antella
Medici        1           1          2          1          1     1         2            1
       Davanzati Degli Agli Del Forese Del Palagio Della Casa Dietisalvi Doffi Donati Fagni
Medici         1          2          1           2          1          1     3      1     2
       Fortini Frescobaldi Gianfigliazzi Ginori Giugni Guadagni Guasconi Guicciardini
Medici       2           2             1      1      1        2        1            1
       Lamberteschi Lapi Manelli Manovelli Martelli Medici Orlandini Panciatichi Pandolfini
Medici            2    2       2         2        1      0         1           2          1
       Pazzi Pepi Peruzzi Pitti Ricasoli Ridolfi Rondinelli Rossi Rucellai Sacchetti Salviati
Medici     1    3       2     1        2       1          1     2        2         2        1
       Scambrilla Scolari Serragli Serristori Sertini Solosmei Spini Strozzi Tornabuoni
Medici          2       2        2          1       3        1     2       1          1
       Vecchietti Velluti Baronci Bucelli Baroncelli Arnolfi Fioravanti Busini Da Panzano Valori
Medici          1       1       3       2          2       1          1      3          3      1
       Ciai Mancini Pecori Tornaquinci Raugi Aldobrandini
Medici    1       2      1           1     3            2
mean(distances(florentine, 'Medici')[lower.tri(distances(florentine))])
[1] NA
mean(distances(florentine)[lower.tri(distances(florentine))])
[1] 2.443312
mean_distance(florentine)
[1] 2.443312
average.path.length(florentine)
[1] 2.443312
diameter(florentine)
[1] 5
max(distances(florentine))
[1] 5

Aşağıda, bu ağdaki derece dağılımını gösteriyoruz.

degree(florentine)
   Acciaiuoli       Adimari       Albizzi      Altoviti   Ardinghelli       Arrighi 
            4            13            24            11            20             1 
    Arrigucci  Baldovinetti     Barbadori         Bardi       Bartoli   Belfradelli 
            4            15            18            38             2            10 
   Bencivenni       Benizzi  Berlinghieri      Bischeri     Brancacci       Capponi 
            9            14             2             6             7             5 
     Carducci   Carnesecchi    Castellani    Cavalcanti    Corbinelli         Corsi 
            7             5            23            10             5             3 
    Da Uzzano  Dall'Antella     Davanzati    Degli Agli    Del Forese   Del Palagio 
           12             4             9             3             3             3 
   Della Casa    Dietisalvi         Doffi        Donati         Fagni       Fortini 
           12             7             1             8             2             2 
  Frescobaldi Gianfigliazzi        Ginori        Giugni      Guadagni      Guasconi 
            8            17             9             8            10            18 
 Guicciardini  Lamberteschi          Lapi       Manelli     Manovelli      Martelli 
           28            11             1            15            12            14 
       Medici     Orlandini   Panciatichi    Pandolfini         Pazzi          Pepi 
          128            10            15             7             8             4 
      Peruzzi         Pitti      Ricasoli       Ridolfi    Rondinelli         Rossi 
           35             3            20             4            17            10 
     Rucellai     Sacchetti      Salviati    Scambrilla       Scolari      Serragli 
            2             5            13             5             3             5 
   Serristori       Sertini      Solosmei         Spini       Strozzi    Tornabuoni 
            6             1             7             9            45             7 
   Vecchietti       Velluti       Baronci       Bucelli    Baroncelli       Arnolfi 
            1            13             2             4             5             3 
   Fioravanti        Busini    Da Panzano        Valori          Ciai       Mancini 
            5             4             2             6             2             1 
       Pecori   Tornaquinci         Raugi  Aldobrandini 
            3             1             1             3 
hist(degree(florentine),breaks=20)

max(degree(florentine))
[1] 128

Düğüm merkezilikleri

Düğümlerin çeşitli merkezilik ölçütlerini (derece, yakınlık, ortadalık, pagerank, aktarma merkezi, otorite) aşağıda hesaplıyoruz. Bu merkezilik ölçütlerine göre aileleri sıralayıp sonuçları incelediğimizde, Medici ailesinin ağda merkezi bir konumda yer aldığını görebiliyoruz.

deg <- degree(florentine, normalized=TRUE)
cls <- closeness(florentine, normalized=TRUE)
btw <- betweenness(florentine, normalized=TRUE)
pgr <- page_rank(florentine)$vector
hub <- hub_score(florentine)$vector
aut <- authority_score(florentine)$vector

famcen <- data.frame(deg, cls, btw, pgr, hub, aut)

famcen[order(-famcen$deg), ]
famcen[order(-famcen$cls), ]
famcen[order(-famcen$btw), ]
famcen[order(-famcen$pgr), ]
famcen[order(-famcen$hub), ]
famcen[order(-famcen$aut), ]
NA

Email Ağı

Aşağıda, Avrupa’daki bir araştırma enstitüsündeki araştırmacılar arasındaki eposta iletişimini gösteren veri setinden yönlü bir ağ oluşturuyoruz.

Orijinal veri kaynağı: J. Leskovec, J. Kleinberg and C. Faloutsos. Graph Evolution: Densification and Shrinking Diameters. ACM Transactions on Knowledge Discovery from Data (ACM TKDD), 1(1), 2007.


emailDF <- read.csv("https://raw.githubusercontent.com/Zhachory1/eu-email-viz/master/email-Eu-core.txt", sep = " ")
email <- graph_from_data_frame(emailDF, directed = TRUE)
vcount(email)
[1] 1005
ecount(email)
[1] 25571
is.directed(email)
[1] TRUE
myViz(email) 

Yukarıdaki görselleştirmede, birbiriyle bağlantılı olmayan bileşenler (component) olduğunu görüyoruz. Aşağıda, yalnızca en çok düğüme sahip olan bileşeni seçerek yeni bir ağ oluşturuyoruz. Devam eden analizleri bu en büyük bağlantılı bileşen üzerinden yapacağız.

is.connected(email)
[1] FALSE
count_components(email, mode ="weak")
[1] 20
components(email, mode = "weak")$csize
 [1] 986   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1
components(email, mode = "weak")$membership[1:10]
 0  2  5  8 10 12 15 17 20 23 
 1  1  1  1  1  1  1  1  1  1 
cl = components(email)

emailLCC = induced.subgraph(email,  which(cl$membership == which.max(cl$csize)))
is.connected(emailLCC)
[1] TRUE
count_components(emailLCC, mode ="weak")
[1] 1
components(emailLCC, mode = "weak")$csize
[1] 986

Floransa evlilik ağı için yaptığımız merkezilik ölçümlerini bu ağ için de tekrar ediyoruz.

email <- emailLCC
deg <- degree(email, normalized=TRUE, mode = "in")
cls <- closeness(email, normalized=TRUE)
At centrality.c:2784 :closeness centrality is not well-defined for disconnected graphs
btw <- betweenness(email, normalized=TRUE)
pgr <- page_rank(email)$vector
hub <- hub_score(email)$vector
aut <- authority_score(email)$vector

emlcen <- data.frame(deg, cls, btw, pgr, hub, aut)

emlcen[order(-emlcen$deg), ]
emlcen[order(-emlcen$cls), ]
emlcen[order(-emlcen$btw), ]
emlcen[order(-emlcen$pgr), ]
emlcen[order(-emlcen$hub), ]
emlcen[order(-emlcen$aut), ]

Kümelenme ve Topluluk Yapısı

Zachary’nin Karate Kulübü

Karate ağındaki düğümler üyeleri, bağlar ise bu üyelerin dersler dışındaki ilişkilerinin var olup olmadığını göstermektedir. Mr. Hi, bu kulüpte karate eğitmeni, John A ise kulübün yöneticisidir. Zamanla Mr. Hi ve John A arasında anlaşmazlık çıkmış ve kulüp üyeleri ikiye ayrılarak farklı kulüplerde yer almışlardır. Aşağıdaki renkler, bu iki grubu temsil etmektedir.

Orijinal veri kaynağı: Wayne Zachary. An information flow model for conflict and fission in small groups. J. of Anthropol. Res., 33:452–473, 1977.

data(karate)
myViz(karate)

Aşağıda, bu ağın tamamı için ve her bir düğüm için kümelenme katsayısını hesaplıyoruz.

degree(karate)
   Mr Hi  Actor 2  Actor 3  Actor 4  Actor 5  Actor 6  Actor 7  Actor 8  Actor 9 Actor 10 
      16        9       10        6        3        4        4        4        5        2 
Actor 11 Actor 12 Actor 13 Actor 14 Actor 15 Actor 16 Actor 17 Actor 18 Actor 19 Actor 20 
       3        1        2        5        2        2        2        2        2        3 
Actor 21 Actor 22 Actor 23 Actor 24 Actor 25 Actor 26 Actor 27 Actor 28 Actor 29 Actor 30 
       2        2        2        5        3        3        2        4        3        4 
Actor 31 Actor 32 Actor 33   John A 
       4        6       12       17 
transitivity(karate, type="global")
[1] 0.2556818
transitivity(karate, type="local")
 [1] 0.1500000 0.3333333 0.2444444 0.6666667 0.6666667 0.5000000 0.5000000 1.0000000 0.5000000
[10] 0.0000000 0.6666667       NaN 1.0000000 0.6000000 1.0000000 1.0000000 1.0000000 1.0000000
[19] 1.0000000 0.3333333 1.0000000 1.0000000 1.0000000 0.4000000 0.3333333 0.3333333 1.0000000
[28] 0.1666667 0.3333333 0.6666667 0.5000000 0.2000000 0.1969697 0.1102941

Louvain algoritmasını kullanarak bu ağı farklı topluluklara böldüğümüzde, gerçek hayatta gerçekleşmiş olan toplulukları birebir yakaladığımızı görebiliyoruz.

V(karate)$color <- cluster_louvain(karate)$membership
myViz(karate)

İkiparçalı Ağlar, Projeksiyon, Öneri Sistemleri, Düğüm Benzerliği

Film - Kullanıcı Ağı

Aşağıda, kullanıcılar ve kullanıcıların oyladığı filmlerden oluşan bir ağ oluşturyoruz. Bu ağdaki linkler, kullanıcının ilgili filme 5 puan verdiğini göstermektedir.

Orijinal veri kaynağı:

movieuserDF <- read.csv("https://raw.githubusercontent.com/triandicAnt/GraphEmbeddingRecommendationSystem/master/data/train_user_ratings.dat", sep = "\t")

head(movieuserDF)
movieuserDF$movieID <- paste(as.character(movieuserDF$movieID) , "mov")
movieuser <- graph_from_data_frame(movieuserDF[movieuserDF$rating==5,1:2], directed = FALSE)

Bu ağdaki tüm linkler, farklı iki düğüm seti (kullanıcılar ve filmler) arasındadır. Dolayısıyla, iki parçalı bir ağ olarak değerlendirebiliriz.

vcount(movieuser)
[1] 607
ecount(movieuser)
[1] 979
is.bipartite(movieuser)
[1] FALSE
bipartite.mapping(movieuser)$res
[1] TRUE
V(movieuser)$type <- bipartite_mapping(movieuser)$type
is.bipartite(movieuser)
[1] TRUE

Bu iki parçalı ağı kullanıcılar ve filmler üzerine ayrı ayrı projekte ederek, kullanıcılar arasında ve filmler arasında ağlar oluşturuyoruz. Örneğin, iki kullanıcının arasındaki bağ, bu iki kullanıcının da 2 veya daha çok sayıda aynı filmlere 5 puan verdiğini göstermektedir.

usernet <- bipartite.projection(movieuser, multiplicity = TRUE)$proj1
usernet <- subgraph.edges(usernet, E(usernet)[E(usernet)$weight> 2])
myViz(usernet)


movienet <- bipartite.projection(movieuser, multiplicity = TRUE)$proj2
movienet <- subgraph.edges(movienet, E(movienet)[E(movienet)$weight> 3])

myViz(movienet)

Bu notları faydalı bulduysanız, bu sayfayı ilgilenebileceğini düşündüğünüz kişilerle paylaşabilirsiniz.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpCdSBzYXlmYWRha2kgacOnZXJpaywgW1ItTGFkaWVzIElzdGFuYnVsXShodHRwczovL3d3dy5tZWV0dXAuY29tL3JsYWRpZXMtaXN0YW5idWwvKSd1biBfUiBpbGUgU29zeWFsIEFnIEFuYWxpemlfIGJhxZ9sxLFrbMSxIEthc8SxbSAyMDE5IGV0a2lubGnEn2luZGUgc3VuZHXEn3VtIGRlcnNpbiB0YXNsYWsgbm90bGFyxLFuZGFuIG9sdcWfbWFrdGFkxLFyLiBEYWhhIGthcHNhbWzEsSB2ZSBnZW5pxZ8gYcOnxLFrbGFtYWxhcsSxbiB5ZXIgYWxkxLHEn8SxIHllbmkgYmlyIHZlcnNpeW9udSB5aW5lIGJ1IHNheWZhZGEgeWF5xLFubGF5YWNhxJ/EsW0uIFNvcnUsIHlvcnVtIHZlIMO2bmVyaWxlcmluaXogacOnaW4gaGVyIHphbWFuIHlhemFiaWxpcnNpbml6OiBbdHdpdHRlcl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9mdXJrYW5ndXJzb3kzNCksIFtlcG9zdGFdKG1haWx0bzpmdXJrYW4uZ3Vyc295QGJvdW4uZWR1LnRyKS4NCg0KDQojIyBHaXJpxZ8NCg0KQcSfLCBlbiBnZW5lbCB0YW7EsW3EsXlsYSwgYmlyYmlybGVyaW5lIGJhxJ9sYW5txLHFnyDFn2V5bGVyZGVuIG9sdcWfYW4gc2lzdGVtbGVyZSBkZW5pci4gQmlyIGHEnywgbm9rdGFsYXIgdmUgYnUgbm9rdGFsYXLEsSBiaXJiaXJpbmUgYmHEn2xheWFuIMOnaXpnaWxlcmxlIGfDtnJzZWxsZcWfdGlyaWxlYmlsaXIuIEJ1IG5va3RhbGFyIGTDvMSfw7xtICjEsG5nLiBub2RlLCB2ZXJ0ZXgpIHZleWEgYWt0w7ZyICjEsG5nLiBha3TDtnIpIG9sYXJhaywgw6dpemdpbGVyIGlzZSBiYcSfICjEsG5nLiBsaW5rLCB0aWUpIHZleWEgYXlyxLF0ICjEsG5nLiBlZGdlKSBvbGFyYWsgYWRsYW5kxLFyxLFsxLFyLg0KDQrEsG5zYW5sYXLEsW4gZml6aWtzZWwgdmV5YSBkaWppdGFsIGhheWF0dGFraSBzb3N5YWwgaWxpxZ9raWxlcmluaW4gYmlyIHNvc3lhbCBhxJ8gb2x1xZ90dXJtYXPEsSBnaWJpLCBoYXl2YW5sYXLEsW4ga2VuZGkgYXJhbGFyxLFuZGFraSBiZW56ZXIgaWxpxZ9raWxlciBkZSBzb3N5YWwgYcSfIG9sdcWfdHVyYWJpbGlyLiBGaXJtYWxhcsSxbiBiaXJiaXJsZXJpIGFyYXPEsW5kYWtpIGZpbmFuc2FsIHZlIHRpY2FyaSBpbGnFn2tpbGVyLCDDvGxrZWxlcmluIGl0aGFsYXQvaWhyYWNhdCBpbGnFn2tpbGVyaSwgZW5lcmppIGFrdGFyxLFtIMWfZWJla2VsZXJpLCBiZXNpbiB6aW5jaXJsZXJpLCBwcm90ZWluIGV0a2lsZcWfaW1sZXJpLCBpbnRlcm5ldCBhbHR5YXDEsXPEsSBkYSBhxJ9sYXJsYSB0ZW1zaWwgZWRpbGViaWxlY2VrIHNpc3RlbWxlcmUgw7ZybmVrdGlyLiBCdSBzaXN0ZW1sZXJpbiBiaXIgw6dvxJ91IGdlbmVsbGlrbGUga2FidWwgZWRpbGVuIGFubGFtZGEgYmlyIHNvc3lhbGxpayBpw6dlcm1leWViaWxpci4gRG9sYXnEsXPEsXlsYSwgX3Nvc3lhbCBhxJ8gYW5hbGl6aV8gdGVyaW1pIHllcmluaSBfYcSfIGFuYWxpemlfIHZlIF9hxJ8gYmlsaW1pXyBnaWJpIHRlcmltbGVyZSBixLFyYWttYXlhIGJhxZ9sYW3EscWfdMSxci4gQW5jYWssIGF5bsSxIHRlbWVsIGHEnyBhbmFsaXogdGVrbmlrbGVyaSDDp29rIGZhcmtsxLEgdGlwbGVyZGVraSBhxJ9sYXJkYSAow7Zybi4gc29zeWFsLCBla29ub21paywgYml5b2xvamlrLCBmaXppa3NlbCkgYmVuemVyIMWfZWtpbGRlIGt1bGxhbsSxbMSxci4NCg0KQcSfIGFuYWxpemksIHRlbWVsbGVyaW5pIFNvc3lvbG9qaSB2ZSBNYXRlbWF0aWt0ZW4gYWzEsXIuIFNvc3lvbG9nbGFyLCBhxJ9sYXIgdmFzxLF0YXPEsXlsYSBpbnNhbmxhcsSxbiBkacSfZXIgaW5zYW5sYXJsYSBvbGFuIGlsacWfa2lsZXJpbmkgdGVtc2lsIGVkZXJlaywgaW5zYW5sYXLEsW4gZGF2cmFuxLHFnyB2ZSDDtnplbGxpa2xlcmluaW4gYnUgaWxpxZ9raWxlcmkgbmFzxLFsIGRlxJ9pxZ90aXJkacSfaW5pIHZlIGF5bsSxIHphbWFuZGEgYnUgaWxpxZ9raWxlcmluIGluc2FubGFyxLFuIGRhdnJhbsSxxZ8gdmUgw7Z6ZWxsaWtsZXJpbmkgbmFzxLFsIGRlxJ9pxZ90aXJkacSfaW5pIGFubGFtYXlhIMOnYWzEscWfbcSxxZ9sYXJkxLFyLiDDlnRlIHlhbmRhbiwgTWF0ZW1hdGlrw6dpbGVyIGRlIGHEn2xhciDDvHplcmluZGUgw6dlxZ9pdGxpIHByb2JsZW1sZXIgKMO2cm4uIGVuIGvEsXNhIHlvbCBwcm9ibGVtaSkgdGFuxLFtbGF5YXJhayBidW5sYXJsYSBpbGdpbGVubWnFn2xlcmRpci4gWmFtYW5sYSwgYmlsZ2lzYXlhciBiaWxpbWksIGlzdGF0aXN0aWtzZWwgZml6aWssIHlhxZ9hbSBiaWxpbWkgZ2liaSBiaXIgw6dvayBhbGFuZGFraSBhcmHFn3TEsXJtYWPEsWxhciBhxJ8gYW5hbGl6aSB0ZWtuaWtsZXJpbmkga3VsbGFubWF5YSBiYcWfbGFtxLHFnywgaGVyIGJpciBhbGFuIGtlbmRpIHBlcnNwZWt0aWZpeWxlIGJ1IGFsYW7EsSBkYWhhIGRhIGdlbGnFn3Rpcm1pxZ8gdmUgemVuZ2lubGXFn3Rpcm1pxZ90aXIuDQoNCg0KQnUgYcOnxLFrIGRlcnN0ZSwgdXlndWxhbWFsxLEgYmlyIHnDtm50ZW0gYmVuaW1zZXllcmVrIGtvbnUgYcOnxLFrbGFtYWxhcsSxbsSxbiB2ZSBSIGRpbGl5bGUgdXlndWxhbWFsYXLEsW7EsW4gZcWfemFtYW5sxLEgZ2l0bWVzaSBpw6dpbiDDp2FiYSBnw7ZzdGVyZWNlxJ9pei4gX2lncmFwaF8ga8O8dMO8cGhhbmVzaW5pIHZlIGRpxJ9lciBrw7x0w7xwaGFuZWxlcmkgYcWfYcSfxLFkYWtpIMWfZWtpbGRlIHnDvGtsZXllbGltLg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoaWdyYXBoKQ0KbGlicmFyeSh2aXNOZXR3b3JrKQ0KbGlicmFyeShpZ3JhcGhkYXRhKQ0KYGBgDQoNCg0KDQpCaXIgYcSfxLFuIGTDvMSfw7xtbGVyIHZlIGJ1IGTDvMSfw7xtbGVyIGFyYXPEsW5kYWtpIGJhxJ9sYXJkYW4gb2x1xZ90dcSfdW51IGJlbGlydG1pxZ90aWsuIEJvxZ8gYmlyIGHEnyBvbHXFn3R1cmFyYWsgYnVuYSDDtm5jZSBkw7zEn8O8bWxlciwgc29ucmEgYmHEn2xhciBla2xleWVsaW0uIEJpciBhxJ9kYWtpIGJhxJ9sYXIgecO2bmzDvCB2ZXlhIHnDtm5zw7x6IG9sYWJpbGlyLiBCdSDDtnJuZWt0ZSB5w7Zuc8O8eiBiYcSfbGFyYSBzYWhpcCBiaXIgYcSfIG9sdcWfdHVyYWNhxJ/EsXouDQpgYGB7cn0NCmcgPC0gbWFrZV9lbXB0eV9ncmFwaChkaXJlY3RlZCA9IEZBTFNFKQ0KZyA8LSBnICsgdmVydGljZXMoIkVkYSIsICJDYW4iLCAiT3lhIiwgIkFsaSIsICJOdXIiLCAiQ2VtIikNCnBsb3QoZykNCmcgPC0gZyArIGVkZ2VzKCJFZGEiLCAiT3lhIiwgIkVkYSIsICJDZW0iLCAiQ2FuIiwgIkNlbSIsICJBbGkiLCAiTnVyIiwgIk51ciIsICJFZGEiKQ0KcGxvdChnKQ0KZyA8LSBnICsgZWRnZSgiQWxpIiwgIk95YSIpICsgZWRnZSgiTnVyIiwgIkNlbSIpDQpwbG90KGcpDQoNCmBgYA0KDQoNCkHEn2RhIGJ1bHVuYW4gZMO8xJ/DvG0gdmUgYmHEn2xhcsSxIGxpc3RlbGV5ZWxpbSwgZMO8xJ/DvG0gdmUgYmHEnyBhZGV0bGVyaW5pIGJ1bGFsxLFtLg0KYGBge3J9DQpWKGcpDQpFKGcpDQp2Y291bnQoZykNCmVjb3VudChnKQ0KYGBgDQoNCkTDvMSfw7xtbGVyaW4ga29txZ91IHNhecSxc8SxIGRlcmVjZSAoxLBuZy4gZGVncmVlKSBvbGFyYWsgaXNpbWxlbmRpcmlsaXIuIETDvMSfw7xtbGVyaW4ga29txZ91IHNhecSxc8SxbsSxLCB2ZSBrb23Fn3VsYXLEsW7EsW4ga2ltIG9sZHXEn3VudSBidWxhbMSxbS4NCmBgYHtyfQ0KZGVncmVlKGcpDQpkZWdyZWUoZywgIkFsaSIpDQpkZWdyZWUoZywgYygiQWxpIiwgIk95YSIpKQ0KbmVpZ2hib3JzKGcsICJBbGkiKQ0KbmVpZ2hib3JzKGcsICJPeWEiKQ0KYGBgDQoNClZlcmlsZW4gaWtpIGTDvMSfw7xtw7xuIGtvbcWfdSBvbHVwIG9sbWFkxLHEn8SxbsSxIGtvbnRyb2wgZWRlbGltLg0KYGBge3J9DQphcmVfYWRqYWNlbnQoZywgIkFsaSIsICJPeWEiKQ0KYXJlX2FkamFjZW50KGcsICJOdXIiLCAiT3lhIikNCmBgYA0KDQoNCg0KQcSfbGFyZGFraSBiYcSfbGFyxLFuIHnDtm5sw7wgZGUgb2xhYmlsZWNlxJ9pbmkgYmVsaXJ0bWnFn3Rpay4gRWxpbWl6ZGVraSBhxJ/EsW4gecO2bnPDvHogb2xkdcSfdW51IHRleWl0IGVkZWxpbSB2ZSB5w7ZubMO8IGJpciBhxJ9hIMOnZXZpcmVsaW0uIMOHZXZpcmlya2VuLCB2YXIgb2xhbiBiYcSfbGFyxLFuIHllcmluZSB5w7ZubMO8IGlraSBiYcSfbGFybGEgZGXEn2nFn3RpcmlsbWVzaW5pIHNhxJ9sYXlhbMSxbS4gQXJkxLFuZGFuIGJ1IGJhxJ9sYXJkYW4gYmF6xLFsYXLEsW7EsSBzaWxlbGltLg0KYGBge3J9DQppcy5kaXJlY3RlZChnKQ0KZzIgPC0gYXMuZGlyZWN0ZWQoZywgbW9kZSA9Im11dHVhbCIpDQpwbG90KGcyKQ0KDQpnMiA8LSBkZWxldGUuZWRnZXMoZzIsIGMoIkNhbnxDZW0iLCAiRWRhfENlbSIsICJDZW18RWRhIiwgIkVkYXxPeWEiLCAiTnVyfEFsaSIsICJBbGl8T3lhIikpDQpwbG90KGcyKQ0KYGBgDQoNClnDtm5sw7wgYcSfbGFyZGEsIGJpciBkw7zEn8O8bcO8biBkZXJlY2VzaSBzYWhpcCBvbGR1xJ91IGJhxJ9sYXLEsW4gecO2bsO8bmUgZ8O2cmUgacOnLWRlcmVjZSB2ZSBkxLHFny1kZXJlY2Ugb2xtYWsgw7x6ZXJlIGlraSDFn2VraWxkZSBoZXNhcGxhbmFiaWxpci4gQcWfYcSfxLFkYSBidW51IGhlc2FwbGF5YWzEsW0uDQpgYGB7cn0NCmRlZ3JlZShnMikNCmRlZ3JlZShnMiwgbW9kZT0iaW4iKQ0KZGVncmVlKGcyLCBtb2RlPSJvdXQiKQ0KYGBgDQoNCg0KWcO2bmzDvCBiaXIgYcSfZGEsIHZlcmlsZW4gYmlyIGTDvMSfw7xtZGVuIGRpxJ9lcmluZSBiaXIgYmHEnyBvbHVwIG9sbWFkxLHEn8SxbsSxIGtvbnRyb2wgZWRlbGltLg0KYGBge3J9DQphcmVfYWRqYWNlbnQoZzIsICJPeWEiLCAiRWRhIikNCmFyZV9hZGphY2VudChnMiwgIkVkYSIsICJPeWEiKQ0KYGBgDQoNCg0KX2lncmFwaF8ga8O8dMO8cGhhbmVzaSBkw7zEn8O8bWxlcmluIHZlIGJhxJ9sYXLEsW4gYWRsYXLEsW7EsSB2ZSBpZCdsZXJpbmkgZmFya2zEsSDFn2VraWxkZSB0dXRtYWt0YWTEsXIuIGhlciBiaXIgZMO8xJ/DvG3DvG4gMSdkZW4gYmHFn2xheWFuIHZlIHPEsXJhbMSxIGdpZGVuIHRhbSBzYXnEsSBpZCdsZXJpIHZhcmTEsXIuIEF5bsSxIGR1cnVtIGJhxJ9sYXIgacOnaW4gZGUgZ2XDp2VybGlkaXIuIEJ1IGR1cnVtZGEsICRuJCBkw7zEn8O8bSB2ZSAkbSQgYmHEn2RhbiBvbHXFn2FuIGJpciBhxJ8gacOnaW4gZMO8xJ/DvG0gaWQnbGVyaSAkezEsIDIsIC4uLCBufSQsIGJhxJ8gaWQnbGVyaSAkezEsIDIsIC4uLCBtfSQgb2xhcmFrIG9sdcWfYWNha3TEsXIuICBCdW5sYXJsYSBpbGdpbGkgYmlyIGthw6cgw7ZybmVrIHlhcGFsxLFtLg0KYGBge3J9DQpWKGcyKQ0KYXMubnVtZXJpYyhWKGcyKSkNCmFzLm51bWVyaWMoVihnMilbIk95YSJdKQ0KYGBgDQoNCg0KYGBge3J9DQpFKGcyKQ0KYXMubnVtZXJpYyhFKGcyKSkNCmFzLm51bWVyaWMoRShnMilbIk95YXxBbGkiXSkNCmdldC5lZGdlLmlkcyhnMiwgYygiT3lhIiwgIkFsaSIpKQ0KDQpgYGANCg0KRMO8xJ/DvG0gdmUgYmHEn2xhcsSxbiBpZCdsZXJpIGTDvMSfw7xtIHZlIGJhxJ9sYXLEsW4gc2lsaW5tZXNpIHZlIGJlbnplcmkgZHVydW1sYXJkYSB5ZW5pZGVuIG51bWFyYWxhbmTEsXLEsWxtYWt0YWTEsXIuIEHFn2HEn8SxZGEga29kIHBhcsOnYXPEsSBidW5hIGJpciDDtnJuZWsgZ8O2c3RlcmlyLg0KYGBge3J9DQpnMyA8LSBkZWxldGVfZWRnZXMoZzIsICJBbGl8TnVyIikNCmdldC5lZGdlLmlkcyhnMywgYygiT3lhIiwgIkFsaSIpKQ0KYGBgDQoNCg0KRMO8xJ/DvG1sZXJpbiBpc2ltbGVyaSBfbmFtZV8gaXNtaW5kZSBiaXIgw7Z6ZWxsaWsgw7x6ZXJpbmRlIHR1dHVsbWFrdGFkxLFyLg0KYGBge3J9DQpWKGczKSRuYW1lDQp2ZXJ0ZXhfYXR0cihnMykNCmVkZ2VfYXR0cihnMykNCmBgYA0KDQpEaWxlcnNlaywga2VuZGltaXogZGUgZMO8xJ/DvG1sZXJlIGZhcmtsxLEgw7Z6ZWxsaWtsZXIgZWtsZXllYmlsaXJpei4NCmBgYHtyfQ0KVihnMykkc3VybmFtZSA8LSBjKCJTb20iLCAiWcO2biIsICJLdW0iLCAiR8O8biIsICJHw7xuIiwgIkfDvG4iKQ0KVihnMylbIkFsaSJdJGFnZSA8LSAyNA0KDQp2ZXJ0ZXhfYXR0cihnMykNCmBgYA0KDQpCZW56ZXIgxZ9la2lsZGUgYmHEn2xhcmEgZGEgw7Z6ZWxsaWtsZXIgZWtsZXllYmlsaXIsIGJ1IMO2emVsbGlrbGVyZSBnw7ZyZSBmaWx0cmVsZW1lbGVyIHlhcGFiaWxpcml6Lg0KYGBge3J9DQoNCkUoZzMpJHdlaWdodCA8LSBjKDAuNSwgMS4sIDIuNSwgMS4sIDAuNSwgMS41LCAyLikNCmVkZ2VfYXR0cihnMykNCg0KRShnMylbd2VpZ2h0ID4gMV0NCmBgYA0KDQoNCkfDtnJzZWxsZXJpIGRlIMO2emVsbGXFn3RpcmViaWxpcml6LiBBbmNhayBidSBkZXJzdGUgYcSfIGfDtnJzZWxsZcWfdGlybWUgw7x6ZXJpbmRlIGR1cm1heWFjYcSfxLF6LiANCmBgYHtyfQ0KcGxvdChnMywgZWRnZS5hcnJvdy5zaXplPS41LCB2ZXJ0ZXguY29sb3I9ImdvbGQiLCB2ZXJ0ZXguc2l6ZT0yNSwgDQoNCiAgICAgdmVydGV4LmZyYW1lLmNvbG9yPSJncmF5IiwgdmVydGV4LmxhYmVsLmNvbG9yPSJibGFjayIsIA0KDQogICAgIHZlcnRleC5sYWJlbC5jZXg9MC44LCB2ZXJ0ZXgubGFiZWwuZGlzdD0yLCBlZGdlLmN1cnZlZD0wLjIpIA0KDQpwbG90KGczLCB2ZXJ0ZXguY29sb3I9ImdvbGQiLCB2ZXJ0ZXguc2l6ZT00MCwgbGF5b3V0PWxheW91dF9pbl9jaXJjbGUpDQoNCg0KYGBgDQoNCg0KQcWfYcSfxLFkYSBkZXJzaW4gZ2VyaSBrYWxhbsSxbmRhIGt1bGxhbmFjYcSfxLFtxLF6IGJpciBnw7Zyc2VsbGXFn3Rpcm1lIGZvbmtzaXlvbnUgdGFuxLFtbMSxeW9ydXouDQpgYGB7cn0NCm15Vml6IDwtIGZ1bmN0aW9uIChteUdyYXBoKXsNCiAgdmlzSWdyYXBoKG15R3JhcGgsIHJhbmRvbVNlZWQgPSAxMSkgICU+JQ0KICB2aXNJZ3JhcGhMYXlvdXQocmFuZG9tU2VlZCA9IDExKSAlPiUNCiAgdmlzTm9kZXMoDQogICAgZm9udCA9IGxpc3Qoc2l6ZSA9IDU5KSwNCiAgICBzaGFwZSA9ICJkb3QiLA0KICAgIGNvbG9yID0gbGlzdCgNCiAgICAgIGJhY2tncm91bmQgPSAiIzAwODVBRiIsDQogICAgICBib3JkZXIgPSAiIzAxMzg0OCIsDQogICAgICBoaWdobGlnaHQgPSAiI0ZGODAwMCINCiAgICApLA0KICAgIHNoYWRvdyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUsIHNpemUgPSAxMCkNCiAgKSAlPiUNCiAgdmlzRWRnZXMoDQogICAgc2hhZG93ID0gRkFMU0UsDQogICAgY29sb3IgPSBsaXN0KGNvbG9yID0gIiMwMDg1QUYiLCBoaWdobGlnaHQgPSAiI0M2MkY0QiIpDQogICkgJT4lDQogIHZpc0xheW91dChyYW5kb21TZWVkID0gMTEpDQp9DQpgYGANCg0KDQoNCiMjIEJhesSxIFRlbWVsIEHEnyDEsHN0YXRpc3Rpa2xlcmkgdmUgRMO8xJ/DvG0gTWVya2V6aWxpxJ9pDQoNCiMjIyBGbG9yYW5zYSdkYWtpIEV2bGlsaWsgQcSfbGFyxLENCg0KQcWfYcSfxLFkYSwgMTQyNi0xNDM0IHnEsWxsYXLEsSBpw6dpbiBGbG9yYW5zYSdkYWtpIGFpbGVsZXIgYXJhc8SxbmRha2kgZXZsaWxpayBiYcSfbGFyxLFuxLEgZ8O2c3RlcmVuIGJpciBhxJ8gb2x1xZ90dXJ1eW9ydXouIEhlciBiaXIgZMO8xJ/DvG0gYmlyIGFpbGV5aSwgaGVyIGJhxJ8gYnUgYWlsZWxlciBhcmFzxLFuZGEgYmlyIGV2bGlsaWsgYmHEn8SxIG9sZHXEn3VudSBnw7ZzdGVybWVrdGVkaXIuDQoNCk9yaWppbmFsIHZlcmkga2F5bmHEn8SxOiBfS2VudCwgRC4gVi4gKDE5NzgpLiBUaGUgcmlzZSBvZiB0aGUgTWVkaWNpOiBGYWN0aW9uIGluIEZsb3JlbmNlLCAxNDI2LTE0MzQuIE94Zm9yZCBVbml2ZXJzaXR5IFByZXNzLCBVU0EuXw0KYGBge3J9DQpmbG9yZW50aW5lUmVsc2hpcHMgPC0gcmVhZC5jc3YoImh0dHA6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xoZWhua2UvcmVuYWlzc2FuY2UtZmxvcmVuY2UtbmV0d29yay1kYXRhL21hc3Rlci9mbG9yZW50aW5lX2ZhbWlsaWVzX3JlbGF0aW9uc19tYXRyaXhfc2FtcGxlLmNzdiIpDQoNCmhlYWQoZmxvcmVudGluZVJlbHNoaXBzKQ0KDQpmbG9yZW50aW5lTWFycmlhZ2VzIDwtIGZsb3JlbnRpbmVSZWxzaGlwc1tmbG9yZW50aW5lUmVsc2hpcHMkbWFycmlhZ2UgPT0xLCAxOjJdDQoNCmlzLmRhdGEuZnJhbWUoZmxvcmVudGluZVJlbHNoaXBzKQ0KDQpmbG9yZW50aW5lIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShmbG9yZW50aW5lUmVsc2hpcHMsIGRpcmVjdGVkID0gRkFMU0UpDQoNCnBsb3QoZmxvcmVudGluZSwgbGF5b3V0PWxheW91dF93aXRoX2xnbCkNCg0KbXlWaXooZmxvcmVudGluZSkNCg0KYGBgDQoNCg0KIyMjIFRlbWVsIGlzdGF0aXN0aWtsZXINCmBgYHtyfQ0KVihmbG9yZW50aW5lKQ0KRShmbG9yZW50aW5lKQ0KaXMuZGlyZWN0ZWQoZmxvcmVudGluZSkNCmBgYA0KDQpBaWxlbGVyIGFyYXPEsW5kYWtpIGVuIGvEsXNhIHlvbGxhcsSxIHZlIGJ1IHlvbGxhcsSxbiB1enVubHXEn3VuYSBiYWthbMSxbS4gQXluxLEgemFtYW5kYSB0w7xtIGFpbGVsZXIgYXJhc8SxbmRha2kgb3J0YWxhbWEgdXp1bmx1xJ9hIChvcnRhbGFtYSBlbiBrxLFzYSB5b2wpIHZlIGVuIHV6YWsgaWtpIGTDvMSfw7xtIGFyYXPEsW5kYWtpIGVuIGvEsXNhIHV6YWtsxLHEn8SxICjDp2FwKSBoZXNhcGzEsXlvcnV6LiBTb251w6dsYXLEsSBrYXLFn8SxbGHFn3TEsXJkxLHEn8SxbcSxemRhLCBNZWRpY2kgYWlsZXNpbmluIGRpxJ9lciBhaWxlbGVyZSBnw7ZyZWNlIGRhaGEgeWFrxLFuIG9sZHXEn3VudSBnw7ZyZWJpbGlyaXouDQoNCmBgYHtyfQ0KDQpzaG9ydGVzdF9wYXRocyhmbG9yZW50aW5lLCAnU2VydGluaScsICdNZWRpY2knKQ0KDQpkaXN0YW5jZXMoZmxvcmVudGluZSwgJ01lZGljaScsICdTZXJ0aW5pJykNCmRpc3RhbmNlcyhmbG9yZW50aW5lLCdNZWRpY2knKQ0KbWVhbihkaXN0YW5jZXMoZmxvcmVudGluZSwgJ01lZGljaScpW2xvd2VyLnRyaShkaXN0YW5jZXMoZmxvcmVudGluZSkpXSkNCg0KbWVhbihkaXN0YW5jZXMoZmxvcmVudGluZSlbbG93ZXIudHJpKGRpc3RhbmNlcyhmbG9yZW50aW5lKSldKQ0KbWVhbl9kaXN0YW5jZShmbG9yZW50aW5lKQ0KYXZlcmFnZS5wYXRoLmxlbmd0aChmbG9yZW50aW5lKQ0KDQoNCmRpYW1ldGVyKGZsb3JlbnRpbmUpDQptYXgoZGlzdGFuY2VzKGZsb3JlbnRpbmUpKQ0KYGBgDQoNCg0KQcWfYcSfxLFkYSwgYnUgYcSfZGFraSBkZXJlY2UgZGHEn8SxbMSxbcSxbsSxIGfDtnN0ZXJpeW9ydXouDQpgYGB7cn0NCmRlZ3JlZShmbG9yZW50aW5lKQ0KaGlzdChkZWdyZWUoZmxvcmVudGluZSksYnJlYWtzPTIwKQ0KbWF4KGRlZ3JlZShmbG9yZW50aW5lKSkNCmBgYA0KDQoNCiMjIyBEw7zEn8O8bSBtZXJrZXppbGlrbGVyaQ0KRMO8xJ/DvG1sZXJpbiDDp2XFn2l0bGkgbWVya2V6aWxpayDDtmzDp8O8dGxlcmluaSAoZGVyZWNlLCB5YWvEsW5sxLFrLCBvcnRhZGFsxLFrLCBwYWdlcmFuaywgYWt0YXJtYSBtZXJrZXppLCBvdG9yaXRlKSBhxZ9hxJ/EsWRhIGhlc2FwbMSxeW9ydXouIEJ1IG1lcmtlemlsaWsgw7Zsw6fDvHRsZXJpbmUgZ8O2cmUgYWlsZWxlcmkgc8SxcmFsYXnEsXAgc29udcOnbGFyxLEgaW5jZWxlZGnEn2ltaXpkZSwgTWVkaWNpIGFpbGVzaW5pbiBhxJ9kYSBtZXJrZXppIGJpciBrb251bWRhIHllciBhbGTEscSfxLFuxLEgZ8O2cmViaWxpeW9ydXouDQpgYGB7cn0NCmRlZyA8LSBkZWdyZWUoZmxvcmVudGluZSwgbm9ybWFsaXplZD1UUlVFKQ0KY2xzIDwtIGNsb3NlbmVzcyhmbG9yZW50aW5lLCBub3JtYWxpemVkPVRSVUUpDQpidHcgPC0gYmV0d2Vlbm5lc3MoZmxvcmVudGluZSwgbm9ybWFsaXplZD1UUlVFKQ0KcGdyIDwtIHBhZ2VfcmFuayhmbG9yZW50aW5lKSR2ZWN0b3INCmh1YiA8LSBodWJfc2NvcmUoZmxvcmVudGluZSkkdmVjdG9yDQphdXQgPC0gYXV0aG9yaXR5X3Njb3JlKGZsb3JlbnRpbmUpJHZlY3Rvcg0KDQpmYW1jZW4gPC0gZGF0YS5mcmFtZShkZWcsIGNscywgYnR3LCBwZ3IsIGh1YiwgYXV0KQ0KDQpmYW1jZW5bb3JkZXIoLWZhbWNlbiRkZWcpLCBdDQpmYW1jZW5bb3JkZXIoLWZhbWNlbiRjbHMpLCBdDQpmYW1jZW5bb3JkZXIoLWZhbWNlbiRidHcpLCBdDQpmYW1jZW5bb3JkZXIoLWZhbWNlbiRwZ3IpLCBdDQpmYW1jZW5bb3JkZXIoLWZhbWNlbiRodWIpLCBdDQpmYW1jZW5bb3JkZXIoLWZhbWNlbiRhdXQpLCBdDQoNCmBgYA0KDQoNCiMjIyBFbWFpbCBBxJ/EsQ0KDQpBxZ9hxJ/EsWRhLCBBdnJ1cGEnZGFraSBiaXIgYXJhxZ90xLFybWEgZW5zdGl0w7xzw7xuZGVraSBhcmHFn3TEsXJtYWPEsWxhciBhcmFzxLFuZGFraSBlcG9zdGEgaWxldGnFn2ltaW5pIGfDtnN0ZXJlbiB2ZXJpIHNldGluZGVuIHnDtm5sw7wgYmlyIGHEnyBvbHXFn3R1cnV5b3J1ei4NCg0KT3JpamluYWwgdmVyaSBrYXluYcSfxLE6IF9KLiBMZXNrb3ZlYywgSi4gS2xlaW5iZXJnIGFuZCBDLiBGYWxvdXRzb3MuIEdyYXBoIEV2b2x1dGlvbjogRGVuc2lmaWNhdGlvbiBhbmQgU2hyaW5raW5nIERpYW1ldGVycy4gQUNNIFRyYW5zYWN0aW9ucyBvbiBLbm93bGVkZ2UgRGlzY292ZXJ5IGZyb20gRGF0YSAoQUNNIFRLREQpLCAxKDEpLCAyMDA3Ll8NCg0KDQpgYGB7cn0NCg0KZW1haWxERiA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1poYWNob3J5MS9ldS1lbWFpbC12aXovbWFzdGVyL2VtYWlsLUV1LWNvcmUudHh0Iiwgc2VwID0gIiAiKQ0KZW1haWwgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGVtYWlsREYsIGRpcmVjdGVkID0gVFJVRSkNCnZjb3VudChlbWFpbCkNCmVjb3VudChlbWFpbCkNCmlzLmRpcmVjdGVkKGVtYWlsKQ0KYGBgDQoNCg0KYGBge3J9DQpteVZpeihlbWFpbCkgDQpgYGANCg0KDQpZdWthcsSxZGFraSBnw7Zyc2VsbGXFn3Rpcm1lZGUsIGJpcmJpcml5bGUgYmHEn2xhbnTEsWzEsSBvbG1heWFuIGJpbGXFn2VubGVyIChjb21wb25lbnQpIG9sZHXEn3VudSBnw7Zyw7x5b3J1ei4gQcWfYcSfxLFkYSwgeWFsbsSxemNhIGVuIMOnb2sgZMO8xJ/DvG1lIHNhaGlwIG9sYW4gYmlsZcWfZW5pIHNlw6dlcmVrIHllbmkgYmlyIGHEnyBvbHXFn3R1cnV5b3J1ei4gRGV2YW0gZWRlbiBhbmFsaXpsZXJpIGJ1IGVuIGLDvHnDvGsgYmHEn2xhbnTEsWzEsSBiaWxlxZ9lbiDDvHplcmluZGVuIHlhcGFjYcSfxLF6Lg0KYGBge3J9DQppcy5jb25uZWN0ZWQoZW1haWwpDQpjb3VudF9jb21wb25lbnRzKGVtYWlsLCBtb2RlID0id2VhayIpDQpjb21wb25lbnRzKGVtYWlsLCBtb2RlID0gIndlYWsiKSRjc2l6ZQ0KY29tcG9uZW50cyhlbWFpbCwgbW9kZSA9ICJ3ZWFrIikkbWVtYmVyc2hpcFsxOjEwXQ0KY2wgPSBjb21wb25lbnRzKGVtYWlsKQ0KDQplbWFpbExDQyA9IGluZHVjZWQuc3ViZ3JhcGgoZW1haWwsICB3aGljaChjbCRtZW1iZXJzaGlwID09IHdoaWNoLm1heChjbCRjc2l6ZSkpKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmlzLmNvbm5lY3RlZChlbWFpbExDQykNCmNvdW50X2NvbXBvbmVudHMoZW1haWxMQ0MsIG1vZGUgPSJ3ZWFrIikNCmNvbXBvbmVudHMoZW1haWxMQ0MsIG1vZGUgPSAid2VhayIpJGNzaXplDQpgYGANCg0KDQpGbG9yYW5zYSBldmxpbGlrIGHEn8SxIGnDp2luIHlhcHTEscSfxLFtxLF6IG1lcmtlemlsaWsgw7Zsw6fDvG1sZXJpbmkgYnUgYcSfIGnDp2luIGRlIHRla3JhciBlZGl5b3J1ei4NCmBgYHtyfQ0KZW1haWwgPC0gZW1haWxMQ0MNCmRlZyA8LSBkZWdyZWUoZW1haWwsIG5vcm1hbGl6ZWQ9VFJVRSwgbW9kZSA9ICJpbiIpDQpjbHMgPC0gY2xvc2VuZXNzKGVtYWlsLCBub3JtYWxpemVkPVRSVUUpDQpidHcgPC0gYmV0d2Vlbm5lc3MoZW1haWwsIG5vcm1hbGl6ZWQ9VFJVRSkNCnBnciA8LSBwYWdlX3JhbmsoZW1haWwpJHZlY3Rvcg0KaHViIDwtIGh1Yl9zY29yZShlbWFpbCkkdmVjdG9yDQphdXQgPC0gYXV0aG9yaXR5X3Njb3JlKGVtYWlsKSR2ZWN0b3INCg0KZW1sY2VuIDwtIGRhdGEuZnJhbWUoZGVnLCBjbHMsIGJ0dywgcGdyLCBodWIsIGF1dCkNCg0KZW1sY2VuW29yZGVyKC1lbWxjZW4kZGVnKSwgXQ0KZW1sY2VuW29yZGVyKC1lbWxjZW4kY2xzKSwgXQ0KZW1sY2VuW29yZGVyKC1lbWxjZW4kYnR3KSwgXQ0KZW1sY2VuW29yZGVyKC1lbWxjZW4kcGdyKSwgXQ0KZW1sY2VuW29yZGVyKC1lbWxjZW4kaHViKSwgXQ0KZW1sY2VuW29yZGVyKC1lbWxjZW4kYXV0KSwgXQ0KYGBgDQoNCg0KDQoNCiMjIEvDvG1lbGVubWUgdmUgVG9wbHVsdWsgWWFwxLFzxLENCg0KIyMjIFphY2hhcnknbmluIEthcmF0ZSBLdWzDvGLDvA0KDQpLYXJhdGUgYcSfxLFuZGFraSBkw7zEn8O8bWxlciDDvHllbGVyaSwgYmHEn2xhciBpc2UgYnUgw7x5ZWxlcmluIGRlcnNsZXIgZMSxxZ/EsW5kYWtpIGlsacWfa2lsZXJpbmluIHZhciBvbHVwIG9sbWFkxLHEn8SxbsSxIGfDtnN0ZXJtZWt0ZWRpci4gTXIuIEhpLCBidSBrdWzDvHB0ZSBrYXJhdGUgZcSfaXRtZW5pLCBKb2huIEEgaXNlIGt1bMO8YsO8biB5w7ZuZXRpY2lzaWRpci4gDQpaYW1hbmxhIE1yLiBIaSB2ZSBKb2huIEEgYXJhc8SxbmRhIGFubGHFn21hemzEsWsgw6fEsWttxLHFnyB2ZSBrdWzDvHAgw7x5ZWxlcmkgaWtpeWUgYXlyxLFsYXJhayBmYXJrbMSxIGt1bMO8cGxlcmRlIHllciBhbG3EscWfbGFyZMSxci4gQcWfYcSfxLFkYWtpIHJlbmtsZXIsIGJ1IGlraSBncnVidSB0ZW1zaWwgZXRtZWt0ZWRpci4NCg0KDQpPcmlqaW5hbCB2ZXJpIGtheW5hxJ/EsTogX1dheW5lIFphY2hhcnkuIEFuIGluZm9ybWF0aW9uIGZsb3cgbW9kZWwgZm9yIGNvbmZsaWN0IGFuZCBmaXNzaW9uIGluIHNtYWxsIGdyb3Vwcy4gSi4gb2YgQW50aHJvcG9sLiBSZXMuLCAzMzo0NTLigJM0NzMsIDE5NzcuXw0KDQpgYGB7cn0NCmRhdGEoa2FyYXRlKQ0KbXlWaXooa2FyYXRlKQ0KYGBgDQoNCkHFn2HEn8SxZGEsIGJ1IGHEn8SxbiB0YW1hbcSxIGnDp2luIHZlIGhlciBiaXIgZMO8xJ/DvG0gacOnaW4ga8O8bWVsZW5tZSBrYXRzYXnEsXPEsW7EsSBoZXNhcGzEsXlvcnV6Lg0KYGBge3J9DQpkZWdyZWUoa2FyYXRlKQ0KdHJhbnNpdGl2aXR5KGthcmF0ZSwgdHlwZT0iZ2xvYmFsIikNCnRyYW5zaXRpdml0eShrYXJhdGUsIHR5cGU9ImxvY2FsIikNCg0KYGBgDQoNCg0KTG91dmFpbiBhbGdvcml0bWFzxLFuxLEga3VsbGFuYXJhayBidSBhxJ/EsSBmYXJrbMSxIHRvcGx1bHVrbGFyYSBiw7ZsZMO8xJ/DvG3DvHpkZSwgZ2Vyw6dlayBoYXlhdHRhIGdlcsOnZWtsZcWfbWnFnyBvbGFuIHRvcGx1bHVrbGFyxLEgYmlyZWJpciB5YWthbGFkxLHEn8SxbcSxesSxIGfDtnJlYmlsaXlvcnV6Lg0KYGBge3J9DQpWKGthcmF0ZSkkY29sb3IgPC0gY2x1c3Rlcl9sb3V2YWluKGthcmF0ZSkkbWVtYmVyc2hpcA0KbXlWaXooa2FyYXRlKQ0KYGBgDQoNCiMjIMSwa2lwYXLDp2FsxLEgQcSfbGFyLCBQcm9qZWtzaXlvbiwgw5ZuZXJpIFNpc3RlbWxlcmksIETDvMSfw7xtIEJlbnplcmxpxJ9pDQoNCiMjIyBGaWxtIC0gS3VsbGFuxLFjxLEgQcSfxLENCg0KQcWfYcSfxLFkYSwga3VsbGFuxLFjxLFsYXIgdmUga3VsbGFuxLFjxLFsYXLEsW4gb3lsYWTEscSfxLEgZmlsbWxlcmRlbiBvbHXFn2FuIGJpciBhxJ8gb2x1xZ90dXJ5b3J1ei4gQnUgYcSfZGFraSBsaW5rbGVyLCBrdWxsYW7EsWPEsW7EsW4gaWxnaWxpIGZpbG1lIDUgcHVhbiB2ZXJkacSfaW5pIGfDtnN0ZXJtZWt0ZWRpci4NCg0KT3JpamluYWwgdmVyaSBrYXluYcSfxLE6IF8uLi5fDQoNCmBgYHtyfQ0KbW92aWV1c2VyREYgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS90cmlhbmRpY0FudC9HcmFwaEVtYmVkZGluZ1JlY29tbWVuZGF0aW9uU3lzdGVtL21hc3Rlci9kYXRhL3RyYWluX3VzZXJfcmF0aW5ncy5kYXQiLCBzZXAgPSAiXHQiKQ0KDQpoZWFkKG1vdmlldXNlckRGKQ0KbW92aWV1c2VyREYkbW92aWVJRCA8LSBwYXN0ZShhcy5jaGFyYWN0ZXIobW92aWV1c2VyREYkbW92aWVJRCkgLCAibW92IikNCm1vdmlldXNlciA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUobW92aWV1c2VyREZbbW92aWV1c2VyREYkcmF0aW5nPT01LDE6Ml0sIGRpcmVjdGVkID0gRkFMU0UpDQpgYGANCg0KQnUgYcSfZGFraSB0w7xtIGxpbmtsZXIsIGZhcmtsxLEgaWtpIGTDvMSfw7xtIHNldGkgKGt1bGxhbsSxY8SxbGFyIHZlIGZpbG1sZXIpIGFyYXPEsW5kYWTEsXIuIERvbGF5xLFzxLF5bGEsIGlraSBwYXLDp2FsxLEgYmlyIGHEnyBvbGFyYWsgZGXEn2VybGVuZGlyZWJpbGlyaXouDQpgYGB7cn0NCnZjb3VudChtb3ZpZXVzZXIpDQplY291bnQobW92aWV1c2VyKQ0KaXMuYmlwYXJ0aXRlKG1vdmlldXNlcikNCmJpcGFydGl0ZS5tYXBwaW5nKG1vdmlldXNlcikkcmVzDQpWKG1vdmlldXNlcikkdHlwZSA8LSBiaXBhcnRpdGVfbWFwcGluZyhtb3ZpZXVzZXIpJHR5cGUNCmlzLmJpcGFydGl0ZShtb3ZpZXVzZXIpDQpgYGANCg0KQnUgaWtpIHBhcsOnYWzEsSBhxJ/EsSBrdWxsYW7EsWPEsWxhciB2ZSBmaWxtbGVyIMO8emVyaW5lIGF5csSxIGF5csSxIHByb2pla3RlIGVkZXJlaywga3VsbGFuxLFjxLFsYXIgYXJhc8SxbmRhIHZlIGZpbG1sZXIgYXJhc8SxbmRhIGHEn2xhciBvbHXFn3R1cnV5b3J1ei4gw5ZybmXEn2luLCBpa2kga3VsbGFuxLFjxLFuxLFuIGFyYXPEsW5kYWtpIGJhxJ8sIGJ1IGlraSBrdWxsYW7EsWPEsW7EsW4gZGEgMiB2ZXlhIGRhaGEgw6dvayBzYXnEsWRhIGF5bsSxIGZpbG1sZXJlIDUgcHVhbiB2ZXJkacSfaW5pIGfDtnN0ZXJtZWt0ZWRpci4NCmBgYHtyfQ0KdXNlcm5ldCA8LSBiaXBhcnRpdGUucHJvamVjdGlvbihtb3ZpZXVzZXIsIG11bHRpcGxpY2l0eSA9IFRSVUUpJHByb2oxDQp1c2VybmV0IDwtIHN1YmdyYXBoLmVkZ2VzKHVzZXJuZXQsIEUodXNlcm5ldClbRSh1c2VybmV0KSR3ZWlnaHQ+IDJdKQ0KbXlWaXoodXNlcm5ldCkNCg0KbW92aWVuZXQgPC0gYmlwYXJ0aXRlLnByb2plY3Rpb24obW92aWV1c2VyLCBtdWx0aXBsaWNpdHkgPSBUUlVFKSRwcm9qMg0KbW92aWVuZXQgPC0gc3ViZ3JhcGguZWRnZXMobW92aWVuZXQsIEUobW92aWVuZXQpW0UobW92aWVuZXQpJHdlaWdodD4gM10pDQoNCm15Vml6KG1vdmllbmV0KQ0KYGBgDQoNCg0KDQpCdSBub3RsYXLEsSBmYXlkYWzEsSBidWxkdXlzYW7EsXosIGJ1IHNheWZhecSxIGlsZ2lsZW5lYmlsZWNlxJ9pbmkgZMO8xZ/DvG5kw7zEn8O8bsO8eiBracWfaWxlcmxlIHBheWxhxZ9hYmlsaXJzaW5pei4NCg==