Clifton_Baldwin

Data Science Journal of Clif Baldwin

Analyzing Tweets about the Philadelphia Flyers Part 2

8 April ’18

Twitter Analysis of Philly Flyers

This post is a continuation of my previous post on Twitter analysis of the Philadelphia Flyers during most of March 2018. Again, it will be presented from a R Notebook.

Here is part 2 for the Philly Flyers R Notebook Analysis: <!DOCTYPE html>

Flyers Tweets - The Tweets

Part 2 of My Analysis of the Philadelphia Flyers Twitter Activity

The previous post examined the Twitter users that tweet about the Philly Flyers. In this post, we will look at the tweets themselves.

Through my position at Stockton University, I have heard that the Philadelphia Flyers are looking for ways to increase ticket sales for games. I determined several data research questions related to how word can be spread for the Philadelphia Flyers.

Specifically, I asked five questions that I want to answer (or attempt to answer) with my study. The first three questions were addressed in the previous post. Then I determined what Twitter data I wanted to analyze in an attempt to address my questions. Those data are tweets with the hashtags #Flyers, #FlyersNation, and #LETSGOFLYERS. Of course I could have added more hashtags, such as #PhillyFlyers, but the chosen hashtags resulted in sufficient data for an initial analysis. Thirdly, I determined what I wanted to measure in that data, and I divided my data questions into two groups. The first group is concerned mostly with the Twitter users and was the main subject of the previous post. This post will focus on the text of the tweets. Lastly I will present the results from the analysis in an attempt to learn something about using Twitter to attract fans who may then attend the games.

For this post, I asked the following questions: 4. How do events (wins vs losses, opponents) impact the amount of tweets? 5. What are the common characteristics of highly retweeted tweets?

I believe answering these questions will provide insight into when and what to tweet about the Flyers. If I were looking for ways to promote the Flyers, I might try to use what I learn to improve the use of social media.

As this is a R Notebook, all the code is in R, version 3.4.4 (2018-03-15) to be exact.

In March 2018, I scraped Twitter several times in order to gather all tweets that had the hashtags #Flyers, #FlyersNation, or #LETSGOFLYERS. The dates of the collections were March 11, March 20, and March 26, 2018. See the previous post for the code I used to scrape Twitter.

First, several R libraries are needed. Note, I tried to use only high quality libraries, such as those developed by the RStudio group.

library(rtweet) # for users_data()
library(tidyverse) # Instead of just ggplot2 and dplyr
library(tidytext)  # For Twitter text manipulation
library(lubridate)  # for date manipulation
library(reshape2)  # for mutate()
library(scales) # for date_breaks() in the ggplot - scale_x_date()
library(stringr) # for string manipulations
library("RColorBrewer") # Because I want to print with Flyers colors!

Read the three datasets into memory and combine into one master dataset. Then clean the datasets. For more information on the data preparation, see the previous post.

# Load the RData files that were saved after scraping Twitter
load(file="rtweets20180311.RData")
tw11 <- rstats_tweets
users11 <- users_data(rstats_tweets)
load(file="rtweets20180320.RData")
tw20 <- rstats_tweets
users20 <- users_data(rstats_tweets)
load(file="rtweets20180326.RData")
# Combine the two datasets
tw <- bind_rows(tw11, tw20, rstats_tweets)
users <- users_data(rstats_tweets)
users <- bind_rows(users11, users20, users)
rm(tw11, users11, tw20, users20, rstats_tweets)
# Remove duplicates, due to overlapping dates in the individual datasets.
tw <- unique(tw)
users <- unique(users)
### Clean up the data
# Remove users that do not (or should not) contribute value to this study.
users <- users[!(users$user_id %in% c("19618527", "471268712", "154699499", "426029765", "19276719", "493658381", "938072969552826368", "321035743")),]
# Only analyze "local" tweeters - location identified as PA, NJ, or DE
select <- grepl("Phil", users$location, ignore.case = TRUE) | grepl("PA", users$location, ignore.case = FALSE) | grepl("NJ", users$location, ignore.case = FALSE) | grepl("DE", users$location, ignore.case = FALSE)
users <- users[select,]
rm(select)
# Verified accounts include professional radio, TV, and news stations (e.g. NBC), and some names (a spot check identifies the selected as broadcastsers and reporters)
users <- users[!users$verified,] # Save only nonverified accounts
# Now select only the tweets that belong to these user_ids
tw <- tw[tw$user_id %in% users$user_id,]
# Save only the tweets that are in English (at least for now)
tw <- tw[tw$lang=="en",]

Prepare a working dataset that groups the tweets by the hour. In other words, how many tweets are there each hour over the time period.

twperhr <- tw %>%
  group_by(Group.1=format(tw$created_at, "%Y-%m-%d %H")) %>%
  summarise(x=n()) 

The data extends over the time period from 2018-03-04 20:56:42 to 2018-03-26 13:00:21. During that time, the Flyers played ten games, but we may want to consider the game previous to the time period as well as what was expected at the end of the time period.

  • Flyers lost to the Panthers (1-4) on March 4 3:00pm
  • Flyers lost to the Penguins (2-5) on March 7 8:00pm
  • Flyers lost to the Bruins (2-3) on March 8 7:00pm
  • Flyers beat the Jets (2-1) on March 10 1:00pm
  • Flyers lost to the Golden Knights (2-3) on March 12 7:00pm
  • Flyers lost to the Blue Jackets (3-5) on March 15 7:00pm
  • Flyers beat the Hurricanes (4-2) on March 17 7:00pm
  • Flyers beat the Capitals (6-3) on March 18 5:00pm
  • Flyers lost to the Red Wings (4-5) on March 20 7:30pm
  • Flyers beat the Rangers (4-3) on March 22 7:00pm
  • Flyers lost to the Penguins (4-5) on March 25 12:30pm
  • Nothing scheduled for March 26
  • Next scheduled game against Dallas on March 27 8:30pm

Load this information into data vectors.

schedule = c("2018-03-07 20", "2018-03-08 19", "2018-03-10 13", "2018-03-12 19", "2018-03-15 19", "2018-03-17 19", "2018-03-18 17", "2018-03-20 19", "2018-03-22 19", "2018-03-25 12")
result <- c("Loss", "Loss", "Win", "Loss", "Loss", "Win", "Win", "Loss", "Win", "Loss")
opponent <- c(" \nPenguins", "Bruins", "Jets", "Golden\nKnights", "Blue\nJackets", "Hurricanes", " \nCapitals", "Red\nWings", "Rangers", "Penguins")
d1 <- format(as.Date(min(tw$created_at),format="%Y-%m-%d"), "%m-%d")
d2 <- format(as.Date(max(tw$created_at),format="%Y-%m-%d"), "%m-%d")

Printing the data as a table

date1 <- data.frame(Date = substr(schedule, 1, 10), Opponent = trimws(sub("\n", " ", opponent)), Result = result)
date1

Using the information gathered, we can create a graph of the time period.

4. How do events (wins vs losses, opponents) impact the amount of tweets?

# Determine the vector locations for game times
matches <- grep(paste(schedule,collapse="|"), twperhr$Group.1)
# Determine the average time from a game official start time that the tweets peak
avPeak <- paste("Average time from game start when tweets peak,", 
  mean(sapply(matches, function(i) 
    which(twperhr$x[(i-12):(i+12)]==max(twperhr$x[(i-12):(i+12)]))-13 )), 
  "hours", sep=" ")
  
# Create a graph of tweets over time and indicate when games occured
ggplot(data=twperhr, aes(x=seq_len(nrow(twperhr)), y=x)) + geom_line() +
    scale_x_continuous(breaks = grep(" 00", twperhr$Group.1), 
      labels = substr(twperhr$Group.1[grepl(" 00", twperhr$Group.1)], 6, 10) ) +
    theme(axis.text.x = element_text(color="darkorange", angle=45), 
          panel.background = element_rect(fill = "white", colour = "orange"),
          panel.grid.minor = element_blank()) +
    annotate("text", x = matches, y = twperhr[matches+6,]$x, 
             label = paste(opponent, "\n(", substr(result,1,1),")",sep=""), 
             colour = gsub("Loss", "red", gsub("Win", "darkblue", result))) +
    labs(title="Tweets Mentioning the Flyers", subtitle=paste(d1,"to",d2,sep=" "), 
         caption= avPeak, x = "Date", y = "Tweets")

# Clean up variables that are no longer needed
rm(matches, avPeak) 

Over the specified time, it does not appear that a win or a loss regularly impacts the number of tweets. The fact that a game is played has a huge impact, but not the outcome. And since the number of tweets peak after the conclusion of the game, the peaks are not the result of anticipation.

While the previous graph was by the hour, perhaps there is something to be gained by looking at the number of tweets per day.

ggplot(data = tw, aes(x = day(created_at))) +
  geom_bar(aes(fill = ..count..)) +
  theme(legend.position = "none") +
#  xlab("March") + ylab("Number of tweets") + 
  labs(title="Tweets Mentioning the Flyers", subtitle=paste(d1,"to",d2,sep=" "), 
         x = "March", y = "Number of Tweets") +
  scale_fill_gradient(low = "orange", high = "orangered2")

There may be something else going on to impact the number of tweets. Perhaps looking at the data by day of the week as well as when games are played would have some pattern?

5. What are the common characteristics of retweeted tweets?

Let us look at the tweets themselves. To do so, we need to clean them up. By that I mean remove references to screen names, hashtags, spaces, numbers, punctuations, and urls.

clean_tweet <- gsub('\\n', '', tw$text) %>% 
  str_replace_all("http\\S+\\s*","") %>%
  str_replace("RT @[a-z,A-Z,0-9]*: ","") %>%
  str_replace_all("#[a-z,A-Z]*","") %>%
  str_replace_all("@[a-z,A-Z]*","") %>%
  str_replace_all("[0-9]","") %>%
  str_replace_all(" "," ")

First we will look at the words used in tweets, and then we will consider the tweets as a whole.

tweets <- data_frame(text=clean_tweet) %>% unnest_tokens(word, text)
data(stop_words)
tweets <- tweets %>% anti_join(stop_words)
tweets %>% count(word, sort = TRUE) 

Graph the words that occur at least 200 times.

tweets %>%
  count(word, sort = TRUE) %>%
  filter(n > 200) %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n)) +
  geom_col(aes(fill = n)) +
  scale_fill_distiller(palette="Oranges") +
  theme(legend.position = "none") +
  xlab("Popular Words") + ylab("Number Occurences") +  
  labs(title="Most Popular Words of Tweets") +
  coord_flip()

Using a sentiment score, analyze each tweet as a whole. To accomplish this, i am determining the sentiment of the words in the tweet, with positive sentiments getting a positive score and negative sentiments getting a negative score. Then I am summing the score for each tweet. Tweets of the form “that is not good” will get a sentiment score of 0, since “not” is negative and “good” is positive, but at least that should not sway the analysis. If “not good” got a score of positive due to the “good”, that would be bad. The sentiment scores will be determined by hour so we can see how the games impact the sentiments of the tweets.

#Determine the sentiments of the tweets
sentiment <- tibble(index = 1:nrow(tw),
                    created = tw$created_at,
                    text = clean_tweet) %>%
  unnest_tokens(word, text) %>%
  inner_join(get_sentiments("nrc")) %>%
  filter(sentiment %in% c("positive", "negative")) %>%
  group_by(index, created) %>%
  count(index, sentiment) %>%
  spread(sentiment, n, fill = 0) %>%
  group_by(Time = round_date(created, unit="hours")) %>%
  mutate(score = positive - negative) %>%
  summarise(score = sum(score))
sentiment$sentiment <- factor(ifelse(sentiment$score > 0, "Negative", "Positive"), labels=c("Positive", "Negative"))

Now we can graph the tweets.

date2 <- format(sentiment$Time, "%Y-%m-%d %H")
for (i in seq_len(10)) { date2 <- sub(schedule[i], opponent[i], date2, ignore.case = TRUE) }
date2[grep("2018-03-", date2, ignore.case = TRUE)] <- ""
ggplot(sentiment, aes(x=as.Date(Time), y=score)) +
  geom_line(size = 1.5, alpha = 0.7, aes(colour = sentiment)) +
  labs(title="Sentiments of Tweets", subtitle=paste(d1,"to",d2,sep=" "), 
       x = "March", y = "Sentiment Score") +
  geom_text(y=rep(c(50, 75, 100), len = nrow(sentiment)), label=date2) +
  theme(legend.position="none")

rm(date2,i)  

The graph may be a little misleading since it appears the Bruins game causes a large spike in tweets. When I manually inspect the data, it appears that the large spike occurs after the conclusion of the March 7 Penguins game and just prior to the start of the March 8 Bruins game.

Moving on, let us now look at the characteristics of retweets. Since retweets, by definition, have multiple occurences, we want just one tweet to represent each retweet set.

clean_tweet <- tw[tw$status_id %in% unique(tw$retweet_status_id),]$text

Clean the text again.

# For removing retweets, references to screen names, hashtags, spaces, numbers, punctuations, urls.
clean_tweet <- gsub('\\n', '', clean_tweet) %>% 
  str_replace_all("http\\S+\\s*","") %>%
  str_replace("RT @[a-z,A-Z,0-9]*: ","") %>%
  str_replace_all("#[a-z,A-Z]*","") %>%
  str_replace_all("@[a-z,A-Z]*","") %>%
  str_replace_all("[0-9]","") %>%
  str_replace_all(" "," ")

Determine the sentiment of each tweet and graph. The top line graphs the positive sentiment, with higher numbers indicating a higher positive sentiment. The lower line graphs the negative sentiment tweets, with lower numbers indicating an increase in negative sentiments.

sentiment <- tibble(index = 1:length(clean_tweet),
  created = tw[tw$status_id %in% unique(tw$retweet_status_id),]$created_at,
              text = clean_tweet) %>%
  unnest_tokens(word, text) %>%
  inner_join(get_sentiments("nrc")) %>%
  filter(sentiment %in% c("positive", "negative")) %>%
  group_by(index, created) %>%
  count(index, sentiment) %>%
  spread(sentiment, n, fill = 0) %>%
  group_by(Time = round_date(created, unit="hours")) %>%
  mutate(score = positive - negative) %>%
  summarise(score = sum(score))
  
sentiment$sentiment <- factor(ifelse(sentiment$score > 0, "Negative", "Positive"), labels=c("Positive", "Negative"))
ggplot(sentiment, aes(x=as.Date(Time), y=score)) +
  geom_line(size = 1.5, alpha = 0.7, aes(colour = sentiment)) +
  labs(title="Sentiments of Tweets", subtitle=paste(d1,"to",d2,sep=" "), 
       x = "March", y = "Sentiment Score") +
  theme(legend.position="none")

date1

Looking at the graph, it appears the loss to the Golden Knights on March 12 was followed by a large spike in positive sentiment with a reduced trend of negative sentiment. Did the loss of the one game cause a high level of encouragement for the upcoming Blue Jackets game? However, after losing to the Blue Jackets, there is a jump in negative sentiment.

I admit that these results do little to address the 5th question, and a more thorough analysis is needed. This further analysis would require more of the same, just in more detail. Maybe looking at what other events occured during the time? Definitley expanding the list of words since the top results are somewhat expected (e.g. game, flyers, win, goal). Another interesting result might be if the sentiment causes more or less retweets? That would be useful information if we wanted to spread the word better.

There is much more I could analyze from this data. For one thing, I should expand on my conclusions from analyzing the data. Since this study was “for fun,” I may or may not return to write a more thorough conclusion, but the reader is free to look at the analysis and form their own conclusions. I guess we will see if I continue with this study or get distracted by another “for fun” project in the next post.

