SlideShare una empresa de Scribd logo
1 de 100
Caso Real Big Data
Análisis de weblogs de empresa de
contenidos multimedia online
Problemática
● Empresa que pretende mejorar su servicio de
películas online mediante un motor de
recomendaciones personalizadas al usuario,
teniendo en cuenta su historial de descargas,
eventos, etc.
● La única información con la que contamos son
los logs del último año en formato JSON
{"created_at": "2013-05-08T08:00:00Z", "payload": {"item_id": "11086", "marker":
3540}, "session_id": "b549de69-a0dc-4b8a-8ee1-01f1a1f5a66e", "type": "Play",
"user": 81729334, "user_agent": "Mozilla/5.0 (iPad; CPU OS 5_0_1 like Mac OS X)
AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9A405"}
Requisitos básicos
● El equipo desea:
1) Conocer cuales son los contenidos consumidos en
mayor medida por usuarios jóvenes
2) Identificar patrones de conducta de los usuarios
en sus sesiones, con el objetivo de mejorar la
experiencia/usabilidad de la página web
3) Aplicar un motor de recomendaciones a su web
que ayude a los usuarios a encontrar el contenido que
buscan, con el objetivo de que visiten más el portal
¿Cómo abordamos el problema?
● Como expertos en Big Data, dividimos el
problema en:
Clasificación binaria Clústering Análisis predictivo
Se clasifican los usuarios en dos
grupos (adulto, niño) para ofrecer
contenidos acordes al grupo al
que pertenecen
Agrupación de usuarios según
su comportamiento
Recomendador de películas
basado en la idoneidad de un
item concreto para un usuario
específico
¿Cómo funciona el servicio online?
● Debemos entender cual es el funcionamiento natural del
servicio ofertado por la empresa
– El usuario paga una suscripción para poder acceder a los
contenidos online
– Una vez logueado en el sistema, se le presenta al usuario una
serie de recomendaciones y contenidos populares
– El usuario puede reproducir el contenido online de su cola,
buscar nuevos contenidos, y reproducirlos directamente o
añadirlos a la cola de reproducción
– La página soporta puntuaciones y reviews
– El usuario puede realizar otras operaciones como pagos y
gestión de contraseñas
1. Exploración de los datos
Explorando los datos
● Partimos de los weblogs
● Además, se nos dice que:
– Los logs contienen eventos de control parental que
pueden ser utilizados para identificar si algunas cuentas
son utilizadas por adultos o niños
– El campo “marker” indica la posición del reproductor en el
contenido
– Las puntuaciones van de 1 a 5 (5 es el máximo)
– Identificadores de contenido que contienen una 'e' son
series de television ('e' de “episode”)
● El número que le sigue indica el número de episodio
Explorando los datos
– Cuando el contenido alcanza la marca “end” se
registra un evento “stop”
● Si el usuario abandona la página o cambia de contenido
puede que no se registre el evento
– La mayor parte de los usuarios residen en EEUU
{"auth": "1208d4c:279737f7", "createdAt": "2013-05-12T00:00:11-08:00", "payload":
{"itemId"": "3702e4", "marker": 780}, "refId": "7586e549", "sessionID": "d4a244cb-
d502-4c94-a80d-3d26ca54a449", "type": "Play", "user": 18910540, "userAgent":
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.2; SLCC2; .NET
CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0;
InfoPath.2)"}
Explorando los datos
● A parte del “Play” que hemos visto en el
ejemplo, ¿qué otros eventos existen?
● Usamos Hadoop streaming para poder pasarle
un script que filtre los resultados
grep: filtra los resultados de acuerdo al
patrón indicado
E: activa las expresiones regulares
o: hace que 'grep' saque únicamente
(output) la porción del input que cumple el
patrón
La expresión regular está dividida en 3 partes por el elemento
pipe |
""$1": [^,]+" $1 hace referencia al argumento 1 que le
pasemos (en este caso será “type” para
comprobar qué eventos existen)
cut -d: -f2- cortar delimitando (-d) por ':' el campo 2
tr -d '" ' borra (-d) el caracter “
#!/bin/bash
grep -Eo ""$1": [^,]+" | cut -d: -f2- | tr -d '" '
Explorando los datos
● Lanzamos el script con Hadoop Streaming
-D: evitamos que grep devuelva 1 si no encuentra nada
-stream.non.zero.exit.is.failure=false: Por defecto es true
-input: datos de entrada (directorio o ficheros) para el mapper
-ouptut: directorio de salida para el reducer
-mapper/-reducer: qué ejecutar como map/reduce
-file: indica a Hadoop que se asegure de que el fichero es accesible
desde todos los nodos que van a ejcutar la tarea
uniq -c es un reducer que agrega los campos y nos devuelve la cantidad
de repeticiones
$ hadoop jar $STREAMING -D stream.non.zero.exit.is.failure=false -input data/heckle/
-input data/jeckle/ -output types -mapper "grep_field.sh type" -file grep_field.sh
-reducer "uniq -c"
Filtrado por
campo type
Explorando los datos
● Visualizamos la salida
– 19 eventos
Antes dijimos que el
fichero de salida se iba a
llamar type. Así es como
HDFS gestiona los
resultados
Explorando los datos
● Visualizamos un ejemplo de weblog de cada
evento
– Saldrán 19 elementos JSON completos, cada uno
con un “type” diferente
$ hadoop fs -cat types/part* | awk '{ print $2 }' | xargs -n 1 grep -Rhm 1 data -e
-cat y awk para extraer los type únicos
Xargs y grep para encontrar una ocurrencia
de una línea que contengan cada tipo único
Explorando los datos
● Comprobamos la estructura de los datos de los
que disponemos
– ¿Comparten la misma estructura?
● Necesitamos un MapReduce que pueda manejar datos
JSON
● summary_map.py #!/usr/bin/python
import json
import sys
# Read all lines from stdin
for line in sys.stdin:
data = json.loads(line)
for field in data.keys():
print field
Parseamos el JSON
Mostramos cada campo
Explorando los datos
● Ejecutamos el script
Problema: hay datos con
campos que no coinciden
Explorando los datos
● Algunos JSON están mal formados y tienen
varias dobles comillas consecutivas
– Arreglamos el script
#!/usr/bin/python
import json
import sys
for line in sys.stdin:
try:
data = json.loads(line.replace('""', '"'))
for field in data.keys():
print field
except ValueError:
# Log the error so we can see it
sys.stderr.write("%sn" % line)
exit(1)
Explorando los datos
● Resultado
Problemas:
- Campos iguales con diferente nombre
- Campos que no aparecen siempre
Explorando los datos
● Volvemos a editar el script
para evitar confusiones
– Simplemente
normalizamos los campos
“problemáticos”
#!/usr/bin/python
import json
import sys
for line in sys.stdin:
try:
data = json.loads(line.replace('""', '"'))
for field in data.keys():
if field == 'type':
print "%s" % (data[field])
else:
# Normalize the file name
real = field
if real == 'user_agent':
real = 'userAgent'
elif real == 'session_id':
real = 'sessionID'
elif real == 'created_at' or real == 'craetedAt':
real = 'createdAt'
# Emit the normalized field
print "%s:%s" % (data['type'], real)
# Emit all subfields, if there are any
if type(data[field]) is dict:
for subfield in data[field]:
print "%s:%s:%s" % (data['type'], real, subfield)
except ValueError:
sys.stderr.write("%sn" % line)
exit(1)
Explorando los datos
● Volvemos a ejecutar el script
Explorando los datos
● Comprobamos...
Más problemas:
- Subcampos con diferente nombre
Explorando los datos
● Volvemos a editar el script
...
for line in sys.stdin:
try:
...
elif real == 'created_at' or real == 'craetedAt':
real = 'createdAt'
# Emit all subfields, if there are any
if type(data[field]) is dict:
print "%s:%s" % (data['type'], real)
# Normalize and print the subfields
for subfield in data[field]:
subreal = subfield
if subreal == 'item_id':
subreal = 'itemId'
print "%s:%s:%st%s" % (data['type'], real, subreal, data[field][subfield])
else:
# Emit the normalized field
print "%s:%st%s" % (data['type'], real, data[field])
except ValueError:
sys.stderr.write("%sn" % line)
exit(1)
Este cambio aborda dos problemas.
1. Normaliza los subcampos
2. Añade el valor del campo y del
subcampo a la salida
Explorando los datos
● Tenemos que hacer un resumen de los
datos → REDUCE
– Resumir los valores de campos y subcampos
– Identificar cada valor de campo (numérico,
fecha…)
– Valores categóricos (pocas posibilidades para
un campo)
– Identificadores sin patrón aparente
Explorando los datos
● Ver summary_reduce.py
Explorando los datos
● Resumen de los datos
– Identificadores de usuario entre 1000000 y 1000000
– No todos los Item ids son numéricos (de lo contrario no apaecerían como
identificadores)
– La media para los eventos de puntuaciones (Rating) es de 3.5, y para las
reviews (WriteReview) de 4
– Los timestamps no están todos en la misma zona. Algunos están en UTC
otros en UTC-8. Puede que haya otros. Tener en cuenta a la hora de
limpiar los datos
– Account tiene 3 posibles sub-acciones: updatePassword,
updatePaymentInfo, parentalControl
– Account:playload → no hay control parental que deshabilite opciones
(extraño)
– Play:playload → Faltan algunos valores de ytemID y marker (en ambos
casos, aparecen 543129)
Explorando los datos
● Número de usuarios diferentes y sesiones
– 2195 usuarios
– 5308 sesiones
$ hadoop jar $STREAMING -D stream.non.zero.exit.is.failure=false -input data/heckle/
-input data/jeckle/ -output users -mapper "grep_field.sh user" -file grep_field.sh -reducer
'bash -c "uniq | wc -l"'
...
$ hadoop fs -cat users/part*
2195
$ hadoop jar $STREAMING -D stream.non.zero.exit.is.failure=false -input data/heckle/
-input data/jeckle/ -output sessions -mapper 'grep_field.sh session(ID|_id)' -file
grep_field.sh -reducer 'bash -c "uniq | wc -l"'
…
$ hadoop fs -cat sessions/part*
5308
2. Limpieza de datos
Limpiando los datos
● Tenemos que solucionar los problemas que
hemos ido identificando en la fase de
preparación
● Vamos a reducir el volumen de los datos para
hacerlos más manejables
● La idea es tener un único fichero con un único
registro agregado por reducer
– Mapper
Limpiando los datos
● Mapper → clean_map.py
● Reducer → clean_reduce.py (con modificación
de playload por estar vacío)
Limpiamos el directorio (se
supone que hemos
lanzado antes un reduce
con error)
Volvemos a lanzar
nuestro MapReduce
personalizado
¡Éxito! Ahora tenemos un
único fichero limpio y
agregado con cada registro
conteniendo sesiones
completas
Limpiando los datos
● Visualizamos el resultado
● ¿Qué hemos conseguido?
– Logs reducidos a 4 MB
3. Clasificando usuarios
3.1 Extrayendo contenido de los
items reproducidos
● Necesitamos extraer el contenido reproducido, puntuado o
revisado (review) por cada usuario
– Mapper que nos permita agregar por usuario pero también diferenciar
por fecha
● Sacaremos una clave compuesta que contendrá el id del usuario, la fecha de
inicio de sesión, y la fecha de fin
– Reducer
● Las claves que recibe el Reducer están agregadas por usuario y agrupadas
por fecha
● Tratamiento:
– Si vemos una etiqueta nunca vista → adoptamos la nueva
– Si vemos una etiqueta repetida → nada cambia
– Si vemos una etiqueta conflictiva → lo tratamos como un usuario diferente
– Si no vemos nunca una etiqueta de un usuario → usuario sin etiqueta
– Para usuarios etiquetados como adultos → añadimos una 'a'
– Para usuarios etiquetados como niños → añadimos una 'k'
3.1 Extrayendo contenido de los
items reproducidos
● Mapper
– kid_map.py
#!/usr/bin/python
import json
import sys
def main():
# Read all lines from stdin
for line in sys.stdin:
data = json.loads(line)
# Collect all items touched
items = set()
items.update(data['played'].keys())
items.update(data['rated'].keys())
items.update(data['reviewed'].keys())
# Generate a comma-separated list
if items:
itemstr = ','.join(items)
else:
itemstr = ','
# Emit a compound key and compound value
print "%s,%010d,%010dt%s,%s" % (data['user'], long(data['start']),
long(data['end']), data['kid'], itemstr)
if __name__ == '__main__':
main()
3.1 Extrayendo contenido de los
items reproducidos
● Reducer
– Ver kid_reduce.py
3.1 Extrayendo contenido de los
items reproducidos
● Ejecutamos...
3.2 Preparando el algoritmo
SimRank
● En nuestro caso tenemos:
– 2000 usuarios que han visto 0 o más contenidos
– 8500 items que han sido vistos por 0 o más usuarios
● Grafo bipartido
● Solución basada en grafos
– Las etiquetas conocidas para los usuarios se propagarán
por los items que han visualizado, y de ahí a otros
usuarios que hayan visualizado el mismo contenido
● Lo mismo para comentarios y puntuaciones
– Progagación influenciada → SimRank
3.2 Preparando el algoritmo
SimRank
● SimRank necesita una matriz de adyacencia y
una lista de nodos
3.3 Construyendo la matriz
● Mapper → item_map.py
#!/usr/bin/python
import sys
def main():
# Read all lines from stdin
for line in sys.stdin:
key, value = line.strip().split('t')
items = value.split(',')
# Emit every item in the set paired with the user ID
for item in items:
print "%st%s" % (item, key)
if __name__ == '__main__':
main()
3.3 Construyendo la matriz
● Reducer → item_reduce.py
#!/usr/bin/python
import sys
def main():
last = None
# Read all lines from stdin
for line in sys.stdin:
item, user = line.strip().split('t')
if item != last:
if last != None:
# Emit the previous key
print "%st%s" % (last, ','.join(users))
last = item
users = set()
users.add(user)
# Emit the last key
print "%st%s" % (last, ','.join(users))
if __name__ == '__main__':
main()
3.3 Construyendo la matriz
● Lanzamos el job
– Tenemos una matriz de adyacencia que
implementa SimRank
3.4 Implementación de SimRank
● Mapper → simrank_map.py
– Lo único que tiene que hacer es leer el vector
SimRank y calcular el producto de la matriz con la
matriz de adyacencia
● Primero lee del vector SimRank de HDFS y lo guarda en
un diccionario obteniendo las columnas de la matriz de
adyacencia como registros de entrada
● Para cada entrada distinta de cero en una columna, se
multiplica por la correspondiente entrada en el vector
SimRank y se emite el resultado con la etiqueta de fila
como clave
3.4 Implementación de SimRank
● Mapper → simrank_map.py
#!/usr/bin/python
import sys
def main():
if len(sys.argv) < 2:
sys.stderr.write("Missing args: %sn" % ":".join(sys.argv))
sys.exit(1)
v = {}
# Read in the vector
with open(sys.argv[1]) as f:
for line in f:
(key, value) = line.strip().split("t")
v[key] = float(value)
# Now read the matrix from the mapper and do the math
for line in sys.stdin:
col, value = line.strip().split("t")
rows = value.split(',')
for row in rows:
try:
# Add the product to the sum
print "%st%.20f" % (row, v[col] / float(len(rows)))
except KeyError:
# KeyError equates to a zero, which we don't need to output.
pass
if __name__ == '__main__':
main()
3.4 Implementación de SimRank
● Reducer → simrank_reduce.py
– Se suman los valores intermedios de cada fila, se
suma la “teleport contribution” y se emite el
resultado final como el valor de la fila en un nuevo
vector SimRank
● El “teleport contribution” se obtiene extrayendo del
training set
3.4 Implementación de SimRank
● Testeando la convergencia → simrank_diff.py
● Lanzador → simrank.sh
$ chmod a+x simrank.sh
$ ./simrank.sh adults_train
rm: v: No such file or directory
rm: `v': No such file or directory
Beginning pass 1
/tmp/hadoop-training/hadoop-unjar2754804698549065604/]
...
Beginning pass 2
...
Así hasta 24
iteraciones
Ojo a los permisos
de ejecución
3.4 Implementación de SimRank
$ hadoop fs -cat simrank24/part* | head
10081e1 0.00001761487440064474
10081e10 0.00000278115643514088
10081e11 0.00000278115643514088
10081e2 0.00001761487440064474
10081e3 0.00001761487440064474
10081e4 0.00001761487440064474
10081e5 0.00001761487440064474
10081e7 0.00000278115643514088
10081e8 0.00000278115643514088
10081e9 0.00000278115643514088
cat: Unable to write to output stream.
Vector SimRank
final
3.4 Implementación de SimRank
● Movemos el resultado a adult_final y repetimos
el proceso, esta vez con kids
$ hadoop fs -mv simrank24 adult_final
$ hadoop fs -rm -R simrank*
$ ./simrank.sh kids_train
rm: v: No such file or directory
rm: `v': No such file or directory
Beginning pass 1
…
$ hadoop fs -mv simrank24 kid_final
Así hasta 24
iteraciones
3.4 Implementación de SimRank
● Interpretamos y comparamos los vectores
SimRank obtenidos en 2 pasos:
1) Normalizamos ambos vectores
2) Comparamos cada entrada y asignamos una
etiqueta basada en el valor más alto
3.4 Implementación de SimRank
1) Normalizando vectores
● Mapper → adult_map.py
#!/usr/bin/python
import sys
def main():
if len(sys.argv) < 3:
sys.stderr.write("Missing args: %sn" % sys.argv)
# Calculate conversion factor
num_adults = float(sys.argv[1])
num_kids = float(sys.argv[2])
factor = -num_adults / num_kids
# Apply the conversion to every record and emit it
for line in sys.stdin:
key, value = line.strip().split('t')
print "%st%.20f" % (key, float(value) * factor)
if __name__ == "__main__":
main()
3.4 Implementación de SimRank
1) Normalizando vectores
● Reducer → combine_reduce.py
3.4 Implementación de SimRank
1) Normalizando vectores
● Lanzamos los jobs
$ hadoop fs -cat adults_train | wc -l
84
$ hadoop fs -cat kids_train | wc -l
96
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -input adult_final -output adult_mod -mapper "adult_map.py 84 96" -file
adult_map.py
...
$ hadoop jar $STREAMING -D mapred.textoutputformat.separator=, -input adult_mod -input kid_final -output final -reducer
combine_reduce.py -file combine_reduce.py -inputformat org.apache.hadoop.mapred.KeyValueTextInputFormat
…
Formato separado por comas
3.4 Implementación de SimRank
1) Normalizando vectores
● Testeamos los resultados
● Los IDs de los adultos terminan con 'a'
● Los IDs de los niños terminan con 'k'
$ hadoop fs -cat final/part* | grep a, | grep -v ,0
$ hadoop fs -cat final/part* | grep k, | grep -v ,1
Haciendo grep por cada entrada de
adulto ('a') y a continuación por
aquellos que no incluyen el valor ',0'
encontramos todas las entradas de
adulto mal etiquetadas.
Lo mismo para kids
Los comandos no devuelven nada,
luego hemos obtenido el resultado
esperado
3.4 Implementación de SimRank
1) Normalizando vectores
● Re-lanzamos el job con datos combinados
$ hadoop fs -rm -R adult_final adult_mod kid_final final simrank*
$ hadoop fs -cat kid/part-* | cut -f1 | grep a | hadoop fs -put - adults_all
$ hadoop fs -cat kid/part-* | cut -f1 | grep k | hadoop fs -put - kids_all
$ ./simrank.sh adults_all
…
$ hadoop fs -mv simrank24 adult_final
$ hadoop fs -rm -R simrank*
$ ./simrank.sh kids_all
…
$ hadoop fs -mv simrank24 kid_final
$ hadoop fs -cat adults_all | wc -l
104
$ hadoop fs -cat kids_all | wc -l
120
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -input adult_final -output adult_mod -mapper "adult_map.py 104 120" -file
adult_map.py
...
$ hadoop jar $STREAMING -input adult_mod -input kid_final -output final -reducer combine_reduce.py -file combine_reduce.py
-inputformat org.apache.hadoop.mapred.KeyValueTextInputFormat
…
$ hadoop fs -getmerge final Task1Solution.csv
3.4 Implementación de SimRank
1) Normalizando vectores
Using the scoring methodology from the challenge, this solution scores an accuracy of 99.64%, mislabeling a total of 9
records.
4. Clusterizando sesiones
4. Clusterizando sesiones
● El segundo desafío es clusterizar las sesiones de usuario basadas
en propiedades (features) en los datos
●
Mediante el clústering obtenemos qué grupos de sesiones son
notablemente más similares entre ellos que hacia otras sesiones
●
Súper importante elegir bien las propiedades o features
●
El análisis de clúster nos aportará
– El número de grupos naturales existentes en los datos y las propiedades
– Qué sesiones pertenecen a cada grupo
4. Clusterizando sesiones
● El proceso a seguir para análisis de clúster es
básicamente heurístico (prueba y error)
– Iremos probando algunas propiedades y acotando
aquellas que mejores resultados arrojen
4.1 Eligiendo propiedades
● ¿Qué propiedades podemos sacar de los datos
disponibles?
$ hadoop fs -cat clean/part* | head -1
{"session": "2b5846cb-9cbf-4f92-a1e7-b5349ff08662", "hover": ["16177",
"10286", "8565", "10596", "29609", "13338"], "end": "1368189995", "played":
{"16316": "4990"}, "browsed": [], "recommendations": ["13338", "10759",
"39122", "26996", "10002", "25224", "6891", "16361", "7489", "16316",
"12023", "25803", "4286e89", "1565", "20435", "10596", "29609", "14528",
"6723", "35792e23", "25450", "10143e155", "10286", "25668", "37307"],
"actions": ["login"], "reviewed": {}, "start": "1368189205", "recommended":
["8565", "10759", "10002", "25803", "10286"], "rated": {}, "user": "10108881",
"searched": [], "popular": ["16177", "26365", "14969", "38420", "7097"], "kid":
null, "queued": ["10286", "13338"], "recent": ["18392e39"]}
cat: Unable to write to output stream.
Propiedades extraíbles:
● Actions (a feature for each,
except login and logout)
● Number of items hovered over
● Session duration (end)
● Number of items played
● Number of items browsed
● Number of items reviewed
● Number of items rated
● Number of items searched
● Number of recommendations
that were reviewed
● Kid (parental controls)
● Number of items queued
4.1 Eligiendo propiedades
● Podemos calcular otras propiedades menos directas:
– Mean play time
– Shortest play time
– Longest play time
– Total play time
– Total play time as fraction of session duration
– Longest play: less than 5 minutes, between 5 and 60 minutes, more than 60 minutes
– Shortest play: less than 5 minutes, between 5 and 60 minutes, more than 60 minutes
– Number of items played less than 5 minutes
– Number of items played more than 60 minutes
– Number of items hovered over that were played
– Number of browsed items that were played
– Number of reviewed items that were played
– Number of recommended items that were played
– Number of rated items that were played
– Number of searched items that were played
– Number of popular items that were played
– Number of queued items that were played
– Number of recent items that were played
– Number of recent items that were reviewed
– Number of recent items that were rated
4.2 Fusionando los datos
● Tenemos que parsear los datos y generar los
vectores de propiedades
– Usaremos Python de nuevo
– Vamos a volver a unir los datos de sesión
separados en el apartado anterior
4.2 Fusionando los datos
– Para volver a fusionar los datos:
● Mapper → merge_map.py
#!/usr/bin/python
import re
import sys
def main():
p = re.compile('.*"session": "([^"]+)".*')
# Read all the lines from stdin
for line in sys.stdin:
m = p.match(line)
if m:
print "*%s*t*%s*" % (m.group(1), line.strip())
else:
sys.strerr.write("Failed to find ID in line: *%s*" % line)
if __name__ == '__main__':
main()
Utilizamos una expresión regular en
lugar de parsear el JSON porque la
expresión consume menos
recursos
4.2 Fusionando los datos
– Para volver a fusionar los datos:
● Reducer → merge_reduce.py
4.2 Fusionando los datos
– Lanzamos el job
$ hadoop jar $STREAMING -mapper merge_map.py -file merge_map.py
-reducer merge_reduce.py -file merge_reduce.py -input clean -output merged
4.3 Generamos los vectores de prop
● Mapper → features_map.py
#!/usr/bin/env python
import sys
import json
def main():
# Read all lines from stdin
for line in sys.stdin:
session = json.loads(line)
fields = []
fields.append(session['session'])
fields.append('updatePassword' in session['actions'])
fields.append('updatePaymentInfo' in session['actions'])
fields.append('verifiedPassword' in session['actions'])
fields.append('reviewedQueue' in session['actions'])
fields.append(session['kid'])
played = set(session['played'].keys())
fields.append(len(played))
print ','.join(map(str, fields))
if __name__ == '__main__':
main()
4.3 Generamos los vectores de prop
● Lo lanzamos
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D
mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper
features_map.py -file features_map.py -input merged -output features0
...
$ hadoop fs -getmerge features0 features.csv
$ hadoop fs -put features.csv
4.4 Construimos el workflow de ML
de Cloudera
● Generamos el fichero de cabeceras
– headers.csv
session_id,identifier
updatePassword,categorical
updatePaymentInfo,categorical
verifiedPassword,categorical
reviewedQueue,categorical
kid,categorical
num_plays
4.4 Construimos el workflow de ML
de Cloudera
● Creamos el fichero de resumen utilizando las
cabeceras generadas
● Normalizamos las características/propiedades
– 2 opciones: unit normal o rango
– Salida en Avro porque vamos a acceder a los datos
normalizados programáticamente
$ ml summary --summary-file summary.json --header-file header.csv --format
text --input-paths features.csv
$ ml normalize --summary-file summary.json --format text --id-column 0
--transform Z --input-paths features.csv --output-path part2normalized --output-
type avro
4.5 K-means++ sketch
● Utilizando ls datos normalizados
$ ml ksketch --format avro --input-paths part2normalized --output-file
part2sketch.avro --points-per-iteration 1000 --iterations 10 --seed 1729
4.5 K-means++ sketch
● Especificamos la semilla (seed)
– De esta forma podemos comparar los resultados a
través de múltiples ejecuciones
● Sin una semilla en cada ejecución se seleccionaría un
conjunto de centroides aleatorios, produciendo
resultados que podrían ser arbitrariamente mejores o
peores que anteriores ejecuciones
4.5 K-means++ sketch
● Especificamos la semilla (seed)
– Especificamos la semilla para poder comparar los
resultados y ejecutamos el algoritmo paralelo k-
means en los sketch de datos
$ ml kmeans --input-file part2sketch.avro --centers-file part2centers.avro
--clusters 40,60,80,100,120,140,160,180,200 --best-of 3 --seed 1729 --num-
threads 1 --eval-details-file part2evaldetails.csv --eval-stats-file
part2evalstats.csv
La lista de clústers indica los
diferentes tamaños de clúster
que va a intentar el algoritmo
El parámetro best-of le
indica a Cloudera ML que
ejecute el algoritmo 3 veces
por cada tamaño del clúster
Indicamos 1 único hilo, puesto
que la MV está configurada con
un procesador únicamente
La ejecución puede tardar bastante,
mejor asignar a la MV otro procesador
y poner 2 hilos en paralelo
4.5 K-means++ sketch
● Analizamos el resultado
– Buscamos una predictive strength
de al menos 0.8 con buenos
números de estabilización
– Predective strength es una métrica
que indica cuánto de bien
describen los datos los clústers
– Las métricas de estabilidad indican
cuánto cambian los clústers y los
puntos de datos entre los datos de
entrenamiento y los de test
● Nos da 1.0, lo cual es perfecto
4.5 K-means++ sketch
● Añadimos características al mapper
#!/usr/bin/env python
import sys
import json
def main():
# Read all lines from stdin
for line in sys.stdin:
session = json.loads(line)
fields = []
fields.append(session['session'])
fields.append('updatePassword' in session['actions'])
fields.append('updatePaymentInfo' in session['actions'])
fields.append('verifiedPassword' in session['actions'])
fields.append('reviewedQueue' in session['actions'])
fields.append(session['kid'])
session_duration = (long(session['end']) - long(session['start']))
fields.append(session_duration)
played = set(session['played'].keys())
browsed = set(session['browsed'])
hovered = set(session['hover'])
queued = set(session['queued'])
recommendations = set(session['recommendations'])
rated = set(session['rated'].keys())
reviewed = set(session['reviewed'].keys())
searched = set(session['searched'])
fields.append(len(played))
fields.append(len(browsed))
fields.append(len(hovered))
fields.append(len(queued))
fields.append(len(recommendations))
fields.append(len(rated))
fields.append(len(reviewed))
fields.append(len(searched))
print ','.join(map(str, fields))
if __name__ == '__main__':
main()
4.5 K-means++ sketch
● Añadimos características a las cabeceras
session_id,identifier
updatePassword,categorical
updatePaymentInfo,categorical
verifiedPassword,categorical
reviewedQueue,categorical
kid,categorical
session_duration
num_plays
num_browsed
num_hovered
num_queued
num_recommendations
num_rated
num_reviewed
num_searched
4.5 K-means++ sketch
● Ejecutamos...
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D
stream.map.output.field.separator=, -mapper features_map.py -file features_map.py -input merged -output
features1
...
$ hadoop fs -getmerge features1 features.csv
$ hadoop fs -rm features.csv
Deleted features.csv
$ hadoop fs -put features.csv
$ ml summary --summary-file summary.json --header-file header.csv --format text --input-paths features.csv
...
$ ml normalize --summary-file summary.json --format text --id-column 0 --transform Z --input-paths
features.csv --output-path part2normalized --output-type avro
...
$ ml ksketch --format avro --input-paths part2normalized --output-file part2sketch.avro --points-per-iteration
1000 --iterations 10 --seed 1729
...
$ ml kmeans --input-file part2sketch.avro --centers-file part2centers.avro --clusters
40,60,80,100,120,140,160,180,200 --best-of 3 --seed 1729 --num-threads 1 --eval-details-file
part2evaldetails.csv --eval-stats-file part2evalstats.csv
4.5 K-means++ sketch
● Analizamos el resultado
– Tenemos una predective
strength muy baja
● Infinity es malo
– Esto indica que los clústers
no son suficientemente
diferentes
– ¡¡Prueba y error!!
● Quitamos algunas
características añadidas en el
paso anterior y volvemos a
probar
4.5 K-means++ sketch
● Comentamos características del mapper
#!/usr/bin/env python
import sys
import json
def main():
# Read all lines from stdin
for line in sys.stdin:
session = json.loads(line)
fields = []
fields.append(session['session'])
fields.append('updatePassword' in session['actions'])
fields.append('updatePaymentInfo' in session['actions'])
fields.append('verifiedPassword' in session['actions'])
fields.append('reviewedQueue' in session['actions'])
fields.append(session['kid'])
# session_duration = (long(session['end']) - long(session['start']))
# fields.append(session_duration)
played = set(session['played'].keys())
# browsed = set(session['browsed'])
# hovered = set(session['hover'])
# queued = set(session['queued'])
recommendations = set(session['recommendations'])
rated = set(session['rated'].keys())
reviewed = set(session['reviewed'].keys())
searched = set(session['searched'])
fields.append(len(played))
# fields.append(len(browsed))
# fields.append(len(hovered))
# fields.append(len(queued))
fields.append(len(recommendations))
fields.append(len(rated))
fields.append(len(reviewed))
fields.append(len(searched))
print ','.join(map(str, fields))
if __name__ == '__main__':
main()
4.5 K-means++ sketch
● Eliminamos algunas características del fichero
de cabeceras
session_id,identifier
updatePassword,categorical
updatePaymentInfo,categorical
verifiedPassword,categorical
reviewedQueue,categorical
kid,categorical
num_plays
num_recommendations
num_rated
num_reviewed
num_searched
4.5 K-means++ sketch
● Ejecutamos de nuevo...
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D
stream.map.output.field.separator=, -mapper features_map.py -file features_map.py -input merged -output
features2
...
$ hadoop fs -getmerge features2 features.csv
$ hadoop fs -rm features.csv
Deleted features.csv
$ hadoop fs -put features.csv
$ ml summary --summary-file summary.json --header-file header.csv --format text --input-paths features.csv
...
$ ml normalize --summary-file summary.json --format text --id-column 0 --transform Z --input-paths
features.csv --output-path part2normalized --output-type avro
...
$ ml ksketch --format avro --input-paths part2normalized --output-file part2sketch.avro --points-per-iteration
1000 --iterations 10 --seed 1729
...
$ ml kmeans --input-file part2sketch.avro --centers-file part2centers.avro --clusters
40,60,80,100,120,140,160,180,200 --best-of 3 --seed 1729 --num-threads 1 --eval-details-file
part2evaldetails.csv --eval-stats-file part2evalstats.csv
4.5 K-means++ sketch
● Analizamos el resultado
– Mejores resultados que
antes
– Podemos probar a volver a
añadir alguna de las
características que hemos
borrado en el paso anterior
5. Prediciendo las valoraciones de
los usuarios
(construyendo un recomendador)
5. Recomendador
● Tenemos que predecir valoraciones para un
conjunto dado de usuarios e items
● ¿Qué opciones tenemos?
– Construirnos nuestro recomendador
matemáticamente
– Taste (Mahout) para experimentar con sus
algoritmos
5. Recomendador
● Revisando los datos tenemos:
– 926 valoraciones
● De 751 usuarios sobre 757 items
– Unos 500.000 eventos que pueden considerarse como
“Play”
● El propio “Play”
● Añadir a la cola de reproducción
● Si los sumamos tenemos 2193 usuarios y 6504 items reproducidos
– Vamos a trabajar únicamente con las valoraciones explícitas
e implícitas
5.1 Preparando los datos
● Mahout solo acepta entradas en .csv
– Vamos a necesitar 2 entradas
● Valoraciones explícitas
● Valoraciones implícitas → Eventos tipo “play” para cada usuario e item
● Generamos los ficheros (3 opciones):
1) JSON loader en Pig para cargar los datos y extraer los campos
correctos
2) Definir una tabla Hive usando JSON SerDe y seleccionar los campos
deseados de la tabla
3) Tarea de streaming map-only en Hadoop
• Es la opción más sencilla por la facilidad de parsear JSON en Python sin
necesidad de definir un schema
5.1 Preparando los datos
● Mapper para datos explícitos
– Recorremos todas las valoraciones y extraemos el
userID, itemID y tripleta de valoración y las
emitimos si cumplen el criterio dado
#!/usr/bin/python
import datetime
import dateutil.parser
import json
import sys
def main():
before = sys.argv[1] == 'before'
cutoff = dateutil.parser.parse(sys.argv[2])
epoch = datetime.datetime(1970,1,1,tzinfo=cutoff.tzinfo)
# Read all lines from stdin
for line in sys.stdin:
data = json.loads(line)
start = cutoff - datetime.timedelta(seconds=long(data['start']))
if (before and start > epoch) or (not before and start <= epoch):
for item, rating in data['rated'].iteritems():
print "%s,%s,%s" % (data['user'], item, rating)
for item, dict in data['reviewed'].iteritems():
print "%s,%s,%s" % (data['user'], item, dict['rating'])
if __name__ == '__main__':
main()
Para evitar problemas en
Python que son
dependientes de la versión y
complicadas
5.1 Preparando los datos
● Mapper para datos implícitos
– Recorremos las valoraciones, reviews, películas
reproducidas, encoladas y buscadas
#!/usr/bin/python
import json
import sys
def main():
# Read all lines from stdin
for line in sys.stdin:
data = json.loads(line)
for item in data['rated'].keys() + data['reviewed'].keys() +
data['played'].keys() + data['browsed'] + data['queued']:
print "%s,%s" % (data['user'], item)
if __name__ == '__main__':
main()
5.1 Preparando los datos
● Ejecutamos ambos
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D
stream.map.output.field.separator=, -mapper 'explicit.py before "2013-05-11 00:00:00-08:00"' -file explicit.py
-input clean -output explicit_train
...
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D
stream.map.output.field.separator=, -mapper 'explicit.py after "2013-05-11 00:00:00-08:00"' -file explicit.py
-input clean -output explicit_test
...
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D
stream.map.output.field.separator=, -mapper implicit.py -file implicit.py -input clean -output implicit
$ hadoop fs -cat explicit_train/part* | head
10142325,9614,5
10760746,27597,4
10796192,30975e17,3
10905688,36598,3
10905688,15337,5
1091145,14303e57,1
11657116,9146e38,4
11749679,27017e20,4
11751432,38193e81,4
11764279,21482e41,3
$ hadoop fs -cat implicit/part* | head
10108881,16316
10108881,10286
10108881,13338
10108881,9107
10108881,39122
10142325,9614
10142325,9614
10142325,38579
10151338,34645
10151338,34645
Mahout solo permite
identificadores numéricos
→ Hay que traducirlos
5.1 Preparando los datos
● Traducimos los itemID
– ¿Cómo de grande es el problema?
– La 'e' pertenece únicamente a los itemIDs, y aparecía en aquellos
que se referían a episodios de series de TV
$ hadoop fs -cat implicit/part* | cut -d, -f1 | tr -d '1234567890' | sort | uniq
$ hadoop fs -cat implicit/part* | cut -d, -f2 | tr -d '1234567890' | sort | uniq
e
cut -d, -f1: Cortar por columna 1 en la coma
tr -d '123456789': eliminar de f1 los números
sort: para ordenar la salida
uniq: no repetir en la salida (nos saldrían
muchas 'e')
5.1 Preparando los datos
● Traducimos los itemID
– Determinemos el rango de episodios
$ hadoop fs -cat implicit/part* | cut -d, -f2 | cut -de -f1 | sort -n | head -1
1094
$ hadoop fs -cat implicit/part* | cut -d, -f2 | cut -de -f1 | sort -n | tail -1
39984
cut -d, -f2: Cortar por columna 2 en la coma
cut -d, -f1: De la nueva columna, cortar por la 'e'
sort -n: ordenar numéricamente
head -1: mostrar el primero
tail -1: mostrar el último
5.1 Preparando los datos
● Traducimos los itemID
– Determinemos número de episodios
– No parece que vayan a dar problemas
● Traducimos la 'e' por '00'
$ hadoop fs -cat implicit/part* | cut -d, -f2 | grep e | cut -de -f2 | sort -n | uniq | head
1
2
3
4
5
6
7
8
9
10
$ hadoop fs -cat implicit/part* | cut -d, -f2 | grep e | cut -de -f2 | sort -n | tail -1
217
5.1 Preparando los datos
● Traducimos los itemID
– Utilizamos 'sed'
– Ya tenemos un conjunto de entrenamiento y test
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D
mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper 'sed
"s/e/00/"' -input explicit_train -output explicit_train_clean
...
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D
mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper 'sed
"s/e/00/"' -input explicit_test -output explicit_test_clean
...
$ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D
mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper 'sed
"s/e/00/"' -input implicit -output implicit_clean
...
5.2 Establecer los promedios base
● Necesitamos establecer unas bases contra las
cuales evaluar el recomendador
● La métrica más sencilla sería calcular el
promedio global para todos nuestros datos de
test
– Se puede hacer de forma simple en Hive
$ hive
hive> create external table explicit_train (user int, item bigint, rating int) row format delimited
fields terminated by ',' location '/user/cloudera/explicit_train_clean';
OK
Time taken: 0.215 seconds
hive> create external table explicit_test (user int, item bigint, rating int) row format delimited
fields terminated by ',' location '/user/cloudera/explicit_test_clean';
OK
Time taken: 0.191 seconds
5.2 Establecer los promedios base
● Una vez tenemos las tablas, podemos utilizar
las funciones matemáticas de Hive para
calcular el promedio y el RMSE (root-mean-
square-deviation)
hive> select avg(rating) from explicit_train;
...
OK
3.597826086956522
Time taken: 6.132 seconds
hive> select sqrt(sum(pow(rating - 3.597826086956522, 2))/count(*)) from explicit_test;
...
OK
1.2733209628271343
Time taken: 4.222 seconds
5.2 Establecer los promedios base
● Comparamos el promedio con las puntuaciones
de los usuarios
– El promedio global es muy superior a los promedios
de los usuarios → datos muy dispersos
hive> create table baseline (user int, item bigint, rating float) row format delimited fields
terminated by ',';
OK
Time taken: 1.099 seconds
hive> insert into table baseline select explicit_test.user, explicit_test.item, if
(avg.avg_rating > 0, avg.avg_rating, 3.597826086956522) from (select user, avg(rating)
as avg_rating from explicit_train group by user) avg full outer join explicit_test on
explicit_test.user == avg.user;
...
OK
Time taken: 7.462 seconds
hive> select sqrt(sum(pow(e.rating - b.rating, 2))/count(*)) from baseline b join
explicit_test e on b.user == e.user and b.item == e.item;
OK
1.362417948479424
Time taken: 4.768 seconds
5.3 Recomendadores de Mahout
● Dos clases de recomendadores en Mahout:
– Basados en similaridad
● El algoritmo determina la similaridad entre usuarios e
items y estima valoraciones desconocidas basándose en
esas similitudes y valoraciones conocidas
– Factorización matricial
● ¿Cual escogemos?
– Prueba y error
● Se puede utilizar la línea de comandos, pero es
más recomendable usar el API Java de Mahout
5.3 Recomendadores de Mahout
● Trabajamos en local → Nos descargamos los
datos
$ hadoop fs -getmerge implicit_clean implicit.csv
$ hadoop fs -cat explicit_train_clean/part* explicit_test_clean/part* > explicit.csv
Con merge volvemos a juntar ambos
conjuntos de datos (train y test)
5.3 Recomendadores de Mahout
● Preparamos el proyecto Maven
● Configuramos el pom.xml →
$ mvn archetype:create
-DarchetypeGroupId=org.apache.maven.archetypes
-DgroupId=ccp.challenge1.recommend -DartifactId=recommend
Debemos tener especial cuidado en donde se quedan descargados los csv, ya
que la clase Java que los accederá hace un ../fichero.csv
final DataModel ratingDataModel = new FileDataModel(new File("../explicit.csv"));
final DataModel allDataModel = new FileDataModel(new File("../implicit.csv"));
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-core</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-math</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-math</artifactId>
<version>0.7</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-utils</artifactId>
<version>0.5</version>
</dependency>
5.3 Recomendadores de Mahout
● Compilamos y ejecutamos
$ mvn compile
…
BUILD SUCCESS
...
$ mvn exec:java
-Dexec.mainClass="ccp.challenge1.recommender.Recommend"
…
Item-item with Tanimoto: 1.2142
Item-item with log-likelihood: 1.2151
SVD with EM: 1.2284
SVD with ALS: 1.9169
SVD with implicit linear regression: NaN
Slope One: 0.7175
User-user n-nearest neighbor with Tanimoto: NaN
User-user n-nearest neighbor with log-likelihood: NaN
User-user threshold with Tanimoto: NaN
User-user threshold with log-likelihood: NaN
NaN: not a number (no
hay suficientes datos)
Mejor que el promedio
base que obtuvimos
anteriormente
Mejor resultado
5.3 Recomendadores de Mahout
● Refinamos los algoritmos
5.4 Generando ratings
● Refinamos los algoritmos
– Ahora que hemos elegido el algoritmo (Slope One)
podemos generar las valoraciones
– Recalculamos el promedio global para todo el
dataset
$ hive
hive> SELECT avg(IF(a.rating IS NULL, b.rating, a.rating)) AS avg FROM explicit_train a FULL
OUTER JOIN explicit_test b ON (a.user == b.user and a.item == b.item);
...
OK
3.683585313174946
Time taken: 7.881 seconds
5.4 Generando ratings
● Creamos el recomendador
– FinalRecommend.java
public class FinalRecommend {
private static final Float GLOBAL_AVERAGE = 3.6836f;
public static void main(String[] args) throws Exception {
DataModel explicitDataModel = new FileDataModel(new
File("../explicit.csv"));
Recommender recommender = new
SlopeOneRecommender(explicitDataModel);
PrintWriter out = new PrintWriter("../Task3Solution.csv");
File in = new File("../rateme.csv");
for (String testDatum : new FileLineIterable(in)) {
String[] tokens = testDatum.split(",");
String itemIdString = tokens[1];
long userId = Long.parseLong(tokens[0]);
long itemId = Long.parseLong(itemIdString);
float estimate;
try {
estimate = recommender.estimatePreference(userId, itemId);
} catch(NoSuchUserException e) {
estimate = GLOBAL_AVERAGE;
} catch(NoSuchItemException e) {
estimate = GLOBAL_AVERAGE;
}
if (Float.isNaN(estimate)) {
estimate = GLOBAL_AVERAGE;
}
if (itemId > 50000) {
int i = itemIdString.lastIndexOf("00");
itemIdString = itemIdString.substring(0, i) + 'e' +
itemIdString.substring(i+2);
}
out.printf("%d,%s,%.4fn", userId, itemIdString, estimate);
}
out.close();
}
}
5.4 Generando ratings
● Ejecutamos
$ cd <projectdir>
$ mvn compile
…
BUILD SUCCESS
...
$ mvn exec:java -Dexec.mainClass="ccp.challenge1.recommender.FinalRecommend"
...
5.4 Generando ratings
● Analizamos los resultados
$ wc -l ../Task3Solution.csv
1000 ../Task3Solution.csv
$ head ../Task3Solution.csv
91059173,6155,3.6836
34025317,39419,3.6836
15309904,11248,3.6836
60633959,18963,3.6836
31917316,36814,3.6836
53736706,26212,3.6836
33961447,22606,3.6836
93036062,39815,3.6836
93165942,32989,3.6836
91328973,26806,3.6836
Los 10 primeros resultados
utilizan el promedio global
calculado antes en Hive
5.4 Generando ratings
● Para saber cuantos
● Sobre el 85% de los ratings generados utilizan
el promedio global calculado
– Dado el esparcimiento de los datos no es un mal
resultado
$ grep 3.6836 ../Task3Solution.csv | wc -l
852

