Saltar al contenido

Archivos de categoría Estadística

Recomendaciones basado en filtrado colaborativo en Python

Filtrado colaborativo

Los Sistemas Recomendadores son una colección de algoritmos utilizados para sugerir temas a los usuarios, basados en información tomada desde el punto de vista del usuario. Estos sistemas ubicuos pueden ser comúnmente vistos en tiendas online, bases de datos de películas y buscadores de empleos. Vamos a ver los sistemas de recomendación basados en Contenido e implementaremos una versión utilizando Python y la librería Pandas.

Primero, importemos las librerías necesarias:

#librería de manipulación de dataframes
import pandas as pd
#Funciones matemáticas, necesitaremos sólo importar la función sqrt
from math import sqrt
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

Adquisición de Datos

Para adquirir y extraer los datos, simplemente ejecuta los siguientes scripts Bash:

!wget -O moviedataset.zip https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/moviedataset.zip
print('unziping ...')
!unzip -o -j moviedataset.zip 
--2020-03-06 17:12:19--  https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/moviedataset.zip
Resolving s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)... 67.228.254.196
Connecting to s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)|67.228.254.196|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 160301210 (153M) [application/zip]
Saving to: ‘moviedataset.zip’

moviedataset.zip    100%[===================>] 152.88M  19.2MB/s    in 8.4s    

2020-03-06 17:12:27 (18.1 MB/s) - ‘moviedataset.zip’ saved [160301210/160301210]

unziping ...
Archive:  moviedataset.zip
  inflating: links.csv               
  inflating: movies.csv              
  inflating: ratings.csv

Ahora carguemos cada archivo dentro de su dataframe:

#Guardando la información de la película dentro de un dataframe de panda
movies_df = pd.read_csv('movies.csv')
#Guardando información del usuario dentro de un dataframe de panda
ratings_df = pd.read_csv('ratings.csv')

Miremos cada uno de ellos a ver cómo están organizados:

#Head es una función que obtiene las primeras N filas de un dataframe. El valor por omisión de N es 5.
movies_df.head()
movieId title genres
0 1 Toy Story (1995) Adventure|Animation|Children|Comedy|Fantasy
1 2 Jumanji (1995) Adventure|Children|Fantasy
2 3 Grumpier Old Men (1995) Comedy|Romance
3 4 Waiting to Exhale (1995) Comedy|Drama|Romance
4 5 Father of the Bride Part II (1995) Comedy

Cada película tiene un único ID, un título con su año de estreno (El cual puede contener caracteres unicode) y muchos géneros diferentes en el mismo campo. Saquemos el año de la columna del título y ubiquemoslo en su propia columna con la función extract que viene con Pandas.

Saquemos el año de la columna title utilizando la función de pandas replace y guardémoslo en la nueva columna year.

#Utilizar expresiones regulares para encontrar un año guardado entre paréntesis
#Especificamos los paréntesis de forma tal de que no haya problemas con las películas que tiene el año en sus títulos
movies_df['year'] = movies_df.title.str.extract('(\d\d\d\d)',expand=False)
#Sacando los paréntesis
movies_df['year'] = movies_df.year.str.extract('(\d\d\d\d)',expand=False)
#Sacando los años de la columna 'title'
movies_df['title'] = movies_df.title.str.replace('(\d\d\d\d)', '')
#Aplicando la función strip para sacar los espacios finales que pudiera haber
movies_df['title'] = movies_df['title'].apply(lambda x: x.strip())

Veamos el resultado

movies_df.head()
movieId title genres year
0 1 Toy Story Adventure|Animation|Children|Comedy|Fantasy 1995
1 2 Jumanji Adventure|Children|Fantasy 1995
2 3 Grumpier Old Men Comedy|Romance 1995
3 4 Waiting to Exhale Comedy|Drama|Romance 1995
4 5 Father of the Bride Part II Comedy 1995

Luego, saquemos la columna de los géneros ya que no los necesitaremos para este sistema recomendador.

#Eliminando la columna géneros
movies_df = movies_df.drop('genres', 1)

Aquí está el dataframe final:

movies_df.head()
movieId title year
0 1 Toy Story 1995
1 2 Jumanji 1995
2 3 Grumpier Old Men 1995
3 4 Waiting to Exhale 1995
4 5 Father of the Bride Part II 1995

Ahora, veamos el dataframe de los ratings.

ratings_df.head()
userId movieId rating timestamp
0 1 169 2.5 1204927694
1 1 2471 3.0 1204927438
2 1 48516 5.0 1204927435
3 2 2571 3.5 1436165433
4 2 109487 4.0 1436165496

Cada fila en el dataframe ratings tiene un id de usuario asociado con al menos una película, un rating y una marca de tiempo que muestra cuando se revisó. No se necesitará la columna timestamp, por lo que se eliminará para ahorrar memoria.

#Drop elimina una fila en particular o columna dentro de un dataframe
ratings_df = ratings_df.drop('timestamp', 1)

Asi es cómo el Dataframe definitivo se ve:

ratings_df.head()
userId movieId rating
0 1 169 2.5
1 1 2471 3.0
2 1 48516 5.0
3 2 2571 3.5
4 2 109487 4.0

Filtrado Colaborativo

Ahora, comenzaremos el trabajo con los sistemas recomendadores.

La primer técnica que vas a ver se llama Filtrado Colaborativo, también conocido como Filtrado de Usuario a Usuario. Como lo indica su nombre alternativo, esta técnica utiliza otros usuarios para recomendar items al ingreso de datos. Se intenta encontrar usuarios que tengan preferencias y opiniones parecidas para entonces recomendar items que se hayan parecido al ingreso anterior. Existen varios métodos para encontrar usuarios parecidos (Incluso los que utilizan Machine Learning), y el método que vamos a utilizar estará basado en la Función de Correlación de Pearson.
El proceso para crear un sistema de recomendación Basado en el Usuario es el siguiente:

  1. Elegir un usuario con las películas que el usuario ha mirado
  2. Basado en su indice de selección de películas, encuentra a los primeros X vecinos
  3. Obtener el registro de la película que miró el usuario para cada vecino.
  4. Calcular un puntaje de similitud utilizando alguna fórmula
  5. Recomendar los ítems con los puntajes más altos

Comencemos creando un usuario a quien recomendar películas:
Nota: Para agregar más películas, aumenta la catidad de elementos en userInput. Agrega tantos como desees! Solo asegúrate de escribir en letras mayúsculas y si una película comienza con un “The”, como “The Matrix” entonces escríbelo así: ‘Matrix, The’.

userInput = [
            {'title':'Breakfast Club, The', 'rating':5},
            {'title':'Toy Story', 'rating':3.5},
            {'title':'Jumanji', 'rating':2},
            {'title':"Pulp Fiction", 'rating':5},
            {'title':'Akira', 'rating':4.5}
         ] 