LS0tCnRpdGxlOiAiRmx5ZXJzIFR3ZWV0cyAtIFRoZSBUd2VldHMiCmF1dGhvcjogIkRyLiBDbGlmdG9uIEJhbGR3aW4iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgUGFydCAyIG9mIE15IEFuYWx5c2lzIG9mIHRoZSBQaGlsYWRlbHBoaWEgRmx5ZXJzIFR3aXR0ZXIgQWN0aXZpdHkKClRoZSBwcmV2aW91cyBwb3N0IGV4YW1pbmVkIHRoZSBUd2l0dGVyIHVzZXJzIHRoYXQgdHdlZXQgYWJvdXQgdGhlIFBoaWxseSBGbHllcnMuIEluIHRoaXMgcG9zdCwgd2Ugd2lsbCBsb29rIGF0IHRoZSB0d2VldHMgdGhlbXNlbHZlcy4KClRocm91Z2ggbXkgcG9zaXRpb24gYXQgU3RvY2t0b24gVW5pdmVyc2l0eSwgSSBoYXZlIGhlYXJkIHRoYXQgdGhlIFBoaWxhZGVscGhpYSBGbHllcnMgYXJlIGxvb2tpbmcgZm9yIHdheXMgdG8gaW5jcmVhc2UgdGlja2V0IHNhbGVzIGZvciBnYW1lcy4gSSBkZXRlcm1pbmVkIHNldmVyYWwgZGF0YSByZXNlYXJjaCBxdWVzdGlvbnMgcmVsYXRlZCB0byBob3cgd29yZCBjYW4gYmUgc3ByZWFkIGZvciB0aGUgUGhpbGFkZWxwaGlhIEZseWVycy4KClNwZWNpZmljYWxseSwgSSBhc2tlZCBmaXZlIHF1ZXN0aW9ucyB0aGF0IEkgd2FudCB0byBhbnN3ZXIgKG9yIGF0dGVtcHQgdG8gYW5zd2VyKSB3aXRoIG15IHN0dWR5LiBUaGUgZmlyc3QgdGhyZWUgcXVlc3Rpb25zIHdlcmUgYWRkcmVzc2VkIGluIHRoZSBwcmV2aW91cyBwb3N0LiBUaGVuIEkgZGV0ZXJtaW5lZCB3aGF0IFR3aXR0ZXIgZGF0YSBJIHdhbnRlZCB0byBhbmFseXplIGluIGFuIGF0dGVtcHQgdG8gYWRkcmVzcyBteSBxdWVzdGlvbnMuIFRob3NlIGRhdGEgYXJlIHR3ZWV0cyB3aXRoIHRoZSBoYXNodGFncyAjRmx5ZXJzLCAjRmx5ZXJzTmF0aW9uLCBhbmQgI0xFVFNHT0ZMWUVSUy4gT2YgY291cnNlIEkgY291bGQgaGF2ZSBhZGRlZCBtb3JlIGhhc2h0YWdzLCBzdWNoIGFzICNQaGlsbHlGbHllcnMsIGJ1dCB0aGUgY2hvc2VuIGhhc2h0YWdzIHJlc3VsdGVkIGluIHN1ZmZpY2llbnQgZGF0YSBmb3IgYW4gaW5pdGlhbCBhbmFseXNpcy4gVGhpcmRseSwgSSBkZXRlcm1pbmVkIHdoYXQgSSB3YW50ZWQgdG8gbWVhc3VyZSBpbiB0aGF0IGRhdGEsIGFuZCBJIGRpdmlkZWQgbXkgZGF0YSBxdWVzdGlvbnMgaW50byB0d28gZ3JvdXBzLiBUaGUgZmlyc3QgZ3JvdXAgaXMgY29uY2VybmVkIG1vc3RseSB3aXRoIHRoZSBUd2l0dGVyIHVzZXJzIGFuZCB3YXMgdGhlIG1haW4gc3ViamVjdCBvZiB0aGUgcHJldmlvdXMgcG9zdC4gVGhpcyBwb3N0IHdpbGwgZm9jdXMgb24gdGhlIHRleHQgb2YgdGhlIHR3ZWV0cy4gTGFzdGx5IEkgd2lsbCBwcmVzZW50IHRoZSByZXN1bHRzIGZyb20gdGhlIGFuYWx5c2lzIGluIGFuIGF0dGVtcHQgdG8gbGVhcm4gc29tZXRoaW5nIGFib3V0IHVzaW5nIFR3aXR0ZXIgdG8gYXR0cmFjdCBmYW5zIHdobyBtYXkgdGhlbiBhdHRlbmQgdGhlIGdhbWVzLgoKRm9yIHRoaXMgcG9zdCwgSSBhc2tlZCB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uczoKNC4gSG93IGRvIGV2ZW50cyAod2lucyB2cyBsb3NzZXMsIG9wcG9uZW50cykgaW1wYWN0IHRoZSBhbW91bnQgb2YgdHdlZXRzPwo1LiBXaGF0IGFyZSB0aGUgY29tbW9uIGNoYXJhY3RlcmlzdGljcyBvZiBoaWdobHkgcmV0d2VldGVkIHR3ZWV0cz8KCkkgYmVsaWV2ZSBhbnN3ZXJpbmcgdGhlc2UgcXVlc3Rpb25zIHdpbGwgcHJvdmlkZSBpbnNpZ2h0IGludG8gd2hlbiBhbmQgd2hhdCB0byB0d2VldCBhYm91dCB0aGUgRmx5ZXJzLiBJZiBJIHdlcmUgbG9va2luZyBmb3Igd2F5cyB0byBwcm9tb3RlIHRoZSBGbHllcnMsIEkgbWlnaHQgdHJ5IHRvIHVzZSB3aGF0IEkgbGVhcm4gdG8gaW1wcm92ZSB0aGUgdXNlIG9mIHNvY2lhbCBtZWRpYS4KCkFzIHRoaXMgaXMgYSBSIE5vdGVib29rLCBhbGwgdGhlIGNvZGUgaXMgaW4gUiwgdmVyc2lvbiAzLjQuNCAoMjAxOC0wMy0xNSkgdG8gYmUgZXhhY3QuIAoKSW4gTWFyY2ggMjAxOCwgSSBzY3JhcGVkIFR3aXR0ZXIgc2V2ZXJhbCB0aW1lcyBpbiBvcmRlciB0byBnYXRoZXIgYWxsIHR3ZWV0cyB0aGF0IGhhZCB0aGUgaGFzaHRhZ3MgI0ZseWVycywgI0ZseWVyc05hdGlvbiwgb3IgI0xFVFNHT0ZMWUVSUy4gVGhlIGRhdGVzIG9mIHRoZSBjb2xsZWN0aW9ucyB3ZXJlIE1hcmNoIDExLCBNYXJjaCAyMCwgYW5kIE1hcmNoIDI2LCAyMDE4LiBTZWUgdGhlIHByZXZpb3VzIHBvc3QgZm9yIHRoZSBjb2RlIEkgdXNlZCB0byBzY3JhcGUgVHdpdHRlci4KCkZpcnN0LCBzZXZlcmFsIFIgbGlicmFyaWVzIGFyZSBuZWVkZWQuIE5vdGUsIEkgdHJpZWQgdG8gdXNlIG9ubHkgaGlnaCBxdWFsaXR5IGxpYnJhcmllcywgc3VjaCBhcyB0aG9zZSBkZXZlbG9wZWQgYnkgdGhlIFJTdHVkaW8gZ3JvdXAuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHJ0d2VldCkgIyBmb3IgdXNlcnNfZGF0YSgpCmxpYnJhcnkodGlkeXZlcnNlKSAjIEluc3RlYWQgb2YganVzdCBnZ3Bsb3QyIGFuZCBkcGx5cgpsaWJyYXJ5KHRpZHl0ZXh0KSAgIyBGb3IgVHdpdHRlciB0ZXh0IG1hbmlwdWxhdGlvbgpsaWJyYXJ5KGx1YnJpZGF0ZSkgICMgZm9yIGRhdGUgbWFuaXB1bGF0aW9uCmxpYnJhcnkocmVzaGFwZTIpICAjIGZvciBtdXRhdGUoKQpsaWJyYXJ5KHNjYWxlcykgIyBmb3IgZGF0ZV9icmVha3MoKSBpbiB0aGUgZ2dwbG90IC0gc2NhbGVfeF9kYXRlKCkKbGlicmFyeShzdHJpbmdyKSAjIGZvciBzdHJpbmcgbWFuaXB1bGF0aW9ucwpsaWJyYXJ5KCJSQ29sb3JCcmV3ZXIiKSAjIEJlY2F1c2UgSSB3YW50IHRvIHByaW50IHdpdGggRmx5ZXJzIGNvbG9ycyEKYGBgCgoKUmVhZCB0aGUgdGhyZWUgZGF0YXNldHMgaW50byBtZW1vcnkgYW5kIGNvbWJpbmUgaW50byBvbmUgbWFzdGVyIGRhdGFzZXQuIFRoZW4gY2xlYW4gdGhlIGRhdGFzZXRzLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGUgZGF0YSBwcmVwYXJhdGlvbiwgc2VlIHRoZSBwcmV2aW91cyBwb3N0LgpgYGB7cn0KIyBMb2FkIHRoZSBSRGF0YSBmaWxlcyB0aGF0IHdlcmUgc2F2ZWQgYWZ0ZXIgc2NyYXBpbmcgVHdpdHRlcgpsb2FkKGZpbGU9InJ0d2VldHMyMDE4MDMxMS5SRGF0YSIpCnR3MTEgPC0gcnN0YXRzX3R3ZWV0cwp1c2VyczExIDwtIHVzZXJzX2RhdGEocnN0YXRzX3R3ZWV0cykKbG9hZChmaWxlPSJydHdlZXRzMjAxODAzMjAuUkRhdGEiKQp0dzIwIDwtIHJzdGF0c190d2VldHMKdXNlcnMyMCA8LSB1c2Vyc19kYXRhKHJzdGF0c190d2VldHMpCmxvYWQoZmlsZT0icnR3ZWV0czIwMTgwMzI2LlJEYXRhIikKCiMgQ29tYmluZSB0aGUgdHdvIGRhdGFzZXRzCnR3IDwtIGJpbmRfcm93cyh0dzExLCB0dzIwLCByc3RhdHNfdHdlZXRzKQp1c2VycyA8LSB1c2Vyc19kYXRhKHJzdGF0c190d2VldHMpCnVzZXJzIDwtIGJpbmRfcm93cyh1c2VyczExLCB1c2VyczIwLCB1c2VycykKCnJtKHR3MTEsIHVzZXJzMTEsIHR3MjAsIHVzZXJzMjAsIHJzdGF0c190d2VldHMpCgojIFJlbW92ZSBkdXBsaWNhdGVzLCBkdWUgdG8gb3ZlcmxhcHBpbmcgZGF0ZXMgaW4gdGhlIGluZGl2aWR1YWwgZGF0YXNldHMuCnR3IDwtIHVuaXF1ZSh0dykKdXNlcnMgPC0gdW5pcXVlKHVzZXJzKQojIyMgQ2xlYW4gdXAgdGhlIGRhdGEKIyBSZW1vdmUgdXNlcnMgdGhhdCBkbyBub3QgKG9yIHNob3VsZCBub3QpIGNvbnRyaWJ1dGUgdmFsdWUgdG8gdGhpcyBzdHVkeS4KdXNlcnMgPC0gdXNlcnNbISh1c2VycyR1c2VyX2lkICVpbiUgYygiMTk2MTg1MjciLCAiNDcxMjY4NzEyIiwgIjE1NDY5OTQ5OSIsICI0MjYwMjk3NjUiLCAiMTkyNzY3MTkiLCAiNDkzNjU4MzgxIiwgIjkzODA3Mjk2OTU1MjgyNjM2OCIsICIzMjEwMzU3NDMiKSksXQoKIyBPbmx5IGFuYWx5emUgImxvY2FsIiB0d2VldGVycyAtIGxvY2F0aW9uIGlkZW50aWZpZWQgYXMgUEEsIE5KLCBvciBERQpzZWxlY3QgPC0gZ3JlcGwoIlBoaWwiLCB1c2VycyRsb2NhdGlvbiwgaWdub3JlLmNhc2UgPSBUUlVFKSB8IGdyZXBsKCJQQSIsIHVzZXJzJGxvY2F0aW9uLCBpZ25vcmUuY2FzZSA9IEZBTFNFKSB8IGdyZXBsKCJOSiIsIHVzZXJzJGxvY2F0aW9uLCBpZ25vcmUuY2FzZSA9IEZBTFNFKSB8IGdyZXBsKCJERSIsIHVzZXJzJGxvY2F0aW9uLCBpZ25vcmUuY2FzZSA9IEZBTFNFKQoKdXNlcnMgPC0gdXNlcnNbc2VsZWN0LF0Kcm0oc2VsZWN0KQoKIyBWZXJpZmllZCBhY2NvdW50cyBpbmNsdWRlIHByb2Zlc3Npb25hbCByYWRpbywgVFYsIGFuZCBuZXdzIHN0YXRpb25zIChlLmcuIE5CQyksIGFuZCBzb21lIG5hbWVzIChhIHNwb3QgY2hlY2sgaWRlbnRpZmllcyB0aGUgc2VsZWN0ZWQgYXMgYnJvYWRjYXN0c2VycyBhbmQgcmVwb3J0ZXJzKQp1c2VycyA8LSB1c2Vyc1shdXNlcnMkdmVyaWZpZWQsXSAjIFNhdmUgb25seSBub252ZXJpZmllZCBhY2NvdW50cwoKIyBOb3cgc2VsZWN0IG9ubHkgdGhlIHR3ZWV0cyB0aGF0IGJlbG9uZyB0byB0aGVzZSB1c2VyX2lkcwp0dyA8LSB0d1t0dyR1c2VyX2lkICVpbiUgdXNlcnMkdXNlcl9pZCxdCgojIFNhdmUgb25seSB0aGUgdHdlZXRzIHRoYXQgYXJlIGluIEVuZ2xpc2ggKGF0IGxlYXN0IGZvciBub3cpCnR3IDwtIHR3W3R3JGxhbmc9PSJlbiIsXQoKYGBgCgpQcmVwYXJlIGEgd29ya2luZyBkYXRhc2V0IHRoYXQgZ3JvdXBzIHRoZSB0d2VldHMgYnkgdGhlIGhvdXIuIEluIG90aGVyIHdvcmRzLCBob3cgbWFueSB0d2VldHMgYXJlIHRoZXJlIGVhY2ggaG91ciBvdmVyIHRoZSB0aW1lIHBlcmlvZC4KYGBge3J9CnR3cGVyaHIgPC0gdHcgJT4lCiAgZ3JvdXBfYnkoR3JvdXAuMT1mb3JtYXQodHckY3JlYXRlZF9hdCwgIiVZLSVtLSVkICVIIikpICU+JQogIHN1bW1hcmlzZSh4PW4oKSkgCmBgYAoKVGhlIGRhdGEgZXh0ZW5kcyBvdmVyIHRoZSB0aW1lIHBlcmlvZCBmcm9tIGByIG1pbih0dyRjcmVhdGVkX2F0KWAgdG8gYHIgbWF4KHR3JGNyZWF0ZWRfYXQpYC4gRHVyaW5nIHRoYXQgdGltZSwgdGhlIEZseWVycyBwbGF5ZWQgdGVuIGdhbWVzLCBidXQgd2UgbWF5IHdhbnQgdG8gY29uc2lkZXIgdGhlIGdhbWUgcHJldmlvdXMgdG8gdGhlIHRpbWUgcGVyaW9kIGFzIHdlbGwgYXMgd2hhdCB3YXMgZXhwZWN0ZWQgYXQgdGhlIGVuZCBvZiB0aGUgdGltZSBwZXJpb2QuCgotIEZseWVycyBsb3N0IHRvIHRoZSBQYW50aGVycyAoMS00KSBvbiBNYXJjaCA0IDM6MDBwbQotIEZseWVycyBsb3N0IHRvIHRoZSBQZW5ndWlucyAoMi01KSBvbiBNYXJjaCA3IDg6MDBwbQotIEZseWVycyBsb3N0IHRvIHRoZSBCcnVpbnMgKDItMykgb24gTWFyY2ggOCA3OjAwcG0KLSBGbHllcnMgYmVhdCB0aGUgSmV0cyAoMi0xKSBvbiBNYXJjaCAxMCAxOjAwcG0KLSBGbHllcnMgbG9zdCB0byB0aGUgR29sZGVuIEtuaWdodHMgKDItMykgb24gTWFyY2ggMTIgNzowMHBtCi0gRmx5ZXJzIGxvc3QgdG8gdGhlIEJsdWUgSmFja2V0cyAoMy01KSBvbiBNYXJjaCAxNSA3OjAwcG0KLSBGbHllcnMgYmVhdCB0aGUgSHVycmljYW5lcyAoNC0yKSBvbiBNYXJjaCAxNyA3OjAwcG0KLSBGbHllcnMgYmVhdCB0aGUgQ2FwaXRhbHMgKDYtMykgb24gTWFyY2ggMTggNTowMHBtCi0gRmx5ZXJzIGxvc3QgdG8gdGhlIFJlZCBXaW5ncyAoNC01KSBvbiBNYXJjaCAyMCA3OjMwcG0KLSBGbHllcnMgYmVhdCB0aGUgUmFuZ2VycyAoNC0zKSBvbiBNYXJjaCAyMiA3OjAwcG0KLSBGbHllcnMgbG9zdCB0byB0aGUgUGVuZ3VpbnMgKDQtNSkgb24gTWFyY2ggMjUgMTI6MzBwbQotIE5vdGhpbmcgc2NoZWR1bGVkIGZvciBNYXJjaCAyNiAKLSBOZXh0IHNjaGVkdWxlZCBnYW1lIGFnYWluc3QgRGFsbGFzIG9uIE1hcmNoIDI3IDg6MzBwbQoKTG9hZCB0aGlzIGluZm9ybWF0aW9uIGludG8gZGF0YSB2ZWN0b3JzLgpgYGB7cn0Kc2NoZWR1bGUgPSBjKCIyMDE4LTAzLTA3IDIwIiwgIjIwMTgtMDMtMDggMTkiLCAiMjAxOC0wMy0xMCAxMyIsICIyMDE4LTAzLTEyIDE5IiwgIjIwMTgtMDMtMTUgMTkiLCAiMjAxOC0wMy0xNyAxOSIsICIyMDE4LTAzLTE4IDE3IiwgIjIwMTgtMDMtMjAgMTkiLCAiMjAxOC0wMy0yMiAxOSIsICIyMDE4LTAzLTI1IDEyIikKCnJlc3VsdCA8LSBjKCJMb3NzIiwgIkxvc3MiLCAiV2luIiwgIkxvc3MiLCAiTG9zcyIsICJXaW4iLCAiV2luIiwgIkxvc3MiLCAiV2luIiwgIkxvc3MiKQoKb3Bwb25lbnQgPC0gYygiIFxuUGVuZ3VpbnMiLCAiQnJ1aW5zIiwgIkpldHMiLCAiR29sZGVuXG5LbmlnaHRzIiwgIkJsdWVcbkphY2tldHMiLCAiSHVycmljYW5lcyIsICIgXG5DYXBpdGFscyIsICJSZWRcbldpbmdzIiwgIlJhbmdlcnMiLCAiUGVuZ3VpbnMiKQoKZDEgPC0gZm9ybWF0KGFzLkRhdGUobWluKHR3JGNyZWF0ZWRfYXQpLGZvcm1hdD0iJVktJW0tJWQiKSwgIiVtLSVkIikKZDIgPC0gZm9ybWF0KGFzLkRhdGUobWF4KHR3JGNyZWF0ZWRfYXQpLGZvcm1hdD0iJVktJW0tJWQiKSwgIiVtLSVkIikKYGBgCgpQcmludGluZyB0aGUgZGF0YSBhcyBhIHRhYmxlCmBgYHtyfQpkYXRlMSA8LSBkYXRhLmZyYW1lKERhdGUgPSBzdWJzdHIoc2NoZWR1bGUsIDEsIDEwKSwgT3Bwb25lbnQgPSB0cmltd3Moc3ViKCJcbiIsICIgIiwgb3Bwb25lbnQpKSwgUmVzdWx0ID0gcmVzdWx0KQpkYXRlMQpgYGAKClVzaW5nIHRoZSBpbmZvcm1hdGlvbiBnYXRoZXJlZCwgd2UgY2FuIGNyZWF0ZSBhIGdyYXBoIG9mIHRoZSB0aW1lIHBlcmlvZC4KCiMjIDQuIEhvdyBkbyBldmVudHMgKHdpbnMgdnMgbG9zc2VzLCBvcHBvbmVudHMpIGltcGFjdCB0aGUgYW1vdW50IG9mIHR3ZWV0cz8KCmBgYHtyfQojIERldGVybWluZSB0aGUgdmVjdG9yIGxvY2F0aW9ucyBmb3IgZ2FtZSB0aW1lcwptYXRjaGVzIDwtIGdyZXAocGFzdGUoc2NoZWR1bGUsY29sbGFwc2U9InwiKSwgdHdwZXJociRHcm91cC4xKQojIERldGVybWluZSB0aGUgYXZlcmFnZSB0aW1lIGZyb20gYSBnYW1lIG9mZmljaWFsIHN0YXJ0IHRpbWUgdGhhdCB0aGUgdHdlZXRzIHBlYWsKYXZQZWFrIDwtIHBhc3RlKCJBdmVyYWdlIHRpbWUgZnJvbSBnYW1lIHN0YXJ0IHdoZW4gdHdlZXRzIHBlYWssIiwgCiAgbWVhbihzYXBwbHkobWF0Y2hlcywgZnVuY3Rpb24oaSkgCiAgICB3aGljaCh0d3BlcmhyJHhbKGktMTIpOihpKzEyKV09PW1heCh0d3BlcmhyJHhbKGktMTIpOihpKzEyKV0pKS0xMyApKSwgCiAgImhvdXJzIiwgc2VwPSIgIikKICAKIyBDcmVhdGUgYSBncmFwaCBvZiB0d2VldHMgb3ZlciB0aW1lIGFuZCBpbmRpY2F0ZSB3aGVuIGdhbWVzIG9jY3VyZWQKZ2dwbG90KGRhdGE9dHdwZXJociwgYWVzKHg9c2VxX2xlbihucm93KHR3cGVyaHIpKSwgeT14KSkgKyBnZW9tX2xpbmUoKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZ3JlcCgiIDAwIiwgdHdwZXJociRHcm91cC4xKSwgCiAgICAgIGxhYmVscyA9IHN1YnN0cih0d3BlcmhyJEdyb3VwLjFbZ3JlcGwoIiAwMCIsIHR3cGVyaHIkR3JvdXAuMSldLCA2LCAxMCkgKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChjb2xvcj0iZGFya29yYW5nZSIsIGFuZ2xlPTQ1KSwgCiAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAib3JhbmdlIiksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtYXRjaGVzLCB5ID0gdHdwZXJoclttYXRjaGVzKzYsXSR4LCAKICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUob3Bwb25lbnQsICJcbigiLCBzdWJzdHIocmVzdWx0LDEsMSksIikiLHNlcD0iIiksIAogICAgICAgICAgICAgY29sb3VyID0gZ3N1YigiTG9zcyIsICJyZWQiLCBnc3ViKCJXaW4iLCAiZGFya2JsdWUiLCByZXN1bHQpKSkgKwogICAgbGFicyh0aXRsZT0iVHdlZXRzIE1lbnRpb25pbmcgdGhlIEZseWVycyIsIHN1YnRpdGxlPXBhc3RlKGQxLCJ0byIsZDIsc2VwPSIgIiksIAogICAgICAgICBjYXB0aW9uPSBhdlBlYWssIHggPSAiRGF0ZSIsIHkgPSAiVHdlZXRzIikKCiMgQ2xlYW4gdXAgdmFyaWFibGVzIHRoYXQgYXJlIG5vIGxvbmdlciBuZWVkZWQKcm0obWF0Y2hlcywgYXZQZWFrKSAKYGBgCgpPdmVyIHRoZSBzcGVjaWZpZWQgdGltZSwgaXQgZG9lcyBub3QgYXBwZWFyIHRoYXQgYSB3aW4gb3IgYSBsb3NzIHJlZ3VsYXJseSBpbXBhY3RzIHRoZSBudW1iZXIgb2YgdHdlZXRzLiBUaGUgZmFjdCB0aGF0IGEgZ2FtZSBpcyBwbGF5ZWQgaGFzIGEgaHVnZSBpbXBhY3QsIGJ1dCBub3QgdGhlIG91dGNvbWUuIEFuZCBzaW5jZSB0aGUgbnVtYmVyIG9mIHR3ZWV0cyBwZWFrIGFmdGVyIHRoZSBjb25jbHVzaW9uIG9mIHRoZSBnYW1lLCB0aGUgcGVha3MgYXJlIG5vdCB0aGUgcmVzdWx0IG9mIGFudGljaXBhdGlvbi4gCgpXaGlsZSB0aGUgcHJldmlvdXMgZ3JhcGggd2FzIGJ5IHRoZSBob3VyLCBwZXJoYXBzIHRoZXJlIGlzIHNvbWV0aGluZyB0byBiZSBnYWluZWQgYnkgbG9va2luZyBhdCB0aGUgbnVtYmVyIG9mIHR3ZWV0cyBwZXIgZGF5LgoKYGBge3J9CmdncGxvdChkYXRhID0gdHcsIGFlcyh4ID0gZGF5KGNyZWF0ZWRfYXQpKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsID0gLi5jb3VudC4uKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwojICB4bGFiKCJNYXJjaCIpICsgeWxhYigiTnVtYmVyIG9mIHR3ZWV0cyIpICsgCiAgbGFicyh0aXRsZT0iVHdlZXRzIE1lbnRpb25pbmcgdGhlIEZseWVycyIsIHN1YnRpdGxlPXBhc3RlKGQxLCJ0byIsZDIsc2VwPSIgIiksIAogICAgICAgICB4ID0gIk1hcmNoIiwgeSA9ICJOdW1iZXIgb2YgVHdlZXRzIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIm9yYW5nZSIsIGhpZ2ggPSAib3JhbmdlcmVkMiIpCmBgYAoKVGhlcmUgbWF5IGJlIHNvbWV0aGluZyBlbHNlIGdvaW5nIG9uIHRvIGltcGFjdCB0aGUgbnVtYmVyIG9mIHR3ZWV0cy4gUGVyaGFwcyBsb29raW5nIGF0IHRoZSBkYXRhIGJ5IGRheSBvZiB0aGUgd2VlayBhcyB3ZWxsIGFzIHdoZW4gZ2FtZXMgYXJlIHBsYXllZCB3b3VsZCBoYXZlIHNvbWUgcGF0dGVybj8gCgojIyA1LiBXaGF0IGFyZSB0aGUgY29tbW9uIGNoYXJhY3RlcmlzdGljcyBvZiByZXR3ZWV0ZWQgdHdlZXRzPwoKTGV0IHVzIGxvb2sgYXQgdGhlIHR3ZWV0cyB0aGVtc2VsdmVzLiBUbyBkbyBzbywgd2UgbmVlZCB0byBjbGVhbiB0aGVtIHVwLiBCeSB0aGF0IEkgbWVhbiByZW1vdmUgcmVmZXJlbmNlcyB0byBzY3JlZW4gbmFtZXMsIGhhc2h0YWdzLCBzcGFjZXMsIG51bWJlcnMsIHB1bmN0dWF0aW9ucywgYW5kIHVybHMuCgpgYGB7cn0KY2xlYW5fdHdlZXQgPC0gZ3N1YignXFxuJywgJycsIHR3JHRleHQpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoImh0dHBcXFMrXFxzKiIsIiIpICU+JQogIHN0cl9yZXBsYWNlKCJSVCBAW2EteixBLVosMC05XSo6ICIsIiIpICU+JQogIHN0cl9yZXBsYWNlX2FsbCgiI1thLXosQS1aXSoiLCIiKSAlPiUKICBzdHJfcmVwbGFjZV9hbGwoIkBbYS16LEEtWl0qIiwiIikgJT4lCiAgc3RyX3JlcGxhY2VfYWxsKCJbMC05XSIsIiIpICU+JQogIHN0cl9yZXBsYWNlX2FsbCgiICIsIiAiKQoKYGBgCgpGaXJzdCB3ZSB3aWxsIGxvb2sgYXQgdGhlIHdvcmRzIHVzZWQgaW4gdHdlZXRzLCBhbmQgdGhlbiB3ZSB3aWxsIGNvbnNpZGVyIHRoZSB0d2VldHMgYXMgYSB3aG9sZS4KYGBge3IsIG1lc3NhZ2U9RkFMU0V9CnR3ZWV0cyA8LSBkYXRhX2ZyYW1lKHRleHQ9Y2xlYW5fdHdlZXQpICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpCgpkYXRhKHN0b3Bfd29yZHMpCnR3ZWV0cyA8LSB0d2VldHMgJT4lIGFudGlfam9pbihzdG9wX3dvcmRzKQoKdHdlZXRzICU+JSBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgCmBgYAoKR3JhcGggdGhlIHdvcmRzIHRoYXQgb2NjdXIgYXQgbGVhc3QgMjAwIHRpbWVzLgpgYGB7cn0KdHdlZXRzICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDIwMCkgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIG4pKSArCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBuKSkgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGU9Ik9yYW5nZXMiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgeGxhYigiUG9wdWxhciBXb3JkcyIpICsgeWxhYigiTnVtYmVyIE9jY3VyZW5jZXMiKSArICAKICBsYWJzKHRpdGxlPSJNb3N0IFBvcHVsYXIgV29yZHMgb2YgVHdlZXRzIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKClVzaW5nIGEgc2VudGltZW50IHNjb3JlLCBhbmFseXplIGVhY2ggdHdlZXQgYXMgYSB3aG9sZS4gVG8gYWNjb21wbGlzaCB0aGlzLCBpIGFtIGRldGVybWluaW5nIHRoZSBzZW50aW1lbnQgb2YgdGhlIHdvcmRzIGluIHRoZSB0d2VldCwgd2l0aCBwb3NpdGl2ZSBzZW50aW1lbnRzIGdldHRpbmcgYSBwb3NpdGl2ZSBzY29yZSBhbmQgbmVnYXRpdmUgc2VudGltZW50cyBnZXR0aW5nIGEgbmVnYXRpdmUgc2NvcmUuIFRoZW4gSSBhbSBzdW1taW5nIHRoZSBzY29yZSBmb3IgZWFjaCB0d2VldC4gVHdlZXRzIG9mIHRoZSBmb3JtICJ0aGF0IGlzIG5vdCBnb29kIiB3aWxsIGdldCBhIHNlbnRpbWVudCBzY29yZSBvZiAwLCBzaW5jZSAibm90IiBpcyBuZWdhdGl2ZSBhbmQgImdvb2QiIGlzIHBvc2l0aXZlLCBidXQgYXQgbGVhc3QgdGhhdCBzaG91bGQgbm90IHN3YXkgdGhlIGFuYWx5c2lzLiBJZiAibm90IGdvb2QiIGdvdCBhIHNjb3JlIG9mIHBvc2l0aXZlIGR1ZSB0byB0aGUgImdvb2QiLCB0aGF0IHdvdWxkIGJlIGJhZC4gVGhlIHNlbnRpbWVudCBzY29yZXMgd2lsbCBiZSBkZXRlcm1pbmVkIGJ5IGhvdXIgc28gd2UgY2FuIHNlZSBob3cgdGhlIGdhbWVzIGltcGFjdCB0aGUgc2VudGltZW50cyBvZiB0aGUgdHdlZXRzLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CiNEZXRlcm1pbmUgdGhlIHNlbnRpbWVudHMgb2YgdGhlIHR3ZWV0cwpzZW50aW1lbnQgPC0gdGliYmxlKGluZGV4ID0gMTpucm93KHR3KSwKICAgICAgICAgICAgICAgICAgICBjcmVhdGVkID0gdHckY3JlYXRlZF9hdCwKICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gY2xlYW5fdHdlZXQpICU+JQogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikpICU+JQogIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpKSAlPiUKICBncm91cF9ieShpbmRleCwgY3JlYXRlZCkgJT4lCiAgY291bnQoaW5kZXgsIHNlbnRpbWVudCkgJT4lCiAgc3ByZWFkKHNlbnRpbWVudCwgbiwgZmlsbCA9IDApICU+JQogIGdyb3VwX2J5KFRpbWUgPSByb3VuZF9kYXRlKGNyZWF0ZWQsIHVuaXQ9ImhvdXJzIikpICU+JQogIG11dGF0ZShzY29yZSA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpICU+JQogIHN1bW1hcmlzZShzY29yZSA9IHN1bShzY29yZSkpCgpzZW50aW1lbnQkc2VudGltZW50IDwtIGZhY3RvcihpZmVsc2Uoc2VudGltZW50JHNjb3JlID4gMCwgIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIiksIGxhYmVscz1jKCJQb3NpdGl2ZSIsICJOZWdhdGl2ZSIpKQoKYGBgCgpOb3cgd2UgY2FuIGdyYXBoIHRoZSB0d2VldHMuCmBgYHtyfQpkYXRlMiA8LSBmb3JtYXQoc2VudGltZW50JFRpbWUsICIlWS0lbS0lZCAlSCIpCmZvciAoaSBpbiBzZXFfbGVuKDEwKSkgeyBkYXRlMiA8LSBzdWIoc2NoZWR1bGVbaV0sIG9wcG9uZW50W2ldLCBkYXRlMiwgaWdub3JlLmNhc2UgPSBUUlVFKSB9CmRhdGUyW2dyZXAoIjIwMTgtMDMtIiwgZGF0ZTIsIGlnbm9yZS5jYXNlID0gVFJVRSldIDwtICIiCgpnZ3Bsb3Qoc2VudGltZW50LCBhZXMoeD1hcy5EYXRlKFRpbWUpLCB5PXNjb3JlKSkgKwogIGdlb21fbGluZShzaXplID0gMS41LCBhbHBoYSA9IDAuNywgYWVzKGNvbG91ciA9IHNlbnRpbWVudCkpICsKICBsYWJzKHRpdGxlPSJTZW50aW1lbnRzIG9mIFR3ZWV0cyIsIHN1YnRpdGxlPXBhc3RlKGQxLCJ0byIsZDIsc2VwPSIgIiksIAogICAgICAgeCA9ICJNYXJjaCIsIHkgPSAiU2VudGltZW50IFNjb3JlIikgKwogIGdlb21fdGV4dCh5PXJlcChjKDUwLCA3NSwgMTAwKSwgbGVuID0gbnJvdyhzZW50aW1lbnQpKSwgbGFiZWw9ZGF0ZTIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQoKcm0oZGF0ZTIsaSkgIAoKYGBgCgpUaGUgZ3JhcGggbWF5IGJlIGEgbGl0dGxlIG1pc2xlYWRpbmcgc2luY2UgaXQgYXBwZWFycyB0aGUgQnJ1aW5zIGdhbWUgY2F1c2VzIGEgbGFyZ2Ugc3Bpa2UgaW4gdHdlZXRzLiBXaGVuIEkgbWFudWFsbHkgaW5zcGVjdCB0aGUgZGF0YSwgaXQgYXBwZWFycyB0aGF0IHRoZSBsYXJnZSBzcGlrZSBvY2N1cnMgYWZ0ZXIgdGhlIGNvbmNsdXNpb24gb2YgdGhlIE1hcmNoIDcgUGVuZ3VpbnMgZ2FtZSBhbmQganVzdCBwcmlvciB0byB0aGUgc3RhcnQgb2YgdGhlIE1hcmNoIDggQnJ1aW5zIGdhbWUuIAoKTW92aW5nIG9uLCBsZXQgdXMgbm93IGxvb2sgYXQgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiByZXR3ZWV0cy4gU2luY2UgcmV0d2VldHMsIGJ5IGRlZmluaXRpb24sIGhhdmUgbXVsdGlwbGUgb2NjdXJlbmNlcywgd2Ugd2FudCBqdXN0IG9uZSB0d2VldCB0byByZXByZXNlbnQgZWFjaCByZXR3ZWV0IHNldC4KCmBgYHtyfQpjbGVhbl90d2VldCA8LSB0d1t0dyRzdGF0dXNfaWQgJWluJSB1bmlxdWUodHckcmV0d2VldF9zdGF0dXNfaWQpLF0kdGV4dApgYGAKCkNsZWFuIHRoZSB0ZXh0IGFnYWluLgpgYGB7cn0KIyBGb3IgcmVtb3ZpbmcgcmV0d2VldHMsIHJlZmVyZW5jZXMgdG8gc2NyZWVuIG5hbWVzLCBoYXNodGFncywgc3BhY2VzLCBudW1iZXJzLCBwdW5jdHVhdGlvbnMsIHVybHMuCmNsZWFuX3R3ZWV0IDwtIGdzdWIoJ1xcbicsICcnLCBjbGVhbl90d2VldCkgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiaHR0cFxcUytcXHMqIiwiIikgJT4lCiAgc3RyX3JlcGxhY2UoIlJUIEBbYS16LEEtWiwwLTldKjogIiwiIikgJT4lCiAgc3RyX3JlcGxhY2VfYWxsKCIjW2EteixBLVpdKiIsIiIpICU+JQogIHN0cl9yZXBsYWNlX2FsbCgiQFthLXosQS1aXSoiLCIiKSAlPiUKICBzdHJfcmVwbGFjZV9hbGwoIlswLTldIiwiIikgJT4lCiAgc3RyX3JlcGxhY2VfYWxsKCIgIiwiICIpCgpgYGAKCkRldGVybWluZSB0aGUgc2VudGltZW50IG9mIGVhY2ggdHdlZXQgYW5kIGdyYXBoLiBUaGUgdG9wIGxpbmUgZ3JhcGhzIHRoZSBwb3NpdGl2ZSBzZW50aW1lbnQsIHdpdGggaGlnaGVyIG51bWJlcnMgaW5kaWNhdGluZyBhIGhpZ2hlciBwb3NpdGl2ZSBzZW50aW1lbnQuIFRoZSBsb3dlciBsaW5lIGdyYXBocyB0aGUgbmVnYXRpdmUgc2VudGltZW50IHR3ZWV0cywgd2l0aCBsb3dlciBudW1iZXJzIGluZGljYXRpbmcgYW4gaW5jcmVhc2UgaW4gbmVnYXRpdmUgc2VudGltZW50cy4gCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpzZW50aW1lbnQgPC0gdGliYmxlKGluZGV4ID0gMTpsZW5ndGgoY2xlYW5fdHdlZXQpLAogIGNyZWF0ZWQgPSB0d1t0dyRzdGF0dXNfaWQgJWluJSB1bmlxdWUodHckcmV0d2VldF9zdGF0dXNfaWQpLF0kY3JlYXRlZF9hdCwKICAgICAgICAgICAgICB0ZXh0ID0gY2xlYW5fdHdlZXQpICU+JQogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikpICU+JQogIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpKSAlPiUKICBncm91cF9ieShpbmRleCwgY3JlYXRlZCkgJT4lCiAgY291bnQoaW5kZXgsIHNlbnRpbWVudCkgJT4lCiAgc3ByZWFkKHNlbnRpbWVudCwgbiwgZmlsbCA9IDApICU+JQogIGdyb3VwX2J5KFRpbWUgPSByb3VuZF9kYXRlKGNyZWF0ZWQsIHVuaXQ9ImhvdXJzIikpICU+JQogIG11dGF0ZShzY29yZSA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpICU+JQogIHN1bW1hcmlzZShzY29yZSA9IHN1bShzY29yZSkpCiAgCnNlbnRpbWVudCRzZW50aW1lbnQgPC0gZmFjdG9yKGlmZWxzZShzZW50aW1lbnQkc2NvcmUgPiAwLCAiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSwgbGFiZWxzPWMoIlBvc2l0aXZlIiwgIk5lZ2F0aXZlIikpCgpnZ3Bsb3Qoc2VudGltZW50LCBhZXMoeD1hcy5EYXRlKFRpbWUpLCB5PXNjb3JlKSkgKwogIGdlb21fbGluZShzaXplID0gMS41LCBhbHBoYSA9IDAuNywgYWVzKGNvbG91ciA9IHNlbnRpbWVudCkpICsKICBsYWJzKHRpdGxlPSJTZW50aW1lbnRzIG9mIFR3ZWV0cyIsIHN1YnRpdGxlPXBhc3RlKGQxLCJ0byIsZDIsc2VwPSIgIiksIAogICAgICAgeCA9ICJNYXJjaCIsIHkgPSAiU2VudGltZW50IFNjb3JlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCgpgYGAKCmBgYHtyfQpkYXRlMQpgYGAKCkxvb2tpbmcgYXQgdGhlIGdyYXBoLCBpdCBhcHBlYXJzIHRoZSBsb3NzIHRvIHRoZSBHb2xkZW4gS25pZ2h0cyBvbiBNYXJjaCAxMiB3YXMgZm9sbG93ZWQgYnkgYSBsYXJnZSBzcGlrZSBpbiBwb3NpdGl2ZSBzZW50aW1lbnQgd2l0aCBhIHJlZHVjZWQgdHJlbmQgb2YgbmVnYXRpdmUgc2VudGltZW50LiBEaWQgdGhlIGxvc3Mgb2YgdGhlIG9uZSBnYW1lIGNhdXNlIGEgaGlnaCBsZXZlbCBvZiBlbmNvdXJhZ2VtZW50IGZvciB0aGUgdXBjb21pbmcgQmx1ZSBKYWNrZXRzIGdhbWU/IEhvd2V2ZXIsIGFmdGVyIGxvc2luZyB0byB0aGUgQmx1ZSBKYWNrZXRzLCB0aGVyZSBpcyBhIGp1bXAgaW4gbmVnYXRpdmUgc2VudGltZW50LgoKSSBhZG1pdCB0aGF0IHRoZXNlIHJlc3VsdHMgZG8gbGl0dGxlIHRvIGFkZHJlc3MgdGhlIDV0aCBxdWVzdGlvbiwgYW5kIGEgbW9yZSB0aG9yb3VnaCBhbmFseXNpcyBpcyBuZWVkZWQuIFRoaXMgZnVydGhlciBhbmFseXNpcyB3b3VsZCByZXF1aXJlIG1vcmUgb2YgdGhlIHNhbWUsIGp1c3QgaW4gbW9yZSBkZXRhaWwuIE1heWJlIGxvb2tpbmcgYXQgd2hhdCBvdGhlciBldmVudHMgb2NjdXJlZCBkdXJpbmcgdGhlIHRpbWU/IERlZmluaXRsZXkgZXhwYW5kaW5nIHRoZSBsaXN0IG9mIHdvcmRzIHNpbmNlIHRoZSB0b3AgcmVzdWx0cyBhcmUgc29tZXdoYXQgZXhwZWN0ZWQgKGUuZy4gZ2FtZSwgZmx5ZXJzLCB3aW4sIGdvYWwpLiBBbm90aGVyIGludGVyZXN0aW5nIHJlc3VsdCBtaWdodCBiZSBpZiB0aGUgc2VudGltZW50IGNhdXNlcyBtb3JlIG9yIGxlc3MgcmV0d2VldHM/IFRoYXQgd291bGQgYmUgdXNlZnVsIGluZm9ybWF0aW9uIGlmIHdlIHdhbnRlZCB0byBzcHJlYWQgdGhlIHdvcmQgYmV0dGVyLiAKClRoZXJlIGlzIG11Y2ggbW9yZSBJIGNvdWxkIGFuYWx5emUgZnJvbSB0aGlzIGRhdGEuIEZvciBvbmUgdGhpbmcsIEkgc2hvdWxkIGV4cGFuZCBvbiBteSBjb25jbHVzaW9ucyBmcm9tIGFuYWx5emluZyB0aGUgZGF0YS4gU2luY2UgdGhpcyBzdHVkeSB3YXMgImZvciBmdW4sIiBJIG1heSBvciBtYXkgbm90IHJldHVybiB0byB3cml0ZSBhIG1vcmUgdGhvcm91Z2ggY29uY2x1c2lvbiwgYnV0IHRoZSByZWFkZXIgaXMgZnJlZSB0byBsb29rIGF0IHRoZSBhbmFseXNpcyBhbmQgZm9ybSB0aGVpciBvd24gY29uY2x1c2lvbnMuIEkgZ3Vlc3Mgd2Ugd2lsbCBzZWUgaWYgSSBjb250aW51ZSB3aXRoIHRoaXMgc3R1ZHkgb3IgZ2V0IGRpc3RyYWN0ZWQgYnkgYW5vdGhlciAiZm9yIGZ1biIgcHJvamVjdCBpbiB0aGUgbmV4dCBwb3N0Lg==