Más contenido relacionado

Similar a Big Data: análisis de weblogs

Cómo construir una plataforma poderosa de datos en un ambiente con recursos r...
Cómo construir una plataforma poderosa de datos en un ambiente con recursos r...Cómo construir una plataforma poderosa de datos en un ambiente con recursos r...
Cómo construir una plataforma poderosa de datos en un ambiente con recursos r...Software Guru
 
Escalabilidad - Apache y MySQL
Escalabilidad - Apache y MySQLEscalabilidad - Apache y MySQL
Escalabilidad - Apache y MySQLLorena Fernández
 
Documentación de pruebas del software
Documentación de pruebas del softwareDocumentación de pruebas del software
Documentación de pruebas del softwareYenny Aldana
 
Lightning connect dug_26_nov2015
Lightning connect dug_26_nov2015Lightning connect dug_26_nov2015
Lightning connect dug_26_nov2015Alba Azcona Rivas
 
Cloud para tu juego en una tarde
Cloud para tu juego en una tardeCloud para tu juego en una tarde
Cloud para tu juego en una tardeIgnacio Segura
 
Sysmana 2017 monitorización de logs con el stack elk
Sysmana 2017   monitorización de logs con el stack elkSysmana 2017   monitorización de logs con el stack elk
Sysmana 2017 monitorización de logs con el stack elkJosé Ignacio Álvarez Ruiz
 
