<p><div><div><div><div data-width="100" data-show-faces="false" data-send="true" data-layout="button_count" data-height="21" data-href="https://www.r-bloggers.com/seinfeld-characters-a-post-about-nothing/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://stoltzmaniac.com/seinfeld-characters-a-post-about-nothing/"> R-Projects - Stoltzmaniac</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>This post is dedicated to my mother – Seinfeld’s greatest fan.</p>
<p>Seinfeld is a classic TV sitcom. It featured four main characters surrounded by relatively normal, everyday, run of the mill scenarios. In the spirit of Seinfeld, this post will also “be about nothing.”</p>
<p>Load Required Libraries </p>
<pre><code>library(scales)  
library(RMySQL)  
library(stringr)  
library(tidyr)  
library(dplyr)  
library(igraph)  
library(ggplot2)  
</code></pre>
<p>I used python to create a web scraper which gathered scripts from various sites on the internet and input them into a local MySQL database.</p>
<h6>Data From Local MySQL</h6>
<pre><code>db = dbConnect(MySQL(),  
                 user='root', 
                 password='root', 
                 dbname='tvscripts', 
                 host='localhost')
rs = dbSendQuery(db, &quot;select episodeTitle,rawLine from seinfeld_raw&quot;)  
data = fetch(rs, n=-1)

print(data$rawLine[600:616])  
</code></pre>
<p>Scraping the raw lines without parsing looked simple enough. Parsing before inserting into MySQL created some difficulties so it made sense to parse once loaded into R. <br>
A sample of the raw data: </p>
<pre><code> [1] &quot;Jerry: I don't know. He's an importer.&quot;                            
 [2] &quot;Vanessa: Importer?&quot;                                                
 [3] &quot;George: ...And exporter.&quot;                                          
 [4] &quot;Jerry: He's an importer/exporter.&quot;                                 
 [5] &quot;George: I'm, uh, I'm an architect.&quot;                                
 [6] &quot;Vanessa: Really. What do you design?&quot;                              
 [7] &quot;George: Uh, railroads, uh...&quot;                                      
 [8] &quot;Vanessa: I thought engineers do that.&quot;                             
 [9] &quot;George: They can...&quot;                                               
[10] &quot;Jerry: Y'know I'm sorry you had to leave so early the other night.&quot;
[11] &quot;Vanessa: Oh, me too. My cousin had to go back to Boston.&quot;          
[12] &quot;Jerry: Oh, that guy was your *cousin*!&quot;                            
[13] &quot;Vanessa: Yeah, and that woman was your--&quot;                          
[14] &quot;Jerry: Friend!&quot;                                                    
[15] &quot;George: I'll just, uh, get a paper...&quot;                             
[16] &quot;Jerry: So, um, do you date immature men?&quot;                          
[17] &quot;Vanessa: Almost exclusively...&quot;                                       
</code></pre>
<p>I modified the raw data in order to strip out the character name and line spoken. I also removed lines to clean up the data a bit. </p>
<pre><code>data$characterName = str_extract(sub(&quot; &quot;,&quot;&quot;,  
                                     sub(&quot;:.*&quot;,&quot;&quot;,data$rawLine)),
                                 &quot;[A-Z][A-Z]+&quot;)
data = data[is.na(data$characterName)==FALSE,]  
data = data %>%  
  filter(characterName != 'TV') %>% 
  filter(characterName != 'DVD') %>%
  filter(characterName != 'MAN')
</code></pre>
<p>I used a shift function to create a new column which is the same as the character column but moved up by one row. This should help to show the conversation between two people. Inherently, this will be flawed because the beginning and ends of scenes will run together. I made the assumption that it wouldn’t impact the results since the instances would likely be evenly distributed across characters. </p>
<pre><code>shift &lt;- function(x, n){  
  c(x[-(seq(n))], rep(NA, n))
}

#One character to the next in new column
data$characterNext = shift(data$characterName, 1)  
data$characterInteraction = paste(data$characterName  
                                  ,data$characterNext,sep=&quot;-->&quot;)
</code></pre>
<p>I created a list of characters with the most lines recorded. </p>
<p><strong>Observations</strong> </p>
<ul>
<li>Jerry is obviously the main character – the show is named after him after all</li>
<li>The main characters: Jerry, George, Elaine, and Kramer make up roughly 90% of the top 20 character’s lines</li>
<li>George and Jerry’s parents are actually a major part of the show (they showed up in the top 10)</li>
</ul>
<pre><code>df = data %>%  
  group_by(characterName) %>%
  summarise(n=n()) %>%
  arrange(desc(n)) %>%
  top_n(20) %>%
  mutate(freq = n / sum(n))

p = ggplot(df,aes(reorder(characterName,n),y=freq))  
p + geom_bar(stat = 'identity') +  
  coord_flip() + 
  scale_y_continuous(labels=percent_format()) +
  geom_text(aes(label=paste(round(100*freq,0),&quot;%&quot;,sep=''),hjust=0)) +
  labs(x='',y='Volume') + 
  ggtitle('Total Lines')
