Map Reduce es un componente del ecosistema Hadoop. Consiste en un modelo de programación que permite procesar en paralelo grandes volúmenes de datos. Ya hemos dedicado varios artículos a Map Reduce. Llegó la hora de poner en práctica a este modelo de programación.

Así como “Hola Mundo” es el clásico primer ejemplo que implementamos al introducirnos en un nuevo lenguaje de programación, “Cuenta Palabras” es el ejemplo por excelencia para introducir Map Reduce.

Hoy vamos a utilizar Map Reduce para contar las palabras que aparecen en el clásico “Don Quijote de la Mancha”. ¿Cómo lo hacemos? Como siempre, trabajaremos en nuestra máquina virtual Cloudera. A continuación la invocación a Map Reduce:

hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming.jar \
   -input /user/toadworld/input \
   -output /user/toadworld/output \
   -mapper /home/cloudera/toadworld/mapper_cuentapalabras.py \
   -reducer /home/cloudera/toadworld/reducer_cuentapalabras.py

Vamos a tomarnos un momento para analizar los argumentos: 

  • Input: En este directorio HDFS está alojado el texto del “Quijote”.
  • Output: En este directorio Map Reduce dejará los resultados.
  • Mapper: la función de map que vamos a construir en el lenguaje Python.
  • Reducer: la función de reducción que vamos a construir en el lenguaje Python.

Avancemos con los pasos necesarios para proveer los argumentos que necesita Map Reduce.

1)      Llevar los textos de “El Quijote” a un directorio en HDFS
2)      Desarrollar un programa Python con la función “Map”
3)      Desarrollar un programa Python con la función “Reduce”


Llevar los textos de “El Quijote” a un directorio en HDFS.

Para simplificar el ejemplo, tomamos unas pocas líneas del clásico de Cervantes.

[cloudera@quickstart toadworld]$ pwd
/home/cloudera/toadworld

[cloudera@quickstart toadworld]$ ls -l
total 12

-rw-rw-r-- 1 cloudera cloudera 390 Jun 26 08:50 quijote1.txt
-rw-rw-r-- 1 cloudera cloudera 420 Jun 26 08:51 quijote2.txt
-rw-rw-r-- 1 cloudera cloudera 431 Jun 26 08:52 quijote3.txt

[cloudera@quickstart toadworld]$ cat quijote1.txt

En un lugar de la Mancha, de cuyo nombre no quiero acordarme, no ha mucho tiempo que vivía un hidalgo de los de lanza en astillero, adarga antigua, rocín flaco y galgo corredor. Una olla de algo más vaca que carnero, salpicón las más noches, duelos y quebrantos los sábados, lantejas los viernes, algún palomino de añadidura los domingos, consumían las tres partes de su hacienda.

[cloudera@quickstart toadworld]$ cat quijote2.txt

El resto della concluían sayo de velarte, calzas de velludo para las fiestas, con sus pantuflos de lo mesmo, y los días de entresemana se honraba con su vellorí de lo más fino12. Tenía en su casa una ama que pasaba de los cuarenta y una sobrina que no llegaba a los veinte, y un mozo de campo y plaza que así ensillaba el rocín como tomaba la podadera. Frisaba la edad de nuestro hidalgo con los cincuenta años.

[cloudera@quickstart toadworld]$ cat quijote3.txt

Era de complexión recia, seco de carnes, enjuto de rostro, gran madrugador y amigo de la caza. Quieren decir que tenía el sobrenombre de «Quijada», o «Quesada», que en esto hay alguna diferencia en los autores que deste caso escriben, aunque por conjeturas verisímilesII se deja entender que se llamaba «Quijana». Pero esto importa poco a nuestro cuento: basta que en la narración dél no se salga un punto de la verdad.

[cloudera@quickstart toadworld]$

Llevamos los archivos al sistema de archivos distribuido HDFS:

[cloudera@quickstart toadworld]$ hdfs dfs -mkdir /user/toadworld
[cloudera@quickstart toadworld]$ hdfs dfs -mkdir /user/toadworld/input
[cloudera@quickstart toadworld]$ hdfs dfs -put /home/cloudera/toadworld/quijote*.txt /user/toadworld/input

[cloudera@quickstart toadworld]$ hdfs dfs -ls /user/toadworld/input

Found 3 items

-rw-r--r-- 1 cloudera supergroup 390 2016-06-26 09:02 /user/toadworld/input/quijote1.txt
-rw-r--r-- 1 cloudera supergroup 420 2016-06-26 09:02 /user/toadworld/input/quijote2.txt
-rw-r--r-- 1 cloudera supergroup 431 2016-06-26 09:02 /user/toadworld/input/quijote3.txt

[cloudera@quickstart toadworld]$


Desarrollar un programa Python con la función “Map”

A continuación veamos el código del “mapper” codificado en lenguaje Python:

[cloudera@quickstart toadworld]$ cat mapper_cuentapalabras.py

#!/usr/bin/env python
import sys
for linea in sys.stdin:
    linea = linea.strip()
    claves = linea.split()
    for clave in claves:
        valor = 1
        print('{0}\t{1}'.format(clave, valor) )

 

Desarrollar un programa Python con la función “Reduce”

A continuación veamos el código del “reducer” codificado en lenguaje Python:

[cloudera@quickstart toadworld]$ cat reducer_cuentapalabras.py

#!/usr/bin/env python
import sys
ultima_clave = None
total_palabra = 0
for linea in sys.stdin:
    linea = linea.strip()
    clave, valor = linea.split("\t", 1)
    valor = int(valor)
    if ultima_clave == clave:
        total_palabra += valor
    else:
        if ultima_clave:
            print( "{0}\t{1}".format(ultima_clave, total_palabra) )
        total_palabra = valor
        ultima_clave = clave
