library(pacman)
p_load("xfun", "ggplot2" ,"cluster", "vembedr", "dplyr", "stringr", "wordcloud", "rmdformats")

Clasificación de datos

Clasificación supervisada

disponemos de un conjunto de datos (por ejemplo, imágenes de letras escritas a mano) que vamos a llamar datos de entrenamiento y cada dato está asociado a una etiqueta (a qué letra corresponde cada imagen). Construímos un modelo en la fase de entrenamiento (training) utilizando dichas etiquetas, que nos dicen si una imagen está clasificada correcta o incorrectamente por el modelo. Una vez construído el modelo podemos utilizarlo para clasificar nuevos datos que, en esta fase, ya no necesitan etiqueta para su clasificación, aunque sí la necesitan para evaluar el porcentaje de objetos bien clasificados.

Clasificación no supervisada

los datos no tienen etiquetas (o no queremos utilizarlas) y estos se clasifican a partir de su estructura interna (propiedades, características).

Clasificación semisupervisada

algunos datos de entrenamiento tienen etiquetas, pero no todos. Este último caso es muy típico en clasificación de imágenes, donde es habitual disponer de muchas imágenes mayormente no etiquetadas. Estos se pueden considerar algoritmos supervisados que no necesitan todas las etiquetas de los datos de entrenamiento.

El algoritmo k-means (K medias)

K-means es un algoritmo de clasificación no supervisada (clusterización) que agrupa objetos en k grupos basándose en sus características. El agrupamiento se realiza minimizando la suma de distancias entre cada objeto y el centroide de su grupo o cluster. Se suele usar la distancia cuadrática. El algoritmo consta de tres pasos:

pasos del algoritmo k means

  1. Inicialización: una vez escogido el número de grupos, k, se establecen k centroides en el espacio de los datos, por ejemplo, escogiéndolos aleatoriamente.

  2. Asignación objetos a los centroides: cada objeto de los datos es asignado a su centroide más cercano.

  3. Actualización centroides: se actualiza la posición del centroide de cada grupo tomando como nuevo centroide la posición del promedio de los objetos pertenecientes a dicho grupo.

Centroides para K means Se repiten los pasos 2 y 3 hasta que los centroides no se mueven, o se mueven por debajo de una distancia umbral en cada paso.

El algoritmo k-means resuelve un problema de optimización, siendo la función a optimizar (minimizar) la suma de las distancias cuadráticas de cada objeto al centroide de su cluster.

Los objetos se representan con vectores reales de d dimensiones (x1,x2,…,xn) y el algoritmo k-means construye k grupos donde se minimiza la suma de distancias de los objetos, dentro de cada grupo S={S1,S2,…,Sk} , a su centroide. El problema se puede formular de la siguiente forma:

\[ \underset{\mathbf{S}}{\mathrm{min}}\; E\left(\boldsymbol{\mu_{i}}\right)=\underset{\mathbf{S}}{\mathrm{min}}\sum_{i=1}^{k}\sum_{\mathbf{x}_{j}\in S_i}\left\Vert \mathbf{x}_{j}-\boldsymbol{\mu}_{i}\right\Vert ^{2} \quad (1) \]

donde S es el conjunto de datos cuyos elementos son los objetos xj representados por vectores, donde cada uno de sus elementos representa una característica o atributo. Tendremos k grupos o clusters con su correspondiente centroide μi . En cada actualización de los centroides, desde el punto de vista matemático, imponemos la condición necesaria de extremo a la función E(μi) que, para la función cuadrática (1) es:

\[ \frac{\partial E}{\partial\boldsymbol{\mu}_{i}}=0\;\Longrightarrow\;\boldsymbol{\mu}_{i}^{(t+1)}=\frac{1}{\left|S_{i}^{(t)}\right|}\sum_{\mathbf{x}_{j}\in S_{i}^{(t)}}\mathbf{x}_{j} \] y se toma el promedio de los elementos de cada grupo como nuevo centroide. Las principales ventajas del método k-means son que es un método sencillo y rápido. Pero es necesario decidir el valor de k y el resultado final depende de la inicialización de los centroides. En principio no converge al mínimo global sino a un mínimo local.

Encontrado el codo para los clusters

hasta que punto se incluyen nuevos clusters

Ejercicio de clasifiaccion no supervisada usando k means aplicado a iris dataset

Datos

df <- iris 
head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa

Primer grafica coloreando las clases (especies)

ggplot(df, aes(Petal.Length, Petal.Width)) + geom_point(aes(col=Species), size=4 )            

Como podemos ver, setosa se agrupará más fácilmente. Mientras tanto, hay ruido entre versicolor y virginica incluso cuando parecen perfectamente agrupados.

