sunnuntai 31. tammikuuta 2016

Social network of US presidential hopefuls

The US presidential elections will be finally kicked off tomorrow with Iowa being the first state to vote in primaries. Democratic contest will be largely between Hillary Clinton and Bernie Sanders with Martin O'Malley providing the third alternative. Donald Trump heads to Iowa leading the more contested Republican field. In due time, more and more hopefuls will drop from the race and then it's a race to gather the scraps between the remaining candidates. But who is there to most likely gain from dropout's supporters looking for a new political home?

In this blogpost we will use data from Facebook to plot a network graph of US presidential nominees. We build on the assumption that "liking" a Facebook post indicates a level of support for the person and liking more than one candidate's posts indicates that the person in question supports both/all of the candidates and could consider voting any of them in the actual election.

So, our indicator of closeness between Facebook candidates will be amount of individuals who like a post of both candidates.




In order to achieve this, we need help from three R libraries. Already introduced RFacebook to access the Facebook API, text mining package tm to create the candidate X candidate matrix with amount of shared "likers" in cells and finally qgraph (a convenient package for creating automatic network graphs when you don't want to tinker with details in igraph)

The code used in this blogpost is below:

pages <- c("DonaldTrump","hillaryclinton","berniesanders","MartinOMalley","realbencarson","govchristie",
           "tedcruzpage","CarlyFiorina","gilmoreforamerica","mikehuckabee","JohnKasich","RandPaul","MarcoRubio",
           "RickSantorum","jebbush")


listing <- list()
for (i in 1:length(pages)) {
temp <- getPage(pages[i], token=fb_oauth, n=3) 
temp2 <- temp$id 
id <- c() 

for (j in 1:length(temp2))  {
temp3 <- temp2[j]
post <- getPost(temp3,token=fb_oauth) 
likes <- post$likes$from_id
id <- c(id,likes) 
}

id <- unique(id)
listing[[i]] <- id

}

doc <- Corpus(VectorSource(listing))

shorts <- c("DT","HC","BS","MM","BC","CC","TC","CF","JG","MH","JK","RP","MR","RS","JB")

dtm <- DocumentTermMatrix(doc)
dtm <- as.matrix(dtm)
dtm <- dtm %*% t(dtm)
dtm <- dtm/diag(dtm)
row.names(dtm) <- shorts
colnames(dtm) <- shorts
diag(dtm) <- 0
qgraph(dtm)

First part is pretty much the same as used in the previous foray into the mysterious world of Facebook API. Remaining part is pretty straightforward as well. After collecting all the unique id:s from Facebook and saved them to a list called listing (sometimes I feel I am really bad at making up these object names) we transform the data to a text corpus. Text corpus is then turned into a document term matrix (an object with documents on the rows and terms (in this case Facebook ID:s) in the columns. We then multiply this document term matrix with it's transpose matrix to create the final matrix with candidates on both rows and columns and cell values indicating amount of shared likers.

However, simple absolute cell counts are a poor indicator for two candidate's closeness. Say, candidate A has 10 000 likes and candidate B has 1000. They share 500 likes. Obviously for candidate B, A is a very close candidate but the other way around the importance is lesser. Thus we want the relationsships to be relative and directed.

To achieve this, we divide each cell with the value in the diagonal cell of that row. Then we end up with a value ranging from 0 to 1, indicating how big a percentage of candidate's likers also liked the other candidate. So the value for A and B respectively is different, depending on the perspective. After the division, we set diagonal to 0 as we don't want candidates to have networks with themselves.

In order to know who is who, we name the rows and columns by the initials of candidates (sorry Bernie, no hard feelings?)



Finally, we end up with a graph seen above (sample used was three most recent Facebook posts due to constraints in Facebook API. And also because after manually collecting likes from 20 latest posts manually, the resulting network graph lost in clarity due to bigger masses leading to stronger networks)

Obviously, Republican candidates are far away from the Democrats. Martin O'Malley shares some likers with Chris Christie, John Kasich and Jim Gilmore but Bernie and Hillary are safely isolated from Republican social media, with very weak ties between themselves. It seems that with a narrow selection of candidates, people are more focused on supporting their own preferred man/woman.

In the republican field the people have more ties. The ultimate outsider and thorn in the backs of Republican establishment, Donald Trump, is relatively isolated from the rest of the pack but with strongest ties to two other more or less fiery and controversial anti-establishment candidates, Ben Carson and Ted Cruz. Carson and Cruz share the strongest tie seen in the sample, with Mike Huckabee also a close match for the duo. On the more moderate Republican camp, Jeb Bush, Chris Christie and Jim Gilmore create a close trio. Bush, the sorry establishment favourite sorely lacking in popular support, is however also close tied to Carly Fiorina, Rick Santorum and John Kasich. However, even with all these candidates dropping and leaving their supporters to Bush, that might still not enough to save him. Rand Paul, the son of libertarian Ron Paul with apparently almost equal distaste for federal government meddling as his father, is another lone wolf championing for the libertarian cause.

The sample used here has a total of 17161 observations (handily calculated using lapply, length and sum functions like this: sum(unlist(lapply(listing,length)))) ) so while the time frame of three posts is narrow, total amount of cases is still high enough to draw some initial insights. Especially on the Republican field, each candidate should carefully try to calculate the possible supporters gained after each drop out.

Ei kommentteja:

Lähetä kommentti