Analyzing Tweets about the Philadelphia Flyers

7 April ’18

Twitter Analysis of Philly Flyers

I decided to analyze the tweets that mention the Philadelphia Flyers during most of March 2018. At the moment, I have two R Notebooks. Well, actually one completed, which I am posting here, and one R Notebook I am working on, which I will post later.

Here is part 1 for the Philly Flyers R Notebook Analysis: <!DOCTYPE html>

Flyers Tweets - Tweeters

Through my position at Stockton University, I have heard that the Philadelphia Flyers are looking for ways to increase ticket sales for games. I determined several data research questions related to how word can be spread for the Philadelphia Flyers. As a start, I asked the following questions: 1. Who tweeted the most texts over the data collection time period? 2. Of those who tweeted, who had the most followers? 3. Of those who tweeted, Who had the most retweeted texts?

I believe answering these questions will provide the Twitter users who currently promote the Flyers and which ones have the potential to influence other Twitter users. If I were looking for ways to promote the Flyers, I might try to persuade these users further to use their social media contacts.

In a follow on study, I plan to ask questions related to the textual content of the texted tweets.

In March 2018, I scraped Twitter several times in order to gather all tweets that had the hashtags #Flyers, #FlyersNation, or #LETSGOFLYERS. I used these hashtags based on anecdotal evidence only. Future studies may want to consider additional hashtags if there is any doubt that these hashtags are not truly representative. Twitter limits the number of tweets that can be obtained at any one time, which in part is why I scraped the data over three time periods. Although there are other workarounds, these three hashtags returned just shy of the limit of available tweets (15,000 tweets limit). Nonetheless it appears the data is sufficient for this initial study.

