lunes, 25 de enero de 2010

Software estadístico- R - Trabajar con grandes conjuntos de datos. (I)

En mi interés por trabajar en entornos fuera de los comercialmente afianzados, y disponibles sólo en grandes empresas, debido fundamentalmente a su coste, llegue a conocer R.

R es un entorno de análisis estadístico donde, uno, aparte de su estructura digamos, común, puede encontrar multitud de paquetes detinados a conexión con sistemas de base de datos, tratamiento de datos y todo tipo de procedimientos estadísticos y matemáticos. Básicamente nos movemos en una línea de comandos desde donde damos instrucciones ordenadas de qué queremos hacer, modo consola de SO. Dispone también de librerías como RCommander para trabajar en un entorno gráfico que ataja algunos de esos comandos.

Al poco tiempo de empezar a documentarme sobre el entorno pude ver cómo era un problema recurrente y ya extendido en el tiempo la dificultad de trabajar con grandes volúmenes de datos. ¿Por qué? Muy sencillo. R trabaja con los datos cargados en memoria. Por lo que el conjunto de datos con el que trabajamos debe ser como mucho inferior a la memoria que tengamos libre. Esta es una limitación muy seria en mi opinión si se quiere tratar de tomar R como un entorno serio, puesto que cada vez tenemos que enfrentarnos a colecciones de datos mayores y más complejas, cuando no llegar directamente a trabajar sobre textos, etc

En este proceso de búsqueda acabé en esta magnífica página/proyecto al que deseo francamente larga vida: Análisis y decisión.

Aquí pude encontrar este artículo tres fracasos y medio con R donde se dan detalles del problema. A partir de aquí me decidí a tratar de alguna manera de abordar esto, o directamente no seguir interesándome mucho por un entorno que no ofrece mucha posibilidad de superar este inconveniente.

Una vez vistas algunas vías de salida, me decidí a investigar un poco en serio dos paquetes diseñados para alojar datos en disco, en vez de en RAM y por tanto son escalables; Los paquetes ff y filehash. Empezaremos por este último. Podéis obtener información (no muy extensa, pero suficiente) aquí. Básicamente y sobre el papel esta librería ofrece la posibilidad de trabajar directamente con bases de datos o ficheros alojados en el disco duro, sin necesidad de alojar todo en memoria.

La diversidad de documentación o ejemplos de uso variados de este paquete es más bien escasa, por lo que me lancé a la aventura para ir aprendiendo sobre la marcha. La idea es cargar un fichero de más de 40 variables con casi 10.000.000 de registros, en toal 4.7 GB. La RAM de mi equipo es de 4GB.
Filehash crea bbdd orientadas a clave, y tiene pocas pero simples funciones posibles. Una de ellas carga data frames en ficheros en disco duro.

La instrucción es directamente
> dumpDF(read.table("c:\fichero.txt",header=TRUE,sep="|";fill=TRUE),"basedatos")

Esto lee los datos de un fichero y los carga directamente en una bbdd filehash de nombre "basedatos". Aunque en algunos textos alumbran que esto se hace directamente a disco, no es cierto. Es proceso lee el fichero normalmente (a memoria) y por tanto falla.

Segunda opción: Partir nuestro fichero en pongamos 5 ó 6 ficheros de la misma estructura con menos datos. La idea sería cargar un fichero , liberar memoria, y anexar los siguientes, repitiendo el proceso. Intento inútil porque no fui capaz(no sé si se podrá hacer) de anexar nada a la base de datos. No funcionan las cosas que podrían hacerse con un dataframe, porque no lo es y la función dbInsert de filehash parece que sólo carga claves (columnas o variables, vamos). Tampoco sirve cargar tal cual, porque sustituye lo anterior.

Tercera opción: Dado que parece que se pueden insertar vectores completos, pues la respuesta está en el ar´ticulo que puse arriba . Utilizo el paquete colbycol , me desagrega el fichero original en vectores individuales. Ahora sí, tiene sentido untilizar la función dbInsert

-primero leemos el fichero con colbycol
> data<-cbc.read.table("f:/fichero.txt",header=TRUE,sep="|",fill=TRUE)
-creamos la bbdd filehash
> dbCreate("vacia")
> bvacia<-dbInit("vacia")
-ahora probamos a insertar vectores extraidos por el colbycol, de diversos modos

> dbInsert(bvacia,"CAMPO1",CAMPO1)
> dbInsert(bvacia,colnames(data)[1],cbc.get.col(data, 1 ))
> dbInsert(bvacia,"CAMPO1",cbc.get.col( data, 1 ))



Estas tres instrucciones hacen lo mismo, insertan el vector correspodiente al primer campo del fichero desmenuzado por colbycol.
En principio hice las pruebas con 6 ó 7 campos por separado y lo cierto es que fue bien en todos menos en uno de ellos, sospecho que por algún tema de memoria, ya que tenía en sistema muy cargado (no había limpiado nada y había hecho muchas pruebas de lecturas etc.) y el vector era un campo de texto (factor).

En este punto, la táctica será utilizar el número de campos del fichero como variable contador para extraer el nombre e información de los vectores y cargar todo de un tirón:
>NUMBER=ncol(data)
>for (i in 1:NUMBER){CONTENT= cbc.get.col(data, i )
+NAME=colnames(data)[i]
+dbInsert(bvacia,NAME,CONTENT)
+rm(NAME)
+rm(CONTENT) }

Esto, aunque es lento funciona. Os preguntaréis quizá por qué asigno las columnas a variables intemedias. El caso es que hacerlo directamente me resultó más lento, no sé si por que lo es, o porque en ese momento el sistema estaba más cargado. He de decir que estas pruebas no están hechas en un entorno profesional, sino en un PC casero con aplicaciones p2p, firefox, antivirus y sobre windows. Tengo pendiente testar esto en Ubuntu server , sólo dedicado a estos menesteres.

Bien, eso de arriba crea una base de datos en disco que siempre podremos recuperar mediante la asignación a un objeto:

>bvacia<- dbInit("vacia")


Para trabajar con esta bbdd , se utiliza la función with, por ejemplo, nada más tener los datos, probé:

>media<-with(bvacia,mean(campo3)) , con éxito, pero al hacer:
>with(bvacia,lm(campo3~campo8))

volvemos al principio, aunque con lo aprendido por el camino. Aquí, R comienza a actuar normalmente y traslada los datos a RAM, llegando al error "Reached total allocation of xxxxx" . Bueno al menos siempre podremos tomar muestras y analizar..

El próximo paso del que les daré cuenta será testar la librería ff , también existente con el propósito de alojar datos en disco, si bien esta promete más rapidez e incluso integración con métodos estadísticos de R, como biglm.

Salimos hasta la próxima entrada.

3 comentarios:

cjgb dijo...

Muy bien por los comentarios. De hecho, habrás visto que muchas de mis entradas están incompletas: siempre espero que venga alguien a completar los detalles.

A todo esto: déjame un correo. El mío, por si acaso, es

cgb de datanalytics y luego, punto y lo de siempre.

cjgb dijo...
Este comentario ha sido eliminado por el autor.
Unknown dijo...

Muy interesante. Impresionante entrada. Perfecto manual para el tratamiento de grandes volumenes de datos.