Ejecutemos el modelo. kmeans está instalado en el paquete base de R, por lo que no tenemos que instalar ningún paquete.

En la función kmeans, es necesario establecer el centro, que es el número de grupos que queremos agrupar. En este caso, sabemos que este valor será 3. Configuremos eso.

, pero veamos cómo construiríamos el modelo si no lo supiéramos.

set.seed(101)
irisCluster <- kmeans(df[,1:4], center=3, nstart = 20 )
irisCluster
## K-means clustering with 3 clusters of sizes 38, 62, 50
## 
## Cluster means:
##   Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1     6.850000    3.073684     5.742105    2.071053
## 2     5.901613    2.748387     4.393548    1.433871
## 3     5.006000    3.428000     1.462000    0.246000
## 
## Clustering vector:
##   [1] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
##  [38] 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
##  [75] 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 1 1 1 2 1 1 1 1
## [112] 1 1 2 2 1 1 1 1 2 1 2 1 2 1 1 2 2 1 1 1 1 1 2 1 1 1 1 2 1 1 1 2 1 1 1 2 1
## [149] 1 2
## 
## Within cluster sum of squares by cluster:
## [1] 23.87947 39.82097 15.15100
##  (between_SS / total_SS =  88.4 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

Comparando estos clusters con los datos originales

table(irisCluster$cluster, df$Species)
##    
##     setosa versicolor virginica
##   1      0          2        36
##   2      0         48        14
##   3     50          0         0

Agrupando los datos en clusters

clusplot(iris, irisCluster$cluster, color=T, shade=T, lines=0)

Podemos ver el racimo setosa perfectamente explicado, mientras que virginica y versicolor tienen un poco de ruido entre sus racimos.

Como decía antes, no siempre tendremos los datos etiquetados. Si quisiéramos saber el número exacto de centros, deberíamos haber construido el método del codo.

tot.withinss <- vector(mode="character", length=10)
for (i in 1:10){
  irisCluster <- kmeans(df[,1:4], center=i, nstart=20)
  tot.withinss[i] <- irisCluster$tot.withinss
}

Graficar el codo obtenido

plot(1:10, tot.withinss, type="b", pch=19)

Resumen del video “Ciudades inteligentes: el poder de nuestros datos”

Video de YouTube.

embed_url("https://youtu.be/RhfM5LropRw")

El poder de los datos

Con la cantidad correcta de datos podemos obtener una gran cantidad de información útil, hacer inferencias, tomar decisiones y realizar mejoras a sistemas.

El problema con los datos, es cuando no tenemos control sobre ellos; cosa que está sucediendo en la actualidad a causa de los flujos de datos que generamos a través del internet y nuestros dispositivos; lo cual les permite a otras personas o incluso máquinas, tomar decisiones o hacer inferencias sobre nosotros.

Teniendo los suficientes datos, tienes la cosa en si”.

Haciendo uso de los suficientes datos podemos:

  • Exigir a los gobernadores un cambio en algún área que ayude a nuestras comunidades a mejorar.
  • Predecir enfermedades.
  • Patrones de comportamiento antisocial.

pero, también se pueden usar en nuestra contra, por las diversas empresas que sólo buscan un beneficio propio.

Por lo anterior, los datos tienen un alto poder, y es imprescindible que seamos capaces de controlar nuestros datos personales, para poder decidir qué hacer con ellos, porque con los datos suficientes, tienes la capacidad de inferir, por ejemplo, con una imagen, puedes ver que se habló en el video.

Resumen del video en forma de histograma de frecuencias

Datos

Archivo TXT con la transcripción del video.

setwd("~/ea1130")
video <- readLines("video.txt")
## Warning in readLines("video.txt"): incomplete final line found on 'video.txt'

Limpieza de palabras

# separar
lstUNPrfLines <- str_split(video," ")
# palabras por linea
vciUNPrfWperL <- unlist(lapply(lstUNPrfLines, length))
# deslistar para obtener un vector de palabras
vcsUNPrfWords <- unlist(lstUNPrfLines)
# recuento total de palabras = longitud del vector
intWordCount <- length(vcsUNPrfWords)