inputMovies = pd.DataFrame(userInput)
inputMovies
title rating
0 Breakfast Club, The 5.0
1 Toy Story 3.5
2 Jumanji 2.0
3 Pulp Fiction 5.0
4 Akira 4.5

Agregar movieId al ingreso del usuario: Con las datos ingresados completos, extraigamos los ID de las películas del dataframe de películas y agreguémosla.
Esto se logra primero sacando las filas que tienen que tienen títulos de películas y luego une este subconjunto con el dataframe de entrada. También sacamos columnas que no se necesitan para ahorrar espacio de memoria.

#Filtrar las películas por título
inputId = movies_df[movies_df['title'].isin(inputMovies['title'].tolist())]
#Luego juntarlas para obtener el movieId. Implícitamente, lo está uniendo por título.
inputMovies = pd.merge(inputId, inputMovies)
#Eliminando información que no utilizaremos del dataframe de entrada
inputMovies = inputMovies.drop('year', 1)
#Dataframe de entrada final
#Si una película que se agregó no se encuentra, entonces podría no estar en el dataframe 
#original o podría estar escrito de otra forma, por favor revisar mayúscula o minúscula.
inputMovies
movieId title rating
0 1 Toy Story 3.5
1 2 Jumanji 2.0
2 296 Pulp Fiction 5.0
3 1274 Akira 4.5
4 1968 Breakfast Club, The 5.0

Usuarios que han visto las mismas películas: Ahora, que el ID de la película está como entrada, podemos obtener el subconjunto de usuarios que han visto o revisado las películas en nuestra entrada.

#Filtrando los usuarios que han visto las películas y guardándolas
userSubset = ratings_df[ratings_df['movieId'].isin(inputMovies['movieId'].tolist())]
userSubset.head()
userId movieId rating
19 4 296 4.0
441 12 1968 3.0
479 13 2 2.0
531 13 1274 5.0
681 14 296 2.0

Ahora agrupamos las filas por el ID del usuario.

#Groupby crea varios dataframes donde todos tienen el mismo valor para la columna especificada como parámetro
userSubsetGroup = userSubset.groupby(['userId'])

Miremos a uno de los usuarios, por ejemlo, el userID=1130

userSubsetGroup.get_group(1130)
userId movieId rating
104167 1130 1 0.5
104168 1130 2 4.0
104214 1130 296 4.0
104363 1130 1274 4.5
104443 1130 1968 4.5

Ahora hagamos ordenamiento dentro de cada grupo de forma tal que los usuarios que compartan la mayor cantidad de películas tengan prioridad. Esto brinda una recomendación mejorada ya que no será necesario pasar por todos los usuarios.

#Ordenamiento de forma tal de que los usuarios con más películas en común tengan prioridad
userSubsetGroup = sorted(userSubsetGroup,  key=lambda x: len(x[1]), reverse=True)

Ahora, miremos al primer usuario

userSubsetGroup[0:3]
[(75,       userId  movieId  rating
  7507      75        1     5.0
  7508      75        2     3.5
  7540      75      296     5.0
  7633      75     1274     4.5
  7673      75     1968     5.0), (106,       userId  movieId  rating
  9083     106        1     2.5
  9084     106        2     3.0
  9115     106      296     3.5
  9198     106     1274     3.0
  9238     106     1968     3.5), (686,        userId  movieId  rating
  61336     686        1     4.0
  61337     686        2     3.0
  61377     686      296     4.0
  61478     686     1274     4.0
  61569     686     1968     5.0)]

Similitud entre usuarios y usuarios ingresantes

Luego, compararemos a todos los usuarios (casi todos) con nuestro usuario indicado y encontraremos el que más se parece.
Encontraremos cómo cada usuario similar se relacionan entre si a través del Coeficiente de Correlación de Pearson. Se utiliza para medir la fuerza de una asociación lineal entre dos variables. La fórmula para encontrar este coeficiente entre los conjuntos X e Y con los valores de N se puede ver en la fórmula:
r=\frac{\sum_{i=1}^{n}(x_i-\bar{x})(y_i-\bar{y})}{\sqrt{\sum_{i=1}^{n}(x_i-\bar{x})^2}\sqrt{\sum_{i=1}^{n}(y_i-\bar{y})^2}}

¿Por qué la Correlación Pearson?
La correlación Pearson no varía con la escala, ejemplo: si se multiplican todos los elementos por una constante distinta a cero o si se agrega cualquier constante a todos los elementos. Por ejemplo, si tienes dos vectores X e Y, entonces, pearson(X, Y)=pearson(X, 2 \cdot Y + 3). Esta es una propiedad muy importante en los sistemas recomendadores por que dos usuarios podrían putnuar dos series de elementos de manera completamente diferente, pero serían usuarios parecidos (ejemplo: con ideas similares) con puntuaciones parecidas en escalas variadas .

Los valores brindados por la fórumula puede variar de r=-1 a r=1, donde 1 se correlaciona directamente entre las dos entidades (esto sería una correlación positiva perfecta) y -1 forma una correlación negativa perfecta. En nuestro caso, un 1 se refiere a que dos usuarios tiene gustos parecidos, mientras que -1 es lo opuesto.
Elegiremos un subconjunto de usuarios para hacer las iteraciones. Este limite existe porque no queremos desperdiciar mucho tiempo pasando por cada usuario.

userSubsetGroup = userSubsetGroup[0:100]

Ahora, calculemos la Correlación Pearson entre la entrada del usuario el grupo, para almacenarlo en el diccionario, donde la clave es el Id del usuario y el valor es el coeficiente

#Guardar la Correlación Pearson en un diccionario, donde la clave es el Id del usuario y el valor es el coeficiente
pearsonCorrelationDict = {}

#Para cada grupo de usuarios en nuestro subconjunto 
for name, group in userSubsetGroup:
    #Comencemos ordenando el usuario actual y el ingresado de forma tal que los valores no se mezclen luego
    group = group.sort_values(by='movieId')
    inputMovies = inputMovies.sort_values(by='movieId')
    #Obtener el N para la fórmula
    nRatings = len(group)
    #Obtener los puntajes de revisión para las películas en común
    temp_df = inputMovies[inputMovies['movieId'].isin(group['movieId'].tolist())]
    #Guardarlas en una variable temporal con formato de lista para facilitar cálculos futuros
    tempRatingList = temp_df['rating'].tolist()
    #Pongamos también las revisiones de grupos de usuarios en una lista
    tempGroupList = group['rating'].tolist()
    #Calculemos la Correlación Pearson entre dos usuarios, x e y
    Sxx = sum([i**2 for i in tempRatingList]) - pow(sum(tempRatingList),2)/float(nRatings)
    Syy = sum([i**2 for i in tempGroupList]) - pow(sum(tempGroupList),2)/float(nRatings)
    Sxy = sum( i*j for i, j in zip(tempRatingList, tempGroupList)) - sum(tempRatingList)*sum(tempGroupList)/float(nRatings)

    #Si el denominador es diferente a cero, entonces dividir, sino, la correlación es 0.
    if Sxx != 0 and Syy != 0:
        pearsonCorrelationDict[name] = Sxy/sqrt(Sxx*Syy)
    else:
        pearsonCorrelationDict[name] = 0