The dates of the collections were March 11, March 20, and March 26, 2018. The following is the code I used each time.

# load twitter library - the rtweet library is recommended now over twitteR
library(rtweet)

# Enter your Twitter credentials
appname <- "Flyers_Data"  # name I assigned my app in Twitter
key <- "XXXXXXXXX"  # I am not sharing my actual credentials
secret <- "XXXXXXXXXXXXXXXXXXX" # Anyone wishing to duplicate this code can sign up for their own Twitter app - it is free

# create token named "twitter_token"
twitter_token <- create_token(
  app = appname,
  consumer_key = key,
  consumer_secret = secret)
saveRDS(twitter_token, "~/.rtweet-oauth.rds")

# Scrape tweets
rstats_tweets <- search_tweets2(q = "#Flyers OR #FlyersNation OR #LETSGOFLYERS", n = 15000, parse = TRUE, type="mixed")

# Save tweets to a RData file
save(rstats_tweets, file="rtweets2018MMDD.RData")

After all the data was scraped and saved to R dataset files, we can start working with the data. First, several R libraries are needed. Note, I tried to use only high quality libraries, such as those developed by the RStudio group.

library(rtweet) # for users_data()
library(tidyverse) # Instead of just ggplot2 and dplyr
── Attaching packages ──────────────────── tidyverse 1.2.1 ──
✔ ggplot2 2.2.1     ✔ purrr   0.2.4
✔ tibble  1.4.2     ✔ dplyr   0.7.4
✔ tidyr   0.8.0     ✔ stringr 1.3.0
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ─────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(tidytext)  # For Twitter text manipulation
library("RColorBrewer") # Because I want to print with Flyers colors!

Read the three datasets back into memory and combine into one master dataset.

