This is the first part of an ongoing series. The second part can be found here. Poets pay attention to the natural stresses in words, and sometimes they arrange words so that the stresses form patterns. Typical patterns stress every other syllable (duple meter) or every third syllable (triple meter). Conventions exist to further classify poetic lines according to a unit of two or three syllables, called a foot. I choose not to follow this convention, instead looking at the line of poetry as a continuous pattern. The goal of step 1 is to display the pattern of a line of poetry graphically around the printed syllables .
The function below accepts a line of English poetry (or prose) and returns the stress pattern with syllables. It gets the stress information from the "PhoneticForm" property in WordData and the syllabification information from the "Hyphenation" property. Sometimes words are not in WordData, or the database doesn't have phonetic or hyphenation values for the word. Much of the code deals with how to guess at those values when they are missing. Also, 1-syllable words are stressed in the database, but stopwords are usually unstressed in context. So the code demotes single-syllable stopwords from stressed to undetermined. A series of replacement rules attempts to resolve syllables that the program has not yet determined to be stressed or unstressed.
analyzeMeter[verse_] := {
ipaVowels = {"a?", "a?", "e?", "??", "o?", "?", "?", "?", "?", "?",
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
"?", "?", "a", "æ", "e", "i", "o", "", "ø", "u", "y"};
words = ToLowerCase[TextWords[verse]];
getWordInfo[wd_] := {
ipa = WordData[wd, "PhoneticForm"];
str = If[StringQ[ipa],
vow = StringCases[ipa, "?" | "?" ... ~~ ipaVowels];
ToExpression[
StringReplace[
vow, {"?" ~~ __ -> "1", "?" ~~ __ -> ".5", __ -> "0"}]],
dips = {"ae", "ai", "au", "ay", "ea", "ee", "ei", "eu", "ey",
"ie", "oa", "oe", "oi", "oo", "ou", "oy", "ue", "ui", "uy"};
vows = {"a", "e", "i", "o", "u", "y"};
Table[.5,
Total[ToExpression[
Characters[
StringReplace[
wd, {StartOfString ~~ "y" -> "0",
"e" ~~ EndOfString -> "0", dips -> "1",
vows -> "1", _ -> "0"}]]]]]];
hyp = WordData[wd, "Hyphenation"];
fauxSyl =
StringPartition[wd, UpTo[Ceiling[StringLength[wd]/Length[str]]]];
syl =
If[ListQ[hyp] && Length[hyp] == Length[fauxSyl], hyp, fauxSyl];
{wd, str, syl}};
wordInfo = getWordInfo[#][[1]] & /@ words;
stops1IPA =
Select[DeleteMissing[
WordData[#, "PhoneticForm"] & /@ WordData[All, "Stopwords"]],
StringCount[#, ipaVowels] < 2 &];
wordInfo =
wordInfo /. {a_, b_List, c_} /;
MemberQ[stops1IPA, WordData[a, "PhoneticForm"]] -> {a, {.5}, c};
wordInfo =
wordInfo /. {a_, b_List,
c_} /; ! MemberQ[stops1IPA, WordData[a, "PhoneticForm"]] &&
b == {.5} -> {a, {1}, c};
preMeter = wordInfo[[;; , 2]] // Flatten;
meter =
preMeter //. {
{a___, .5, 1, 1, b___} -> {a, 0, 1, 1, b},
{a___, 1, 1, .5, b___} -> {a, 1, 1, 0, b},
{a___, 1, .5, 1, b___} -> {a, 1, 0, 1, b},
{a___, 0, .5, 0, b___} -> {a, 0, 1, 0, b},
{a___, .5, 1, b___} -> {a, 0, 1, b},
{a___, 1, .5, b___} -> {a, 1, 0, b},
{a___, 0, .5} -> {a, 0, 1},
{.5, 0, b___} -> {1, 0, b},
{a___, .5, 0, 1, 0, 1, b___} -> {a, 1, 0, 1, 0, 1, b},
{a___, .5, 1, 0, 1, 0, b___} -> {a, 0, 1, 0, 1, 0, b},
{a___, .5, 0, 0, 1, 0, 0, 1, b___} -> {a, 1, 0, 0, 1, 0, 0, 1,
b},
{a___, 1, 0, 1, 0, .5, b___} -> {a, 1, 0, 1, 0, 1, b},
{a___, 0, 1, 0, 1, .5, b___} -> {a, 0, 1, 0, 1, 0, b},
{a___, 1, 0, 0, 1, 0, 0, .5, b___} -> {a, 1, 0, 0, 1, 0, 0, 1,
b},
{a___, .5, .5, .5} -> {a, 0, 1, 0},
{.5, .5, b___} -> {1, 0, b}};
coords = Partition[Riffle[Range[Length[meter]], meter], 2];
syllab = Flatten[wordInfo[[;; , 3]]];
visual =
Graphics[{Line[coords],
MapIndexed[
Style[Text[#1, {#2[[1]], .5}], 15, FontFamily -> "Times"] &,
syllab]}, ImageMargins -> {{10, 10}, {0, 0}},
ImageSize -> 48*Length[meter]]
};
analyzeMeter["Once upon a midnight dreary, while I pondered, weak and \
weary,"]
Thanks to Edgar Allan Poe for his poem "The Raven." The zigzag line zigs up for stressed syllables and down for unstressed. The program analyzes this verse without error or deviation from the expected meter. However, poets don't always follow the expected pattern, and the program occasional makes mistakes. Consider the program's output for the entire second stanza of "The Raven."
The graphic makes it easy to see deviations from the pattern. In the second line of this stanza, the program mistakenly considers "separate" to have three syllables as if it were a verb. However, when "separate" is used as an adjective, as in "separate dying ember," it only has two syllables. In the third verse, the last syllable of "eagerly" is so weak that the program marks it as unstressed. This is a reasonable and arguably correct way to assess the syllable, though traditionally it should be marked as stressed. The fifth verse also has an anomaly. Poe has added an extra syllable to the line with the word "radiant."
As an English teacher, I think this visual gives insight into such subtle poetic notions as elision, secondary stress, and masculine/feminine rhyme. A possible activity is for students to use the program to analyze the prevailing pattern in a stanza of poetry and then explain the variations from that pattern as nuances of the language (as in "eagerly"), deliberate deviations by the poet (as in "radiant"), or mistakes by the program (as in "separate").
"The Raven" follows a duple meter pattern of alternating stressed and unstressed syllables. The program can also handle poems that follow the other major metrical pattern, triple meter. Here are verses from "Evangeline" by Henry Wadsworth Longfellow and "'Twas the Night Before Christmas" by Clement Clarke Moore.
One would expect that the program would show free verse and prose as having no recognizable metrical pattern. Let's see. Here are two lines of Walt Whitman's free verse poem "When I Heard the Learn'd Astronomer":
And here is a sentence from the Wikipedia article on butterflies.
The traditional way to teach meter in poetry is to explain about iambs, trochees, etc. and then have students try to mark lines of poetry with those units. Students, who may be distinguishing stressed syllables for the first time are hard pressed to find metrical feet in a verse. With this program, a student has a starting point to explore, analyze, interpret, and critique. It's like using Wolfram Alpha to understand the graph of a rational function rather than trying to sketch it yourself following the rules the teacher lectured about.
I would call the program a work in progress rather than a success. If you experiment with poems of your choice, you'll find that it sometimes fails to resolve a syllable, leaving it stuck halfway between stressed and unstressed. Also, if it misinterprets a syllable, marking it stressed, for instance, when it shouldn't be, the error can spread to neighboring syllables and corrupt the interpretation of the whole line. It works more consistently with duple meter than triple meter.
Twice I tried to improve the program with machine learning. I thought that if machine learning could classify the unresolved pattern as either duple meter, triple meter, or neither, then the program could better resolve the undetermined syllables. I was encouraged when it had 99% confidence that lines from "The Raven" were duple meter, but then I realized it was just as certain that any input was duple meter. My second attempt was to make a neural net that accepted a word and returned a likely stress pattern. For instance, I would feed it "Lenore" and it would return {0,1}. I think this should be doable, training it on data from WordData, but I am not strong enough in machine learning to make it happen (yet).
I subtitled this "Part 1," which implies that there is more to come. I intend to follow this with a program that makes rhyme visible, including alliteration, assonance, and other sound features loosely associated with rhyme.
Thanks for sticking with this to the end,
Mark Greenberg