Analyzing Poetic Patterns with Python: From Particle Frequency to Algorithmic Rhyme Search
Digital humanities make it possible to apply natural language processing tools to deconstruct artistic texts. Let's explore how basic Python methods can uncover hidden phonetic and rhythmic structures in poetry, evaluating the frequency of function words and the mechanics of assonant rhymes without relying on heavy NLP frameworks.
Preparing the Text Corpus and Working with Regular Expressions
The first stage of any linguistic analysis is normalizing the input data. The source text is split into lines, stripped of extra whitespace, and converted to a uniform case. Here, the focus is on detecting the frequency of the particle “b” and its full counterpart “by”. A direct count using the string method .count() will produce noisy results, as the letter “b” frequently appears in content words. To precisely isolate the particle, word boundaries are essential.
lines_with_b = [line for line in lines if 'b' in line or 'would' in line]
count_b_lines = len(lines_with_b)
count_by = sum(line.lower().count('by') for line in lines)
count_b = sum(
len(re.findall(r'\bb\b', line.lower()))
for line in lines
)
The list comprehension filters lines containing the target tokens. Aggregation is then applied using sum(). The key element is the regular expression \bb\b. The metacharacter \b denotes the boundary between a word and non-word elements (space, punctuation, start or end of line). This ensures matches are captured only for the isolated particle, ignoring occurrences within lexemes like “bokal” or “ryba”. For production solutions, keep in mind that the standard re module doesn't always handle Cyrillic word boundaries correctly in complex cases involving hyphens or apostrophes, so industrial NLP pipelines often turn to third-party libraries or prior tokenization.
Algorithmic Search for Rhyme Bases
Classical rhyming relies on matching stressed vowels and the sounds that follow. Since raw text data lacks stress markings, a heuristic approach is used: scanning for vowel sequences from the end of the word. The function reverses the string, iterates through characters, accumulates vowels until hitting the first consonant, then returns the fragment in its original order.
def get_rhyme(word):
vowels = "aeiouy"
reversed_word = word[::-1].lower()
rhyme_part = ''
for char in reversed_word:
if char in vowels:
rhyme_part += char
elif rhyme_part:
break
return rhyme_part[::-1]
rhymes = []
for i in range(0, len(lines), 2):
if i + 1 < len(lines):
line1 = lines[i].strip().rstrip('.,!?').split()[-1]
line2 = lines[i+1].strip().rstrip('.,!?').split()[-1]
rhymes.append((get_rhyme(line1), get_rhyme(line2)))
The algorithm processes line pairs to simulate checks for adjacent or cross-rhyming. The rstrip() method removes trailing punctuation, while split()[-1] grabs the last word. This approach is intentionally simplified: it ignores stress position, reduction of unstressed vowels, and sonorant consonants, which in Russian poetic tradition often contribute to rhymes. Still, for rapid prototyping and spotting assonant patterns, the heuristic delivers solid accuracy. String operations can be optimized by swapping loop concatenation for slicing, which cuts memory allocation overhead on large corpora.
Validating Results and Linguistic Interpretation
Running the script yields quantitative metrics that tie directly into literary analysis. The stats reveal dominance of the truncated particle: out of 18 lines, the target marker appears in 12. The algorithm logs 8 isolated “b” occurrences and 2 “by” cases. This imbalance underscores the deliberate use of colloquial register, evoking improvisation and shattering bookish conventions.
The rhyme endings array looks like this: [('and', 'e'), ('about', 'about'), ('and', 'and'), ('u', 'and'), ('', 'about'), ...]. Partial vowel matches point to assonant rhyming, typical of modernist poetry. Alternating four- and three-foot iambs with an ABAB cross scheme create a jagged rhythmic flow. The standout pair “point — bud” highlights semantic contrast: static finality versus biological renewal. Phonetically, the explosive consonant [b] serves as an acoustic anchor, evoking the sound of an opening window or first raindrops, elevating the text from mere description to soundscape.
When scaling this analysis to full collected works, consider these technical aspects:
• Integrating stress dictionaries to pinpoint rhyme bases accurately and cut false positives.
• Swapping naive string parsing for syntactic analysis to handle inversions and enjambments properly.
• Using Levenshtein distance or phonetic algorithms to gauge imperfect rhyme consonance.
• Vectorizing text fragments with embeddings for clustering thematic and stylistic shifts in the corpus.
Key Takeaways
• The regex \bb\b strictly isolates the particle, ruling out false positives in content words.
• Heuristic rhyme detection via string reversal and vowel filtering works great for quick prototypes but needs stress handling for refinement.
• The numerical dominance of the truncated “b” particle mathematically backs the shift toward colloquial improvisation.
• Assonant mismatches in the rhymes array signal a deliberate break from precise classical rhyming toward sonic texture.
• For production-level poetry analysis, deploy accentological dictionaries and phonetic similarity metrics over string heuristics.
— Editorial Team
No comments yet.