La poesía a menudo se ve como una forma de arte puro, desde la estructura rígida de un haiku hasta la naturaleza fluida y sin restricciones de la poesía de verse por libre. Sin embargo, al analizar estos trabajos, hasta qué punto pueden las matemáticas y Análisis de datos ¿Se utiliza para obtener el significado de esta literatura de flujo libre? Por supuesto, se puede analizar la retórica, se pueden encontrar referencias y se puede cuestionar la elección de palabras, pero ¿se puede encontrar el proceso de pensamiento subyacente, incluso subconsciente, de un autor utilizando tácticas analíticas sobre literatura? Como una exploración inicial en el análisis de literatura asistida por cómputo, intentaremos usar un programa de transformación de Fourier para buscar periodicidad en un poema. Para probar nuestro código, usaremos dos estudios de casos: «No te vuelvas gentil en esa buena noche«Por Dylan Thomas, seguido de Lewis Carroll»Jabberwocky. »
1. Adquisición de datos
a. División de línea y recuento de palabras
Antes de hacer cualquier cálculo, se deben recopilar todos los datos necesarios. Para nuestros propósitos, queremos un conjunto de datos del número de letras, palabras, sílabas y longitud visual de cada línea. Primero, necesitamos analizar el poema en sí (que se ingresa como un archivo de texto plano) en sustrings de cada línea. Esto se hace bastante fácilmente en Python con el .split()
método; Pasando el delimitador «n
«En el método, dividirá el archivo por línea, devolviendo una lista de cadenas para cada línea. (El método completo es poem.split(“n”))
. Contar el número de palabras es tan simple como dividir las líneas, y sigue muy bien de ella: primero, iterando en todas las líneas, aplique el .split()
Método nuevamente, esta vez sin delimitador, de modo que se divide de forma predeterminada en espacios en blanco, convirtiendo cada cadena de línea en una lista de cadenas de palabras. Luego, para contar el número de palabras en cualquier línea dada, simplemente llame al incorporado len()
función en cada línea; Dado que cada línea se ha dividido en una lista de palabras, len()
devolverá el número de elementos en la lista de línea, que es el recuento de palabras.
b. Recuento de cartas
Para calcular el número de letras en cada línea, todo lo que necesitamos hacer es tomar la suma del recuento de letras de cada palabra, por lo que para una línea dada itera sobre cada palabra, llamando len()
Para obtener el recuento de personajes de una palabra dada. Después de iterar sobre todas las palabras en una línea, los caracteres se suman para el número total de caracteres en la línea; El código para realizar esto es sum(len(word) for word in words)
.
do. Longitud visual
Calcular la longitud visual de cada línea es simple; asumiendo un fuente monoespacialla longitud visual de cada línea es simplemente el número total de caracteres (¡incluidos los espacios!) Presente en la línea. Por lo tanto, la longitud visual es simplemente len(line)
. Sin embargo, la mayoría de las fuentes no son monoespaciales, especialmente fuentes literarias comunes como Caslón, Garamondy Georgia – Esto presenta un problema porque sin saber la fuente exacta con la que un autor estaba escribiendo, no podemos calcular la longitud de línea precisa. Si bien esta suposición deja espacio para el error, considerar la longitud visual de alguna manera es importante, por lo que la suposición monoespacial tendrá que ser utilizada.
d. Recuento de sílabas
Obtener el recuento de sílabas sin leer manualmente cada línea es la parte más desafiante de la recopilación de datos. Para identificar una sílaba, usaremos grupos de vocales. Tenga en cuenta que en mi programa definí una función, count_syllables(word)
para contar las sílabas en cada palabra. Para preformar la palabra, la establecemos en todos los minúsculas usando word = word.lower()
y eliminar cualquier puntuación que pueda estar contenida en la palabra usando word = re.sub(r'[^a-z]', '', word)
. A continuación, encuentre todas las vocales o grupos de vocales: cada uno debe ser una sílaba, ya que una sola sílaba se define expresamente como una unidad de pronunciación que contiene un sonido de vocales continuas rodeada de consonantes. Para encontrar cada clúster vocal, podemos usar la regex de todas las vocales, incluidas las y: syllables = re.findall(r'[aeiouy]+', word)
. Después de definir las sílabas, será una lista de todos los grupos vocales en una palabra determinada. Finalmente, debe haber al menos una sílaba por palabra, por lo que incluso si ingresa una palabra de vowless (CWM, por ejemplo), la función devolverá una sílaba. La función es:
def count_syllables(word):
"""Estimate syllable count in a word using a simple vowel-grouping method."""
word = word.lower()
word = re.sub(r'[^a-z]', '', word) # Remove punctuation
syllables = re.findall(r'[aeiouy]+', word) # Find vowel clusters
return max(1, len(syllables)) # At least one syllable per word
Esa función devolverá el recuento de sílabas para cualquier palabra entrada, por lo que para encontrar el recuento de sílabas para una línea completa de texto, volver al bucle anterior (utilizado para la recopilación de datos en 1.A-1.C) e iterar sobre la lista de palabras que devolverá el recuento de sílabas en cada palabra. Sumar los recuentos de sílabas dará el recuento para la línea completa: num_syllables = sum(count_syllables(word) for word in words)
.
mi. Resumen de la recopilación de datos
El algoritmo de recopilación de datos se compila en una sola función, que comienza a dividir el poema ingresado en sus líneas, itera sobre cada línea del poema que realiza todas las operaciones descritas anteriormente, y agrega cada punto de datos a una lista designada para ese conjunto de datos, y finalmente un diccionario para almacenar todos los puntos de datos para una sola línea y acumularlo a un conjunto de datos maestro. Si bien la complejidad del tiempo es efectivamente irrelevante para las pequeñas cantidades de datos de entrada que se utilizan, la función se ejecuta en tiempo lineal, lo que es útil en el caso de que se utiliza para analizar grandes cantidades de datos. La función de recopilación de datos en su totalidad es:
def analyze_poem(poem):
"""Analyzes the poem line by line."""
data = []
lines = poem.split("n")
for line in lines:
words = line.split()
num_words = len(words)
num_letters = sum(len(word) for word in words)
visual_length = len(line) # Approximate visual length (monospace)
num_syllables = sum(count_syllables(word) for word in words)
word.append(num_words)
letters.append(num_letters)
length.append(visual_length)
sylls.append(num_syllables)
data.append({
"line": line,
"words": num_words,
"letters": num_letters,
"visual_length": visual_length,
"syllables": num_syllables
})
return data
2. Transformación discreta de Fourier
Prefacio: Esta sección asume una comprensión del (discreto) Transformación de Fourier; Para una introducción relativamente breve y manejable, intente este Artículo de SHO Nakagome.
a. Algoritmo DFT específico
Para abordar con cierta especificidad el algoritmo DFT particular que he usado, necesitamos tocar el método de transformación de Fourier Fast Numpy. Suponer norte es el número de valores discretos que se transforman: si norte es un poder de 2, Numpy usa el Algoritmo Radix-2 Cooley-Tukeyque divide recursivamente la entrada en índices uniformes y impares. Si norte no es una potencia de 2, Numpy aplica un enfoque de radio mixto, donde la entrada se factoriza en factores primos más pequeños, y las FFT se calculan utilizando casos base eficientes.
b. Aplicando el DFT
Para aplicar el DFT a los datos recopilados previamente, he creado una función fourier_analysis
que solo toma el conjunto de datos maestros (una lista de diccionarios con todos los puntos de datos para cada línea) como argumento. Afortunadamente, dado que Numpy es tan experto en Matemáticas, el código es simple. Primero, encontrar nortesiendo el número de puntos de datos que se transformarán; esto es simplemente N = len(data)
. A continuación, aplique el algoritmo FFT de Numpy a los datos utilizando el método np.fft.fft(data)
que devuelve una matriz de los coeficientes complejos que representan la amplitud y fase de la serie de Fourier. Finalmente, el np.abs(fft_result)
El método extrae las magnitudes de cada coeficiente, representando su resistencia en los datos originales. La función devuelve el espectro de magnitud de Fourier como una lista de pares de magnitud de frecuencia.
def fourier_analysis(data):
"""Performs Fourier Transform and returns frequency data."""
N = len(data)
fft_result = np.fft.fft(data) # Compute Fourier Transform
frequencies = np.fft.fftfreq(N) # Get frequency bins
magnitudes = np.abs(fft_result) # Get magnitude of FFT coefficients
return list(zip(frequencies, magnitudes)) # Return (freq, magnitude) pairs
El código completo se puede encontrar aquí, en Github.
3. Estudios de casos
a. Introducción
Hemos superado todos los algoritmos de código y lengua-twister, finalmente es hora de poner a prueba el programa. Por el tiempo, el análisis literario realizado aquí será mínimo, poniendo el estrés en el análisis de datos. Tenga en cuenta que si bien este algoritmo de transformación de Fourier devuelve un espectro de frecuencia, queremos un espectro de época, por lo que la relación (t = frac {1} {f}) se utilizará para obtener un espectro de período. Con el fin de comparar diferentes niveles de ruido de los espectros, utilizaremos la métrica de relación señal / ruido (SNR). El ruido promedio de la señal se calcula como una media aritmética, dada por (p_ {ruido} = frac {1} {n-1} sum_ {k = 0}^{n-1} | x_k |), donde (x_k) es el coeficiente para cualquier índice (k), y la suma excluye (x_ {}), el coeficiente de la señal del pico de la señal. Para encontrar el snr, simplemente tomar (frac {x_ {pico}} {p_ {ruido}}); Una SNR más alta significa que una SNR más alta significa una SNR más alta significa una mayor resistencia a la señal en relación con el ruido de fondo. La SNR es una fuerte opción para detectar la periodicidad poética porque cuantifica cuánto de la señal (es decir, patrones rítmicos estructurados) se destaca contra el ruido de fondo (variaciones aleatorias en la longitud de las palabras o el recuento de sílabas). A diferencia de la varianza, que mide la dispersión general, o la autocorrelación, que captura la repetición en retrasos específicos, SNR resalta directamente cómo un patrón periódico dominante es relativo a las fluctuaciones irregulares, lo que lo hace ideal para identificar estructuras metricas en la poesía.
b. «No entres gentil en esas buenas noches» – Dylan Thomas
Este trabajo tiene una estructura periódica definida y visible, por lo que son excelentes datos de prueba. Desafortunadamente, los datos de la sílaba aquí no encontrarán nada interesante aquí (el poema de Thomas está escrito en pentámetro yámbico); Los datos del recuento de palabras, por otro lado, tienen el valor SNR más alto de cualquiera de las cuatro métricas, 6.086.
El espectro anterior muestra una señal dominante en un período de 4 líneas, y relativamente poco ruido en los otros rangos de período. Además, considerando su valor SNR más alto en comparación con el conteo de letras, el conteo de sílabas y la longitud visual ofrece una observación interesante: el poema sigue un esquema de rima de ABA (en blanco); Esto significa que el recuento de palabras de cada línea se repite perfectamente en conjunto con el esquema de la rima. Las SNR de los otros dos espectros relevantes no están muy lejos de la SNR de conteo de palabras, con el conteo de letras en 5.724 y la longitud visual en 5.905. Esos dos espectros también tienen sus picos en un período de 4 líneas, lo que indica que también coinciden con el esquema de rima del poema.
do. «Jabberwocky» – Lewis Carroll
La escritura de Carrol también es principalmente periódica en estructura, pero tiene algunas irregularidades; En el espectro del período de la palabra hay un pico distinto a ~ 5 líneas, pero el ruido considerablemente bajo (SNR = 3.55) se rompe por tres subpelas distintas a 3.11 líneas, 2.54 líneas y 2.15 líneas. Este pico secundario se muestra en la Figura 2, lo que implica que hay un patrón de repetición secundario significativo en las palabras Carroll utilizadas. Además, debido a la naturaleza creciente de los picos a medida que se acercan a un período de 2 líneas, una conclusión es que Carroll tiene una estructura de recuentos de palabras alternos en su escritura.
Este patrón alterno se refleja en los espectros de período de longitud visual y recuento de letras, ambos con picos secundarios a 2.15 líneas. Sin embargo, el espectro de la sílaba que se muestra en la Figura 3 muestra una baja magnitud en el período de línea 2.15, lo que indica que el recuento de palabras, el recuento de letras y la longitud visual de cada línea están correlacionadas, pero no el recuento de sílabas.
Curiosamente, el poema sigue un esquema de rima Abab, lo que sugiere una conexión entre la longitud visual de cada línea y el patrón de rima en sí. Una posible conclusión es que Carroll lo encontró más atractivo visualmente al escribir para que los extremos de rimas de las palabras se alineen verticalmente en la página. Esta conclusión, que la estética visual de cada línea alteró el estilo de escritura de Carroll, se puede dibujar antes de leer el texto.
4. Conclusión
La aplicación de análisis de Fourier a la poesía revela que las herramientas matemáticas pueden descubrir estructuras ocultas en obras literarias, paternos que pueden reflejar las tendencias estilísticas de un autor o incluso las opciones subconscientes. En ambos estudios de casos, se encontró una relación cuantificable entre la estructura del poema y las métricas (conteo de palabras, etc.) que a menudo se pasan por alto en el análisis literario. Si bien este enfoque no reemplaza el análisis literario tradicional, proporciona una nueva forma de explorar las cualidades formales de la escritura. La intersección de las matemáticas, la informática, el análisis de datos y Literatura es una frontera prometedora, y esta es solo una forma en que la tecnología puede conducir a nuevos descubrimientos, teniendo potencial en campos de ciencia de datos más amplios como estilometría, sentimiento y análisis de emociones, y modelado de temas. \[\]