# Cambiar todas a minúsculas 
vcsUNPrfWords <- str_to_lower(vcsUNPrfWords)
# Reemplazar los números por espacios vacíos
vcsUNPrfWords <- str_replace_all(vcsUNPrfWords, pattern="[[:digit:]]", "")
# remover signos de puntuación
vcsUNPrfWords <- str_replace_all(vcsUNPrfWords, pattern="[[:punct:]]", "")
# remover espacios en blanco
vcsUNPrfWords <- str_replace_all(vcsUNPrfWords, pattern="[[:space:]]", "")
# remover caracteres especiales
vcsUNPrfWords <- str_replace_all(vcsUNPrfWords, pattern="[~@#$%&-_=<>]", "")
# remover vectores vacíos 
vcsUNPrfWords <- vcsUNPrfWords[vcsUNPrfWords != ""]
# hack & remove $
vcsUNPrfWords <- str_replace_all(vcsUNPrfWords, pattern="$", "")
# head
head(vcsUNPrfWords,100)
##   [1] "aquã­"        "mãºsica"      "vivimos"      "una"          "revoluciã³n" 
##   [6] "de"           "los"          "datos"        "estamos"      "conectados"  
##  [11] "las"          "horas"        "del"          "dã­a"         "a"           
##  [16] "travã©s"      "de"           "nuestros"     "dispositivos" "mã³viles"    
##  [21] "y"            "sensores"     "corporales"   "le"           "preguntamos" 
##  [26] "a"            "google"       "aquello"      "que"          "no"          
##  [31] "nos"          "animarã­amos" "ni"           "siquiera"     "preguntarle" 
##  [36] "a"            "nuestro"      "psicã³logo"   "registramos"  "nuestro"     
##  [41] "periodo"      "en"           "aplicaciones" "mã³viles"     "y"           
##  [46] "hasta"        "somos"        "capaces"      "de"           "enviar"      
##  [51] "nuestra"      "saliva"       "por"          "correo"       "para"        
##  [56] "que"          "una"          "empresa"      "en"           "eeuu"        
##  [61] "los"          "secuencia"    "el"           "adn"          "y"           
##  [66] "lo"           "suba"         "a"            "la"           "nube"        
##  [71] "justamente"   "hace"         "dos"          "meses"        "un"          
##  [76] "artã­culo"    "en"           "el"           "new"          "york"        
##  [81] "times"        "dio"          "a"            "conocer"      "que"         
##  [86] "el"           "adn"          "del"          "de"           "la"          
##  [91] "poblaciã³n"   "de"           "eeuu"         "ya"           "estã"        
##  [96] "en"           "la"           "nube"         "y"            "esto"

Data frame de palabras normales

# make data frame
dfrUNPrfWords <- data.frame(vcsUNPrfWords)
colnames(dfrUNPrfWords) <- c("Words")
dfrUNPrfWords$Words <- as.character(dfrUNPrfWords$Words)
# normal word count
head(dfrUNPrfWords,10)
##          Words
## 1        aquã­
## 2      mãºsica
## 3      vivimos
## 4          una
## 5  revoluciã³n
## 6           de
## 7          los
## 8        datos
## 9      estamos
## 10  conectados

Conteo de palabras normales

# resumiendo los datos 
dfrUNPrfFreq <- dfrUNPrfWords %>% 
                group_by(Words) %>% 
                summarise(Freq=n()) %>% 
                arrange(desc(Freq))
head(dfrUNPrfFreq)
## # A tibble: 6 x 2
##   Words  Freq
##   <chr> <int>
## 1 de       83
## 2 que      51
## 3 la       46
## 4 y        44
## 5 en       40
## 6 a        38

Data frame de palabras realmente significativas

Removemos las palabras que se repitan menos de 3 veces.

# Remover todas las palabras que se repitan menos de 3 veces 
dfrUNPrfWords <- filter(dfrUNPrfWords, str_length(Words)>3)

Listado de remoción

Limpiamos las palabras no significativas

vcsCmnWords <- c("de","que","en","y","la", "pues" , "una" , "como" , "para" , "las" , "los" , "con" , "más" , "por")
dfrUNPrfWords <- filter(dfrUNPrfWords,!(Words %in% vcsCmnWords) )
head(dfrUNPrfWords)
##         Words
## 1       aquã­
## 2     mãºsica
## 3     vivimos
## 4 revoluciã³n
## 5       datos
## 6     estamos

Conteo de palabras significativas

# resumiendo los datos 
dfrUNPrfFreq <- dfrUNPrfWords %>% 
                group_by(Words) %>% 
                summarise(Freq=n()) %>% 
                arrange(desc(Freq))
head(dfrUNPrfFreq)
## # A tibble: 6 x 2
##   Words       Freq
##   <chr>      <int>
## 1 datos         31
## 2 ciudadanos    11
## 3 nuestros      10
## 4 cada           8
## 5 esto           7
## 6 poder          7

Nube de palabras del video.

# nube de palabras 
wordcloud(dfrUNPrfFreq$Words[1:100], dfrUNPrfFreq$Freq[1:100], random.order=F, max.words=100, colors=brewer.pal(8, "Dark2"))

Descarga este código

xfun::embed_file("A3U1.Rmd")

Download A3U1.Rmd