pearsonCorrelationDict.items()
dict_items([(75, 0.8272781516947562), (106, 0.5860090386731182), (686, 0.8320502943378437), (815, 0.5765566601970551), (1040, 0.9434563530497265), (1130, 0.2891574659831201), (1502, 0.8770580193070299), (1599, 0.4385290096535153), (1625, 0.716114874039432), (1950, 0.179028718509858), (2065, 0.4385290096535153), (2128, 0.5860090386731196), (2432, 0.1386750490563073), (2791, 0.8770580193070299), (2839, 0.8204126541423674), (2948, -0.11720180773462392), (3025, 0.45124262819713973), (3040, 0.89514359254929), (3186, 0.6784622064861935), (3271, 0.26989594817970664), (3429, 0.0), (3734, -0.15041420939904673), (4099, 0.05860090386731196), (4208, 0.29417420270727607), (4282, -0.4385290096535115), (4292, 0.6564386345361464), (4415, -0.11183835382312353), (4586, -0.9024852563942795), (4725, -0.08006407690254357), (4818, 0.4885967564883424), (5104, 0.7674257668936507), (5165, -0.4385290096535153), (5547, 0.17200522903844556), (6082, -0.04728779924109591), (6207, 0.9615384615384616), (6366, 0.6577935144802716), (6482, 0.0), (6530, -0.3516054232038709), (7235, 0.6981407669689391), (7403, 0.11720180773462363), (7641, 0.7161148740394331), (7996, 0.626600514784504), (8008, -0.22562131409856986), (8086, 0.6933752452815365), (8245, 0.0), (8572, 0.8600261451922278), (8675, 0.5370861555295773), (9101, -0.08600261451922278), (9358, 0.692178738358485), (9663, 0.193972725041952), (9994, 0.5030272728659587), (10248, -0.24806946917841693), (10315, 0.537086155529574), (10368, 0.4688072309384945), (10607, 0.41602514716892186), (10707, 0.9615384615384616), (10863, 0.6020183016345595), (11314, 0.8204126541423654), (11399, 0.517260600111872), (11769, 0.9376144618769914), (11827, 0.4902903378454601), (12069, 0.0), (12120, 0.9292940047327363), (12211, 0.8600261451922278), (12325, 0.9616783115081544), (12916, 0.5860090386731196), (12921, 0.6611073566849309), (13053, 0.9607689228305227), (13142, 0.6016568375961863), (13260, 0.7844645405527362), (13366, 0.8951435925492911), (13768, 0.8770580193070289), (13888, 0.2508726030021272), (13923, 0.3516054232038718), (13934, 0.17200522903844556), (14529, 0.7417901772340937), (14551, 0.537086155529574), (14588, 0.21926450482675766), (14984, 0.716114874039432), (15137, 0.5860090386731196), (15157, 0.9035841064985974), (15466, 0.7205766921228921), (15670, 0.516015687115336), (15834, 0.22562131409856986), (16292, 0.6577935144802716), (16456, 0.7161148740394331), (16506, 0.5481612620668942), (17246, 0.48038446141526137), (17438, 0.7093169886164387), (17501, 0.8168748513121271), (17502, 0.8272781516947562), (17666, 0.7689238340176859), (17735, 0.7042381820123422), (17742, 0.3922322702763681), (17757, 0.64657575013984), (17854, 0.537086155529574), (17897, 0.8770580193070289), (17944, 0.2713848825944774), (18301, 0.29838119751643016), (18509, 0.1322214713369862)])
pearsonDF = pd.DataFrame.from_dict(pearsonCorrelationDict, orient='index')
pearsonDF.columns = ['similarityIndex']
pearsonDF['userId'] = pearsonDF.index
pearsonDF.index = range(len(pearsonDF))
pearsonDF.head()
similarityIndex userId
0 0.827278 75
1 0.586009 106
2 0.832050 686
3 0.576557 815
4 0.943456 1040

Ahora obtengamos los 50 primeros usuarios más parecidos a los que se ingresaron.

topUsers=pearsonDF.sort_values(by='similarityIndex', ascending=False)[0:50]
topUsers.head()
similarityIndex userId
64 0.961678 12325
34 0.961538 6207
55 0.961538 10707
67 0.960769 13053
4 0.943456 1040

Recomendemos películas al usuario de entrada puntuando a los usuarios elegidos para todas las películas

Haremos esto tomando el peso promedio de los ratings de las películas utilizando la Correlación Pearson. Pero para hacer esto, primero necesitamos que los usuarios vean las películas en nuestro pearsonDF a partir del dataframe de puntajes y luego guardar su correlación en una nueva columna llamada _similarityIndex”. Estos se logra juntando estas dos tablas de debajo.

topUsersRating=topUsers.merge(ratings_df, left_on='userId', right_on='userId', how='inner')
topUsersRating.head()
similarityIndex userId movieId rating
0 0.961678 12325 1 3.5
1 0.961678 12325 2 1.5
2 0.961678 12325 3 3.0
3 0.961678 12325 5 0.5
4 0.961678 12325 6 2.5

Ahora todo lo que se necesita hacer es multiplicar el puntaje de la película por su peso (El índice de similitud), luego se suman los nuevos puntajes y dividen por la suma de los pesos.
Esto se logra sencillamente multiplicando dos columnas, luego agrupando el dataframe por la columna movieId y luego dividiendo dos columnas:
Aqui se muestra la idea de todos los usuarios similares respecto de las películas candidatas para el usuario ingresado:

#Se multiplica la similitud de los puntajes de los usuarios
topUsersRating['weightedRating'] = topUsersRating['similarityIndex']*topUsersRating['rating']
topUsersRating.head()
similarityIndex userId movieId rating weightedRating
0 0.961678 12325 1 3.5 3.365874
1 0.961678 12325 2 1.5 1.442517
2 0.961678 12325 3 3.0 2.885035
3 0.961678 12325 5 0.5 0.480839
4 0.961678 12325 6 2.5 2.404196
#Se aplica una suma a los topUsers luego de agruparlos por userId
tempTopUsersRating = topUsersRating.groupby('movieId').sum()[['similarityIndex','weightedRating']]
tempTopUsersRating.columns = ['sum_similarityIndex','sum_weightedRating']
tempTopUsersRating.head()
sum_similarityIndex sum_weightedRating
movieId
1 38.376281 140.800834
2 38.376281 96.656745
3 10.253981 27.254477
4 0.929294 2.787882
5 11.723262 27.151751
#Se crea un dataframe vacío
recommendation_df = pd.DataFrame()
#Ahora se toma el promedio ponderado
recommendation_df['weighted average recommendation score'] = tempTopUsersRating['sum_weightedRating']/tempTopUsersRating['sum_similarityIndex']
recommendation_df['movieId'] = tempTopUsersRating.index
recommendation_df.head()
weighted average recommendation score movieId
movieId
1 3.668955 1
2 2.518658 2
3 2.657941 3
4 3.000000 4
5 2.316058 5

Luego, ordenémoslo y veamos las primeras 20 películas que el algoritmo recomendó!

recommendation_df = recommendation_df.sort_values(by='weighted average recommendation score', ascending=False)
recommendation_df.head(10)
weighted average recommendation score movieId
movieId
5073 5.0 5073
3329 5.0 3329
2284 5.0 2284
26801 5.0 26801
6776 5.0 6776
6672 5.0 6672
3759 5.0 3759
3769 5.0 3769
3775 5.0 3775
90531 5.0 90531
movies_df.loc[movies_df['movieId'].isin(recommendation_df.head(10)['movieId'].tolist())]
movieId title year
2200 2284 Bandit Queen 1994
3243 3329 Year My Voice Broke, The 1987
3669 3759 Fun and Fancy Free 1947
3679 3769 Thunderbolt and Lightfoot 1974
3685 3775 Make Mine Music 1946
4978 5073 Son’s Room, The (Stanza del figlio, La) 2001
6563 6672 War Photographer 2001
6667 6776 Lagaan: Once Upon a Time in India 2001
9064 26801 Dragon Inn (Sun lung moon hak chan) 1992
18106 90531 Shame 2011

Ventajas y Desventajas del Filtro Colaborativo

  • Ventajas
    • Tiene en cuenta el puntaje de otros usuarios
    • No necesita estudiar o extraer la información del elemento recomendado
    • Se adapta al interés del usuario mientras cambia
  • Desventajas
    • La función de aproximación puede ser lenta
    • Podriá surgir una cantidad baja de usuarios para aproximar
    • Temas de privacidad cuando se intenta aprender de las preferencias del usuario
0 Seguir leyendo →

Recomendaciones basado en filtrado colaborativo

Recomendaciones basado en filtrado colaborativo.

Las recomendaciones basado en filtrado colaborativo se basa en el hecho de que existen relaciones entre productos y los intereses de la gente. Muchos sistemas de recomendaciones utilizan Filtrado colaborativo para encontrar estas relaciones y para dar una recomendación precisa de un producto que el usuario puede que le guste o que le interese. El filtrado colaborativo tiene básicamente dos enfoques: basado en el usuario y basado en artículos.

  • El filtrado colaborativo basado en el usuario se basa en la similitud o el vecindario del usuario.
  • El filtrado colaborativo basado en artículos se basa en la similaridad entre los articulos.

Recomendaciones de filtrado colaborativo basado en el usuario

En el filtrado colaborativo basado en el usuario, tenemos un usuario activo para el que la recomendación está dirigida. El motor de filtrado colaborativo, primero busca a los usuarios que son similares, es decir, los usuarios que comparten los patrones de calificación del usuario activo. El filtrado colaborativo basa esta similitud en cosas como la historia, la preferencia y las opciones que los usuarios hacen al comprar, observar, o al disfrutar de algo. Por ejemplo, las películas que usuarios similares han calificado altamente. Entonces, utiliza las calificaciones de estos usuarios similares para predecir las posibles calificaciones por parte del usuario activo para una película que no había visto previamente. Por ejemplo, si 2 usuarios son similares o son vecinos, en términos de su interés en las películas, podemos recomendar una película para el usuario activo que su vecino ya ha visto.

Recomendaciones colaborativas pesosSupongamos que tenemos una matriz simple de ítem de usuario, que muestra las calificaciones de 4 usuarios para 5 diferentes películas. Supongamos también que nuestro usuario activo ha visto y evaluado 3 de estas 5 películas. Vamos a averiguar cuál de las dos películas que nuestro usuario activo no ha visto, debe ser recomendada para el. El primer paso es descubrir lo similar que es el usuario activo a los otros usuarios. Esto se puede hacer a través de muchas técnicas estadísticas y vectoriales, tales como mediciones de distancia o similaridad, incluyendo Distancia Euclidiana, Correlación de Pearson, Similitud de Cosine, etc… Para calcular el nivel de similitud entre 2 usuarios, utilizamos las 3 películas que los usuarios han calificado en el pasado. Independientemente de lo que utilizamos para la medición de similaridad, digamos, por ejemplo, la similitud, podría ser 0.7, 0.9, y 0.4 entre el usario activo y otros usuarios. Estos números representan pesos de similitud, o la proximidad del usuario activo a otros usuarios en el dataset.
El siguiente paso es crear una matriz de evaluación ponderada. Acabamos de calcular la similaridad de los usuarios a nuestro usuario activo en la diapositiva anterior. Ahora podemos usarlo para calcular la posible opinión del usuario activo acerca de nuestras 2 peliculas objetivo. Esto se logra multiplicando los pesos de similitud a las calificaciones de usuario. Esto da como resultado una matriz de calificaciones ponderada, que representa la opinión de la vecindad del usuario acerca de nuestras 2 películas candidatas para la recomendación. De hecho, incorpora el comportamiento de otros usuarios y da más peso a las calificaciones de los usuarios que son más parecidos al usuario activo. Ahora podemos generar la matriz de recomendaciones mediante la suma de todos los pesos ponderados. Sin embargo, como 3 usuarios calificaron a la primera película potencial, y 2 usuarios calificaron la segunda película, tenemos que normalizar los valores ponderados de la calificación. Lo hacemos dividiéndolo por la suma del índice de similitud para los usuarios. El resultado es la calificación potencial que nuestro usuario activo dará a estas películas, en base a su similitud con otros usuarios. Es obvio que podemos usarlo para clasificar las películas para dar una recomendación a nuestro usuario activo.
Recomendaciones colaborativas cálculo

Recomendaciones de filtrado colaborativo basado en el artículo

