sábado, abril 26, 2014

Primeros pasos con Apache Pig. Usando Apache Pig para hacer un Wordcount en modo local

Descargando e instalando Pig

Algunos links de interés:

Lo primero será descargar, extraer Pig y decirle donde está la instalación de Hadoop (ver tutorial Instalando Hadoop en modo pseudo-distribuido (esto no es estrictamente necesario en el modo local pero nos ahorrará tiempo y quebraderos de cabeza en el futuro):
 
wget 'http://apache.rediris.es/pig/pig-0.12.1/pig-0.12.1.tar.gz'
tar -zxvf pig-0.12.1.tar.gz
cd pig-0.12.1/bin
export PIG_CLASSPATH=$HADOOP_INSTALL


Bien, ahora vamos a hacer el mismo contador de palabras del tutorial anterior pero con Pig, para eso vamos a descargar el mismo archivo en formato de texto plano http://www.gutenberg.org/ebooks/11101 y lo vamos a poner en la carpeta bin dentro de Pig. Deberíamos tener los siguientes ficheros dentro de la carpeta de Pig:
total 204K
-rwxr-xr-x 1 fedora fedora 13K abr 5 10:43 pig
-rw-r--r-- 1 fedora fedora 161K abr 19 23:51 pg11101.txt
-rwxr-xr-x 1 fedora fedora 5,6K abr 5 10:43 pig.cmd
-rwxr-xr-x 1 fedora fedora 14K abr 5 10:43 pig.py
/home/fedora/Descargas/pig-0.12.1/bin
[fedora@localhost bin]$


Ejecutando Pig en modo local

pig -x local
Despues de unos cuantos mensajes de log aparece grunt
grunt>

Ahora vamos a escribir el contador de palabras en Pig, lo primero será cargar el archivo con la instrucción LOAD:
grunt>myfile = LOAD 'pg11101.txt' AS (words:chararray);

Si usamos la instrucción DUMP para ver los contenidos de la variable myfile, podemos ver la estructura general que tiene ahora cada linea:
grunt>DUMP myfile;
...
(EBooks posted since November 2003, with etext numbers OVER #10000, are)
(filed in a different way. The year of a release date is no longer part)
(of the directory path. The path is based on the etext number (which is)
...

Todavía falta bastante, ni siquiera tenemos una lista de palabras. Para poder separarlo por palabras, podemos usar la instrucción TOKENIZE:
grunt> wordsList = FOREACH myfile GENERATE TOKENIZE($0);
...
({(EBooks),(posted),(since),(November),(2003),(with),(etext),(numbers),(OVER),(#10000),(are)})
({(filed),(in),(a),(different),(way.),(The),(year),(of),(a),(release),(date),(is),(no),(longer),(part)})
({(of),(the),(directory),(path.),(The),(path),(is),(based),(on),(the),(etext),(number),(which),(is)})
...

Si nos fijamos bien, ahora tenemos tuplas (arrays) de palabras pero seguimos sin tener un listado de palabras separadas: Para conseguir dicho listado, tenemos la instrucción FLATTEN:
grunt>words = FOREACH wordsList GENERATE FLATTEN($0);
...
(at:)
()
(http://www.gutenberg.net/1/0/2/3/10234)
()
(or)
(filename)
(24689)
(would)
(be)como
(found)
(at:)
...

Con lo que ahora tenemos las listas separadas, una palabra por linea (aunque todavía están repetidas). Ahora es el momento de agruparlas usando la instrucción GROUP:
grunt>groupedWords = GROUP words BY $0;
...
(www.gutenberg.net,{(www.gutenberg.net),(www.gutenberg.net),(www.gutenberg.net)})
(Constantinopolitan,{(Constantinopolitan),(Constantinopolitan)})
...

La cosa marcha, ahora tenemos listados agrupados de la misma palabra por cada línea. Sólo nos falta contar cuantas ocurrencias hay por línea. Para ello le diremos que por cada linea nos tiene que crear una nueva pareja clave/valor que clave ($0) siga siendo el nombre de la palabra que estamos contando, como hasta ahora y el valor la cuenta de ítems en la segunda posición ($1) con la instrucción COUNT.
grunt>final = FOREACH groupedWords GENERATE $0, COUNT($1);
...
(www.gutenberg.net,3)
(Constantinopolitan,2)
...

Y ahí lo tenemos, nuestro conteo de palabras.


Bonus: Ordenando ocurrencias en Pig

Y como extra, vamos a ordenar el resultado del conteo de palabras en orden descendente, de tal manera que el último de los resultados de la lista sea la palabra mas usada de todo el texto:
grunt>sortedWords = ORDER final BY $1 ASC;
...
(a,697)
(in,833)
(and,1137)
(of,1323)
(the,2047)
...

Por fin, parece que nuestro ganador es la palabra "the". Espero que el tutorial haya resulado interesante :)