# Load the RData files that were saved after scraping Twitter
load(file="rtweets20180311.RData")
tw11 <- rstats_tweets
users11 <- users_data(rstats_tweets)
load(file="rtweets20180320.RData")
tw20 <- rstats_tweets
users20 <- users_data(rstats_tweets)
load(file="rtweets20180326.RData")
# Combine the two datasets
tw <- bind_rows(tw11, tw20, rstats_tweets)
users <- users_data(rstats_tweets)
users <- bind_rows(users11, users20, users)
# Delete the indivual datasets since we now have the master file
rm(tw11, users11, tw20, users20, rstats_tweets)

Due to the times when the data was scraped, there are some overlapping time periods in the data. For example, the scraping run of March 20 collected some tweets that were scraped on March 11. We do not want these duplicates.

users <- unique(users)

As I worked with the data, I found some users that I rather exclude from my analysis.

# user_id = "19618527" is the Philadelphia Flyers. We know the Flyers tweet for the team.
# user_id = "471268712" is PHLFlyersNation. We know the PHLFlyers tweet for the team.
# user_id = "154699499" is sportstalkphl We know the sportstalkphl tweet for the team.
# user_id = "426029765" is XFINITYLive We know XFINITYLive tweet for the team.
# user_id = "19276719" and "321035743" are not real people and directs to naughty websites
# user_id = "493658381" is FlyersNation. We know the Flyers tweet for the team.
# user_id = "938072969552826368" is the Philly Sports Network Flyers.
users <- users[!(users$user_id %in% c("19618527", "471268712", "154699499", "426029765", "19276719", "493658381", "938072969552826368", "321035743")),]

There are many people who tweet about the Flyers that do not live locally. I do not doubt they are fans, but they are probably not attending many games (due to their geographic locations). Perhaps they should be included in further analysis, but for now I am only keeping tweets by users who identify their location as Pennsylvania, New Jersey, or Delaware. This restriction may lose a few local people who do not identify their location as local, but it is unavoidable.

# Only analyze "local" tweeters - location identified as PA, NJ, or DE
select <- grepl("Phil", users$location, ignore.case = TRUE) | grepl("PA", users$location, ignore.case = FALSE) | grepl("NJ", users$location, ignore.case = FALSE) | grepl("DE", users$location, ignore.case = FALSE)
users <- users[select,]
rm(select)

Twitter allows certain users to be verified. Verified accounts include professional radio, TV, and news stations (e.g. NBC), and some celebrity names (a spot check identifies the selected as broadcastsers and reporters). I am sure they are quite helpful in boosting Flyers ticket sales, but then there is no reason to keep them in the analysis. We know they already are working on boosting sales. For the purposes of this study, the verified accounts will not be considered.

users <- users[!users$verified,] # Save only nonverified accounts

I am sure the remaining users include a few bots that I might have missed, but the resulting group is a start for this study.

Twitter data is formatted so that it can be saved in two datasets. The users data and the tweets data. The users data includes the following features: user_id, name, screen_name, location, description, url, protected, followers_count, friends_count, listed_count, statuses_count, favourites_count, account_created_at, verified, profile_url, profile_expanded_url, account_lang, profile_banner_url, profile_background_url, profile_image_url. We need to select the tweets from only the users that remain in the users dataset.

# There are a few tweets repeated due to overlapping dates. Save only one instance of each.
tw <- unique(tw)
# Now select only the tweets that belong to the remaining user_ids
tw <- tw[tw$user_id %in% users$user_id,]
# Save only the tweets that are in English - at this time
tw <- tw[tw$lang=="en",]

The tweets dataset has the following features: status_id, created_at, user_id, screen_name, text, source, reply_to_status_id, reply_to_user_id, reply_to_screen_name, is_quote, is_retweet, favorite_count, retweet_count, hashtags, symbols, urls_url, urls_t.co, urls_expanded_url, media_url, media_t.co, media_expanded_url, media_type, ext_media_url, ext_media_t.co, ext_media_expanded_url, ext_media_type, mentions_user_id, mentions_screen_name, lang, quoted_status_id, quoted_text, retweet_status_id, retweet_text, place_url, place_name, place_full_name, place_type, country, country_code, geo_coords, coords_coords, bbox_coords, query.

The collected tweets span the time from 2018-03-04 20:56:42 to 2018-03-26 13:00:21. The resulting count of users who tweeted is 1957 and 7359 tweets, although many are retweets.

Now we can address the first data research question. ## 1. What Twitter user had the most tweets over the selected time period?

tw %>%
  count(user_id, sort = TRUE) %>%
  filter(n > 60) %>%
  inner_join(distinct(users[,c(1:3)], user_id, .keep_all = TRUE)) %>%
#  ggplot(aes(x = reorder(name, n), y = n)) + # If I wanted to show actual names
  ggplot(aes(x = reorder(user_id, n), y = n)) +
  geom_col(aes(fill = n)) +
  scale_fill_distiller(palette="Oranges") +
  theme(legend.position = "none") +
  xlab("User_Ids") + ylab("Number of Tweets") +
  labs(title="Users with Most Tweets", subtitle="In Shades of Orange!") +
  coord_flip()

If I was doing this to get the actual names of the Twitter users, I could have listed the user names, as they are available in the dataset, but since this exercise is only academic, I print the user_id numbers, to maintain some anonymity.

Next let us look at the number of followers the users have. We could choose to see all regardless of followers. Viewing all users with their followers overloads the graph, and I do not want just a list of names. A spot check shows a large number of users have more than 20,000 followers. Specifically, 20 have more than 20,000 followers. If it turns out that these users are not real people, we could remove them from the users dataset or look at users with followers between 100 and 500, or whatever span we think appropriate.

2. Of those who tweeted, who had the most followers?

unique(users[,c(1,2,3,8)]) %>%
  filter(followers_count > 20000) %>%
  ggplot(aes(x = reorder(user_id, followers_count), y = followers_count)) +
  geom_col(aes(fill = followers_count)) +
  scale_fill_distiller(palette="Oranges") +
  theme(legend.position = "none") +
  xlab("User_Ids") + ylab("Number of Followers") +
  labs(title="Users who Tweeted #Flyers with Most Followers", subtitle="More than 20,000 followers") +
  coord_flip()

Again, I could have printed the graph with user names instead of user ids, but I am protecting identities for this initial study.

Finally, let us find the originators of tweets that were highly retweeted. It is possible that others retweeted the retweeted texts, but the following chart considers only the original authors of the tweets.

3. Of those who tweeted, Who had the most retweeted text?

tw[!tw$is_retweet,c(3,13)]  %>%
  group_by(user_id) %>%
  summarise(n=max(retweet_count)) %>%
  filter(n > 10) %>%
  inner_join(distinct(users[,c(1,2,3,8,13)], user_id, .keep_all = TRUE)) %>%
  ggplot(aes(x = reorder(user_id, n), y = n)) +
  geom_col(aes(fill = n)) +
  scale_fill_distiller(palette="Oranges") +
  theme(legend.position = "none") +
  xlab("User_Ids") + ylab("Retweet Count") +
  labs(title="Users who had the Most Retweets") +
  coord_flip()

This post has examined the Twitter users that tweet about the Philly Flyers. In the next post, we will look at the tweets themselves. I will post again soon.

