Stochastic Shakespeare: Sonnets Produced by Markov Chains in R

Update with markovifyR

Thanks to Maëlle Salmon, who referred me to this post by Julia Silge and Nick Larsen, I explored doing this using the markovifyR package, and the results are unbelievable. See the bottom of the post for an updated batch of sonnets!

Original post

I recently saw Katie Jolly’s post, in which she produced Rupi Kuar-style poems using Markov Chains in R. I absolutely loved it, so I decided to try it with Shakespeare’s 154 sonnets using her post as a skeleton.

Downloading and cleaning the sonnets

In addition to markovchain and tidyverse, I’m going to use the gutenberger package to download the sonnets.

library(gutenbergr)
library(tidyverse) 
library(markovchain) 
shakespeare <- gutenberg_works(title == "Shakespeare's Sonnets") %>% 
  pull(gutenberg_id) %>% 
  gutenberg_download(verbose = FALSE)

shakespeare
## # A tibble: 2,625 x 2
##    gutenberg_id text                                          
##           <int> <chr>                                         
##  1         1041 THE SONNETS                                   
##  2         1041 ""                                            
##  3         1041 by William Shakespeare                        
##  4         1041 ""                                            
##  5         1041 ""                                            
##  6         1041 ""                                            
##  7         1041 ""                                            
##  8         1041 "  I"                                         
##  9         1041 ""                                            
## 10         1041 "  From fairest creatures we desire increase,"
## # ... with 2,615 more rows

Because the sonnets are in gutenberger, they’re already in a nice format to work with. I just need to do a little cleaning up: like Katie, I removed the punctuation, but I also have to clear out the sonnet titles, which were Roman numerals, and some title info.

#  a little function to make life easier
`%not_in%` <- function(lhs, rhs) {
  !(lhs %in% rhs)
}

#  remove new lines symbol, sonnet Roman numerals, and punctation
#  and split into vector
bills_words <- shakespeare %>% 
  mutate(text = text %>% 
    str_trim() %>% 
    str_replace_all("--", " ") %>% 
    str_replace_all("[^[:alnum:][:space:]']", "") %>% 
    str_replace_all("^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$", 
                    "") %>% 
    str_to_lower()) %>% 
  filter(text %not_in% c("the sonnets", "by william shakespeare", "", " ")) %>% 
  pull(text) %>% 
  str_split(" ") %>% 
  unlist() 

I’m also going to extract the punctuation and assess how many of each there are for when I actually assemble the sonnets later.

punctuation <- shakespeare %>% 
  pull(text) %>% 
  str_extract_all("[^[:alnum:][:space:]']") %>% 
  unlist()

punctuation_probs <- punctuation[punctuation %not_in% c("-", "(", ")")] %>% 
  table() %>% 
  prop.table()

Fit the Markov Chain

Now fit the Markov Chain with the vector of words.

#  fit a Markov Chain
sonnet_chain <- markovchainFit(bills_words)
cat(markovchainSequence(n = 10, markovchain = sonnet_chain$estimate), collapse =  " ")

with white nor my self thou with gentle verse which

