ian curtis

En diciembre doy una pequeña charla sobre la película Control (2007) en el Seminario Rock en Cine de la Universidad Complutense de Madrid (UCM). Así que he decidido realizar mi primer script con las letras de Joy Division, grupo protagonista de la película y pionero del sonido Madchester. Aún no sé utilizar la API de LyricWiki, así que descargué a la vieja usanza las letras de sus dos álbumes, Unknown Pleasures (1979) y Closer (1980).

Guardamos los albumes en una carpeta, cada uno en su archivo de texto UTF-8. Primero seleccionamos con setwd () el directorio donde vamos a trabajar y luego cargamos todos los documentos que se encuentran en la carpeta “/discos“.

> setwd("/home/rubent/documents/PlantillasR/MusycaR/Joy Division/")
> cname <- file.path(".","discos")

Comprobamos que tenemos una variable de longitud dos compuesta por los archivos:

> dir(cname)
[1] "Closer.txt"            "Unknown Pleasures.txt"

El siguiente paso es crear el corpus, así que cargamos la libreria tm (sino está previamente instalada puedes incluir el comando install.packages(“tm”)). Ahí tenemos un corpus con dos documentos de texto.

#install.packages("tm")
> corpus <- Corpus(DirSource(cname))
> corpus
<<VCorpus (documents: 2, metadata (corpus/indexed): 0/0)>>
> summary(corpus)
                      Length Class             Mode
Closer.txt            2      PlainTextDocument list
Unknown Pleasures.txt 2      PlainTextDocument list

Le echamos un vistazo al primero de los documentos:

> inspect(corpus[1])
<<VCorpus (documents: 1, metadata (corpus/indexed): 0/0)>>

[[1]]
<<PlainTextDocument (metadata: 7)>>
Closer (1980)

1 Atrocity exhibition

Asylums with doors open wide
Where people had paid to see inside
For entertainment they watch his body twist
Behind his eyes he says, "I still exist"
...

Ahora vamos con lo interesante del paquete tm. Primero convertimos las letras de los textos en minúsculas, luego quitamos los números y las signos de puntuación. Luego eliminamos varias palabras comunes del ingles (“stopwords”). Al final de estos procesos eliminamos los espacios en blanco que se han generado con “stripWhitespace”.

##Transformaciones
# Convertimos todo en minúsculas
corpus <- tm_map(corpus, tolower)
# Quitamos los números
corpus <- tm_map(corpus, removeNumbers)
# Quitamos los signos de puntuación
corpus <- tm_map(corpus, removePunctuation)
# Se eliminan varias palabras comunes del inglés
corpus <- tm_map(corpus, removeWords, stopwords("english"))
# Quitamos los espacios que han salido con tanto retoque
corpus <- tm_map(corpus, stripWhitespace)
# Nos aseguramos que el corpus es texto plano
corpus <- tm_map(corpus, PlainTextDocument)

Vamos ahora a cargar el paquete “SnowballC” para hacer Stemming. Básicamente, el paquete de Stemming borra los sufijos de las palabras para dejarlas en su “raíz”, por ejemplo “run”,“runs” y “running” se convertirán en “run”. Hay que ir con cuidado porque a veces deja unas palabras un tanto “raras”, pero se entienden. Después volvemos a eliminar los espacios.

library(SnowballC)
corpus <- tm_map(corpus, stemDocument)
# Quitamos los espacios que han salido con tanto retoque
corpus <- tm_map(corpus, stripWhitespace)

Así queda el documento después de lo anterior:

> inspect(corpus[1])
<<VCorpus (documents: 1, metadata (corpus/indexed): 0/0)>>

[[1]]
<<PlainTextDocument (metadata: 7)>>
closer 

 atrocity exhibition

asylums doors open wide
 people paid see inside
 entertainment watch body twist
behind eyes says still exist

 way step inside
 way step inside
 way step inside
 way step inside

Tras la batería de transformaciones vamos a crear la Matriz de términos del documento (Document Term Matrix), para realizar el resto de análisis. Nuestra matriz tiene 667 palabras de las cúales el 38% están dispersas. Más adelante nos encargaremos de ellas.

> # Matriz de términos
> dtm <- DocumentTermMatrix(corpus)
> dtm
<<DocumentTermMatrix (documents: 2, terms: 667)>>
Non-/sparse entries: 822/512
Sparsity           : 38%
Maximal term length: 13
Weighting          : term frequency (tf)