En el enfoque basado en el usuario, la recomendación se basa en los usuarios del mismo vecindario, con el o ella comparte preferencias comunes. Por ejemplo, como el Usuario1 y el Usuario3, a ambos les gustó el artículo 3 y el artículo 4, los consideramos similares o usuarios vecinos, y recomendamos el artículo 1, que es evaluado positivamente por Usuario1 al Usuario3. En el enfoque basado en artículos, los artículos similares crean vecindarios en el comportamiento de los usuarios.
Por ejemplo, el artículo 1 y el artículo 3 se consideran vecinos, ya que fueron valorados positivamente por tanto Usuario1 como Usuario2. Por lo tanto, el artículo 1 se puede recomendar al Usuario3 como él ya ha mostrado interés en el artículo 3. Por lo que, las recomendaciones se basan en los artículos en el vecindario que un usuario puede preferir.

Recomendaciones colaborativasInconvenientes

El filtro colaborativo es un sistema de recomendaciones muy eficaz, sin embargo, existen algunos desafíos con él también.
Uno de ellos es la dispersión de datos. La dispersión de datos se produce cuando se dispone de un dataset grande de usuarios, que generalmente sólo califican un número limitado de artículos. Como se ha mencionado, los recomendadores basados en la colaboración sólo puede predecir la puntuación de un artículo si hay otros usuarios que lo han calificado. Debido a la dispersión, es posible que no tengamos calificaciones suficientes en el dataset de artículos de usuario, lo que hace que sea imposible proporcionar recomendaciones adecuadas.
Otra cuestión que hay que tener en cuenta es algo llamado “inicio en frío”. El inicio en frío hace referencia a la dificultad del sistema de recomendación tiene cuando hay un nuevo usuario y, como tal, un perfil no existe para ellos todavía. El inicio en frío también puede ocurrir cuando tenemos un nuevo artículo que no ha recibido una calificación.
La escalabilidad también se puede convertir en un problema. A medida que aumenta el número de usuarios o artículos y la cantidad de datos se expande, los algoritmos de filtrado colaborativo comenzarán a sufrir caídas en el rendimiento, simplemente debido al crecimiento en el cálculo de similaridad.
Hay algunas soluciones para cada uno de estos desafíos, tales como el uso de sistemas de recomendación basados en híbridos.

Podemos ver un ejemplo en Python en esta entrada: Recomendaciones basado en filtrado colaborativo en Python

Ir al artículo anterior de la serie: Recomendaciones basado en contenido

0 Seguir leyendo →

Recomendaciones basado en contenido

Recomendaciones basado en contenido.

Un sistema de recomendaciones basado en contenido trata de recomendar artículos a los usuarios, en función de su perfil. El perfil del usuario gira en torno a sus preferencias y sus gustos. Se configura en función de las valoraciones de los usuarios, incluyendo el número de veces que el usuario ha dado click a distintos elementos o tal vez, incluso le ha dado like esos artículos. El proceso de recomendación se basa en la similitud entre estos elementos. La similaridad, o cercanía de los artículos, se mide basado en la similitud en el contenido de dichos elementos. Cuando decimos contenido, estamos hablando de cosas como la categoría del artículo, la etiqueta, el género, etc… Por ejemplo, si tenemos 4 películas, y si al usuario le gusta o califica los 2 primeros elementos, y si el elemento 3 es similar al elemento 1, en términos de su genero, el motor también recomendará el artículo 3 al usuario.

En esencia, esto es lo que hacen los motores del sistema de recomendaciones basados en contenido. Vamos a bucear en un sistema de recomendaciones basado en contenido para ver cómo funciona: Supongamos que tenemos un conjunto de datos de 6 películas. Este conjunto de datos muestra películas que nuestro usuario ha visto, y también el género de cada una de las películas. Por ejemplo, “Batman versus Superman” está en los géneros de aventura y superhéroes. Y “Guardianes de la Galaxia” está en comedia, aventura, superhéroes y ciencia ficción. Digamos que el usuario ha visto y clasificado 3 películas hasta ahora y que ha dado una calificación de 2 de 10 a la primera película, 10 de 10 a la segunda película, y un 8 de 10 a la tercera. La tarea del motor de recomendaciones es recomendar una de las 3 películas candidatas a este usuario. En otras palabras, queremos predecir cuál es la posible calificación que el usuario le daría a las 3 películas candidatas si las viera.

Para lograr esto, tenemos que construir el perfil de usuario. En primer lugar, creamos un vector para mostrar las calificaciones para las películas que el usuario ya vió. Lo llamamos “input user ratings”. Luego, codificamos las películas a través del enfoque “Hot Encoding”. El genero de las películas se utiliza como un conjunto de características. Usamos las primeras 3 películas para hacer esta matriz, que representa la matriz ‘feature-set’ de película. Si multiplicamos estas 2 matrices, podemos obtener el “eighted feature set” para las películas. Al resultado también se le llama matriz “Weighted Genre”, y representa los intereses del usuario por cada género basado en las películas que ha visto.

perfil de usuario
Ahora, dada la matriz “Weighted Genre”, podemos formar el perfil de nuestro usuario activo. Esencialmente, podemos agregar los géneros ponderados, y luego normalizarlos para encontrar el perfil de usuario. Claramente indica que le gustan las películas de “super héroe” más que de otros géneros. Utilizamos este perfil para averiguar qué película es adecuada para recomendar a este usuario. Así que codificamos las 3 películas candidatas para la recomendación, que no han sido vistas por el usuario. Ahora ya podemos averiguar cuál de ellas es más adecuada para que se recomienden al usuario. Simplemente multiplicamos la matriz de perfil de usuario por la matriz de películas candidatas, y da como resultado la matriz de “películas ponderadas” que muestra el peso de cada género respecto al perfil de usuario. Si agregamos estas valoraciones ponderadas, obtenemos el posible nivel de interés del usuario activo en estas 3 películas. En esencia, es nuestra lista de “recomendaciones”, la que podemos utilizar para clasificar las películas y recomendar al usuario. Por ejemplo, podemos decir que el “Hitchhiker el Guía de la Galaxia” tiene la puntuación más alta de nuestra lista, y es factible recomendarsela.

RecomendacionesUn modelo de este tipo es muy eficiente. Sin embargo, en algunos casos no funciona. Por ejemplo, supongamos que tenemos una película en el género de “drama”, que el usuario nunca ha visto. Por lo tanto, este género no estaría en su perfil. Por lo tanto, sólo tendrá recomendaciones relacionadas con los géneros que ya están en su perfil, pues el motor de recomenteraciones no puede recomendar ninguna película dentro de otros géneros. Este problema puede ser resuelto por otros tipos de sistemas de recomendaciones, como “Filtrado colaborativo.”

Podemos ver un ejemplo en Python en esta entrada: Recomendaciones basado en contenido en Python

Ir al artículo anterior de la serie: Introducción a los sistemas de recomendación

Ir al artículo siguiente de la serie: Recomendaciones basado en filtrado colaborativo

0 Seguir leyendo →

Introducción a los sistemas de recomendación

Introducción a los sistemas de recomendación.