Scraping avanzado o Cómo hacer de internet tu base de datos #seoplus2018
Scraping avanzado o Cómo hacer de internet tu base de datos #seoplus2018Scraping avanzado o Cómo hacer de internet tu base de datos #seoplus2018
Scraping avanzado o Cómo hacer de internet tu base de datos #seoplus2018nacho mascort
 
Web matrix y j querymobile
Web matrix y j querymobileWeb matrix y j querymobile
Web matrix y j querymobileGonzalo C.
 
base de datos #1
base de datos #1base de datos #1
base de datos #1sergio804
 
Documentación de pruebas del software
Documentación de pruebas del softwareDocumentación de pruebas del software
Documentación de pruebas del softwareLina Vega
 
Thinking of CPU and Memory (2.0)
Thinking of CPU and Memory (2.0)Thinking of CPU and Memory (2.0)
Thinking of CPU and Memory (2.0)Oriol Jiménez
 
Taller de introducción al desarrollo web con Django
Taller de introducción al desarrollo web con DjangoTaller de introducción al desarrollo web con Django
Taller de introducción al desarrollo web con DjangoJuan Rodríguez
 
Recoleccion de Informacion con Google (OWASP Argentina)
Recoleccion de Informacion con Google (OWASP Argentina)Recoleccion de Informacion con Google (OWASP Argentina)
Recoleccion de Informacion con Google (OWASP Argentina)Maximiliano Soler
 