LS0tCnRpdGxlOiAiRmx5ZXJzIFR3ZWV0cyAtIFR3ZWV0ZXJzIgphdXRob3I6ICJEci4gQ2xpZnRvbiBCYWxkd2luIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpUaHJvdWdoIG15IHBvc2l0aW9uIGF0IFN0b2NrdG9uIFVuaXZlcnNpdHksIEkgaGF2ZSBoZWFyZCB0aGF0IHRoZSBQaGlsYWRlbHBoaWEgRmx5ZXJzIGFyZSBsb29raW5nIGZvciB3YXlzIHRvIGluY3JlYXNlIHRpY2tldCBzYWxlcyBmb3IgZ2FtZXMuIEkgZGV0ZXJtaW5lZCBzZXZlcmFsIGRhdGEgcmVzZWFyY2ggcXVlc3Rpb25zIHJlbGF0ZWQgdG8gaG93IHdvcmQgY2FuIGJlIHNwcmVhZCBmb3IgdGhlIFBoaWxhZGVscGhpYSBGbHllcnMuIEFzIGEgc3RhcnQsIEkgYXNrZWQgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnM6CjEuIFdobyB0d2VldGVkIHRoZSBtb3N0IHRleHRzIG92ZXIgdGhlIGRhdGEgY29sbGVjdGlvbiB0aW1lIHBlcmlvZD8KMi4gT2YgdGhvc2Ugd2hvIHR3ZWV0ZWQsIHdobyBoYWQgdGhlIG1vc3QgZm9sbG93ZXJzPwozLiBPZiB0aG9zZSB3aG8gdHdlZXRlZCwgV2hvIGhhZCB0aGUgbW9zdCByZXR3ZWV0ZWQgdGV4dHM/CgpJIGJlbGlldmUgYW5zd2VyaW5nIHRoZXNlIHF1ZXN0aW9ucyB3aWxsIHByb3ZpZGUgdGhlIFR3aXR0ZXIgdXNlcnMgd2hvIGN1cnJlbnRseSBwcm9tb3RlIHRoZSBGbHllcnMgYW5kIHdoaWNoIG9uZXMgaGF2ZSB0aGUgcG90ZW50aWFsIHRvIGluZmx1ZW5jZSBvdGhlciBUd2l0dGVyIHVzZXJzLiBJZiBJIHdlcmUgbG9va2luZyBmb3Igd2F5cyB0byBwcm9tb3RlIHRoZSBGbHllcnMsIEkgbWlnaHQgdHJ5IHRvIHBlcnN1YWRlIHRoZXNlIHVzZXJzIGZ1cnRoZXIgdG8gdXNlIHRoZWlyIHNvY2lhbCBtZWRpYSBjb250YWN0cy4KCkluIGEgZm9sbG93IG9uIHN0dWR5LCBJIHBsYW4gdG8gYXNrIHF1ZXN0aW9ucyByZWxhdGVkIHRvIHRoZSB0ZXh0dWFsIGNvbnRlbnQgb2YgdGhlIHRleHRlZCB0d2VldHMuCgpJbiBNYXJjaCAyMDE4LCBJIHNjcmFwZWQgVHdpdHRlciBzZXZlcmFsIHRpbWVzIGluIG9yZGVyIHRvIGdhdGhlciBhbGwgdHdlZXRzIHRoYXQgaGFkIHRoZSBoYXNodGFncyAjRmx5ZXJzLCAjRmx5ZXJzTmF0aW9uLCBvciAjTEVUU0dPRkxZRVJTLiBJIHVzZWQgdGhlc2UgaGFzaHRhZ3MgYmFzZWQgb24gYW5lY2RvdGFsIGV2aWRlbmNlIG9ubHkuIEZ1dHVyZSBzdHVkaWVzIG1heSB3YW50IHRvIGNvbnNpZGVyIGFkZGl0aW9uYWwgaGFzaHRhZ3MgaWYgdGhlcmUgaXMgYW55IGRvdWJ0IHRoYXQgdGhlc2UgaGFzaHRhZ3MgYXJlIG5vdCB0cnVseSByZXByZXNlbnRhdGl2ZS4gVHdpdHRlciBsaW1pdHMgdGhlIG51bWJlciBvZiB0d2VldHMgdGhhdCBjYW4gYmUgb2J0YWluZWQgYXQgYW55IG9uZSB0aW1lLCB3aGljaCBpbiBwYXJ0IGlzIHdoeSBJIHNjcmFwZWQgdGhlIGRhdGEgb3ZlciB0aHJlZSB0aW1lIHBlcmlvZHMuIEFsdGhvdWdoIHRoZXJlIGFyZSBvdGhlciB3b3JrYXJvdW5kcywgdGhlc2UgdGhyZWUgaGFzaHRhZ3MgcmV0dXJuZWQganVzdCBzaHkgb2YgdGhlIGxpbWl0IG9mIGF2YWlsYWJsZSB0d2VldHMgKDE1LDAwMCB0d2VldHMgbGltaXQpLiBOb25ldGhlbGVzcyBpdCBhcHBlYXJzIHRoZSBkYXRhIGlzIHN1ZmZpY2llbnQgZm9yIHRoaXMgaW5pdGlhbCBzdHVkeS4KClRoZSBkYXRlcyBvZiB0aGUgY29sbGVjdGlvbnMgd2VyZSBNYXJjaCAxMSwgTWFyY2ggMjAsIGFuZCBNYXJjaCAyNiwgMjAxOC4gVGhlIGZvbGxvd2luZyBpcyB0aGUgY29kZSBJIHVzZWQgZWFjaCB0aW1lLgoKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgbG9hZCB0d2l0dGVyIGxpYnJhcnkgLSB0aGUgcnR3ZWV0IGxpYnJhcnkgaXMgcmVjb21tZW5kZWQgbm93IG92ZXIgdHdpdHRlUgpsaWJyYXJ5KHJ0d2VldCkKCiMgRW50ZXIgeW91ciBUd2l0dGVyIGNyZWRlbnRpYWxzCmFwcG5hbWUgPC0gIkZseWVyc19EYXRhIiAgIyBuYW1lIEkgYXNzaWduZWQgbXkgYXBwIGluIFR3aXR0ZXIKa2V5IDwtICJYWFhYWFhYWFgiICAjIEkgYW0gbm90IHNoYXJpbmcgbXkgYWN0dWFsIGNyZWRlbnRpYWxzCnNlY3JldCA8LSAiWFhYWFhYWFhYWFhYWFhYWFhYWCIgIyBBbnlvbmUgd2lzaGluZyB0byBkdXBsaWNhdGUgdGhpcyBjb2RlIGNhbiBzaWduIHVwIGZvciB0aGVpciBvd24gVHdpdHRlciBhcHAgLSBpdCBpcyBmcmVlCgojIGNyZWF0ZSB0b2tlbiBuYW1lZCAidHdpdHRlcl90b2tlbiIKdHdpdHRlcl90b2tlbiA8LSBjcmVhdGVfdG9rZW4oCiAgYXBwID0gYXBwbmFtZSwKICBjb25zdW1lcl9rZXkgPSBrZXksCiAgY29uc3VtZXJfc2VjcmV0ID0gc2VjcmV0KQpzYXZlUkRTKHR3aXR0ZXJfdG9rZW4sICJ+Ly5ydHdlZXQtb2F1dGgucmRzIikKCiMgU2NyYXBlIHR3ZWV0cwpyc3RhdHNfdHdlZXRzIDwtIHNlYXJjaF90d2VldHMyKHEgPSAiI0ZseWVycyBPUiAjRmx5ZXJzTmF0aW9uIE9SICNMRVRTR09GTFlFUlMiLCBuID0gMTUwMDAsIHBhcnNlID0gVFJVRSwgdHlwZT0ibWl4ZWQiKQoKIyBTYXZlIHR3ZWV0cyB0byBhIFJEYXRhIGZpbGUKc2F2ZShyc3RhdHNfdHdlZXRzLCBmaWxlPSJydHdlZXRzMjAxOE1NREQuUkRhdGEiKQoKYGBgCgpBZnRlciBhbGwgdGhlIGRhdGEgd2FzIHNjcmFwZWQgYW5kIHNhdmVkIHRvIFIgZGF0YXNldCBmaWxlcywgd2UgY2FuIHN0YXJ0IHdvcmtpbmcgd2l0aCB0aGUgZGF0YS4gRmlyc3QsIHNldmVyYWwgUiBsaWJyYXJpZXMgYXJlIG5lZWRlZC4gTm90ZSwgSSB0cmllZCB0byB1c2Ugb25seSBoaWdoIHF1YWxpdHkgbGlicmFyaWVzLCBzdWNoIGFzIHRob3NlIGRldmVsb3BlZCBieSB0aGUgUlN0dWRpbyBncm91cC4KCmBgYHtyfQpsaWJyYXJ5KHJ0d2VldCkgIyBmb3IgdXNlcnNfZGF0YSgpCmxpYnJhcnkodGlkeXZlcnNlKSAjIEluc3RlYWQgb2YganVzdCBnZ3Bsb3QyIGFuZCBkcGx5cgpsaWJyYXJ5KHRpZHl0ZXh0KSAgIyBGb3IgVHdpdHRlciB0ZXh0IG1hbmlwdWxhdGlvbgpsaWJyYXJ5KCJSQ29sb3JCcmV3ZXIiKSAjIEJlY2F1c2UgSSB3YW50IHRvIHByaW50IHdpdGggRmx5ZXJzIGNvbG9ycyEKYGBgCgoKUmVhZCB0aGUgdGhyZWUgZGF0YXNldHMgYmFjayBpbnRvIG1lbW9yeSBhbmQgY29tYmluZSBpbnRvIG9uZSBtYXN0ZXIgZGF0YXNldC4KYGBge3J9CiMgTG9hZCB0aGUgUkRhdGEgZmlsZXMgdGhhdCB3ZXJlIHNhdmVkIGFmdGVyIHNjcmFwaW5nIFR3aXR0ZXIKbG9hZChmaWxlPSJydHdlZXRzMjAxODAzMTEuUkRhdGEiKQp0dzExIDwtIHJzdGF0c190d2VldHMKdXNlcnMxMSA8LSB1c2Vyc19kYXRhKHJzdGF0c190d2VldHMpCmxvYWQoZmlsZT0icnR3ZWV0czIwMTgwMzIwLlJEYXRhIikKdHcyMCA8LSByc3RhdHNfdHdlZXRzCnVzZXJzMjAgPC0gdXNlcnNfZGF0YShyc3RhdHNfdHdlZXRzKQpsb2FkKGZpbGU9InJ0d2VldHMyMDE4MDMyNi5SRGF0YSIpCgojIENvbWJpbmUgdGhlIHR3byBkYXRhc2V0cwp0dyA8LSBiaW5kX3Jvd3ModHcxMSwgdHcyMCwgcnN0YXRzX3R3ZWV0cykKdXNlcnMgPC0gdXNlcnNfZGF0YShyc3RhdHNfdHdlZXRzKQp1c2VycyA8LSBiaW5kX3Jvd3ModXNlcnMxMSwgdXNlcnMyMCwgdXNlcnMpCgojIERlbGV0ZSB0aGUgaW5kaXZ1YWwgZGF0YXNldHMgc2luY2Ugd2Ugbm93IGhhdmUgdGhlIG1hc3RlciBmaWxlCnJtKHR3MTEsIHVzZXJzMTEsIHR3MjAsIHVzZXJzMjAsIHJzdGF0c190d2VldHMpCgpgYGAKCkR1ZSB0byB0aGUgdGltZXMgd2hlbiB0aGUgZGF0YSB3YXMgc2NyYXBlZCwgdGhlcmUgYXJlIHNvbWUgb3ZlcmxhcHBpbmcgdGltZSBwZXJpb2RzIGluIHRoZSBkYXRhLiBGb3IgZXhhbXBsZSwgdGhlIHNjcmFwaW5nIHJ1biBvZiBNYXJjaCAyMCBjb2xsZWN0ZWQgc29tZSB0d2VldHMgdGhhdCB3ZXJlIHNjcmFwZWQgb24gTWFyY2ggMTEuIFdlIGRvIG5vdCB3YW50IHRoZXNlIGR1cGxpY2F0ZXMuCgpgYGB7cn0KdXNlcnMgPC0gdW5pcXVlKHVzZXJzKQpgYGAKCkFzIEkgd29ya2VkIHdpdGggdGhlIGRhdGEsIEkgZm91bmQgc29tZSB1c2VycyB0aGF0IEkgcmF0aGVyIGV4Y2x1ZGUgZnJvbSBteSBhbmFseXNpcy4gCmBgYHtyfQojIHVzZXJfaWQgPSAiMTk2MTg1MjciIGlzIHRoZSBQaGlsYWRlbHBoaWEgRmx5ZXJzLiBXZSBrbm93IHRoZSBGbHllcnMgdHdlZXQgZm9yIHRoZSB0ZWFtLgojIHVzZXJfaWQgPSAiNDcxMjY4NzEyIiBpcyBQSExGbHllcnNOYXRpb24uIFdlIGtub3cgdGhlIFBITEZseWVycyB0d2VldCBmb3IgdGhlIHRlYW0uCiMgdXNlcl9pZCA9ICIxNTQ2OTk0OTkiIGlzIHNwb3J0c3RhbGtwaGwgV2Uga25vdyB0aGUgc3BvcnRzdGFsa3BobCB0d2VldCBmb3IgdGhlIHRlYW0uCiMgdXNlcl9pZCA9ICI0MjYwMjk3NjUiIGlzIFhGSU5JVFlMaXZlIFdlIGtub3cgWEZJTklUWUxpdmUgdHdlZXQgZm9yIHRoZSB0ZWFtLgojIHVzZXJfaWQgPSAiMTkyNzY3MTkiIGFuZCAiMzIxMDM1NzQzIiBhcmUgbm90IHJlYWwgcGVvcGxlIGFuZCBkaXJlY3RzIHRvIG5hdWdodHkgd2Vic2l0ZXMKIyB1c2VyX2lkID0gIjQ5MzY1ODM4MSIgaXMgRmx5ZXJzTmF0aW9uLiBXZSBrbm93IHRoZSBGbHllcnMgdHdlZXQgZm9yIHRoZSB0ZWFtLgojIHVzZXJfaWQgPSAiOTM4MDcyOTY5NTUyODI2MzY4IiBpcyB0aGUgUGhpbGx5IFNwb3J0cyBOZXR3b3JrIEZseWVycy4KCnVzZXJzIDwtIHVzZXJzWyEodXNlcnMkdXNlcl9pZCAlaW4lIGMoIjE5NjE4NTI3IiwgIjQ3MTI2ODcxMiIsICIxNTQ2OTk0OTkiLCAiNDI2MDI5NzY1IiwgIjE5Mjc2NzE5IiwgIjQ5MzY1ODM4MSIsICI5MzgwNzI5Njk1NTI4MjYzNjgiLCAiMzIxMDM1NzQzIikpLF0KCmBgYAoKVGhlcmUgYXJlIG1hbnkgcGVvcGxlIHdobyB0d2VldCBhYm91dCB0aGUgRmx5ZXJzIHRoYXQgZG8gbm90IGxpdmUgbG9jYWxseS4gSSBkbyBub3QgZG91YnQgdGhleSBhcmUgZmFucywgYnV0IHRoZXkgYXJlIHByb2JhYmx5IG5vdCBhdHRlbmRpbmcgbWFueSBnYW1lcyAoZHVlIHRvIHRoZWlyIGdlb2dyYXBoaWMgbG9jYXRpb25zKS4gUGVyaGFwcyB0aGV5IHNob3VsZCBiZSBpbmNsdWRlZCBpbiBmdXJ0aGVyIGFuYWx5c2lzLCBidXQgZm9yIG5vdyBJIGFtIG9ubHkga2VlcGluZyB0d2VldHMgYnkgdXNlcnMgd2hvIGlkZW50aWZ5IHRoZWlyIGxvY2F0aW9uIGFzIFBlbm5zeWx2YW5pYSwgTmV3IEplcnNleSwgb3IgRGVsYXdhcmUuIFRoaXMgcmVzdHJpY3Rpb24gbWF5IGxvc2UgYSBmZXcgbG9jYWwgcGVvcGxlIHdobyBkbyBub3QgaWRlbnRpZnkgdGhlaXIgbG9jYXRpb24gYXMgbG9jYWwsIGJ1dCBpdCBpcyB1bmF2b2lkYWJsZS4KCmBgYHtyfQojIE9ubHkgYW5hbHl6ZSAibG9jYWwiIHR3ZWV0ZXJzIC0gbG9jYXRpb24gaWRlbnRpZmllZCBhcyBQQSwgTkosIG9yIERFCnNlbGVjdCA8LSBncmVwbCgiUGhpbCIsIHVzZXJzJGxvY2F0aW9uLCBpZ25vcmUuY2FzZSA9IFRSVUUpIHwgZ3JlcGwoIlBBIiwgdXNlcnMkbG9jYXRpb24sIGlnbm9yZS5jYXNlID0gRkFMU0UpIHwgZ3JlcGwoIk5KIiwgdXNlcnMkbG9jYXRpb24sIGlnbm9yZS5jYXNlID0gRkFMU0UpIHwgZ3JlcGwoIkRFIiwgdXNlcnMkbG9jYXRpb24sIGlnbm9yZS5jYXNlID0gRkFMU0UpCgp1c2VycyA8LSB1c2Vyc1tzZWxlY3QsXQpybShzZWxlY3QpCgpgYGAKClR3aXR0ZXIgYWxsb3dzIGNlcnRhaW4gdXNlcnMgdG8gYmUgdmVyaWZpZWQuIFZlcmlmaWVkIGFjY291bnRzIGluY2x1ZGUgcHJvZmVzc2lvbmFsIHJhZGlvLCBUViwgYW5kIG5ld3Mgc3RhdGlvbnMgKGUuZy4gTkJDKSwgYW5kIHNvbWUgY2VsZWJyaXR5IG5hbWVzIChhIHNwb3QgY2hlY2sgaWRlbnRpZmllcyB0aGUgc2VsZWN0ZWQgYXMgYnJvYWRjYXN0c2VycyBhbmQgcmVwb3J0ZXJzKS4gSSBhbSBzdXJlIHRoZXkgYXJlIHF1aXRlIGhlbHBmdWwgaW4gYm9vc3RpbmcgRmx5ZXJzIHRpY2tldCBzYWxlcywgYnV0IHRoZW4gdGhlcmUgaXMgbm8gcmVhc29uIHRvIGtlZXAgdGhlbSBpbiB0aGUgYW5hbHlzaXMuIFdlIGtub3cgdGhleSBhbHJlYWR5IGFyZSB3b3JraW5nIG9uIGJvb3N0aW5nIHNhbGVzLiBGb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgc3R1ZHksIHRoZSB2ZXJpZmllZCBhY2NvdW50cyB3aWxsIG5vdCBiZSBjb25zaWRlcmVkLgoKYGBge3J9CnVzZXJzIDwtIHVzZXJzWyF1c2VycyR2ZXJpZmllZCxdICMgU2F2ZSBvbmx5IG5vbnZlcmlmaWVkIGFjY291bnRzCmBgYAoKSSBhbSBzdXJlIHRoZSByZW1haW5pbmcgdXNlcnMgaW5jbHVkZSBhIGZldyBib3RzIHRoYXQgSSBtaWdodCBoYXZlIG1pc3NlZCwgYnV0IHRoZSByZXN1bHRpbmcgZ3JvdXAgaXMgYSBzdGFydCBmb3IgdGhpcyBzdHVkeS4gCgpUd2l0dGVyIGRhdGEgaXMgZm9ybWF0dGVkIHNvIHRoYXQgaXQgY2FuIGJlIHNhdmVkIGluIHR3byBkYXRhc2V0cy4gVGhlIHVzZXJzIGRhdGEgYW5kIHRoZSB0d2VldHMgZGF0YS4gVGhlIHVzZXJzIGRhdGEgaW5jbHVkZXMgdGhlIGZvbGxvd2luZyBmZWF0dXJlczogYHIgbmFtZXModXNlcnMpYC4gV2UgbmVlZCB0byBzZWxlY3QgdGhlIHR3ZWV0cyBmcm9tIG9ubHkgdGhlIHVzZXJzIHRoYXQgcmVtYWluIGluIHRoZSB1c2VycyBkYXRhc2V0LgoKYGBge3J9CiMgVGhlcmUgYXJlIGEgZmV3IHR3ZWV0cyByZXBlYXRlZCBkdWUgdG8gb3ZlcmxhcHBpbmcgZGF0ZXMuIFNhdmUgb25seSBvbmUgaW5zdGFuY2Ugb2YgZWFjaC4KdHcgPC0gdW5pcXVlKHR3KQoKIyBOb3cgc2VsZWN0IG9ubHkgdGhlIHR3ZWV0cyB0aGF0IGJlbG9uZyB0byB0aGUgcmVtYWluaW5nIHVzZXJfaWRzCnR3IDwtIHR3W3R3JHVzZXJfaWQgJWluJSB1c2VycyR1c2VyX2lkLF0KCiMgU2F2ZSBvbmx5IHRoZSB0d2VldHMgdGhhdCBhcmUgaW4gRW5nbGlzaCAtIGF0IHRoaXMgdGltZQp0dyA8LSB0d1t0dyRsYW5nPT0iZW4iLF0KCmBgYAoKVGhlIHR3ZWV0cyBkYXRhc2V0IGhhcyB0aGUgZm9sbG93aW5nIGZlYXR1cmVzOiBgciBuYW1lcyh0dylgLgoKVGhlIGNvbGxlY3RlZCB0d2VldHMgc3BhbiB0aGUgdGltZSBmcm9tIGByIG1pbih0dyRjcmVhdGVkX2F0KWAgdG8gYHIgbWF4KHR3JGNyZWF0ZWRfYXQpYC4gVGhlIHJlc3VsdGluZyBjb3VudCBvZiB1c2VycyB3aG8gdHdlZXRlZCBpcyBgciBsZW5ndGgodW5pcXVlKHR3JHVzZXJfaWQpKWAgYW5kIGByIG5yb3codHcpYCB0d2VldHMsIGFsdGhvdWdoIG1hbnkgYXJlIHJldHdlZXRzLgoKTm93IHdlIGNhbiBhZGRyZXNzIHRoZSBmaXJzdCBkYXRhIHJlc2VhcmNoIHF1ZXN0aW9uLgojIyAxLiBXaGF0IFR3aXR0ZXIgdXNlciBoYWQgdGhlIG1vc3QgdHdlZXRzIG92ZXIgdGhlIHNlbGVjdGVkIHRpbWUgcGVyaW9kPwoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CnR3ICU+JQogIGNvdW50KHVzZXJfaWQsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDYwKSAlPiUKICBpbm5lcl9qb2luKGRpc3RpbmN0KHVzZXJzWyxjKDE6MyldLCB1c2VyX2lkLCAua2VlcF9hbGwgPSBUUlVFKSkgJT4lCiMgIGdncGxvdChhZXMoeCA9IHJlb3JkZXIobmFtZSwgbiksIHkgPSBuKSkgKyAjIElmIEkgd2FudGVkIHRvIHNob3cgYWN0dWFsIG5hbWVzCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcih1c2VyX2lkLCBuKSwgeSA9IG4pKSArCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBuKSkgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGU9Ik9yYW5nZXMiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgeGxhYigiVXNlcl9JZHMiKSArIHlsYWIoIk51bWJlciBvZiBUd2VldHMiKSArCiAgbGFicyh0aXRsZT0iVXNlcnMgd2l0aCBNb3N0IFR3ZWV0cyIsIHN1YnRpdGxlPSJJbiBTaGFkZXMgb2YgT3JhbmdlISIpICsKICBjb29yZF9mbGlwKCkKYGBgCgpJZiBJIHdhcyBkb2luZyB0aGlzIHRvIGdldCB0aGUgYWN0dWFsIG5hbWVzIG9mIHRoZSBUd2l0dGVyIHVzZXJzLCBJIGNvdWxkIGhhdmUgbGlzdGVkIHRoZSB1c2VyIG5hbWVzLCBhcyB0aGV5IGFyZSBhdmFpbGFibGUgaW4gdGhlIGRhdGFzZXQsIGJ1dCBzaW5jZSB0aGlzIGV4ZXJjaXNlIGlzIG9ubHkgYWNhZGVtaWMsIEkgcHJpbnQgdGhlIHVzZXJfaWQgbnVtYmVycywgdG8gbWFpbnRhaW4gc29tZSBhbm9ueW1pdHkuCgpOZXh0IGxldCB1cyBsb29rIGF0IHRoZSBudW1iZXIgb2YgZm9sbG93ZXJzIHRoZSB1c2VycyBoYXZlLiBXZSBjb3VsZCBjaG9vc2UgdG8gc2VlIGFsbCByZWdhcmRsZXNzIG9mIGZvbGxvd2Vycy4gVmlld2luZyBhbGwgdXNlcnMgd2l0aCB0aGVpciBmb2xsb3dlcnMgb3ZlcmxvYWRzIHRoZSBncmFwaCwgYW5kIEkgZG8gbm90IHdhbnQganVzdCBhIGxpc3Qgb2YgbmFtZXMuIEEgc3BvdCBjaGVjayBzaG93cyBhIGxhcmdlIG51bWJlciBvZiB1c2VycyBoYXZlIG1vcmUgdGhhbiAyMCwwMDAgZm9sbG93ZXJzLiBTcGVjaWZpY2FsbHksIGByIG5yb3codXNlcnNbdXNlcnMkZm9sbG93ZXJzX2NvdW50ID4gMjAwMDAsXSlgIGhhdmUgbW9yZSB0aGFuIDIwLDAwMCBmb2xsb3dlcnMuIElmIGl0IHR1cm5zIG91dCB0aGF0IHRoZXNlIHVzZXJzIGFyZSBub3QgcmVhbCBwZW9wbGUsIHdlIGNvdWxkIHJlbW92ZSB0aGVtIGZyb20gdGhlIHVzZXJzIGRhdGFzZXQgb3IgbG9vayBhdCB1c2VycyB3aXRoIGZvbGxvd2VycyBiZXR3ZWVuIDEwMCBhbmQgNTAwLCBvciB3aGF0ZXZlciBzcGFuIHdlIHRoaW5rIGFwcHJvcHJpYXRlLgoKIyMgMi4gT2YgdGhvc2Ugd2hvIHR3ZWV0ZWQsIHdobyBoYWQgdGhlIG1vc3QgZm9sbG93ZXJzPwoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CnVuaXF1ZSh1c2Vyc1ssYygxLDIsMyw4KV0pICU+JQogIGZpbHRlcihmb2xsb3dlcnNfY291bnQgPiAyMDAwMCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcih1c2VyX2lkLCBmb2xsb3dlcnNfY291bnQpLCB5ID0gZm9sbG93ZXJzX2NvdW50KSkgKwogIGdlb21fY29sKGFlcyhmaWxsID0gZm9sbG93ZXJzX2NvdW50KSkgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGU9Ik9yYW5nZXMiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgeGxhYigiVXNlcl9JZHMiKSArIHlsYWIoIk51bWJlciBvZiBGb2xsb3dlcnMiKSArCiAgbGFicyh0aXRsZT0iVXNlcnMgd2hvIFR3ZWV0ZWQgI0ZseWVycyB3aXRoIE1vc3QgRm9sbG93ZXJzIiwgc3VidGl0bGU9Ik1vcmUgdGhhbiAyMCwwMDAgZm9sbG93ZXJzIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCkFnYWluLCBJIGNvdWxkIGhhdmUgcHJpbnRlZCB0aGUgZ3JhcGggd2l0aCB1c2VyIG5hbWVzIGluc3RlYWQgb2YgdXNlciBpZHMsIGJ1dCBJIGFtIHByb3RlY3RpbmcgaWRlbnRpdGllcyBmb3IgdGhpcyBpbml0aWFsIHN0dWR5LgoKRmluYWxseSwgbGV0IHVzIGZpbmQgdGhlIG9yaWdpbmF0b3JzIG9mIHR3ZWV0cyB0aGF0IHdlcmUgaGlnaGx5IHJldHdlZXRlZC4gSXQgaXMgcG9zc2libGUgdGhhdCBvdGhlcnMgcmV0d2VldGVkIHRoZSByZXR3ZWV0ZWQgdGV4dHMsIGJ1dCB0aGUgZm9sbG93aW5nIGNoYXJ0IGNvbnNpZGVycyBvbmx5IHRoZSBvcmlnaW5hbCBhdXRob3JzIG9mIHRoZSB0d2VldHMuCgojIyAzLiBPZiB0aG9zZSB3aG8gdHdlZXRlZCwgV2hvIGhhZCB0aGUgbW9zdCByZXR3ZWV0ZWQgdGV4dD8KCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQp0d1shdHckaXNfcmV0d2VldCxjKDMsMTMpXSAgJT4lCiAgZ3JvdXBfYnkodXNlcl9pZCkgJT4lCiAgc3VtbWFyaXNlKG49bWF4KHJldHdlZXRfY291bnQpKSAlPiUKICBmaWx0ZXIobiA+IDEwKSAlPiUKICBpbm5lcl9qb2luKGRpc3RpbmN0KHVzZXJzWyxjKDEsMiwzLDgsMTMpXSwgdXNlcl9pZCwgLmtlZXBfYWxsID0gVFJVRSkpICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIodXNlcl9pZCwgbiksIHkgPSBuKSkgKwogIGdlb21fY29sKGFlcyhmaWxsID0gbikpICsKICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlPSJPcmFuZ2VzIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHhsYWIoIlVzZXJfSWRzIikgKyB5bGFiKCJSZXR3ZWV0IENvdW50IikgKwogIGxhYnModGl0bGU9IlVzZXJzIHdobyBoYWQgdGhlIE1vc3QgUmV0d2VldHMiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKVGhpcyBwb3N0IGhhcyBleGFtaW5lZCB0aGUgVHdpdHRlciB1c2VycyB0aGF0IHR3ZWV0IGFib3V0IHRoZSBQaGlsbHkgRmx5ZXJzLiBJbiB0aGUgbmV4dCBwb3N0LCB3ZSB3aWxsIGxvb2sgYXQgdGhlIHR3ZWV0cyB0aGVtc2VsdmVzLiBJIHdpbGwgcG9zdCBhZ2FpbiBzb29uLg==