Exploramos un poco nuestra matriz: Los términos menos frecuentes, salen una vez en las canciones, son “abyss”, “accept”,…; y los más frecuentes “ive” (la forma contraída del “I have”), “control”, “time”o “lost”. Muy sugerentes para un análisis.

> ### Exploramos nuestra Matriz de terminos
> freq <- colSums(as.matrix(dtm))
> length(freq)
[1] 667
> ord <- order(freq)
> # Lista terminos menos frecuentes
> freq[head(ord)]
 abyss accept action    add  admir    all 
     1      1      1      1      1      1 
> # Lista terminos más frecuentes
> freq[tail(ord)]
    ive control    time    lost    will     way 
     20      21      21      24      31      32 

Continuando con el análisis de frecuencias, tenemos 414 palabras que aparecen una sola vez en el texto, mientras que hay una palabra que se repite 32 veces.

> ## Distribución de las frecuencias
> # Frequency of frequencies.
> head(table(freq), 15)
freq
  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15 
414  95  45  25  21  16  10   6   5   6   4   1   4   4   1 
> tail(table(freq), 15)
freq
 8  9 10 11 12 13 14 15 17 19 20 21 24 31 32 
 6  5  6  4  1  4  4  1  2  2  1  2  1  1  1 

Como dije antes, vamos a quitar los términos con poca frecuencia.

> ## Eliminar términos infrecuentes
> dim(dtm)
[1]   2 667
> dtms <- removeSparseTerms(dtm, .1)
> dim(dtms)
[1]   2 155
> inspect(dtms)
<<DocumentTermMatrix (documents: 2, terms: 155)>>
Non-/sparse entries: 310/0
Sparsity           : 0%
Maximal term length: 10
Weighting          : term frequency (tf)

Han quedado 155 palabras, un buen número para el siguiente paso. LLegamos a la nube de palabras o wordcloud, tan en boga. Primero cargamos el paquete wordcloud y luego generamos una paleta de colores con RColorBrewer. Yo he usado una paleta gris de acuerdo a las letras de Joy Division (y porque me gusta), para hay varias paletas muy coloridas para gente alegre. Luego generamos la wordcloud con palabras de 15 repeticiones.

##Nube de palabras o Wordcloud
library(wordcloud)
palette <- brewer.pal(9,"Greys")[-(1:5)]
wordcloud(names(freq), freq, min.freq=5, rot.per=0.2, scale=c(3, .1), colors=palette)

wordcloud

Vuelvo a sacar los términos más frecuentes, primero con frecuencia de más de 25 y luego más de 20.

## Identificar terminos más frecuentes y sus relaciones
freq <- colSums(as.matrix(dtms))
> findFreqTerms(dtm, lowfreq=25)
[1] "way"  "will"
> findFreqTerms(dtm, lowfreq=20)
[1] "control" "ive"     "lost"    "time"    "way"     "will

Ahora, buscamos las relaciones de las palabras más frecuentes. En mis textos han salido unos resultados algo extraños, todas palabras tenían una relación de 1; supongo que debido a los estribillos.

# encontrar relaciones de palabras especificas
findAssocs(dtm, "will", corlimit=0.7)
findAssocs(dtm, "lost", corlimit=0.7)

Dibujamos las relaciones de las palabras con una frecuencia mayor a 20.

##Dibujamos las relaciones
plot(dtm, terms=findFreqTerms(dtm, lowfreq=20)[1:6], corThreshold=0.2)

Rplot01

Y por último vamos a crear una tabla frecuencias.

##Dibujamos las frecuencias
freq <- sort(colSums(as.matrix(dtm)), decreasing=TRUE)
head(freq, 14)
wf <- data.frame(word=names(freq), freq=freq)
head(wf)
#Dibujamos las palabras con más de 15 frecuencia
library(ggplot2)
p <- ggplot(subset(wf, freq>15), aes(word, freq))
p <- p + geom_bar(stat="identity")
p <- p + theme(axis.text.x=element_text(angle=45, hjust=1))
p

Rplot02

Bibliografía
  1. Graham Williams. Text Mining. Data Science with R
  2. Zhao R. Text Mining with R — an Analysis of Twitter Data
Anuncios