Introducción a Migrate API en Drupal 8
Introducción a Migrate API en Drupal 8Introducción a Migrate API en Drupal 8
Introducción a Migrate API en Drupal 8Leandro Luvigne
 
Google Analytics para Desarrolladores
Google Analytics para DesarrolladoresGoogle Analytics para Desarrolladores
Google Analytics para DesarrolladoresBruno Barbieri
 

Similar a Big Data: análisis de weblogs (20)

Cómo construir una plataforma poderosa de datos en un ambiente con recursos r...
Cómo construir una plataforma poderosa de datos en un ambiente con recursos r...Cómo construir una plataforma poderosa de datos en un ambiente con recursos r...
Cómo construir una plataforma poderosa de datos en un ambiente con recursos r...
 
Introducción a DJango
Introducción a DJangoIntroducción a DJango
Introducción a DJango
 
Software de Búsqueda
Software de BúsquedaSoftware de Búsqueda
Software de Búsqueda
 
Log Management
Log ManagementLog Management
Log Management
 
Escalabilidad - Apache y MySQL
Escalabilidad - Apache y MySQLEscalabilidad - Apache y MySQL
Escalabilidad - Apache y MySQL
 
Documentación de pruebas del software
Documentación de pruebas del softwareDocumentación de pruebas del software
Documentación de pruebas del software
 
Lightning connect dug_26_nov2015
Lightning connect dug_26_nov2015Lightning connect dug_26_nov2015
Lightning connect dug_26_nov2015
 
Cloud para tu juego en una tarde
Cloud para tu juego en una tardeCloud para tu juego en una tarde
Cloud para tu juego en una tarde
 
Sysmana 2017 monitorización de logs con el stack elk
Sysmana 2017   monitorización de logs con el stack elkSysmana 2017   monitorización de logs con el stack elk
Sysmana 2017 monitorización de logs con el stack elk
 
Scraping avanzado o Cómo hacer de internet tu base de datos #seoplus2018
Scraping avanzado o Cómo hacer de internet tu base de datos #seoplus2018Scraping avanzado o Cómo hacer de internet tu base de datos #seoplus2018
Scraping avanzado o Cómo hacer de internet tu base de datos #seoplus2018
 
Web matrix y j querymobile
Web matrix y j querymobileWeb matrix y j querymobile
Web matrix y j querymobile
 
base de datos #1
base de datos #1base de datos #1
base de datos #1
 
Documentación de pruebas del software
Documentación de pruebas del softwareDocumentación de pruebas del software
Documentación de pruebas del software
 
Thinking of CPU and Memory (2.0)
Thinking of CPU and Memory (2.0)Thinking of CPU and Memory (2.0)
Thinking of CPU and Memory (2.0)
 
Taller de introducción al desarrollo web con Django
Taller de introducción al desarrollo web con DjangoTaller de introducción al desarrollo web con Django
Taller de introducción al desarrollo web con Django
 
Recoleccion de Informacion con Google (OWASP Argentina)
Recoleccion de Informacion con Google (OWASP Argentina)Recoleccion de Informacion con Google (OWASP Argentina)
Recoleccion de Informacion con Google (OWASP Argentina)
 
Introducción a Python
Introducción a PythonIntroducción a Python
Introducción a Python
 
Introducción a Migrate API en Drupal 8
Introducción a Migrate API en Drupal 8Introducción a Migrate API en Drupal 8
Introducción a Migrate API en Drupal 8
 
Paralela10
Paralela10Paralela10
Paralela10
 
Google Analytics para Desarrolladores
Google Analytics para DesarrolladoresGoogle Analytics para Desarrolladores
Google Analytics para Desarrolladores
 

Más de Eduardo Castillejo Gil

Service orchestration and metal as a service with juju and maas
Service orchestration and metal as a service with juju and maasService orchestration and metal as a service with juju and maas
Service orchestration and metal as a service with juju and maasEduardo Castillejo Gil
 
Dynamic User Interface Adaptation Engine Through Semantic Modelling and Reaso...
Dynamic User Interface Adaptation Engine Through Semantic Modelling and Reaso...Dynamic User Interface Adaptation Engine Through Semantic Modelling and Reaso...
Dynamic User Interface Adaptation Engine Through Semantic Modelling and Reaso...Eduardo Castillejo Gil
 
Past, Present and Research Challenge in Adaptive User Interfaces
Past, Present and Research Challenge in Adaptive User InterfacesPast, Present and Research Challenge in Adaptive User Interfaces
Past, Present and Research Challenge in Adaptive User InterfacesEduardo Castillejo Gil
 
Adaptive and Plastic User Interfaces: A review of the State of the Art.
Adaptive and Plastic User Interfaces: A review of the State of the Art.Adaptive and Plastic User Interfaces: A review of the State of the Art.
Adaptive and Plastic User Interfaces: A review of the State of the Art.Eduardo Castillejo Gil
 
An Aspect Based Resource Recommendation System for Smart Hotels
An Aspect Based Resource Recommendation System for Smart HotelsAn Aspect Based Resource Recommendation System for Smart Hotels
An Aspect Based Resource Recommendation System for Smart HotelsEduardo Castillejo Gil
 
Alleviating cold-user start problem with users' social network data in recomm...
Alleviating cold-user start problem with users' social network data in recomm...Alleviating cold-user start problem with users' social network data in recomm...
Alleviating cold-user start problem with users' social network data in recomm...Eduardo Castillejo Gil
 
Distributed Semantic Middleware for Social Robotic Services
Distributed Semantic Middleware for Social Robotic ServicesDistributed Semantic Middleware for Social Robotic Services
Distributed Semantic Middleware for Social Robotic ServicesEduardo Castillejo Gil
 
Easing the Mobility of Disabled People in Supermarket Using a Distributed Sol...
Easing the Mobility of Disabled People in Supermarket Using a Distributed Sol...Easing the Mobility of Disabled People in Supermarket Using a Distributed Sol...
Easing the Mobility of Disabled People in Supermarket Using a Distributed Sol...Eduardo Castillejo Gil
 
Final Degree Project: Traffic Infraction Supervisor (SMIT)
Final Degree Project: Traffic Infraction Supervisor (SMIT)Final Degree Project: Traffic Infraction Supervisor (SMIT)
Final Degree Project: Traffic Infraction Supervisor (SMIT)Eduardo Castillejo Gil
 

Más de Eduardo Castillejo Gil (10)

Service orchestration and metal as a service with juju and maas
Service orchestration and metal as a service with juju and maasService orchestration and metal as a service with juju and maas
Service orchestration and metal as a service with juju and maas
 
Análisis de sentimientos con NLTK
Análisis de sentimientos con NLTKAnálisis de sentimientos con NLTK
Análisis de sentimientos con NLTK
 
Dynamic User Interface Adaptation Engine Through Semantic Modelling and Reaso...
Dynamic User Interface Adaptation Engine Through Semantic Modelling and Reaso...Dynamic User Interface Adaptation Engine Through Semantic Modelling and Reaso...
Dynamic User Interface Adaptation Engine Through Semantic Modelling and Reaso...
 
Past, Present and Research Challenge in Adaptive User Interfaces
Past, Present and Research Challenge in Adaptive User InterfacesPast, Present and Research Challenge in Adaptive User Interfaces
Past, Present and Research Challenge in Adaptive User Interfaces
 