Baseball's Pythagorean Expectation for Curling

11 March ’18

Baseball’s Pythagorean Expectation

I have been reading Mathletics by Wayne L. Winston and watching the 2018 Winter Olympics. I thought it might be fun to combine the Pythagorean Expectation, developed for baseball but applied to various other sports also, to the game of curling. I wrote the code and ran the program prior to the Women’s medal matches. Spoiler alert - Sweden won the Gold medal (with Korean getting the Silver) and Japan won the Bronze medal.

Here is my R Notebook: <!DOCTYPE html>

R Notebook on Women’s Curling 2018 Olympics

An analysis of Women’s Curling at the 2018 Winter Olympics in South Korea. The technique to predict future matches is based on the Baseball Pythagorean Theorem. The exponents for the Pythagorean Expetctaion were tested against games played to date on February 21, 2018 from 1.9 to 4.1 (by 0.1). Empirically 3.4 had the lowest mean-squared-error of that set

library('rvest')
participants <- "Women" # Set for output reasons
#Specifying the url for desired website to be scrapped
url <- 'http://results.worldcurling.org/Championship/DisplayResults?tournamentId=561&associationId=0&teamNumber=0&drawNumber=0'
exponent <- 3.4 # The exponent to use for the Pythagorean Expectation, determined empirically 

The website www.worldcurling.org is accessed for game data

#Reading the HTML code from the website
webpage <- read_html(url)
#Using CSS selectors to scrape the rankings section
rank_data_html <- html_nodes(webpage,'.game-table')
num_games = length(rank_data_html)

The dowloaded webpage is scraped for the appropriate data

games <- matrix(nrow = num_games, ncol = 4)
# Scrape the data from the webpage
for (i in seq_len(num_games)) {
  this_result <- rank_data_html[i]
  teams <- html_text(html_nodes(this_result,".game-team"))
  scores <- html_text(html_nodes(this_result,".game-total"))
  games[i,c(1,2)] <- trimws(gsub('\r\n', '', teams))
  games[i,c(3,4)] <- strtoi(trimws(gsub('\r\n', '', scores)))
}
rm(i, scores, teams, this_result)

At this point, data for all Women’s Curling games at the Olympics has been retrieved and stored in a matrix named games. We will split this matrix into a matrix of only the games that have already completed and the games scheduled to be held (as of February 22, 2018)

completeGames <- games[!is.na(games)[,3],]
# Get a list of the teams that are playing
teams <- unique(games[,1])
if (!is.na(pmatch("To Be", teams))) teams = teams[-length(teams)]
# Take a subset of the games that are scheduled to come
scheduledGames <- matrix(nrow=nrow(games[is.na(games)[,3],]), ncol = 6)
scheduledGames[,1] <- games[is.na(games)[,3],1]
scheduledGames[,4] <- games[is.na(games)[,3],2]
scheduledGames <- scheduledGames[!grepl("To Be", scheduledGames[,1]),]
# Get a list of the teams that are scheduled to play
upcomingTeams <- unique(c(scheduledGames[,1],scheduledGames[,4]))

Based on each team’s record, determine the percentage of games they have won.

standings = matrix(nrow=length(teams), ncol = 5)
count <- 1L
for(team in teams) {
  wins <- sum(strtoi(completeGames[grepl(team, completeGames[,1]),3]) > strtoi(completeGames[grepl(team, completeGames[,1]),4])) +
    sum(strtoi(completeGames[grepl(team, completeGames[,2]),4]) > strtoi(completeGames[grepl(team, completeGames[,2]),3]))
  losses <- sum(strtoi(completeGames[grepl(team, completeGames[,1]),3]) < strtoi(completeGames[grepl(team, completeGames[,1]),4])) +
    sum(strtoi(completeGames[grepl(team, completeGames[,2]),4]) < strtoi(completeGames[grepl(team, completeGames[,2]),3]))
  standings[count,1] <- team
  standings[count,2] <- wins
  standings[count,3] <- losses
#  standings[count,4] <- wins / (wins + losses)
  standings[count,4] <- paste0(formatC(100 * (wins / (wins + losses)), format = "f", digits = 2), "%")
  count <- count + 1L
  print(paste(team, wins, losses, sep = ","))
}
[1] "Japan,5,4"
[1] "Olympic Athlete From Russia,2,7"
[1] "Denmark,1,8"
[1] "Switzerland,4,5"
[1] "Canada,4,5"
[1] "China,4,5"
[1] "Great Britain,6,3"
[1] "United States of America,4,5"
[1] "Korea,8,1"
[1] "Sweden,7,2"
# Clean up
rm(count, wins, losses, team)

No we can compute the Pythagorean Expectation for each team.

count <- 0L
for (team in teams) {
    count <- count + 1L
    scored <- sum(strtoi(completeGames[grepl(team, completeGames[,1]),3])) +
        sum(strtoi(completeGames[grepl(team, completeGames[,2]),4]))
    allowed <- sum(strtoi(completeGames[grepl(team, completeGames[,1]),4])) +
        sum(strtoi(completeGames[grepl(team, completeGames[,2]),3]))
    pythagorean <- (scored^exponent) / ((scored^exponent) + (allowed^exponent))
    pythagorean <- paste0(formatC(100 * pythagorean, format = "f", digits = 2), "%")
    standings[count,5] <- pythagorean
    print(paste(team, pythagorean, sep=" = "))
}
[1] "Japan = 55.94%"
[1] "Olympic Athlete From Russia = 14.41%"
[1] "Denmark = 22.45%"
[1] "Switzerland = 57.34%"
[1] "Canada = 61.84%"
[1] "China = 39.02%"
[1] "Great Britain = 57.22%"
[1] "United States of America = 37.60%"
[1] "Korea = 85.97%"
[1] "Sweden = 72.67%"
standings <- as.data.frame(standings)
names(standings) <- c("Team", "Wins", "Losses", "Standing", "Pythagorean")
print(standings)
rm(count, pythagorean, allowed, scored, team)

Finally, prepare and list the upcoming games with each team’s standings and Pythagorean Expectation.

for (team in upcomingTeams) {
    scheduledGames[grep(team, scheduledGames[,1]),2] <- as.character(standings[grep(team, standings[,1]),4])
    scheduledGames[grep(team, scheduledGames[,1]),3] <- as.character(standings[grep(team, standings[,1]),5])
    scheduledGames[grep(team, scheduledGames[,4]),5] <- as.character(standings[grep(team, standings[,1]),4])
    scheduledGames[grep(team, scheduledGames[,4]),6] <- as.character(standings[grep(team, standings[,1]),5])
}
rm(team)
scheduledGames <- as.data.frame(scheduledGames)
names(scheduledGames) <- c("Team1", "Standings1", "Pythagorean1", "Team2", "Standings2", "Pythagorean2")
print(scheduledGames)