A pesar de que los gustos de los personas pueden variar, en general siguen patrones. Con eso, quiero decir que hay similitudes en las cosas que la gente tiende a guastar. Otra forma de mirarlo es que la gente tiende a gustar las cosas en la misma categoría o cosas que comparten las mismas características. Por ejemplo, si ha adquirido recientemente un libro sobre Machine Learning en Python y le ha gustado leerlo, es muy probable que también lea un libro sobre la Visualización de Datos. La gente también tiende a tener gustos similares a los de la gente a la que están cerca en sus vidas. Los sistemas de recomendación tratan de capturar estos patrones y conductas similares para ayudar a predecir qué más te podría gustar.

Los sistemas de recomendación tienen muchas aplicaciones que estoy seguro de que ya estas familiarizado. De hecho, los sistemas de recomendación suelen estar en juego en muchos sitios web. Por ejemplo, sugiriendo libros en Amazon y películas en Netflix. De hecho, todo en el sitio web de Netflix está controlado por la selección del cliente. Si una cierta pelicula se ve con una frecuencia suficiente, el sistema de recomendación de Netflix asegura que esa película obtenga un número creciente de recomendaciones. Otro ejemplo se puede encontrar en una aplicación móvil de uso diario, donde un motor de recomendaciones se utiliza para recomendar cualquier cosa desde dónde comer. En las redes sociales, sitios como Facebook o LinkedIn, recomiendan regularmente amistades. Los sistemas de recomendación son incluso usados para personalizar tu experiencia en la web. Por ejemplo, cuando vas a una plataforma web de noticias, un sistema de recomendacion hará una nota de los tipos de historias en los que ha hecho click y hará recomendaciones sobre qué tipos de historias podrías estar interesado en leer en el futuro.

Los principales beneficios de usar un sistema de recomendaciones es que los usuarios obtienen una exposición más amplia a muchos productos diferentes en los que podrían estar interesados. Esta exposición anima a los usuarios a utilizar o comprar continuamente su producto. Esto no sólo proporciona una mejor experiencia para el usuario, sino que también beneficia al proveedor de servicios, también, con mayores ingresos potenciales y mejor seguridad para sus clientes.

Por lo general, hay dos tipos principales de sistemas de recomendaciones: el filtrado basado en contenido y el filtrado de colaboración. La principal diferencia entre cada uno, puede resumirse por el tipo de declaración que un consumidor puede hacer. Por ejemplo, el paradigma principal de un sistema de recomendaciones basado en contenido está motivado por la declaración: “Muéstrame más de lo mismo de lo que me ha gustado antes”. Los sistemas basados en contenido intentan de descubrir cuales son los aspectos favoritos de un usuario de un artículo y, a continuación, hace recomendaciones sobre los elementos que comparten esos aspectos. El filtrado colaborativo se basa en un usuario que dice: ” Díganme lo que es popular entre mis vecinos porque a mí también me puede gustar.” Las técnicas de filtrado colaborativo encuentran grupos de usuarios similares, y proporciona recomendaciones basadas en gustos similares dentro de ese grupo. En resumen, supone que un usuario puede estar interesado en lo que los usuarios similares están interesados. Además, hay sistemas de recomendación híbridos, que combinan varios mecanismos.

En términos de implementación de sistemas de recomendación, hay 2 tipos: basado en memoria y basado en modelos. En los enfoques basados en memoria, se utiliza el dataset de elemento de usuario completo para generar un sistema de recomendación. Utiliza técnicas estadísticas para aproximar usuarios o artículos. Ejemplos de estas técnicas son: Correlación de Pearson, Similitud de Cosine y Distancia Euclidiana, entre otros. En los enfoques basados en modelos, un modelo de usuarios se desarrolla en un intento de aprender sus preferencias. Los modelos se pueden crear utilizando técnicas de Machine Learning como la regresión,la agrupación en clústeres,la clasificación,etc.

Ir al artículo anterior de la serie: Clustering DBSCAN

Ir al artículo siguiente de la serie: Recomendaciones basado en contenido

0 Seguir leyendo →

Clustering DBSCAN en Python

Clustering DBSCAN

La mayoría de las técnicas tradicionales de clustering, tales como k-Means, jerárquicas, y el fuzzy clustering, se pueden utilizar para agrupar datos de una forma no supervisada.

Sin embargo, cuando se aplica a tareas con clusteres de forma arbitraria, o clusteres dentro de clusteres, las técnicas tradicionales podrían no ser capaces de lograr buenos resultados. Es decir, los elementos en el mismo cluster pueden no compartir suficiente similitud, o el rendimiento puede ser pobre. Además, el Clustering basado en densidad ubica regiones de alta densidad que se separan una de otra por regiones de baja densidad. Densidad, en este contexto, se define como el número de puntos dentro de un radio específico.

Primero, importemos las librerías necesarias:

import numpy as np 
from sklearn.cluster import DBSCAN 
from sklearn.datasets.samples_generator import make_blobs 
from sklearn.preprocessing import StandardScaler 
import matplotlib.pyplot as plt 
%matplotlib inline

Generación de datos

La siguiente función genera los puntos de datos y necesita estas entradas:

  • centroidLocation: Coordina los centroides que generarán los datos aleatorios. Por ejemplo [[4,3], [2,-1], [-1,4]]
  • numSamples: El número de datos que queremos generar, dividir sobre los centroides (# de centroides definidos en centroidLocation). Por ejemplo 1500
  • clusterDeviation: La desviación standard entre clusters. Mientras más grande el número, más lejos la distancia. Por ejemplo 0.5
def createDataPoints(centroidLocation, numSamples, clusterDeviation):
    # Crear datos aleatorios y almacenar en la matriz X y el vector y.
    X, y = make_blobs(n_samples=numSamples, centers=centroidLocation, 
                                cluster_std=clusterDeviation)

    # Estandarizar características eliminando el promedio y la unidad de escala
    X = StandardScaler().fit_transform(X)
    return X, y

Usar createDataPoints con 3 datos de entrada y guardar la salida en variable X e y.

X, y = createDataPoints([[4,3], [2,-1], [-1,4]] , 1500, 0.5)

Modelado

DBSCAN significa Density-based Spatial Clustering of Applications with Noise (Clustering espacial basado en densidad de aplicaciones con ruido). Esta técnica es una de las más comunes de algoritmos de clustering, que funciona en base a la densidad del objeto. DBSCAN trabaja en la idea de que si un punto en particular pertenece a un cluster, debería estar cerca de un montón de otros puntos en ese cluster.

Funciona en base a 2 parámetros: Radius (Radio) y Puntos Mínimos.
Epsilon determina un radio especificado que, si incluye suficientes puntos dentro de él, lo llamamos de “área densa”. minimumSamples determina el número mínimo de puntos de datos que queremos en un neighborhood (vecindario) para definir un cluster.

epsilon = 0.3
minimumSamples = 7
db = DBSCAN(eps=epsilon, min_samples=minimumSamples).fit(X)
labels = db.labels_
labels
array([0, 0, 1, ..., 0, 1, 0])

Distinguir Valores Atípicos

Reemplacemos todos los elementos con ‘True’ en core_samples_mask que estan en el cluster, ‘False’ si los puntos son valores atípicos (outliers).

# Primer, crear un vector de valores booleanos (valores binarios (verdadero/falso)) usando las etiquetas de la variable db.
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
core_samples_mask
array([ True,  True,  True, ...,  True,  True,  True])
# Número de clusters en etiquetas, ignorando el ruido en caso de estar presente.
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
n_clusters_
3
# Eliminar la repetición en las etiquetas transformándolas en un conjunto.
unique_labels = set(labels)
unique_labels
{0, 1, 2}

Visualización de Datos

# Crear colores para los clusters.
colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels)))
colors
array([[0.61960784, 0.00392157, 0.25882353, 1.        ],
       [0.99807766, 0.99923106, 0.74602076, 1.        ],
       [0.36862745, 0.30980392, 0.63529412, 1.        ]])