</code></pre>
<p><img alt="barplot" src="https://i1.wp.com/github.com/stoltzmaniac/TV-Scripts-Seinfeld/blob/master/Seinfeld_Script_Visualization_files/figure-html/unnamed-chunk-5-1.png?w=456&ssl=1" data-recalc-dims="1"><br>
I created a list of two characters speaking to each other. This is directional data (so Jerry speaking to George is separate from George speaking to Jerry). </p>
<p><strong>Observations</strong> </p>
<ul>
<li>Jerry is involved in the top 6 two person interactions</li>
<li>It seems as if the others don’t speak to each other nearly as often as they do to Jerry</li>
<li>Due to the nature of how this data was created, it would only make sense that character links are so closely related</li>
</ul>
<pre><code>df = data %>%  
  group_by(characterInteraction) %>%
  summarise(n=n()) %>%
  arrange(desc(n)) %>%
  top_n(20) %>%
  mutate(freq = n / sum(n))

p = ggplot(df,aes(reorder(characterInteraction,n),y=freq))  
p + geom_bar(stat = 'identity') +  
  coord_flip()+ scale_y_continuous(labels=percent_format()) +
  geom_text(aes(label=paste(round(100*freq,0),&quot;%&quot;,sep=''),hjust=0)) +
  labs(x='',y='Volume') + 
  ggtitle('Lines Between Two Characters (Directional)')
</code></pre>
<p><img alt="barplot" src="https://i0.wp.com/github.com/stoltzmaniac/TV-Scripts-Seinfeld/blob/master/Seinfeld_Script_Visualization_files/figure-html/unnamed-chunk-6-1.png?w=456&ssl=1" data-recalc-dims="1"></p>
<p>I used a shift function once again to see how the conversation flows two lines after. This will give a hint as to whether the conversation is between two characters or more. Again, this is directional data. </p>
<pre><code>data$characterNext2 = shift(data$characterName, 2)  
data$characterInteraction2 = paste(data$characterName,  
                                   data$characterNext2,sep=&quot;-->&quot;)

df = data %>%  
  group_by(characterInteraction2) %>%
  summarise(n=n()) %>%
  arrange(desc(n)) %>%
  top_n(20) %>%
  mutate(freq = n / sum(n))

p = ggplot(df,aes(reorder(characterInteraction2,n),y=freq))  
p + geom_bar(stat = 'identity') +  
  coord_flip()+ scale_y_continuous(labels=percent_format()) + 
  geom_text(aes(label=paste(round(100*freq,0),&quot;%&quot;,sep=''),hjust=0)) +
  labs(x='',y='Volume') + 
  ggtitle('Lines Skipping One Character')
</code></pre>
<p><img alt="barplot" src="https://i2.wp.com/github.com/stoltzmaniac/TV-Scripts-Seinfeld/blob/master/Seinfeld_Script_Visualization_files/figure-html/unnamed-chunk-7-1.png?w=456&ssl=1" data-recalc-dims="1"><br>
I combined three lines in a row created a nice view of groups which speak in order. </p>
<p><strong>Observations</strong> </p>
<ul>
<li>Kramer did not show up in the top 20 lines between 3 unique characters</li>
<li>Kramer likely interacts more often in one-on-one than the other main characters</li>
</ul>
<pre><code>#Three interactions in a row
data$threeInteraction = paste(data$characterName,  
                              data$characterNext,
                              data$characterNext2,sep=&quot;-->&quot;)
df = data %>%  
  group_by(threeInteraction) %>%
  summarise(n=n()) %>%
  arrange(desc(n)) %>%
  top_n(20) %>%
  mutate(freq = n / sum(n))

p = ggplot(df,aes(reorder(threeInteraction,n),y=freq))  
p + geom_bar(stat = 'identity') +  
  coord_flip() + 
  scale_y_continuous(labels=percent_format()) +
  geom_text(aes(label=paste(round(100*freq,0),&quot;%&quot;,sep=''),hjust=0)) +
  labs(x='',y='Volume') + 
  ggtitle('Lines Between 3 Chars (Directional)')
</code></pre>
<p><img alt="barplot" src="https://i1.wp.com/github.com/stoltzmaniac/TV-Scripts-Seinfeld/blob/master/Seinfeld_Script_Visualization_files/figure-html/unnamed-chunk-8-1.png?w=456&ssl=1" data-recalc-dims="1"><br>
The igraph library allows for the visualization of how two vectors are related to each other. Nodes represent the characters, the edges resemble lines (relationships) between the two.</p>
<p><strong>Observations</strong> </p>
<ul>
<li>The main characters, Jerry, George, Elaine, and Kramer, are the center of attention and have the most relationships</li>
<li>Parents have strong relationships with their kids and each other</li>
<li>Newman seems to play a large role but primarily interacts only with the main characters</li>
<li>Peterman interacts almost solely with Elaine – which makes sense because he’s her boss</li>
<li>Susan primarily interacts with George – which makes sense because she engaged to him. She also interacts with Jerry but to a lesser extent</li>
</ul>
<pre><code>df = data %>%  
  group_by(characterName) %>%
  summarise(n=n()) %>%
  arrange(desc(n)) %>%
  top_n(11)