Adaptive and Plastic User Interfaces: A review of the State of the Art.
Adaptive and Plastic User Interfaces: A review of the State of the Art.Adaptive and Plastic User Interfaces: A review of the State of the Art.
Adaptive and Plastic User Interfaces: A review of the State of the Art.
 
An Aspect Based Resource Recommendation System for Smart Hotels
An Aspect Based Resource Recommendation System for Smart HotelsAn Aspect Based Resource Recommendation System for Smart Hotels
An Aspect Based Resource Recommendation System for Smart Hotels
 
Alleviating cold-user start problem with users' social network data in recomm...
Alleviating cold-user start problem with users' social network data in recomm...Alleviating cold-user start problem with users' social network data in recomm...
Alleviating cold-user start problem with users' social network data in recomm...
 
Distributed Semantic Middleware for Social Robotic Services
Distributed Semantic Middleware for Social Robotic ServicesDistributed Semantic Middleware for Social Robotic Services
Distributed Semantic Middleware for Social Robotic Services
 
Easing the Mobility of Disabled People in Supermarket Using a Distributed Sol...
Easing the Mobility of Disabled People in Supermarket Using a Distributed Sol...Easing the Mobility of Disabled People in Supermarket Using a Distributed Sol...
Easing the Mobility of Disabled People in Supermarket Using a Distributed Sol...
 
Final Degree Project: Traffic Infraction Supervisor (SMIT)
Final Degree Project: Traffic Infraction Supervisor (SMIT)Final Degree Project: Traffic Infraction Supervisor (SMIT)
Final Degree Project: Traffic Infraction Supervisor (SMIT)
 

Último

Sesión 02 TIPOS DE VALORIZACIONES CURSO Cersa
Sesión 02 TIPOS DE VALORIZACIONES CURSO CersaSesión 02 TIPOS DE VALORIZACIONES CURSO Cersa
Sesión 02 TIPOS DE VALORIZACIONES CURSO CersaXimenaFallaLecca1
 
Calavera calculo de estructuras de cimentacion.pdf
Calavera calculo de estructuras de cimentacion.pdfCalavera calculo de estructuras de cimentacion.pdf
Calavera calculo de estructuras de cimentacion.pdfyoseka196
 
CICLO DE DEMING que se encarga en como mejorar una empresa
CICLO DE DEMING que se encarga en como mejorar una empresaCICLO DE DEMING que se encarga en como mejorar una empresa
CICLO DE DEMING que se encarga en como mejorar una empresaSHERELYNSAMANTHAPALO1
 
SOLICITUD-PARA-LOS-EGRESADOS-UNEFA-2022.
SOLICITUD-PARA-LOS-EGRESADOS-UNEFA-2022.SOLICITUD-PARA-LOS-EGRESADOS-UNEFA-2022.
SOLICITUD-PARA-LOS-EGRESADOS-UNEFA-2022.ariannytrading
 
Unidad 3 Administracion de inventarios.pptx
Unidad 3 Administracion de inventarios.pptxUnidad 3 Administracion de inventarios.pptx
Unidad 3 Administracion de inventarios.pptxEverardoRuiz8
 
Seleccion de Fusibles en media tension fusibles
Seleccion de Fusibles en media tension fusiblesSeleccion de Fusibles en media tension fusibles
Seleccion de Fusibles en media tension fusiblesSaulSantiago25
 
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptxGARCIARAMIREZCESAR
 
Presentación Proyecto Trabajo Creativa Profesional Azul.pdf
Presentación Proyecto Trabajo Creativa Profesional Azul.pdfPresentación Proyecto Trabajo Creativa Profesional Azul.pdf
Presentación Proyecto Trabajo Creativa Profesional Azul.pdfMirthaFernandez12
 
Comite Operativo Ciberseguridad 012020.pptx
Comite Operativo Ciberseguridad 012020.pptxComite Operativo Ciberseguridad 012020.pptx
Comite Operativo Ciberseguridad 012020.pptxClaudiaPerez86192
 
Manual_Identificación_Geoformas_140627.pdf
Manual_Identificación_Geoformas_140627.pdfManual_Identificación_Geoformas_140627.pdf
Manual_Identificación_Geoformas_140627.pdfedsonzav8
 
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdfTAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdfAntonioGonzalezIzqui
 
Tiempos Predeterminados MOST para Estudio del Trabajo II
Tiempos Predeterminados MOST para Estudio del Trabajo IITiempos Predeterminados MOST para Estudio del Trabajo II
Tiempos Predeterminados MOST para Estudio del Trabajo IILauraFernandaValdovi
 
Residente de obra y sus funciones que realiza .pdf
Residente de obra y sus funciones que realiza  .pdfResidente de obra y sus funciones que realiza  .pdf
Residente de obra y sus funciones que realiza .pdfevin1703e
 
Diapositiva de Topografía Nivelación simple y compuesta
Diapositiva de Topografía Nivelación simple y compuestaDiapositiva de Topografía Nivelación simple y compuesta
Diapositiva de Topografía Nivelación simple y compuestajeffsalazarpuente
 
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESAIPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESAJAMESDIAZ55
 
Curso intensivo de soldadura electrónica en pdf
Curso intensivo de soldadura electrónica  en pdfCurso intensivo de soldadura electrónica  en pdf
Curso intensivo de soldadura electrónica en pdfFernandaGarca788912
 
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIPSEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIPJosLuisFrancoCaldern
 
Proyecto de iluminación "guia" para proyectos de ingeniería eléctrica
Proyecto de iluminación "guia" para proyectos de ingeniería eléctricaProyecto de iluminación "guia" para proyectos de ingeniería eléctrica
Proyecto de iluminación "guia" para proyectos de ingeniería eléctricaXjoseantonio01jossed
 
clases de dinamica ejercicios preuniversitarios.pdf
clases de dinamica ejercicios preuniversitarios.pdfclases de dinamica ejercicios preuniversitarios.pdf
clases de dinamica ejercicios preuniversitarios.pdfDanielaVelasquez553560
 
TEXTURA Y DETERMINACION DE ROCAS SEDIMENTARIAS
TEXTURA Y DETERMINACION DE ROCAS SEDIMENTARIASTEXTURA Y DETERMINACION DE ROCAS SEDIMENTARIAS
TEXTURA Y DETERMINACION DE ROCAS SEDIMENTARIASfranzEmersonMAMANIOC
 

Último (20)

Sesión 02 TIPOS DE VALORIZACIONES CURSO Cersa
Sesión 02 TIPOS DE VALORIZACIONES CURSO CersaSesión 02 TIPOS DE VALORIZACIONES CURSO Cersa
Sesión 02 TIPOS DE VALORIZACIONES CURSO Cersa
 
Calavera calculo de estructuras de cimentacion.pdf
Calavera calculo de estructuras de cimentacion.pdfCalavera calculo de estructuras de cimentacion.pdf
Calavera calculo de estructuras de cimentacion.pdf
 
CICLO DE DEMING que se encarga en como mejorar una empresa
CICLO DE DEMING que se encarga en como mejorar una empresaCICLO DE DEMING que se encarga en como mejorar una empresa
CICLO DE DEMING que se encarga en como mejorar una empresa
 
SOLICITUD-PARA-LOS-EGRESADOS-UNEFA-2022.
SOLICITUD-PARA-LOS-EGRESADOS-UNEFA-2022.SOLICITUD-PARA-LOS-EGRESADOS-UNEFA-2022.
SOLICITUD-PARA-LOS-EGRESADOS-UNEFA-2022.
 
Unidad 3 Administracion de inventarios.pptx
Unidad 3 Administracion de inventarios.pptxUnidad 3 Administracion de inventarios.pptx
Unidad 3 Administracion de inventarios.pptx
 
Seleccion de Fusibles en media tension fusibles
Seleccion de Fusibles en media tension fusiblesSeleccion de Fusibles en media tension fusibles
Seleccion de Fusibles en media tension fusibles
 
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
 
Presentación Proyecto Trabajo Creativa Profesional Azul.pdf
Presentación Proyecto Trabajo Creativa Profesional Azul.pdfPresentación Proyecto Trabajo Creativa Profesional Azul.pdf
Presentación Proyecto Trabajo Creativa Profesional Azul.pdf
 
Comite Operativo Ciberseguridad 012020.pptx
Comite Operativo Ciberseguridad 012020.pptxComite Operativo Ciberseguridad 012020.pptx
Comite Operativo Ciberseguridad 012020.pptx
 
Manual_Identificación_Geoformas_140627.pdf
Manual_Identificación_Geoformas_140627.pdfManual_Identificación_Geoformas_140627.pdf
Manual_Identificación_Geoformas_140627.pdf
 
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdfTAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
 
Tiempos Predeterminados MOST para Estudio del Trabajo II
Tiempos Predeterminados MOST para Estudio del Trabajo IITiempos Predeterminados MOST para Estudio del Trabajo II
Tiempos Predeterminados MOST para Estudio del Trabajo II
 
Residente de obra y sus funciones que realiza .pdf
Residente de obra y sus funciones que realiza  .pdfResidente de obra y sus funciones que realiza  .pdf
Residente de obra y sus funciones que realiza .pdf
 
Diapositiva de Topografía Nivelación simple y compuesta
Diapositiva de Topografía Nivelación simple y compuestaDiapositiva de Topografía Nivelación simple y compuesta
Diapositiva de Topografía Nivelación simple y compuesta
 
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESAIPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
 
Curso intensivo de soldadura electrónica en pdf
Curso intensivo de soldadura electrónica  en pdfCurso intensivo de soldadura electrónica  en pdf
Curso intensivo de soldadura electrónica en pdf
 
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIPSEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
 
Proyecto de iluminación "guia" para proyectos de ingeniería eléctrica
Proyecto de iluminación "guia" para proyectos de ingeniería eléctricaProyecto de iluminación "guia" para proyectos de ingeniería eléctrica
Proyecto de iluminación "guia" para proyectos de ingeniería eléctrica
 
clases de dinamica ejercicios preuniversitarios.pdf
clases de dinamica ejercicios preuniversitarios.pdfclases de dinamica ejercicios preuniversitarios.pdf
clases de dinamica ejercicios preuniversitarios.pdf
 
TEXTURA Y DETERMINACION DE ROCAS SEDIMENTARIAS
TEXTURA Y DETERMINACION DE ROCAS SEDIMENTARIASTEXTURA Y DETERMINACION DE ROCAS SEDIMENTARIAS
TEXTURA Y DETERMINACION DE ROCAS SEDIMENTARIAS
 