# Dibujar los puntos con colores
for k, col in zip(unique_labels, colors):
    if k == -1:
        # El color negro se utilizará para señalar ruido en los datos.
        col = 'k'

    class_member_mask = (labels == k)

    # Dibujoar los datos que estan agrupados (clusterizados)
    xy = X[class_member_mask & core_samples_mask]
    plt.scatter(xy[:, 0], xy[:, 1],s=50, c=col, marker=u'o', alpha=0.5)

    # Dibujar los valores atípicos
    xy = X[class_member_mask & ~core_samples_mask]
    plt.scatter(xy[:, 0], xy[:, 1],s=50, c=col, marker=u'o', alpha=0.5)

Comparativa con k-medias

Para entender mejor las diferencias entre el clustering basado en densidad y el basado en partición, intentaremos agrupar el conjunto de datos anterior en 3 clusters usando k-Medias.

from sklearn.cluster import KMeans 
k = 3
k_means3 = KMeans(init = "k-means++", n_clusters = k, n_init = 12)
k_means3.fit(X)
fig = plt.figure(figsize=(6, 4))
ax = fig.add_subplot(1, 1, 1)
for k, col in zip(range(k), colors):
    my_members = (k_means3.labels_ == k)
    plt.scatter(X[my_members, 0], X[my_members, 1],  c=col, marker=u'o', alpha=0.5)
plt.show()

Ejemplo con datos reales

DBSCAN es especialmente eficaz para tareas como la identificación de clases en un contexto espacial. El atributo maravilloso del algoritmo DBSCAN es que puede encontrar cualquier forma arbitraria de cluster sin verse afectado por el ruido. Por ejemplo, vamos a ver un conjunto de datos que muestra la ubicación de las estaciones meteorológicas en Canadá. DBSCAN puede ser usado aquí para encontrar el grupo de estaciones que muestran las mismas condiciones climáticas. Como puedremos ver, no sólo encuentra diferentes clusteres de forma arbitraria, sino que puede encontrar la parte más densa de las muestras centradas en datos, ignorando áreas menos densas o ruidos.

Acerca del set de datos

Environment Canada. Monthly Values for July – 2015

Name in the table Meaning
Stn_Name Station Name
Lat Latitude (North+, degrees)
Long Longitude (West – , degrees)
Prov Province
Tm Mean Temperature (°C)
DwTm Days without Valid Mean Temperature
D Mean Temperature difference from Normal (1981-2010) (°C)
Tx Highest Monthly Maximum Temperature (°C)
DwTx Days without Valid Maximum Temperature
Tn Lowest Monthly Minimum Temperature (°C)
DwTn Days without Valid Minimum Temperature
S Snowfall (cm)
DwS Days without Valid Snowfall
S%N Percent of Normal (1981-2010) Snowfall
P Total Precipitation (mm)
DwP Days without Valid Precipitation
P%N Percent of Normal (1981-2010) Precipitation
S_G Snow on the ground at the end of the month (cm)
Pd Number of days with Precipitation 1.0 mm or more
BS Bright Sunshine (hours)
DwBS Days without Valid Bright Sunshine
BS% Percent of Normal (1981-2010) Bright Sunshine
HDD Degree Days below 18 °C
CDD Degree Days above 18 °C
Stn_No Climate station identifier (first 3 digits indicate drainage basin, last 4 characters are for sorting alphabetically).
NA Not Available

Descargar los datos

Para descargar los datos, utilizaremos !wget

!wget -O weather-stations20140101-20141231.csv https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/weather-stations20140101-20141231.csv
--2020-03-01 17:38:18--  https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/weather-stations20140101-20141231.csv
Resolving s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)... 67.228.254.196
Connecting to s3-api.us-geo.objectstorage.softlayer.net (s3-api.us-geo.objectstorage.softlayer.net)|67.228.254.196|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 129821 (127K) 
Saving to: ‘weather-stations20140101-20141231.csv’

weather-stations201 100%[===================>] 126.78K  --.-KB/s    in 0.07s   

2020-03-01 17:38:18 (1.90 MB/s) - ‘weather-stations20140101-20141231.csv’ saved [129821/129821]

Cargar el set de datos

Importaremos el .csv, luego crearemos las columnas para el año, mes y el dia.

import csv
import pandas as pd
import numpy as np

filename='weather-stations20140101-20141231.csv'

#Read csv
pdf = pd.read_csv(filename)
pdf.head(5)
Stn_Name Lat Long Prov Tm DwTm D Tx DwTx Tn DwP P%N S_G Pd BS DwBS BS% HDD CDD Stn_No
0 CHEMAINUS 48.935 -123.742 BC 8.2 0.0 NaN 13.5 0.0 1.0 0.0 NaN 0.0 12.0 NaN NaN NaN 273.3 0.0 1011500
1 COWICHAN LAKE FORESTRY 48.824 -124.133 BC 7.0 0.0 3.0 15.0 0.0 -3.0 0.0 104.0 0.0 12.0 NaN NaN NaN 307.0 0.0 1012040
2 LAKE COWICHAN 48.829 -124.052 BC 6.8 13.0 2.8 16.0 9.0 -2.5 9.0 NaN NaN 11.0 NaN NaN NaN 168.1 0.0 1012055
3 DISCOVERY ISLAND 48.425 -123.226 BC NaN NaN NaN 12.5 0.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 1012475
4 DUNCAN KELVIN CREEK 48.735 -123.728 BC 7.7 2.0 3.4 14.5 2.0 -1.0 2.0 NaN NaN 11.0 NaN NaN NaN 267.7 0.0 1012573