topCharacters = df$characterName  
df.data = data %>%  
  filter(characterName %in% topCharacters) %>% 
  filter(characterNext %in% topCharacters)

df = data.frame(A = df.data$characterName, B = df.data$characterNext)  
df = na.omit(df)  
g4 = graph.data.frame(d=df,directed=FALSE)  
g4 = simplify(g4,  
              remove.multiple=F,
              remove.loops=F,
              edge.attr.comb=c(weight='sum',type='ignore'))
plot(g4,  
     vertex.label.color='black',
     vertex.frame.color=NA,
     vertex.label.font=2,
     vertex.label.dist = 0.5)
</code></pre>
<h4>Relationship of Top Seinfeld Characters</h4>
<p><img alt="barplot" src="https://i1.wp.com/github.com/stoltzmaniac/TV-Scripts-Seinfeld/blob/master/Seinfeld_Script_Visualization_files/figure-html/unnamed-chunk-9-1-small.PNG?w=456&ssl=1" data-recalc-dims="1"></p>
<p><strong>Conclusion</strong> <br>
Ultimately, the “show about nothing” didn’t contain many surprises. Further analysis of the seasons could perhaps show some additional insights. Sentiment analysis would be useful in determining the “tone” of episodes and characters. Decision trees based off of lines or bi-grams, could perhaps predict which character is speaking. Maybe there will be more to come…</p>
<p>Code used in this post is on my <a target="_blank" rel="nofollow" href="https://github.com/stoltzmaniac/TV-Scripts-Seinfeld.git">GitHub</a></p>
<p>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://stoltzmaniac.com/seinfeld-characters-a-post-about-nothing/"> R-Projects - Stoltzmaniac</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" rel="nofollow" href="https://www.r-project.org/">R</a> news and <a title="R tutorials" rel="nofollow" href="https://www.r-bloggers.com/search/tutorial">tutorials</a> on topics such as: <a title="Data science" rel="nofollow" href="https://www.r-bloggers.com/search/data%20science">Data science</a>, <a title="Big Data" rel="nofollow" href="https://www.r-bloggers.com/search/Big%20Data">Big Data, <a title="R jobs" rel="nofollow" href="https://www.r-users.com/">R jobs</a>, visualization (<a title="ggplot and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/search/ggplot2">ggplot2</a>, <a title="Boxplots using lattice and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/search/boxplot">Boxplots</a>, <a title="Maps and gis" rel="nofollow" href="https://www.r-bloggers.com/search/map">maps</a>, <a title="Animation in R" rel="nofollow" href="https://www.r-bloggers.com/search/animation">animation</a>), programming (<a title="RStudio IDE for R" rel="nofollow" href="https://www.r-bloggers.com/search/RStudio">RStudio</a>, <a title="Sweave and literate programming" rel="nofollow" href="https://www.r-bloggers.com/search/sweave">Sweave</a>, <a title="LaTeX in R" rel="nofollow" href="https://www.r-bloggers.com/search/LaTeX">LaTeX</a>, <a title="SQL and databases" rel="nofollow" href="https://www.r-bloggers.com/search/SQL">SQL</a>, <a title="Eclipse IDE for R" rel="nofollow" href="https://www.r-bloggers.com/search/eclipse">Eclipse</a>, <a title="git and github, Version Control System" rel="nofollow" href="https://www.r-bloggers.com/search/git">git</a>, <a title="Large data in R using Hadoop" rel="nofollow" href="https://www.r-bloggers.com/search/hadoop">hadoop</a>, <a title="Web Scraping of google, facebook, yahoo, twitter and more using R" rel="nofollow" href="https://www.r-bloggers.com/search/Web+Scraping">Web Scraping</a>) statistics (<a title="Regressions and ANOVA analysis tutorials" rel="nofollow" href="https://www.r-bloggers.com/search/regression">regression</a>, <a title="principal component analysis tutorial" rel="nofollow" href="https://www.r-bloggers.com/search/PCA">PCA</a>, <a title="Time series" rel="nofollow" href="https://www.r-bloggers.com/search/time+series">time series</a>, <a title="finance trading" rel="nofollow" href="https://www.r-bloggers.com/search/trading">trading</a>) and more...
<img height="1" alt="" width="1" src="http://feeds.feedburner.com/~r/RBloggers/~4/0TRJ03tv2Lo"></a></div>