Interpreting this table, Sweden has been playing at a 72.67% level while winning 77.78% of their games. The other team, Great Britain has been playing at a 57.22% level. It is thought that these percentages are a better indication of their records than their actual record because the Pythagorean Expectation takes the score into account. A team that wins by several points is playing better than a team that barely wins by one point.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2sgb24gV29tZW4ncyBDdXJsaW5nIDIwMTggT2x5bXBpY3MiDQphdXRob3I6ICJEci4gQ2xpZnRvbiBCYWxkd2luIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KQW4gYW5hbHlzaXMgb2YgV29tZW4ncyBDdXJsaW5nIGF0IHRoZSAyMDE4IFdpbnRlciBPbHltcGljcyBpbiBTb3V0aCBLb3JlYS4gVGhlIHRlY2huaXF1ZSB0byBwcmVkaWN0IGZ1dHVyZSBtYXRjaGVzIGlzIGJhc2VkIG9uIHRoZSBCYXNlYmFsbCBQeXRoYWdvcmVhbiBUaGVvcmVtLiANClRoZSBleHBvbmVudHMgZm9yIHRoZSBQeXRoYWdvcmVhbiBFeHBldGN0YWlvbiB3ZXJlIHRlc3RlZCBhZ2FpbnN0IGdhbWVzIHBsYXllZCB0byBkYXRlIG9uIEZlYnJ1YXJ5IDIxLCAyMDE4IGZyb20gMS45IHRvIDQuMSAoYnkgMC4xKS4gRW1waXJpY2FsbHkgMy40IGhhZCB0aGUgbG93ZXN0IG1lYW4tc3F1YXJlZC1lcnJvciBvZiB0aGF0IHNldA0KDQpgYGB7cn0NCmxpYnJhcnkoJ3J2ZXN0JykNCnBhcnRpY2lwYW50cyA8LSAiV29tZW4iICMgU2V0IGZvciBvdXRwdXQgcmVhc29ucw0KDQojU3BlY2lmeWluZyB0aGUgdXJsIGZvciBkZXNpcmVkIHdlYnNpdGUgdG8gYmUgc2NyYXBwZWQNCnVybCA8LSAnaHR0cDovL3Jlc3VsdHMud29ybGRjdXJsaW5nLm9yZy9DaGFtcGlvbnNoaXAvRGlzcGxheVJlc3VsdHM/dG91cm5hbWVudElkPTU2MSZhc3NvY2lhdGlvbklkPTAmdGVhbU51bWJlcj0wJmRyYXdOdW1iZXI9MCcNCg0KZXhwb25lbnQgPC0gMy40ICMgVGhlIGV4cG9uZW50IHRvIHVzZSBmb3IgdGhlIFB5dGhhZ29yZWFuIEV4cGVjdGF0aW9uLCBkZXRlcm1pbmVkIGVtcGlyaWNhbGx5IA0KDQpgYGANCg0KVGhlIHdlYnNpdGUgd3d3LndvcmxkY3VybGluZy5vcmcgaXMgYWNjZXNzZWQgZm9yIGdhbWUgZGF0YQ0KDQpgYGB7cn0NCiNSZWFkaW5nIHRoZSBIVE1MIGNvZGUgZnJvbSB0aGUgd2Vic2l0ZQ0Kd2VicGFnZSA8LSByZWFkX2h0bWwodXJsKQ0KDQojVXNpbmcgQ1NTIHNlbGVjdG9ycyB0byBzY3JhcGUgdGhlIHJhbmtpbmdzIHNlY3Rpb24NCnJhbmtfZGF0YV9odG1sIDwtIGh0bWxfbm9kZXMod2VicGFnZSwnLmdhbWUtdGFibGUnKQ0KDQpudW1fZ2FtZXMgPSBsZW5ndGgocmFua19kYXRhX2h0bWwpDQoNCmBgYA0KDQpUaGUgZG93bG9hZGVkIHdlYnBhZ2UgaXMgc2NyYXBlZCBmb3IgdGhlIGFwcHJvcHJpYXRlIGRhdGENCg0KYGBge3J9DQpnYW1lcyA8LSBtYXRyaXgobnJvdyA9IG51bV9nYW1lcywgbmNvbCA9IDQpDQoNCiMgU2NyYXBlIHRoZSBkYXRhIGZyb20gdGhlIHdlYnBhZ2UNCmZvciAoaSBpbiBzZXFfbGVuKG51bV9nYW1lcykpIHsNCiAgdGhpc19yZXN1bHQgPC0gcmFua19kYXRhX2h0bWxbaV0NCiAgdGVhbXMgPC0gaHRtbF90ZXh0KGh0bWxfbm9kZXModGhpc19yZXN1bHQsIi5nYW1lLXRlYW0iKSkNCiAgc2NvcmVzIDwtIGh0bWxfdGV4dChodG1sX25vZGVzKHRoaXNfcmVzdWx0LCIuZ2FtZS10b3RhbCIpKQ0KICBnYW1lc1tpLGMoMSwyKV0gPC0gdHJpbXdzKGdzdWIoJ1xyXG4nLCAnJywgdGVhbXMpKQ0KICBnYW1lc1tpLGMoMyw0KV0gPC0gc3RydG9pKHRyaW13cyhnc3ViKCdcclxuJywgJycsIHNjb3JlcykpKQ0KfQ0KDQpybShpLCBzY29yZXMsIHRlYW1zLCB0aGlzX3Jlc3VsdCkNCg0KYGBgDQoNCkF0IHRoaXMgcG9pbnQsIGRhdGEgZm9yIGFsbCBXb21lbidzIEN1cmxpbmcgZ2FtZXMgYXQgdGhlIE9seW1waWNzIGhhcyBiZWVuIHJldHJpZXZlZCBhbmQgc3RvcmVkIGluIGEgbWF0cml4IG5hbWVkIGdhbWVzLiBXZSB3aWxsIHNwbGl0IHRoaXMgbWF0cml4IGludG8gYSBtYXRyaXggb2Ygb25seSB0aGUgZ2FtZXMgdGhhdCBoYXZlIGFscmVhZHkgY29tcGxldGVkIGFuZCB0aGUgZ2FtZXMgc2NoZWR1bGVkIHRvIGJlIGhlbGQgKGFzIG9mIEZlYnJ1YXJ5IDIyLCAyMDE4KQ0KDQpgYGB7ciBDb21wbGV0ZWRHYW1lc30NCmNvbXBsZXRlR2FtZXMgPC0gZ2FtZXNbIWlzLm5hKGdhbWVzKVssM10sXQ0KDQojIEdldCBhIGxpc3Qgb2YgdGhlIHRlYW1zIHRoYXQgYXJlIHBsYXlpbmcNCnRlYW1zIDwtIHVuaXF1ZShnYW1lc1ssMV0pDQppZiAoIWlzLm5hKHBtYXRjaCgiVG8gQmUiLCB0ZWFtcykpKSB0ZWFtcyA9IHRlYW1zWy1sZW5ndGgodGVhbXMpXQ0KDQpgYGANCg0KYGBge3IgU2NoZWR1bGVkR2FtZXN9DQojIFRha2UgYSBzdWJzZXQgb2YgdGhlIGdhbWVzIHRoYXQgYXJlIHNjaGVkdWxlZCB0byBjb21lDQpzY2hlZHVsZWRHYW1lcyA8LSBtYXRyaXgobnJvdz1ucm93KGdhbWVzW2lzLm5hKGdhbWVzKVssM10sXSksIG5jb2wgPSA2KQ0Kc2NoZWR1bGVkR2FtZXNbLDFdIDwtIGdhbWVzW2lzLm5hKGdhbWVzKVssM10sMV0NCnNjaGVkdWxlZEdhbWVzWyw0XSA8LSBnYW1lc1tpcy5uYShnYW1lcylbLDNdLDJdDQpzY2hlZHVsZWRHYW1lcyA8LSBzY2hlZHVsZWRHYW1lc1shZ3JlcGwoIlRvIEJlIiwgc2NoZWR1bGVkR2FtZXNbLDFdKSxdDQoNCiMgR2V0IGEgbGlzdCBvZiB0aGUgdGVhbXMgdGhhdCBhcmUgc2NoZWR1bGVkIHRvIHBsYXkNCnVwY29taW5nVGVhbXMgPC0gdW5pcXVlKGMoc2NoZWR1bGVkR2FtZXNbLDFdLHNjaGVkdWxlZEdhbWVzWyw0XSkpDQoNCmBgYA0KDQpCYXNlZCBvbiBlYWNoIHRlYW0ncyByZWNvcmQsIGRldGVybWluZSB0aGUgcGVyY2VudGFnZSBvZiBnYW1lcyB0aGV5IGhhdmUgd29uLg0KDQpgYGB7cn0NCnN0YW5kaW5ncyA9IG1hdHJpeChucm93PWxlbmd0aCh0ZWFtcyksIG5jb2wgPSA1KQ0KY291bnQgPC0gMUwNCmZvcih0ZWFtIGluIHRlYW1zKSB7DQogIHdpbnMgPC0gc3VtKHN0cnRvaShjb21wbGV0ZUdhbWVzW2dyZXBsKHRlYW0sIGNvbXBsZXRlR2FtZXNbLDFdKSwzXSkgPiBzdHJ0b2koY29tcGxldGVHYW1lc1tncmVwbCh0ZWFtLCBjb21wbGV0ZUdhbWVzWywxXSksNF0pKSArDQogICAgc3VtKHN0cnRvaShjb21wbGV0ZUdhbWVzW2dyZXBsKHRlYW0sIGNvbXBsZXRlR2FtZXNbLDJdKSw0XSkgPiBzdHJ0b2koY29tcGxldGVHYW1lc1tncmVwbCh0ZWFtLCBjb21wbGV0ZUdhbWVzWywyXSksM10pKQ0KICBsb3NzZXMgPC0gc3VtKHN0cnRvaShjb21wbGV0ZUdhbWVzW2dyZXBsKHRlYW0sIGNvbXBsZXRlR2FtZXNbLDFdKSwzXSkgPCBzdHJ0b2koY29tcGxldGVHYW1lc1tncmVwbCh0ZWFtLCBjb21wbGV0ZUdhbWVzWywxXSksNF0pKSArDQogICAgc3VtKHN0cnRvaShjb21wbGV0ZUdhbWVzW2dyZXBsKHRlYW0sIGNvbXBsZXRlR2FtZXNbLDJdKSw0XSkgPCBzdHJ0b2koY29tcGxldGVHYW1lc1tncmVwbCh0ZWFtLCBjb21wbGV0ZUdhbWVzWywyXSksM10pKQ0KICBzdGFuZGluZ3NbY291bnQsMV0gPC0gdGVhbQ0KICBzdGFuZGluZ3NbY291bnQsMl0gPC0gd2lucw0KICBzdGFuZGluZ3NbY291bnQsM10gPC0gbG9zc2VzDQojICBzdGFuZGluZ3NbY291bnQsNF0gPC0gd2lucyAvICh3aW5zICsgbG9zc2VzKQ0KICBzdGFuZGluZ3NbY291bnQsNF0gPC0gcGFzdGUwKGZvcm1hdEMoMTAwICogKHdpbnMgLyAod2lucyArIGxvc3NlcykpLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDIpLCAiJSIpDQogIGNvdW50IDwtIGNvdW50ICsgMUwNCiAgcHJpbnQocGFzdGUodGVhbSwgd2lucywgbG9zc2VzLCBzZXAgPSAiLCIpKQ0KfQ0KDQojIENsZWFuIHVwDQpybShjb3VudCwgd2lucywgbG9zc2VzLCB0ZWFtKQ0KYGBgDQoNCk5vIHdlIGNhbiBjb21wdXRlIHRoZSBQeXRoYWdvcmVhbiBFeHBlY3RhdGlvbiBmb3IgZWFjaCB0ZWFtLg0KDQpgYGB7cn0NCmNvdW50IDwtIDBMDQpmb3IgKHRlYW0gaW4gdGVhbXMpIHsNCiAgICBjb3VudCA8LSBjb3VudCArIDFMDQogICAgc2NvcmVkIDwtIHN1bShzdHJ0b2koY29tcGxldGVHYW1lc1tncmVwbCh0ZWFtLCBjb21wbGV0ZUdhbWVzWywxXSksM10pKSArDQogICAgICAgIHN1bShzdHJ0b2koY29tcGxldGVHYW1lc1tncmVwbCh0ZWFtLCBjb21wbGV0ZUdhbWVzWywyXSksNF0pKQ0KICAgIGFsbG93ZWQgPC0gc3VtKHN0cnRvaShjb21wbGV0ZUdhbWVzW2dyZXBsKHRlYW0sIGNvbXBsZXRlR2FtZXNbLDFdKSw0XSkpICsNCiAgICAgICAgc3VtKHN0cnRvaShjb21wbGV0ZUdhbWVzW2dyZXBsKHRlYW0sIGNvbXBsZXRlR2FtZXNbLDJdKSwzXSkpDQogICAgcHl0aGFnb3JlYW4gPC0gKHNjb3JlZF5leHBvbmVudCkgLyAoKHNjb3JlZF5leHBvbmVudCkgKyAoYWxsb3dlZF5leHBvbmVudCkpDQogICAgcHl0aGFnb3JlYW4gPC0gcGFzdGUwKGZvcm1hdEMoMTAwICogcHl0aGFnb3JlYW4sIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMiksICIlIikNCiAgICBzdGFuZGluZ3NbY291bnQsNV0gPC0gcHl0aGFnb3JlYW4NCiAgICBwcmludChwYXN0ZSh0ZWFtLCBweXRoYWdvcmVhbiwgc2VwPSIgPSAiKSkNCn0NCg0Kc3RhbmRpbmdzIDwtIGFzLmRhdGEuZnJhbWUoc3RhbmRpbmdzKQ0KbmFtZXMoc3RhbmRpbmdzKSA8LSBjKCJUZWFtIiwgIldpbnMiLCAiTG9zc2VzIiwgIlN0YW5kaW5nIiwgIlB5dGhhZ29yZWFuIikNCg0KcHJpbnQoc3RhbmRpbmdzKQ0Kcm0oY291bnQsIHB5dGhhZ29yZWFuLCBhbGxvd2VkLCBzY29yZWQsIHRlYW0pDQoNCmBgYA0KDQpGaW5hbGx5LCBwcmVwYXJlIGFuZCBsaXN0IHRoZSB1cGNvbWluZyBnYW1lcyB3aXRoIGVhY2ggdGVhbSdzIHN0YW5kaW5ncyBhbmQgUHl0aGFnb3JlYW4gRXhwZWN0YXRpb24uDQoNCmBgYHtyfQ0KZm9yICh0ZWFtIGluIHVwY29taW5nVGVhbXMpIHsNCiAgICBzY2hlZHVsZWRHYW1lc1tncmVwKHRlYW0sIHNjaGVkdWxlZEdhbWVzWywxXSksMl0gPC0gYXMuY2hhcmFjdGVyKHN0YW5kaW5nc1tncmVwKHRlYW0sIHN0YW5kaW5nc1ssMV0pLDRdKQ0KICAgIHNjaGVkdWxlZEdhbWVzW2dyZXAodGVhbSwgc2NoZWR1bGVkR2FtZXNbLDFdKSwzXSA8LSBhcy5jaGFyYWN0ZXIoc3RhbmRpbmdzW2dyZXAodGVhbSwgc3RhbmRpbmdzWywxXSksNV0pDQogICAgc2NoZWR1bGVkR2FtZXNbZ3JlcCh0ZWFtLCBzY2hlZHVsZWRHYW1lc1ssNF0pLDVdIDwtIGFzLmNoYXJhY3RlcihzdGFuZGluZ3NbZ3JlcCh0ZWFtLCBzdGFuZGluZ3NbLDFdKSw0XSkNCiAgICBzY2hlZHVsZWRHYW1lc1tncmVwKHRlYW0sIHNjaGVkdWxlZEdhbWVzWyw0XSksNl0gPC0gYXMuY2hhcmFjdGVyKHN0YW5kaW5nc1tncmVwKHRlYW0sIHN0YW5kaW5nc1ssMV0pLDVdKQ0KfQ0Kcm0odGVhbSkNCg0Kc2NoZWR1bGVkR2FtZXMgPC0gYXMuZGF0YS5mcmFtZShzY2hlZHVsZWRHYW1lcykNCm5hbWVzKHNjaGVkdWxlZEdhbWVzKSA8LSBjKCJUZWFtMSIsICJTdGFuZGluZ3MxIiwgIlB5dGhhZ29yZWFuMSIsICJUZWFtMiIsICJTdGFuZGluZ3MyIiwgIlB5dGhhZ29yZWFuMiIpDQoNCnByaW50KHNjaGVkdWxlZEdhbWVzKQ0KDQpgYGANCg0KDQpJbnRlcnByZXRpbmcgdGhpcyB0YWJsZSwgYHIgc2NoZWR1bGVkR2FtZXNbMSwxXWAgaGFzIGJlZW4gcGxheWluZyBhdCBhIGByIHNjaGVkdWxlZEdhbWVzWzEsM11gIGxldmVsIHdoaWxlIHdpbm5pbmcgYHIgc2NoZWR1bGVkR2FtZXNbMSwyXWAgb2YgdGhlaXIgZ2FtZXMuIFRoZSBvdGhlciB0ZWFtLCBgciBzY2hlZHVsZWRHYW1lc1sxLDRdYCBoYXMgYmVlbiBwbGF5aW5nIGF0IGEgYHIgc2NoZWR1bGVkR2FtZXNbMSw2XWAgbGV2ZWwuIEl0IGlzIHRob3VnaHQgdGhhdCB0aGVzZSBwZXJjZW50YWdlcyBhcmUgYSBiZXR0ZXIgaW5kaWNhdGlvbiBvZiB0aGVpciByZWNvcmRzIHRoYW4gdGhlaXIgYWN0dWFsIHJlY29yZCBiZWNhdXNlIHRoZSBQeXRoYWdvcmVhbiBFeHBlY3RhdGlvbiB0YWtlcyB0aGUgc2NvcmUgaW50byBhY2NvdW50LiBBIHRlYW0gdGhhdCB3aW5zIGJ5IHNldmVyYWwgcG9pbnRzIGlzIHBsYXlpbmcgYmV0dGVyIHRoYW4gYSB0ZWFtIHRoYXQgYmFyZWx5IHdpbnMgYnkgb25lIHBvaW50Lg==

Introduction to my Data Science Blog

9 March ’18

Introduction to my Blog

This data science blog is built on the Jekyll Travelog theme (both elegant and downright simple). The theme source code can be found on Github here.

Introduction

I am a systems engineer and an adjunct professor of data science. As I cover concepts in data science (or learn new ones), I have been writing code, mostly in R but sometimes in Python, to demonstrate to my classes or better learn certain techniques. I am establishing this blog so I can post code and material.

Bio

Dr. Clifton Baldwin is an adjunct professor for the Data Science and Strategic Analytics Masters degree, Stockton University, Galloway, NJ, a program he helped establish. In addition, Dr. Baldwin is the Southern New Jersey Regional Director for the Delaware Valley Chapter of INCOSE INCOSE. He has over 26 years of experience working in software and systems engineering with the first ten years of his career as a “systems analyst” (what we would now call “data scientist”) at the U.S. Bureau of Economic Analysis [BEA] (https://www.bea.gov). His research interests include system of systems and complex systems modeling. He holds a BA degree in Mathematics from Rutgers University, a MS degree in Information Systems from Johns Hopkins University, and a PhD in Systems Engineering from Stevens Institute of Technology.

Constructing my Data Science Blog

8 March ’18

This data science blog is built on the Jekyll Travelog theme (both elegant and downright simple).

The theme source code can be found on Github here.

I am just getting it set up. So for the moment, Under Construction!