Limpieza

Saquemos las filas que no tienen valor en el campo Tm.

pdf = pdf[pd.notnull(pdf["Tm"])]
pdf = pdf.reset_index(drop=True)
pdf.head(5)
Stn_Name Lat Long Prov Tm DwTm D Tx DwTx Tn DwP P%N S_G Pd BS DwBS BS% HDD CDD Stn_No
0 CHEMAINUS 48.935 -123.742 BC 8.2 0.0 NaN 13.5 0.0 1.0 0.0 NaN 0.0 12.0 NaN NaN NaN 273.3 0.0 1011500
1 COWICHAN LAKE FORESTRY 48.824 -124.133 BC 7.0 0.0 3.0 15.0 0.0 -3.0 0.0 104.0 0.0 12.0 NaN NaN NaN 307.0 0.0 1012040
2 LAKE COWICHAN 48.829 -124.052 BC 6.8 13.0 2.8 16.0 9.0 -2.5 9.0 NaN NaN 11.0 NaN NaN NaN 168.1 0.0 1012055
3 DUNCAN KELVIN CREEK 48.735 -123.728 BC 7.7 2.0 3.4 14.5 2.0 -1.0 2.0 NaN NaN 11.0 NaN NaN NaN 267.7 0.0 1012573
4 ESQUIMALT HARBOUR 48.432 -123.439 BC 8.8 0.0 NaN 13.1 0.0 1.9 8.0 NaN NaN 12.0 NaN NaN NaN 258.6 0.0 1012710

Visualización

Visualización de estaciones usando el package basemap. El toolkit basemap matplotlib es una librería para dibujo de mapas en 2D en Python. Basemap no dibuja por sí mismo, sino que facilita la transformación de las coordenadas a proyecciones de mapas. El tamaño de cada punto de datos representa el promedio máximo de temperatura para cada estación del año.

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from pylab import rcParams
%matplotlib inline
rcParams['figure.figsize'] = (14,10)

llon=-140
ulon=-50
llat=40
ulat=65

pdf = pdf[(pdf['Long'] > llon) & (pdf['Long'] < ulon) & (pdf['Lat'] > llat) &(pdf['Lat'] < ulat)]

my_map = Basemap(projection='merc',
            resolution = 'l', area_thresh = 1000.0,
            llcrnrlon=llon, llcrnrlat=llat, #min longitude (llcrnrlon) and latitude (llcrnrlat)
            urcrnrlon=ulon, urcrnrlat=ulat) #max longitude (urcrnrlon) and latitude (urcrnrlat)

my_map.drawcoastlines()
my_map.drawcountries()
# my_map.drawmapboundary()
my_map.fillcontinents(color = 'white', alpha = 0.3)
my_map.shadedrelief()

# To collect data based on stations        

xs,ys = my_map(np.asarray(pdf.Long), np.asarray(pdf.Lat))
pdf['xm']= xs.tolist()
pdf['ym'] =ys.tolist()

#Visualization1
for index,row in pdf.iterrows():
#   x,y = my_map(row.Long, row.Lat)
   my_map.plot(row.xm, row.ym,markerfacecolor =([1,0,0]),  marker='o', markersize= 5, alpha = 0.75)
#plt.text(x,y,stn)
plt.show()

Clustering de las estaciones basado en su ubicación i.e. Lat & Lon

DBSCAN (parte de la librería sklearn) ejecuta el clustering desde un vector o una matriz de distancia. En nuestro caso, pasamos el arregloNumpy array Clus_dataSet para encontrar ejemplos de alta densidad y expandir clusters a partir de ellos.

from sklearn.cluster import DBSCAN
import sklearn.utils
from sklearn.preprocessing import StandardScaler
sklearn.utils.check_random_state(1000)
Clus_dataSet = pdf[['xm','ym']]
Clus_dataSet = np.nan_to_num(Clus_dataSet)
Clus_dataSet = StandardScaler().fit_transform(Clus_dataSet)

# Calcular con DBSCAN
db = DBSCAN(eps=0.15, min_samples=10).fit(Clus_dataSet)
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
labels = db.labels_
pdf["Clus_Db"]=labels

realClusterNum=len(set(labels)) - (1 if -1 in labels else 0)
clusterNum = len(set(labels)) 


# Muestra de clusters
pdf[["Stn_Name","Tx","Tm","Clus_Db"]].head(5)
Stn_Name Tx Tm Clus_Db
0 CHEMAINUS 13.5 8.2 0
1 COWICHAN LAKE FORESTRY 15.0 7.0 0
2 LAKE COWICHAN 16.0 6.8 0
3 DUNCAN KELVIN CREEK 14.5 7.7 0
4 ESQUIMALT HARBOUR 13.1 8.8 0

Como puedes ver con los valores atípicos, la etiqueta del clustere es -1

set(labels)
{-1, 0, 1, 2, 3, 4}

Visualización de clusters basados en ubicación

Ahora, podemos visualizar los clusters usando basemap:

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from pylab import rcParams
%matplotlib inline
rcParams['figure.figsize'] = (14,10)

my_map = Basemap(projection='merc',
            resolution = 'l', area_thresh = 1000.0,
            llcrnrlon=llon, llcrnrlat=llat, #min longitude (llcrnrlon) and latitude (llcrnrlat)
            urcrnrlon=ulon, urcrnrlat=ulat) #max longitude (urcrnrlon) and latitude (urcrnrlat)

my_map.drawcoastlines()
my_map.drawcountries()
#my_map.drawmapboundary()
my_map.fillcontinents(color = 'white', alpha = 0.3)
my_map.shadedrelief()

# Para crear un mapa de colores
colors = plt.get_cmap('jet')(np.linspace(0.0, 1.0, clusterNum))



#Visualización
for clust_number in set(labels):
    c=(([0.4,0.4,0.4]) if clust_number == -1 else colors[np.int(clust_number)])
    clust_set = pdf[pdf.Clus_Db == clust_number]                    
    my_map.scatter(clust_set.xm, clust_set.ym, color =c,  marker='o', s= 20, alpha = 0.85)
    if clust_number != -1:
        cenx=np.mean(clust_set.xm) 
        ceny=np.mean(clust_set.ym) 
        plt.text(cenx,ceny,str(clust_number), fontsize=25, color='red',)
        print ("Cluster "+str(clust_number)+', Avg Temp: '+ str(np.mean(clust_set.Tm)))
Cluster 0, Avg Temp: -5.538747553816046
Cluster 1, Avg Temp: 1.9526315789473685
Cluster 2, Avg Temp: -9.195652173913045
Cluster 3, Avg Temp: -15.300833333333333
Cluster 4, Avg Temp: -7.769047619047619