Big Data: análisis de weblogs

  • 1. Caso Real Big Data Análisis de weblogs de empresa de contenidos multimedia online
  • 2. Problemática ● Empresa que pretende mejorar su servicio de películas online mediante un motor de recomendaciones personalizadas al usuario, teniendo en cuenta su historial de descargas, eventos, etc. ● La única información con la que contamos son los logs del último año en formato JSON {"created_at": "2013-05-08T08:00:00Z", "payload": {"item_id": "11086", "marker": 3540}, "session_id": "b549de69-a0dc-4b8a-8ee1-01f1a1f5a66e", "type": "Play", "user": 81729334, "user_agent": "Mozilla/5.0 (iPad; CPU OS 5_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9A405"}
  • 3. Requisitos básicos ● El equipo desea: 1) Conocer cuales son los contenidos consumidos en mayor medida por usuarios jóvenes 2) Identificar patrones de conducta de los usuarios en sus sesiones, con el objetivo de mejorar la experiencia/usabilidad de la página web 3) Aplicar un motor de recomendaciones a su web que ayude a los usuarios a encontrar el contenido que buscan, con el objetivo de que visiten más el portal
  • 4. ¿Cómo abordamos el problema? ● Como expertos en Big Data, dividimos el problema en: Clasificación binaria Clústering Análisis predictivo Se clasifican los usuarios en dos grupos (adulto, niño) para ofrecer contenidos acordes al grupo al que pertenecen Agrupación de usuarios según su comportamiento Recomendador de películas basado en la idoneidad de un item concreto para un usuario específico
  • 5. ¿Cómo funciona el servicio online? ● Debemos entender cual es el funcionamiento natural del servicio ofertado por la empresa – El usuario paga una suscripción para poder acceder a los contenidos online – Una vez logueado en el sistema, se le presenta al usuario una serie de recomendaciones y contenidos populares – El usuario puede reproducir el contenido online de su cola, buscar nuevos contenidos, y reproducirlos directamente o añadirlos a la cola de reproducción – La página soporta puntuaciones y reviews – El usuario puede realizar otras operaciones como pagos y gestión de contraseñas
  • 6. 1. Exploración de los datos
  • 7. Explorando los datos ● Partimos de los weblogs ● Además, se nos dice que: – Los logs contienen eventos de control parental que pueden ser utilizados para identificar si algunas cuentas son utilizadas por adultos o niños – El campo “marker” indica la posición del reproductor en el contenido – Las puntuaciones van de 1 a 5 (5 es el máximo) – Identificadores de contenido que contienen una 'e' son series de television ('e' de “episode”) ● El número que le sigue indica el número de episodio
  • 8. Explorando los datos – Cuando el contenido alcanza la marca “end” se registra un evento “stop” ● Si el usuario abandona la página o cambia de contenido puede que no se registre el evento – La mayor parte de los usuarios residen en EEUU {"auth": "1208d4c:279737f7", "createdAt": "2013-05-12T00:00:11-08:00", "payload": {"itemId"": "3702e4", "marker": 780}, "refId": "7586e549", "sessionID": "d4a244cb- d502-4c94-a80d-3d26ca54a449", "type": "Play", "user": 18910540, "userAgent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.2; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2)"}
  • 9. Explorando los datos ● A parte del “Play” que hemos visto en el ejemplo, ¿qué otros eventos existen? ● Usamos Hadoop streaming para poder pasarle un script que filtre los resultados grep: filtra los resultados de acuerdo al patrón indicado E: activa las expresiones regulares o: hace que 'grep' saque únicamente (output) la porción del input que cumple el patrón La expresión regular está dividida en 3 partes por el elemento pipe | ""$1": [^,]+" $1 hace referencia al argumento 1 que le pasemos (en este caso será “type” para comprobar qué eventos existen) cut -d: -f2- cortar delimitando (-d) por ':' el campo 2 tr -d '" ' borra (-d) el caracter “ #!/bin/bash grep -Eo ""$1": [^,]+" | cut -d: -f2- | tr -d '" '
  • 10. Explorando los datos ● Lanzamos el script con Hadoop Streaming -D: evitamos que grep devuelva 1 si no encuentra nada -stream.non.zero.exit.is.failure=false: Por defecto es true -input: datos de entrada (directorio o ficheros) para el mapper -ouptut: directorio de salida para el reducer -mapper/-reducer: qué ejecutar como map/reduce -file: indica a Hadoop que se asegure de que el fichero es accesible desde todos los nodos que van a ejcutar la tarea uniq -c es un reducer que agrega los campos y nos devuelve la cantidad de repeticiones $ hadoop jar $STREAMING -D stream.non.zero.exit.is.failure=false -input data/heckle/ -input data/jeckle/ -output types -mapper "grep_field.sh type" -file grep_field.sh -reducer "uniq -c" Filtrado por campo type
  • 11. Explorando los datos ● Visualizamos la salida – 19 eventos Antes dijimos que el fichero de salida se iba a llamar type. Así es como HDFS gestiona los resultados
  • 12. Explorando los datos ● Visualizamos un ejemplo de weblog de cada evento – Saldrán 19 elementos JSON completos, cada uno con un “type” diferente $ hadoop fs -cat types/part* | awk '{ print $2 }' | xargs -n 1 grep -Rhm 1 data -e -cat y awk para extraer los type únicos Xargs y grep para encontrar una ocurrencia de una línea que contengan cada tipo único
  • 13. Explorando los datos ● Comprobamos la estructura de los datos de los que disponemos – ¿Comparten la misma estructura? ● Necesitamos un MapReduce que pueda manejar datos JSON ● summary_map.py #!/usr/bin/python import json import sys # Read all lines from stdin for line in sys.stdin: data = json.loads(line) for field in data.keys(): print field Parseamos el JSON Mostramos cada campo
  • 14. Explorando los datos ● Ejecutamos el script Problema: hay datos con campos que no coinciden
  • 15. Explorando los datos ● Algunos JSON están mal formados y tienen varias dobles comillas consecutivas – Arreglamos el script #!/usr/bin/python import json import sys for line in sys.stdin: try: data = json.loads(line.replace('""', '"')) for field in data.keys(): print field except ValueError: # Log the error so we can see it sys.stderr.write("%sn" % line) exit(1)
  • 16. Explorando los datos ● Resultado Problemas: - Campos iguales con diferente nombre - Campos que no aparecen siempre
  • 17. Explorando los datos ● Volvemos a editar el script para evitar confusiones – Simplemente normalizamos los campos “problemáticos” #!/usr/bin/python import json import sys for line in sys.stdin: try: data = json.loads(line.replace('""', '"')) for field in data.keys(): if field == 'type': print "%s" % (data[field]) else: # Normalize the file name real = field if real == 'user_agent': real = 'userAgent' elif real == 'session_id': real = 'sessionID' elif real == 'created_at' or real == 'craetedAt': real = 'createdAt' # Emit the normalized field print "%s:%s" % (data['type'], real) # Emit all subfields, if there are any if type(data[field]) is dict: for subfield in data[field]: print "%s:%s:%s" % (data['type'], real, subfield) except ValueError: sys.stderr.write("%sn" % line) exit(1)
  • 18. Explorando los datos ● Volvemos a ejecutar el script
  • 19. Explorando los datos ● Comprobamos... Más problemas: - Subcampos con diferente nombre
  • 20. Explorando los datos ● Volvemos a editar el script ... for line in sys.stdin: try: ... elif real == 'created_at' or real == 'craetedAt': real = 'createdAt' # Emit all subfields, if there are any if type(data[field]) is dict: print "%s:%s" % (data['type'], real) # Normalize and print the subfields for subfield in data[field]: subreal = subfield if subreal == 'item_id': subreal = 'itemId' print "%s:%s:%st%s" % (data['type'], real, subreal, data[field][subfield]) else: # Emit the normalized field print "%s:%st%s" % (data['type'], real, data[field]) except ValueError: sys.stderr.write("%sn" % line) exit(1) Este cambio aborda dos problemas. 1. Normaliza los subcampos 2. Añade el valor del campo y del subcampo a la salida
  • 21. Explorando los datos ● Tenemos que hacer un resumen de los datos → REDUCE – Resumir los valores de campos y subcampos – Identificar cada valor de campo (numérico, fecha…) – Valores categóricos (pocas posibilidades para un campo) – Identificadores sin patrón aparente
  • 22. Explorando los datos ● Ver summary_reduce.py
  • 23. Explorando los datos ● Resumen de los datos – Identificadores de usuario entre 1000000 y 1000000 – No todos los Item ids son numéricos (de lo contrario no apaecerían como identificadores) – La media para los eventos de puntuaciones (Rating) es de 3.5, y para las reviews (WriteReview) de 4 – Los timestamps no están todos en la misma zona. Algunos están en UTC otros en UTC-8. Puede que haya otros. Tener en cuenta a la hora de limpiar los datos – Account tiene 3 posibles sub-acciones: updatePassword, updatePaymentInfo, parentalControl – Account:playload → no hay control parental que deshabilite opciones (extraño) – Play:playload → Faltan algunos valores de ytemID y marker (en ambos casos, aparecen 543129)
  • 24. Explorando los datos ● Número de usuarios diferentes y sesiones – 2195 usuarios – 5308 sesiones $ hadoop jar $STREAMING -D stream.non.zero.exit.is.failure=false -input data/heckle/ -input data/jeckle/ -output users -mapper "grep_field.sh user" -file grep_field.sh -reducer 'bash -c "uniq | wc -l"' ... $ hadoop fs -cat users/part* 2195 $ hadoop jar $STREAMING -D stream.non.zero.exit.is.failure=false -input data/heckle/ -input data/jeckle/ -output sessions -mapper 'grep_field.sh session(ID|_id)' -file grep_field.sh -reducer 'bash -c "uniq | wc -l"' … $ hadoop fs -cat sessions/part* 5308
  • 25. 2. Limpieza de datos
  • 26. Limpiando los datos ● Tenemos que solucionar los problemas que hemos ido identificando en la fase de preparación ● Vamos a reducir el volumen de los datos para hacerlos más manejables ● La idea es tener un único fichero con un único registro agregado por reducer – Mapper
  • 27. Limpiando los datos ● Mapper → clean_map.py ● Reducer → clean_reduce.py (con modificación de playload por estar vacío) Limpiamos el directorio (se supone que hemos lanzado antes un reduce con error) Volvemos a lanzar nuestro MapReduce personalizado ¡Éxito! Ahora tenemos un único fichero limpio y agregado con cada registro conteniendo sesiones completas
  • 28. Limpiando los datos ● Visualizamos el resultado ● ¿Qué hemos conseguido? – Logs reducidos a 4 MB
  • 30. 3.1 Extrayendo contenido de los items reproducidos ● Necesitamos extraer el contenido reproducido, puntuado o revisado (review) por cada usuario – Mapper que nos permita agregar por usuario pero también diferenciar por fecha ● Sacaremos una clave compuesta que contendrá el id del usuario, la fecha de inicio de sesión, y la fecha de fin – Reducer ● Las claves que recibe el Reducer están agregadas por usuario y agrupadas por fecha ● Tratamiento: – Si vemos una etiqueta nunca vista → adoptamos la nueva – Si vemos una etiqueta repetida → nada cambia – Si vemos una etiqueta conflictiva → lo tratamos como un usuario diferente – Si no vemos nunca una etiqueta de un usuario → usuario sin etiqueta – Para usuarios etiquetados como adultos → añadimos una 'a' – Para usuarios etiquetados como niños → añadimos una 'k'
  • 31. 3.1 Extrayendo contenido de los items reproducidos ● Mapper – kid_map.py #!/usr/bin/python import json import sys def main(): # Read all lines from stdin for line in sys.stdin: data = json.loads(line) # Collect all items touched items = set() items.update(data['played'].keys()) items.update(data['rated'].keys()) items.update(data['reviewed'].keys()) # Generate a comma-separated list if items: itemstr = ','.join(items) else: itemstr = ',' # Emit a compound key and compound value print "%s,%010d,%010dt%s,%s" % (data['user'], long(data['start']), long(data['end']), data['kid'], itemstr) if __name__ == '__main__': main()
  • 32. 3.1 Extrayendo contenido de los items reproducidos ● Reducer – Ver kid_reduce.py
  • 33. 3.1 Extrayendo contenido de los items reproducidos ● Ejecutamos...
  • 34. 3.2 Preparando el algoritmo SimRank ● En nuestro caso tenemos: – 2000 usuarios que han visto 0 o más contenidos – 8500 items que han sido vistos por 0 o más usuarios ● Grafo bipartido ● Solución basada en grafos – Las etiquetas conocidas para los usuarios se propagarán por los items que han visualizado, y de ahí a otros usuarios que hayan visualizado el mismo contenido ● Lo mismo para comentarios y puntuaciones – Progagación influenciada → SimRank
  • 35. 3.2 Preparando el algoritmo SimRank ● SimRank necesita una matriz de adyacencia y una lista de nodos
  • 36. 3.3 Construyendo la matriz ● Mapper → item_map.py #!/usr/bin/python import sys def main(): # Read all lines from stdin for line in sys.stdin: key, value = line.strip().split('t') items = value.split(',') # Emit every item in the set paired with the user ID for item in items: print "%st%s" % (item, key) if __name__ == '__main__': main()
  • 37. 3.3 Construyendo la matriz ● Reducer → item_reduce.py #!/usr/bin/python import sys def main(): last = None # Read all lines from stdin for line in sys.stdin: item, user = line.strip().split('t') if item != last: if last != None: # Emit the previous key print "%st%s" % (last, ','.join(users)) last = item users = set() users.add(user) # Emit the last key print "%st%s" % (last, ','.join(users)) if __name__ == '__main__': main()
  • 38. 3.3 Construyendo la matriz ● Lanzamos el job – Tenemos una matriz de adyacencia que implementa SimRank
  • 39. 3.4 Implementación de SimRank ● Mapper → simrank_map.py – Lo único que tiene que hacer es leer el vector SimRank y calcular el producto de la matriz con la matriz de adyacencia ● Primero lee del vector SimRank de HDFS y lo guarda en un diccionario obteniendo las columnas de la matriz de adyacencia como registros de entrada ● Para cada entrada distinta de cero en una columna, se multiplica por la correspondiente entrada en el vector SimRank y se emite el resultado con la etiqueta de fila como clave
  • 40. 3.4 Implementación de SimRank ● Mapper → simrank_map.py #!/usr/bin/python import sys def main(): if len(sys.argv) < 2: sys.stderr.write("Missing args: %sn" % ":".join(sys.argv)) sys.exit(1) v = {} # Read in the vector with open(sys.argv[1]) as f: for line in f: (key, value) = line.strip().split("t") v[key] = float(value) # Now read the matrix from the mapper and do the math for line in sys.stdin: col, value = line.strip().split("t") rows = value.split(',') for row in rows: try: # Add the product to the sum print "%st%.20f" % (row, v[col] / float(len(rows))) except KeyError: # KeyError equates to a zero, which we don't need to output. pass if __name__ == '__main__': main()
  • 41. 3.4 Implementación de SimRank ● Reducer → simrank_reduce.py – Se suman los valores intermedios de cada fila, se suma la “teleport contribution” y se emite el resultado final como el valor de la fila en un nuevo vector SimRank ● El “teleport contribution” se obtiene extrayendo del training set
  • 42. 3.4 Implementación de SimRank ● Testeando la convergencia → simrank_diff.py ● Lanzador → simrank.sh $ chmod a+x simrank.sh $ ./simrank.sh adults_train rm: v: No such file or directory rm: `v': No such file or directory Beginning pass 1 /tmp/hadoop-training/hadoop-unjar2754804698549065604/] ... Beginning pass 2 ... Así hasta 24 iteraciones Ojo a los permisos de ejecución
  • 43. 3.4 Implementación de SimRank $ hadoop fs -cat simrank24/part* | head 10081e1 0.00001761487440064474 10081e10 0.00000278115643514088 10081e11 0.00000278115643514088 10081e2 0.00001761487440064474 10081e3 0.00001761487440064474 10081e4 0.00001761487440064474 10081e5 0.00001761487440064474 10081e7 0.00000278115643514088 10081e8 0.00000278115643514088 10081e9 0.00000278115643514088 cat: Unable to write to output stream. Vector SimRank final
  • 44. 3.4 Implementación de SimRank ● Movemos el resultado a adult_final y repetimos el proceso, esta vez con kids $ hadoop fs -mv simrank24 adult_final $ hadoop fs -rm -R simrank* $ ./simrank.sh kids_train rm: v: No such file or directory rm: `v': No such file or directory Beginning pass 1 … $ hadoop fs -mv simrank24 kid_final Así hasta 24 iteraciones
  • 45. 3.4 Implementación de SimRank ● Interpretamos y comparamos los vectores SimRank obtenidos en 2 pasos: 1) Normalizamos ambos vectores 2) Comparamos cada entrada y asignamos una etiqueta basada en el valor más alto
  • 46. 3.4 Implementación de SimRank 1) Normalizando vectores ● Mapper → adult_map.py #!/usr/bin/python import sys def main(): if len(sys.argv) < 3: sys.stderr.write("Missing args: %sn" % sys.argv) # Calculate conversion factor num_adults = float(sys.argv[1]) num_kids = float(sys.argv[2]) factor = -num_adults / num_kids # Apply the conversion to every record and emit it for line in sys.stdin: key, value = line.strip().split('t') print "%st%.20f" % (key, float(value) * factor) if __name__ == "__main__": main()
  • 47. 3.4 Implementación de SimRank 1) Normalizando vectores ● Reducer → combine_reduce.py
  • 48. 3.4 Implementación de SimRank 1) Normalizando vectores ● Lanzamos los jobs $ hadoop fs -cat adults_train | wc -l 84 $ hadoop fs -cat kids_train | wc -l 96 $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -input adult_final -output adult_mod -mapper "adult_map.py 84 96" -file adult_map.py ... $ hadoop jar $STREAMING -D mapred.textoutputformat.separator=, -input adult_mod -input kid_final -output final -reducer combine_reduce.py -file combine_reduce.py -inputformat org.apache.hadoop.mapred.KeyValueTextInputFormat … Formato separado por comas
  • 49. 3.4 Implementación de SimRank 1) Normalizando vectores ● Testeamos los resultados ● Los IDs de los adultos terminan con 'a' ● Los IDs de los niños terminan con 'k' $ hadoop fs -cat final/part* | grep a, | grep -v ,0 $ hadoop fs -cat final/part* | grep k, | grep -v ,1 Haciendo grep por cada entrada de adulto ('a') y a continuación por aquellos que no incluyen el valor ',0' encontramos todas las entradas de adulto mal etiquetadas. Lo mismo para kids Los comandos no devuelven nada, luego hemos obtenido el resultado esperado
  • 50. 3.4 Implementación de SimRank 1) Normalizando vectores ● Re-lanzamos el job con datos combinados $ hadoop fs -rm -R adult_final adult_mod kid_final final simrank* $ hadoop fs -cat kid/part-* | cut -f1 | grep a | hadoop fs -put - adults_all $ hadoop fs -cat kid/part-* | cut -f1 | grep k | hadoop fs -put - kids_all $ ./simrank.sh adults_all … $ hadoop fs -mv simrank24 adult_final $ hadoop fs -rm -R simrank* $ ./simrank.sh kids_all … $ hadoop fs -mv simrank24 kid_final $ hadoop fs -cat adults_all | wc -l 104 $ hadoop fs -cat kids_all | wc -l 120 $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -input adult_final -output adult_mod -mapper "adult_map.py 104 120" -file adult_map.py ... $ hadoop jar $STREAMING -input adult_mod -input kid_final -output final -reducer combine_reduce.py -file combine_reduce.py -inputformat org.apache.hadoop.mapred.KeyValueTextInputFormat … $ hadoop fs -getmerge final Task1Solution.csv
  • 51. 3.4 Implementación de SimRank 1) Normalizando vectores Using the scoring methodology from the challenge, this solution scores an accuracy of 99.64%, mislabeling a total of 9 records.
  • 53. 4. Clusterizando sesiones ● El segundo desafío es clusterizar las sesiones de usuario basadas en propiedades (features) en los datos ● Mediante el clústering obtenemos qué grupos de sesiones son notablemente más similares entre ellos que hacia otras sesiones ● Súper importante elegir bien las propiedades o features ● El análisis de clúster nos aportará – El número de grupos naturales existentes en los datos y las propiedades – Qué sesiones pertenecen a cada grupo
  • 54. 4. Clusterizando sesiones ● El proceso a seguir para análisis de clúster es básicamente heurístico (prueba y error) – Iremos probando algunas propiedades y acotando aquellas que mejores resultados arrojen
  • 55. 4.1 Eligiendo propiedades ● ¿Qué propiedades podemos sacar de los datos disponibles? $ hadoop fs -cat clean/part* | head -1 {"session": "2b5846cb-9cbf-4f92-a1e7-b5349ff08662", "hover": ["16177", "10286", "8565", "10596", "29609", "13338"], "end": "1368189995", "played": {"16316": "4990"}, "browsed": [], "recommendations": ["13338", "10759", "39122", "26996", "10002", "25224", "6891", "16361", "7489", "16316", "12023", "25803", "4286e89", "1565", "20435", "10596", "29609", "14528", "6723", "35792e23", "25450", "10143e155", "10286", "25668", "37307"], "actions": ["login"], "reviewed": {}, "start": "1368189205", "recommended": ["8565", "10759", "10002", "25803", "10286"], "rated": {}, "user": "10108881", "searched": [], "popular": ["16177", "26365", "14969", "38420", "7097"], "kid": null, "queued": ["10286", "13338"], "recent": ["18392e39"]} cat: Unable to write to output stream. Propiedades extraíbles: ● Actions (a feature for each, except login and logout) ● Number of items hovered over ● Session duration (end) ● Number of items played ● Number of items browsed ● Number of items reviewed ● Number of items rated ● Number of items searched ● Number of recommendations that were reviewed ● Kid (parental controls) ● Number of items queued
  • 56. 4.1 Eligiendo propiedades ● Podemos calcular otras propiedades menos directas: – Mean play time – Shortest play time – Longest play time – Total play time – Total play time as fraction of session duration – Longest play: less than 5 minutes, between 5 and 60 minutes, more than 60 minutes – Shortest play: less than 5 minutes, between 5 and 60 minutes, more than 60 minutes – Number of items played less than 5 minutes – Number of items played more than 60 minutes – Number of items hovered over that were played – Number of browsed items that were played – Number of reviewed items that were played – Number of recommended items that were played – Number of rated items that were played – Number of searched items that were played – Number of popular items that were played – Number of queued items that were played – Number of recent items that were played – Number of recent items that were reviewed – Number of recent items that were rated
  • 57. 4.2 Fusionando los datos ● Tenemos que parsear los datos y generar los vectores de propiedades – Usaremos Python de nuevo – Vamos a volver a unir los datos de sesión separados en el apartado anterior
  • 58. 4.2 Fusionando los datos – Para volver a fusionar los datos: ● Mapper → merge_map.py #!/usr/bin/python import re import sys def main(): p = re.compile('.*"session": "([^"]+)".*') # Read all the lines from stdin for line in sys.stdin: m = p.match(line) if m: print "*%s*t*%s*" % (m.group(1), line.strip()) else: sys.strerr.write("Failed to find ID in line: *%s*" % line) if __name__ == '__main__': main() Utilizamos una expresión regular en lugar de parsear el JSON porque la expresión consume menos recursos
  • 59. 4.2 Fusionando los datos – Para volver a fusionar los datos: ● Reducer → merge_reduce.py
  • 60. 4.2 Fusionando los datos – Lanzamos el job $ hadoop jar $STREAMING -mapper merge_map.py -file merge_map.py -reducer merge_reduce.py -file merge_reduce.py -input clean -output merged
  • 61. 4.3 Generamos los vectores de prop ● Mapper → features_map.py #!/usr/bin/env python import sys import json def main(): # Read all lines from stdin for line in sys.stdin: session = json.loads(line) fields = [] fields.append(session['session']) fields.append('updatePassword' in session['actions']) fields.append('updatePaymentInfo' in session['actions']) fields.append('verifiedPassword' in session['actions']) fields.append('reviewedQueue' in session['actions']) fields.append(session['kid']) played = set(session['played'].keys()) fields.append(len(played)) print ','.join(map(str, fields)) if __name__ == '__main__': main()
  • 62. 4.3 Generamos los vectores de prop ● Lo lanzamos $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper features_map.py -file features_map.py -input merged -output features0 ... $ hadoop fs -getmerge features0 features.csv $ hadoop fs -put features.csv
  • 63. 4.4 Construimos el workflow de ML de Cloudera ● Generamos el fichero de cabeceras – headers.csv session_id,identifier updatePassword,categorical updatePaymentInfo,categorical verifiedPassword,categorical reviewedQueue,categorical kid,categorical num_plays
  • 64. 4.4 Construimos el workflow de ML de Cloudera ● Creamos el fichero de resumen utilizando las cabeceras generadas ● Normalizamos las características/propiedades – 2 opciones: unit normal o rango – Salida en Avro porque vamos a acceder a los datos normalizados programáticamente $ ml summary --summary-file summary.json --header-file header.csv --format text --input-paths features.csv $ ml normalize --summary-file summary.json --format text --id-column 0 --transform Z --input-paths features.csv --output-path part2normalized --output- type avro
  • 65. 4.5 K-means++ sketch ● Utilizando ls datos normalizados $ ml ksketch --format avro --input-paths part2normalized --output-file part2sketch.avro --points-per-iteration 1000 --iterations 10 --seed 1729
  • 66. 4.5 K-means++ sketch ● Especificamos la semilla (seed) – De esta forma podemos comparar los resultados a través de múltiples ejecuciones ● Sin una semilla en cada ejecución se seleccionaría un conjunto de centroides aleatorios, produciendo resultados que podrían ser arbitrariamente mejores o peores que anteriores ejecuciones
  • 67. 4.5 K-means++ sketch ● Especificamos la semilla (seed) – Especificamos la semilla para poder comparar los resultados y ejecutamos el algoritmo paralelo k- means en los sketch de datos $ ml kmeans --input-file part2sketch.avro --centers-file part2centers.avro --clusters 40,60,80,100,120,140,160,180,200 --best-of 3 --seed 1729 --num- threads 1 --eval-details-file part2evaldetails.csv --eval-stats-file part2evalstats.csv La lista de clústers indica los diferentes tamaños de clúster que va a intentar el algoritmo El parámetro best-of le indica a Cloudera ML que ejecute el algoritmo 3 veces por cada tamaño del clúster Indicamos 1 único hilo, puesto que la MV está configurada con un procesador únicamente La ejecución puede tardar bastante, mejor asignar a la MV otro procesador y poner 2 hilos en paralelo
  • 68. 4.5 K-means++ sketch ● Analizamos el resultado – Buscamos una predictive strength de al menos 0.8 con buenos números de estabilización – Predective strength es una métrica que indica cuánto de bien describen los datos los clústers – Las métricas de estabilidad indican cuánto cambian los clústers y los puntos de datos entre los datos de entrenamiento y los de test ● Nos da 1.0, lo cual es perfecto
  • 69. 4.5 K-means++ sketch ● Añadimos características al mapper #!/usr/bin/env python import sys import json def main(): # Read all lines from stdin for line in sys.stdin: session = json.loads(line) fields = [] fields.append(session['session']) fields.append('updatePassword' in session['actions']) fields.append('updatePaymentInfo' in session['actions']) fields.append('verifiedPassword' in session['actions']) fields.append('reviewedQueue' in session['actions']) fields.append(session['kid']) session_duration = (long(session['end']) - long(session['start'])) fields.append(session_duration) played = set(session['played'].keys()) browsed = set(session['browsed']) hovered = set(session['hover']) queued = set(session['queued']) recommendations = set(session['recommendations']) rated = set(session['rated'].keys()) reviewed = set(session['reviewed'].keys()) searched = set(session['searched']) fields.append(len(played)) fields.append(len(browsed)) fields.append(len(hovered)) fields.append(len(queued)) fields.append(len(recommendations)) fields.append(len(rated)) fields.append(len(reviewed)) fields.append(len(searched)) print ','.join(map(str, fields)) if __name__ == '__main__': main()
  • 70. 4.5 K-means++ sketch ● Añadimos características a las cabeceras session_id,identifier updatePassword,categorical updatePaymentInfo,categorical verifiedPassword,categorical reviewedQueue,categorical kid,categorical session_duration num_plays num_browsed num_hovered num_queued num_recommendations num_rated num_reviewed num_searched
  • 71. 4.5 K-means++ sketch ● Ejecutamos... $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper features_map.py -file features_map.py -input merged -output features1 ... $ hadoop fs -getmerge features1 features.csv $ hadoop fs -rm features.csv Deleted features.csv $ hadoop fs -put features.csv $ ml summary --summary-file summary.json --header-file header.csv --format text --input-paths features.csv ... $ ml normalize --summary-file summary.json --format text --id-column 0 --transform Z --input-paths features.csv --output-path part2normalized --output-type avro ... $ ml ksketch --format avro --input-paths part2normalized --output-file part2sketch.avro --points-per-iteration 1000 --iterations 10 --seed 1729 ... $ ml kmeans --input-file part2sketch.avro --centers-file part2centers.avro --clusters 40,60,80,100,120,140,160,180,200 --best-of 3 --seed 1729 --num-threads 1 --eval-details-file part2evaldetails.csv --eval-stats-file part2evalstats.csv
  • 72. 4.5 K-means++ sketch ● Analizamos el resultado – Tenemos una predective strength muy baja ● Infinity es malo – Esto indica que los clústers no son suficientemente diferentes – ¡¡Prueba y error!! ● Quitamos algunas características añadidas en el paso anterior y volvemos a probar
  • 73. 4.5 K-means++ sketch ● Comentamos características del mapper #!/usr/bin/env python import sys import json def main(): # Read all lines from stdin for line in sys.stdin: session = json.loads(line) fields = [] fields.append(session['session']) fields.append('updatePassword' in session['actions']) fields.append('updatePaymentInfo' in session['actions']) fields.append('verifiedPassword' in session['actions']) fields.append('reviewedQueue' in session['actions']) fields.append(session['kid']) # session_duration = (long(session['end']) - long(session['start'])) # fields.append(session_duration) played = set(session['played'].keys()) # browsed = set(session['browsed']) # hovered = set(session['hover']) # queued = set(session['queued']) recommendations = set(session['recommendations']) rated = set(session['rated'].keys()) reviewed = set(session['reviewed'].keys()) searched = set(session['searched']) fields.append(len(played)) # fields.append(len(browsed)) # fields.append(len(hovered)) # fields.append(len(queued)) fields.append(len(recommendations)) fields.append(len(rated)) fields.append(len(reviewed)) fields.append(len(searched)) print ','.join(map(str, fields)) if __name__ == '__main__': main()
  • 74. 4.5 K-means++ sketch ● Eliminamos algunas características del fichero de cabeceras session_id,identifier updatePassword,categorical updatePaymentInfo,categorical verifiedPassword,categorical reviewedQueue,categorical kid,categorical num_plays num_recommendations num_rated num_reviewed num_searched
  • 75. 4.5 K-means++ sketch ● Ejecutamos de nuevo... $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper features_map.py -file features_map.py -input merged -output features2 ... $ hadoop fs -getmerge features2 features.csv $ hadoop fs -rm features.csv Deleted features.csv $ hadoop fs -put features.csv $ ml summary --summary-file summary.json --header-file header.csv --format text --input-paths features.csv ... $ ml normalize --summary-file summary.json --format text --id-column 0 --transform Z --input-paths features.csv --output-path part2normalized --output-type avro ... $ ml ksketch --format avro --input-paths part2normalized --output-file part2sketch.avro --points-per-iteration 1000 --iterations 10 --seed 1729 ... $ ml kmeans --input-file part2sketch.avro --centers-file part2centers.avro --clusters 40,60,80,100,120,140,160,180,200 --best-of 3 --seed 1729 --num-threads 1 --eval-details-file part2evaldetails.csv --eval-stats-file part2evalstats.csv
  • 76. 4.5 K-means++ sketch ● Analizamos el resultado – Mejores resultados que antes – Podemos probar a volver a añadir alguna de las características que hemos borrado en el paso anterior
  • 77. 5. Prediciendo las valoraciones de los usuarios (construyendo un recomendador)
  • 78. 5. Recomendador ● Tenemos que predecir valoraciones para un conjunto dado de usuarios e items ● ¿Qué opciones tenemos? – Construirnos nuestro recomendador matemáticamente – Taste (Mahout) para experimentar con sus algoritmos
  • 79. 5. Recomendador ● Revisando los datos tenemos: – 926 valoraciones ● De 751 usuarios sobre 757 items – Unos 500.000 eventos que pueden considerarse como “Play” ● El propio “Play” ● Añadir a la cola de reproducción ● Si los sumamos tenemos 2193 usuarios y 6504 items reproducidos – Vamos a trabajar únicamente con las valoraciones explícitas e implícitas
  • 80. 5.1 Preparando los datos ● Mahout solo acepta entradas en .csv – Vamos a necesitar 2 entradas ● Valoraciones explícitas ● Valoraciones implícitas → Eventos tipo “play” para cada usuario e item ● Generamos los ficheros (3 opciones): 1) JSON loader en Pig para cargar los datos y extraer los campos correctos 2) Definir una tabla Hive usando JSON SerDe y seleccionar los campos deseados de la tabla 3) Tarea de streaming map-only en Hadoop • Es la opción más sencilla por la facilidad de parsear JSON en Python sin necesidad de definir un schema
  • 81. 5.1 Preparando los datos ● Mapper para datos explícitos – Recorremos todas las valoraciones y extraemos el userID, itemID y tripleta de valoración y las emitimos si cumplen el criterio dado #!/usr/bin/python import datetime import dateutil.parser import json import sys def main(): before = sys.argv[1] == 'before' cutoff = dateutil.parser.parse(sys.argv[2]) epoch = datetime.datetime(1970,1,1,tzinfo=cutoff.tzinfo) # Read all lines from stdin for line in sys.stdin: data = json.loads(line) start = cutoff - datetime.timedelta(seconds=long(data['start'])) if (before and start > epoch) or (not before and start <= epoch): for item, rating in data['rated'].iteritems(): print "%s,%s,%s" % (data['user'], item, rating) for item, dict in data['reviewed'].iteritems(): print "%s,%s,%s" % (data['user'], item, dict['rating']) if __name__ == '__main__': main() Para evitar problemas en Python que son dependientes de la versión y complicadas
  • 82. 5.1 Preparando los datos ● Mapper para datos implícitos – Recorremos las valoraciones, reviews, películas reproducidas, encoladas y buscadas #!/usr/bin/python import json import sys def main(): # Read all lines from stdin for line in sys.stdin: data = json.loads(line) for item in data['rated'].keys() + data['reviewed'].keys() + data['played'].keys() + data['browsed'] + data['queued']: print "%s,%s" % (data['user'], item) if __name__ == '__main__': main()
  • 83. 5.1 Preparando los datos ● Ejecutamos ambos $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper 'explicit.py before "2013-05-11 00:00:00-08:00"' -file explicit.py -input clean -output explicit_train ... $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper 'explicit.py after "2013-05-11 00:00:00-08:00"' -file explicit.py -input clean -output explicit_test ... $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper implicit.py -file implicit.py -input clean -output implicit $ hadoop fs -cat explicit_train/part* | head 10142325,9614,5 10760746,27597,4 10796192,30975e17,3 10905688,36598,3 10905688,15337,5 1091145,14303e57,1 11657116,9146e38,4 11749679,27017e20,4 11751432,38193e81,4 11764279,21482e41,3 $ hadoop fs -cat implicit/part* | head 10108881,16316 10108881,10286 10108881,13338 10108881,9107 10108881,39122 10142325,9614 10142325,9614 10142325,38579 10151338,34645 10151338,34645 Mahout solo permite identificadores numéricos → Hay que traducirlos
  • 84. 5.1 Preparando los datos ● Traducimos los itemID – ¿Cómo de grande es el problema? – La 'e' pertenece únicamente a los itemIDs, y aparecía en aquellos que se referían a episodios de series de TV $ hadoop fs -cat implicit/part* | cut -d, -f1 | tr -d '1234567890' | sort | uniq $ hadoop fs -cat implicit/part* | cut -d, -f2 | tr -d '1234567890' | sort | uniq e cut -d, -f1: Cortar por columna 1 en la coma tr -d '123456789': eliminar de f1 los números sort: para ordenar la salida uniq: no repetir en la salida (nos saldrían muchas 'e')
  • 85. 5.1 Preparando los datos ● Traducimos los itemID – Determinemos el rango de episodios $ hadoop fs -cat implicit/part* | cut -d, -f2 | cut -de -f1 | sort -n | head -1 1094 $ hadoop fs -cat implicit/part* | cut -d, -f2 | cut -de -f1 | sort -n | tail -1 39984 cut -d, -f2: Cortar por columna 2 en la coma cut -d, -f1: De la nueva columna, cortar por la 'e' sort -n: ordenar numéricamente head -1: mostrar el primero tail -1: mostrar el último
  • 86. 5.1 Preparando los datos ● Traducimos los itemID – Determinemos número de episodios – No parece que vayan a dar problemas ● Traducimos la 'e' por '00' $ hadoop fs -cat implicit/part* | cut -d, -f2 | grep e | cut -de -f2 | sort -n | uniq | head 1 2 3 4 5 6 7 8 9 10 $ hadoop fs -cat implicit/part* | cut -d, -f2 | grep e | cut -de -f2 | sort -n | tail -1 217
  • 87. 5.1 Preparando los datos ● Traducimos los itemID – Utilizamos 'sed' – Ya tenemos un conjunto de entrenamiento y test $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper 'sed "s/e/00/"' -input explicit_train -output explicit_train_clean ... $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper 'sed "s/e/00/"' -input explicit_test -output explicit_test_clean ... $ hadoop jar $STREAMING -D mapred.reduce.tasks=0 -D mapred.textoutputformat.separator=, -D stream.map.output.field.separator=, -mapper 'sed "s/e/00/"' -input implicit -output implicit_clean ...
  • 88. 5.2 Establecer los promedios base ● Necesitamos establecer unas bases contra las cuales evaluar el recomendador ● La métrica más sencilla sería calcular el promedio global para todos nuestros datos de test – Se puede hacer de forma simple en Hive $ hive hive> create external table explicit_train (user int, item bigint, rating int) row format delimited fields terminated by ',' location '/user/cloudera/explicit_train_clean'; OK Time taken: 0.215 seconds hive> create external table explicit_test (user int, item bigint, rating int) row format delimited fields terminated by ',' location '/user/cloudera/explicit_test_clean'; OK Time taken: 0.191 seconds
  • 89. 5.2 Establecer los promedios base ● Una vez tenemos las tablas, podemos utilizar las funciones matemáticas de Hive para calcular el promedio y el RMSE (root-mean- square-deviation) hive> select avg(rating) from explicit_train; ... OK 3.597826086956522 Time taken: 6.132 seconds hive> select sqrt(sum(pow(rating - 3.597826086956522, 2))/count(*)) from explicit_test; ... OK 1.2733209628271343 Time taken: 4.222 seconds
  • 90. 5.2 Establecer los promedios base ● Comparamos el promedio con las puntuaciones de los usuarios – El promedio global es muy superior a los promedios de los usuarios → datos muy dispersos hive> create table baseline (user int, item bigint, rating float) row format delimited fields terminated by ','; OK Time taken: 1.099 seconds hive> insert into table baseline select explicit_test.user, explicit_test.item, if (avg.avg_rating > 0, avg.avg_rating, 3.597826086956522) from (select user, avg(rating) as avg_rating from explicit_train group by user) avg full outer join explicit_test on explicit_test.user == avg.user; ... OK Time taken: 7.462 seconds hive> select sqrt(sum(pow(e.rating - b.rating, 2))/count(*)) from baseline b join explicit_test e on b.user == e.user and b.item == e.item; OK 1.362417948479424 Time taken: 4.768 seconds
  • 91. 5.3 Recomendadores de Mahout ● Dos clases de recomendadores en Mahout: – Basados en similaridad ● El algoritmo determina la similaridad entre usuarios e items y estima valoraciones desconocidas basándose en esas similitudes y valoraciones conocidas – Factorización matricial ● ¿Cual escogemos? – Prueba y error ● Se puede utilizar la línea de comandos, pero es más recomendable usar el API Java de Mahout
  • 92. 5.3 Recomendadores de Mahout ● Trabajamos en local → Nos descargamos los datos $ hadoop fs -getmerge implicit_clean implicit.csv $ hadoop fs -cat explicit_train_clean/part* explicit_test_clean/part* > explicit.csv Con merge volvemos a juntar ambos conjuntos de datos (train y test)
  • 93. 5.3 Recomendadores de Mahout ● Preparamos el proyecto Maven ● Configuramos el pom.xml → $ mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=ccp.challenge1.recommend -DartifactId=recommend Debemos tener especial cuidado en donde se quedan descargados los csv, ya que la clase Java que los accederá hace un ../fichero.csv final DataModel ratingDataModel = new FileDataModel(new File("../explicit.csv")); final DataModel allDataModel = new FileDataModel(new File("../implicit.csv")); <dependency> <groupId>org.apache.mahout</groupId> <artifactId>mahout-core</artifactId> <version>0.7</version> </dependency> <dependency> <groupId>org.apache.mahout</groupId> <artifactId>mahout-math</artifactId> <version>0.7</version> </dependency> <dependency> <groupId>org.apache.mahout</groupId> <artifactId>mahout-math</artifactId> <version>0.7</version> <type>test-jar</type> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.mahout</groupId> <artifactId>mahout-utils</artifactId> <version>0.5</version> </dependency>
  • 94. 5.3 Recomendadores de Mahout ● Compilamos y ejecutamos $ mvn compile … BUILD SUCCESS ... $ mvn exec:java -Dexec.mainClass="ccp.challenge1.recommender.Recommend" … Item-item with Tanimoto: 1.2142 Item-item with log-likelihood: 1.2151 SVD with EM: 1.2284 SVD with ALS: 1.9169 SVD with implicit linear regression: NaN Slope One: 0.7175 User-user n-nearest neighbor with Tanimoto: NaN User-user n-nearest neighbor with log-likelihood: NaN User-user threshold with Tanimoto: NaN User-user threshold with log-likelihood: NaN NaN: not a number (no hay suficientes datos) Mejor que el promedio base que obtuvimos anteriormente Mejor resultado
  • 95. 5.3 Recomendadores de Mahout ● Refinamos los algoritmos
  • 96. 5.4 Generando ratings ● Refinamos los algoritmos – Ahora que hemos elegido el algoritmo (Slope One) podemos generar las valoraciones – Recalculamos el promedio global para todo el dataset $ hive hive> SELECT avg(IF(a.rating IS NULL, b.rating, a.rating)) AS avg FROM explicit_train a FULL OUTER JOIN explicit_test b ON (a.user == b.user and a.item == b.item); ... OK 3.683585313174946 Time taken: 7.881 seconds
  • 97. 5.4 Generando ratings ● Creamos el recomendador – FinalRecommend.java public class FinalRecommend { private static final Float GLOBAL_AVERAGE = 3.6836f; public static void main(String[] args) throws Exception { DataModel explicitDataModel = new FileDataModel(new File("../explicit.csv")); Recommender recommender = new SlopeOneRecommender(explicitDataModel); PrintWriter out = new PrintWriter("../Task3Solution.csv"); File in = new File("../rateme.csv"); for (String testDatum : new FileLineIterable(in)) { String[] tokens = testDatum.split(","); String itemIdString = tokens[1]; long userId = Long.parseLong(tokens[0]); long itemId = Long.parseLong(itemIdString); float estimate; try { estimate = recommender.estimatePreference(userId, itemId); } catch(NoSuchUserException e) { estimate = GLOBAL_AVERAGE; } catch(NoSuchItemException e) { estimate = GLOBAL_AVERAGE; } if (Float.isNaN(estimate)) { estimate = GLOBAL_AVERAGE; } if (itemId > 50000) { int i = itemIdString.lastIndexOf("00"); itemIdString = itemIdString.substring(0, i) + 'e' + itemIdString.substring(i+2); } out.printf("%d,%s,%.4fn", userId, itemIdString, estimate); } out.close(); } }
  • 98. 5.4 Generando ratings ● Ejecutamos $ cd <projectdir> $ mvn compile … BUILD SUCCESS ... $ mvn exec:java -Dexec.mainClass="ccp.challenge1.recommender.FinalRecommend" ...
  • 99. 5.4 Generando ratings ● Analizamos los resultados $ wc -l ../Task3Solution.csv 1000 ../Task3Solution.csv $ head ../Task3Solution.csv 91059173,6155,3.6836 34025317,39419,3.6836 15309904,11248,3.6836 60633959,18963,3.6836 31917316,36814,3.6836 53736706,26212,3.6836 33961447,22606,3.6836 93036062,39815,3.6836 93165942,32989,3.6836 91328973,26806,3.6836 Los 10 primeros resultados utilizan el promedio global calculado antes en Hive
  • 100. 5.4 Generando ratings ● Para saber cuantos ● Sobre el 85% de los ratings generados utilizan el promedio global calculado – Dado el esparcimiento de los datos no es un mal resultado $ grep 3.6836 ../Task3Solution.csv | wc -l 852