if ultima_clave == clave:
    print( "{0}\t{1}".format(ultima_clave, total_palabra))

Una vez que hemos construido los programas en Python, asignamos privilegios de ejecución:

[cloudera@quickstart toadworld]$ chmod +x *.py

Ejecución del contador de palabras.

Ahora invocamos al framework y ejecutamos nuestra solución para contar las palabras del Quijote:

[cloudera@quickstart toadworld]$ hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming.jar -input /user/toadworld/input -output /user/toadworld/output -mapper /home/cloudera/toadworld/mapper_cuentapalabras.py -reducer /home/cloudera/toadworld/reducer_cuentapalabras.py

packageJobJar: [] [/usr/jars/hadoop-streaming-2.6.0-cdh5.4.2.jar] /tmp/streamjob7912780591052138691.jar tmpDir=null
16/06/26 09:32:04 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032
16/06/26 09:32:05 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032
16/06/26 09:32:05 INFO mapred.FileInputFormat: Total input paths to process : 3
16/06/26 09:32:06 INFO mapreduce.JobSubmitter: number of splits:3
16/06/26 09:32:06 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1466955622287_0002
16/06/26 09:32:06 INFO impl.YarnClientImpl: Submitted application application_1466955622287_0002
16/06/26 09:32:06 INFO mapreduce.Job: The url to track the job: http://quickstart.cloudera:8088/proxy/application_1466955622287_0002/
16/06/26 09:32:06 INFO mapreduce.Job: Running job: job_1466955622287_0002
16/06/26 09:32:17 INFO mapreduce.Job: Job job_1466955622287_0002 running in uber mode : false
16/06/26 09:32:17 INFO mapreduce.Job:  map 0% reduce 0%
16/06/26 09:32:35 INFO mapreduce.Job:  map 33% reduce 0%
16/06/26 09:32:37 INFO mapreduce.Job:  map 100% reduce 0%
16/06/26 09:32:45 INFO mapreduce.Job:  map 100% reduce 100%
16/06/26 09:32:45 INFO mapreduce.Job: Job job_1466955622287_0002 completed successfully
16/06/26 09:32:45 INFO mapreduce.Job: Counters: 49
               File System Counters
                              FILE: Number of bytes read=2115
                              FILE: Number of bytes written=454143
                              FILE: Number of read operations=0
                              FILE: Number of large read operations=0
                              FILE: Number of write operations=0
                              HDFS: Number of bytes read=1592
                              HDFS: Number of bytes written=1257
                              HDFS: Number of read operations=12
                              HDFS: Number of large read operations=0
                              HDFS: Number of write operations=2
               Job Counters
                              Launched map tasks=3
                              Launched reduce tasks=1
                              Data-local map tasks=3
                              Total time spent by all maps in occupied slots (ms)=51775
                              Total time spent by all reduces in occupied slots (ms)=6869
                              Total time spent by all map tasks (ms)=51775
                              Total time spent by all reduce tasks (ms)=6869
                              Total vcore-seconds taken by all map tasks=51775
                              Total vcore-seconds taken by all reduce tasks=6869
                              Total megabyte-seconds taken by all map tasks=53017600
                              Total megabyte-seconds taken by all reduce tasks=7033856
               Map-Reduce Framework
                              Map input records=3
                              Map output records=217
                              Map output bytes=1675
                              Map output materialized bytes=2127
                              Input split bytes=351
                              Combine input records=0
                              Combine output records=0
                              Reduce input groups=140
                              Reduce shuffle bytes=2127
                              Reduce input records=217
                              Reduce output records=140
                              Spilled Records=434
                              Shuffled Maps =3
                              Failed Shuffles=0
                              Merged Map outputs=3
                              GC time elapsed (ms)=619
                              CPU time spent (ms)=2630
                              Physical memory (bytes) snapshot=800157696
                              Virtual memory (bytes) snapshot=6014185472
                              Total committed heap usage (bytes)=557592576
               Shuffle Errors
                              BAD_ID=0
                              CONNECTION=0
                              IO_ERROR=0
                              WRONG_LENGTH=0
                              WRONG_MAP=0
                              WRONG_REDUCE=0
               File Input Format Counters
                              Bytes Read=1241
               File Output Format Counters
                              Bytes Written=1257
16/06/26 09:32:45 INFO streaming.StreamJob: Output directory: /user/toadworld/output

[cloudera@quickstart toadworld]$

Visualización de resultados.

Lo que sigue son los resultados obtenidos:

[cloudera@quickstart toadworld]$ hdfs dfs -ls /user/toadworld/output
Found 2 items

-rw-r--r--   1 cloudera supergroup          0 2016-06-26 09:32 /user/toadworld/output/_SUCCESS
-rw-r--r--   1 cloudera supergroup       1257 2016-06-26 09:32 /user/toadworld/output/part-00000

[cloudera@quickstart toadworld]$ hdfs dfs -cat /user/toadworld/output/part-00000 

El          1
En          1
Era         1


de         21
decir       1


el          2
en          5

los         9
lugar       1


vivía       1
y           7
«Quesada»,  1
«Quijada»,  1
«Quijana».  1

[cloudera@quickstart toadworld]$

Eso es todo! Hemos completado un ejemplo práctico de uso de Map Reduce para contar las palabras del clásico de Cervantes, “Don Quijote de la Mancha”.

Nos vemos!