And finally, here are a few functions to piece together lines to make them look like a sonnet using the walk() function from purrr to print out the lines (since it’s a side effect). No, they’re not actually iambic pentameter :(

write_a_line <- function(n_lines = 1) {
  walk(1:n_lines, function(.x) {
  # put together lines of more or less average length
    lines <- markovchainSequence(n = sample(c(6:9), 1), 
                               markovchain = sonnet_chain$estimate) %>% 
      paste(collapse = " ")
  
  #  add end-of-line punctuation based on their occurence 
  end_punctuation <- ifelse(.x == n_lines, ".", 
                            sample(names(punctuation_probs), 
                                   size = 1, 
                                   prob = punctuation_probs))
  cat(paste0(lines, end_punctuation, "  \n"))
  })
}

psuedosonnet <- function() {
  walk(1:3, function(.x) {
    write_a_line(4)
    cat("  \n")
  })
  
  write_a_line(2)
}

Generating the sonnets

Let’s try it out.

Psuedosonnet 1:

set.seed(154)
psuedosonnet()

which physic did i toil all too grossly,
of happy you nor falls under the master;
o absence seem’d it fears no form form,
you would i break of the darling buds of.

of brass are my head where our fashion an,
stars of thy book of small glory to swear,
what wealth some child of the painter must die.
the fierce thing replete with that bears it.

seems your feature incapable of such seems seeing for,
in his spoil of trust and,
blood make me soon to thy days are bright,
in your worth held and i swear to wait.

beauty tempting her poor retention could with a,
and the treasure of may be.

Psuedosonnet 2:

psuedosonnet()

my spirit a separable spite take;
it then say so flatter the sessions of,
pen hearing you drink potions of one?
and more hath all art for.

that best acquainted with kings when thou,
where breath but their proud compare thou,
of mouthed graves will grind on thy,
fleece made and thy cheek and this.

and when that is so for’t lies where all,
so is this say that we?
against myself i read self the earth remov’d lord?
mother for now than spurring to please him.

and all determinate for thee in grecian tires are,
and eyes can see till the general evil still.

Psuedosonnet 3:

psuedosonnet()

debt and from accident it may!
shine bright in hope what he doth deceive,
their brave state with tears thou in whose,
muse doth well knows is crown’d crooked knife.

nor white when my body is more then her:
look in my oblation poor infant’s discontent,
sail to his memory but all the other,
the least yet like none lov’st thou.

creature the prey of words by adding one and:
esteem’d when it ten for i have,
bosoms fits but then how shall beauty and leap’d,
my pen hearing you will give them for what.

end doth ride with fulness tomorrow;
methinks i slept in thy trespass with.

Psuedosonnet 4:

psuedosonnet()

a dearer birth to divide the lovely,
lips and beauty lack of worms to die but,
and do i be belov’d of praise.
sail doth use is famish’d for.

by praising him but if this shalt find out,
sweet that i read self resemble creating,
grace you look upon the most which,
make sweet love put beside his beauty of your.

pen reserve their glory but these quicker elements,
be as thou that then thou art my love!
by the store to flow for his,
of thee but mend to the marigold at the.

time you doth live look for.
want nothing sweet self away and therefore.

Psuedosonnet 5:

psuedosonnet()

wh’r better angel in some in,
oaths of good turns to death my comfort,
directed then gentle gait making thus with.
of conscience hold in his spring.

like a zealous pilgrimage to my heart,
politic that through the joy behind,
proud livery so fast as high to set and,
could write of shame commits for her pleasure lost.

sweetest odours made for love when thou canst not.
featureless and there is impannelled a,
exceeded by our time thou shalt hap to wonder,
grief though rosy lips and heart wound.

thou repent yet do thy parts do our!
death do show it is daily new to.

Update

Alright, this time I’m going to try it with the markovifyR package. I’m basically going to do the same cleaning as above, but this time I’ll be putting entire sentences, punctuation and all, into the Markov model. The markovify_text() function also accepts start words, so I thought it might look good to start with a sample of 100 starting words from the sonnets and construct the lines from there.

library(markovifyR)
#  same as above, but maintain as sentences and keep punctuation
bills_sentences <- shakespeare %>% 
  mutate(text = text %>% 
    str_trim() %>% 
    str_replace_all("--", " ") %>% 
    str_replace_all("^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$", 
                    "") %>% 
    str_to_lower()) %>% 
  filter(text %not_in% c("the sonnets", "by william shakespeare", "", " "))

#  fit the Markov Chain
markovify_model <-
  generate_markovify_model(
    input_text = bills_sentences$text,
    markov_state_size = 2L,
    max_overlap_total = 25,
    max_overlap_ratio = .85
  )

#  generate a sonnet
markovify_sonnet <- function() {
  lines <- markovify_text(
      markov_model = markovify_model,
      maximum_sentence_length = 75,
      output_column_name = 'sonnet_line',
      count = 50,
      tries = 1000, 
      start_words = sample(generate_start_words(markovify_model)$wordStart, 100),
      only_distinct = TRUE,
      return_message = FALSE) %>% 
    filter(str_count(sonnet_line, "\\w+") > 5 & str_count(sonnet_line, "\\w+") < 10) %>% 
    slice(sample(1:n(), 14)) %>% 
    mutate(id = 1:n()) %>% 
    select(id, sonnet_line) 
  
   #  add a period to the last line if the last charachter isn't punctuation 
   #  that ends a sentence  
   last_line <- lines[lines$id == 14, "sonnet_line"]
   lines[lines$id == 14, "sonnet_line"] <- str_replace(last_line, 
                                                       ".$(?<!//.//!//?|[:alnum:])", ".")
   
   #  print in a sonnet-like format
   walk(1:14, function(.x) {
     cat(lines$sonnet_line[.x], " \n")
     
     #  add a space every four lines
     if (.x %% 4 == 0) cat("\n") 
   })
}

Markovify Sonnet 1:

markovify_sonnet()

unmoved, cold, and to be belov’d of many,
hung with the drops of this most balmy time,
my heart and eyes have done:
hung with the drops of this madding fever!

my reason, the physician to my purpose bred,
exceeded by the motion of thine
like as, to make some special instant special-blest,
till my bad angel fire my good allow?

our dates are brief, and therefore from my side,
they that have profan’d their scarlet ornaments
two loves i have confess’d that he may
not mine own fears, nor the gilded monuments

no, i am perjur’d most;
feeding on that which on thy face.

Markovify Sonnet 2:

markovify_sonnet()

had, having, and in my sight,
it is so proud thy service to despise,
our dates are brief, and therefore to be invited
or as the death-bed, whereon it must expire,

were to be with you alone,
stirr’d by a part of all his trim,
how would, i say, mine eyes best see,
or, if they sleep, thy picture in my jail:

resembling strong youth in every blessed shape we know.
bearing thy heart, which i will not be shown;
hast thou, the master mistress of my speaking breast,
what potions have i slept in your will,

revenge upon myself with thee shall stay.
thus policy in love, but truly write.

Markovify Sonnet 3:

markovify_sonnet()

save what shall be my comfort of thy jealousy?
within the gentle closure of my heart another youth,
say that i cannot blame thee, for my stain.
at random from the thing they most do show,

be, as thy love is not so?
wishing me like to thee i speed:
no, i am still with thee alone:
when day’s oppression is not forbidden usury,

there lives more life in one of your frame;
no bitterness that i come so near,
suns of the fairest and most most loving breast.
presume not on thy humour doth depend:

savage, extreme, rude, cruel, not to show it,
perforce am thine, and all my best of love.

Markovify Sonnet 4:

markovify_sonnet()

with mine compare thou thine own sweet brood;
o! from what power hast thou this powerful rhyme;
unmoved, cold, and to be a devil,
make answer muse: wilt thou be denied!

revenge upon myself with thee partake?
bring me within the level of your love.
divert strong minds to the learned’s wing
crowning the present, doubting of the time,

looking on thee in the spring,
o! let me be borne alone.
crowning the present, doubting of the spring,
before the golden tresses of the spring,

divert strong minds to the ground;
angry that his prescriptions are not so much hold.

Markovify Sonnet 5:

markovify_sonnet()

of bird, of flower, or shape which it contains,
you are so strongly in my way,
mine own true love that touches me more nearly.
crowning the present, doubting of the roses.

till whatsoever star that ushers in the rose;
before the golden tresses of the fleeting year!
cannot dispraise, but in my purpose nothing.
lord of my flesh were thought,

whereon the stars do i not say so,
in tender embassy of love there bred,
too base of thee to be won,
lord of my silence cannot boast;

most true it is, that i call
save what is your substance, whereof are you made.

Well, call me Shockedspeare.

Exit, pursued by a bear

comments powered by Disqus