Manipulación de ficheros y directorios con R

Una de las características que más me gustan de R es su capacidad de interacturar con ficheros y directorios, algo que me permite desarrollar mi trabajo de forma ordenada por muchas figuras y tablas que genere.

En este post voy a describir las principales funciones y sus argumentos y vamos a crear una rutina de trabajo sencilla en la que podemos comprobar la utilidad de estas funciones.

Supongamos que tenemos que trabajar con la base de datos iris que tiene 5 variables (4 cuantitativas y una cualitativa) y queremos tenerlo ordenado de la siguiente manera: una carpeta principal [analisis] y tres subcarpetas [setosa], [versicolor] y [virginica]. Dentro de cada carpeta guardaremos un gráfico con un boxplot de las cuatro variables cuantitativas para la categoría estudiada y un fichero csv con los datos utilizados.

Después seleccionamos los dos ficheros (la figura y el fichero csv) y los comprimimos en un fichero zip y eliminamos los ficheros. De esta manera tenemos nuestros resultados ordenados y sin ocupar mucho espacio, algo muy interesante si generamos muchas tablas a las que accederemos de forma esporádica pero que necesitamos tener creadas.

rutaoriginal <- getwd()

especies <- levels(iris$Species)

for (i in especies) {
  setwd(rutaoriginal)
  dir.create(paste(rutaoriginal,»/»,i,sep=»»),showWarnings=FALSE)
  setwd(paste(rutaoriginal,»/»,i,sep=»»))
 
  subiris <- iris[iris$Species==i,]
 
  windows(7,7)
  boxplot(subiris[,1:4],
          main=i)
  savePlot(file=paste(i,»_boxplot.jpeg»,sep=»»),type=»jpeg»)
  dev.off()
 
  write.csv(subiris,file=paste(i,».csv»,sep=»»))
 
  ficheros <- list.files()
 
  zip(zipfile=paste(i,».zip»,sep=»»),
      files=ficheros)
 
  file.remove(ficheros)    
 
  unzip(zipfile=paste(i,».zip»,sep=»»),
        files=paste(i,».csv»,sep=»»))

}

Las funciones de manipulación de ficheros que hemos utilizado son:

  • setwd(): Nos sitúa en la ruta de trabajo que pasemos como argumento.
  • dir.create(): Crea un directorio en la ruta de trabajo. El argumento showWarnings es para que nos avise si ya existe el directorio.
  • list.files(): Realiza un listado de los ficheros que hay en el directorio de
    trabajo.
  • zip(): Crea un zip con los ficheros que pasemos como argumento.
  • file.remove(): Elimina los ficheros.
  • unzip(): Descomprimimos un fichero del fichero zip.

Ley de Benford

Hace tiempo colaboré con la revista SIGMA (una revista de divulgación matemática editada por el Gobierno Vasco) con un artículo sobre la Ley de Benford o Ley del Primer Dígito Significativo, en el que describía como comprobarla estudiando el tamaño en bytes de los ficheros de nuestro propio ordenador y viendo como se ajustaban a la Ley de Benford. El script lo escribí con el MATHEMATICA y hoy quiero actualizarlo y escribirlo con R para todo el que quiera comprobarlo con sus propios datos.

El script para leer el tamaño de cada fichero de nuestro ordenador no puede ser más sencillo utilizando las funciones de R, list.files() y file.info().

# LEEMOS LOS DATOS DESDE EL DIRECTORIO RAIZ
mfiles <- list.files(«/home/ibon»,recursive=TRUE,full.names=TRUE)
# GUARDAMOS LA INFORMACIÓN DEL TAMAÑO DE CADA FICHERO
mfilessize <- file.info(mfiles)$size
# ELIMINAMOS LA INFORMACIÓN DE AQUELLOS FICHEROS SIN TAMAÑO DEFINIDO
# Y DE AQUELLOS CON TAMAÑO 0
mfilessize <- mfilessize[!is.na(mfilessize)]
mfilessize <- mfilessize[mfilessize!=0]
# NOS QUEDAMOS CON EL PRIMER DÍGITO
firstdigitmfilessize <- substr(mfilessize,1,1)
# CALCULAMOS LA FRECUENCIA DE CADA DÍGITO
prop.table(table(firstdigitmfilessize))
1      2      3      4      5      6      7      8      9
0.3653 0.1516 0.1035 0.0783 0.0580 0.0561 0.0548 0.0751 0.0574

Programar la función que devuelve la probabilidad de aparición de cada dígito también es sencillo.

pbenford <- function(n) {
  return (log(n+1,10)-log(n,10))
}
pbenford(1:9)
[1] 0.3010 0.1761 0.1249 0.0969 0.0792 0.0669 0.0580 0.0512 0.0458

Ya vemos como los datos son muy similares. Si realizamos una gráfica con estos resultados obtenemos:

La Ley de Benford se puede generalizar a los dos primeros dígitos significativos.

# O CON LOS DOS PRIMEROS DÍGITOS
twofirstdigitmfilessize <- substr(mfilessize,1,2)
# ELIMINAMOS LA INFORMACIÓN DE AQUELLOS FICHEROS CON
# MENOS DE 10 BYTES DE TAMAÑO
twofirstdigitmfilessize <- twofirstdigitmfilessize[nchar(twofirstdigitmfilessize)!=1]

Si realizamos una gráfica con estos resultados obtenemos:

Si te parece que el primer dígito (o los dos primeros dígitos) del tamaño en bytes nuestros ficheros se ajustan bastante a esta Ley, te propongo lo siguiente: tomar el primer dígito significativo de los números de la secuencia de Fibonacci (1, 1, 2, 3, 5, …)

# DEFINIMOS LA FUNCIÓN DE FIBONACCI
fibonacci <- function(n,a=1,b=1){  
  if (n == 1) return(a)  
  if (n == 2) return(b)  
  return(fibonacci(n-1,b,a+b))  
}
vfibonacci <- numeric(1000)
for (i in 1:1000) vfibonacci[i] <- fibonacci(i) 
firstdigitfibonacci <- substr(vfibonacci,1,1)

Ahora representamos gráficamente lo que hemos obtenido:

También siguen la Ley de Benford.

Construye la biblioteca de tus librerías de R

Ultimamente estoy trabajando bastante con R y LaTeX conjuntamente (con Sweave), y es muy frecuente referenciar las librerías que utilizo en mis trabajos, por lo que me es de mucha utilidad tener un fichero *.bib donde tenga la información de las referencias.

Con el siguiente script de R he conseguido crear un fichero *.bib con las referencias bibliográficas de todas las librerías que tengo instaladas en R.

cat(toBibtex(citation()),
    file=»bibliotecaR.bib»,
    append=TRUE)

for (i in 1:dim(installed.packages())[1]) {
  cat(toBibtex(citation(installed.packages()[i])),
      file=»bibliotecaR.bib»,
      append=TRUE)
}

Ya tengo mi fichero bibliotecaR.bib con las referencias de las librerías de R utilizadas.

Una Cita Interesante

Hoy he leido el siguiente artículo de opinión de Miguel Escudero en el periódico El Correo.

Poincaré en el caso Dreyfus, del Miguel Escudero

Acaba de cumplirse un siglo de la muerte de Henri Poincaré, ingeniero de minas francés y matemático de verdadera importancia. Murió con 58 años de edad. Provisto de conocimientos amplios y sólidos, nunca se limitó a la tarea de un estricto especialista. Entendía además que todas las partes del universo son en cierto modo «solidarias» y que cualquier fenómeno es resultante de causas infinitamente numerosas, y no de una sola. Tampoco titubeaba en apreciar que las matemáticas merecen ser cultivadas por si mismas, al margen de su posible aplicación.

Yo quisiera recordar aquí un momento en su biografía que apenas es sabido. Fue su intervención en el asunto Dreyfus: un capitán del Estado Mayor francés que, acusado de espiar para Alemania, fue degradado y condenado a cadena perpetua en 1895. Se le reintegró al Ejercito en 1906, tras reconocerse su inocencia. En ese largo periodo más de cuarenta expertos fueron designados por la Justicia para hacer pruebas periciales.

El objeto de estudio era una hoja escrita a mano y rota en seis trozos -hallada en una papelera de la embajada alemana en París por una señora de la limpieza que trabajaba para el servicio secreto francés- que contenía pruebas de traición militar. Se dio por hecho que aquella caligrafía era de Alfred Dreyfus. Oficialmente aquel era un caso cerrado y no había lugar a duda alguna. Sin embargo comenzaron a salir claros indicios de que no era así, pero media Francia, intozicada por lemas nacionalistas y patrioteros, se exaltó ante esa posibilidad. Poincaré, primo de quien sería presidente de la República, no tenía actividad política pero en 1899 se pronunció por carta en los siguientes términos: <<No sé si el acusado será condenado, pero si lo fuera, sería or otras pruebas. es imposible que una argumentación como esta pueda impresionar  a nadie sin una toma de posición previa y que haya recibido una sólida educación científica>>. Se refería a las deducciones de Alphonse Bertillon, oficial de policia e introductor de la antropometría, quien enredó aquel asunto con una estrambótica tesis de autofalsificación y un uso pésimo del cálculo de probabilidades.

En 1904 se hizo público un trabajo encargado por la Justicia a Poincaré y otros dos matemáticos de renombre. Unas cien páginas concisas y contundentes que desarticularon hasta el más mínimo detalle aquella grave exhibición de pseudociencia, jaleada por razones pasionales opuestas a la decencia de la verdad. Henri Poincaré puso a la ciencia en su sitio, fuera de la credulidad: <<El cálculo de probabilidades no es, como parece creerse, una ciencia maravillosa que dispensa al sabio de tener buen sentido>>. Y no obstante, aquellos tres científicos influyeron en el desenlace de la causa más por recibir un crédito de autoridad que por lo irrefutable del contenido científico de su informe. De todas estas cosas hay que estar siempre sobre aviso.

Suscribo todas y cada una de las palabras del artículo de opinión de Miguel Escudero, además, siempre es interesante conocer algo más de la vida de Henri Poincaré. Pero hay una cita atribuida a Poincaré que me ha parecido muy interesante y que en esencia justifica este blog.

«El cálculo de probabilidades no es, como parece creerse, una ciencia maravillosa que dispensa al sabio de tener buen sentido», Henri Poincaré (1854-1912)

Esta cita resumen lo que intento transmitir a través de este blog.

Clases y Métodos en R

Hace tiempo que estoy trabajando con clases y métodos en R, es realmente eficaz cuando trabajamos con un mismo tipo de matriz o data.frame o cualquier combinación de objetos básicos de R y realizamos siempre los mismos análisis sobre ellos. Definimos un poco los conceptos

  • Una clase es una descripción de algo, cualquier cosa. Una clase puede ser creada utilizando setClass().
  • Un objeto es una instancia de una clase. Los objetos pueden ser creados utilizando el new().
  • Una función genérica generalmente encapsula un concepto genérico (plot(), mean()residual(), predict(), …)
  • Una función genérica en realidad no realiza ningún cálculo.
  • Un método es la aplicación de una función genérica para un objeto de una clase en particular.

Vamos a crear una clase (que denominaremos clase «fdesnedecor») un objecto con las características de esta clase y crearemos métodos para aplicar funciones genéricas a este nuevo objeto.

Creamos la clase «fdesnedecor» con la función setClass(). Podemos ver que con el argumento Class = » fdesnedecor» le damos el nombre a la clase, con el argumento representation le decimos que la clase «fdesnedecor» tiene una variable x numérica y una variable y que será una matriz. Con el argumento prototype le decimos a la clase como va a ser.

setClass(
  Class = «fdesnedecor»,
  representation = representation(x=»numeric», y=»matrix»),
  prototype = list(x=numeric(),y=matrix()))

Con el comando new() creamos un nuevo objeto con la estructura «fdesnedecor».

fdsndcr <- new(
  «fdesnedecor»,
  x=rnorm(10),
  y=matrix(rnorm(100),ncol=10))

Ahora la que para mi es la parte más interesante de trabajar con clases, creamos nuevos métodos. En concreto, creamos el método plot(), el método mean() y el método cor(). Es decir, al aplicar plot() a un objeto «fdesnedecor» lo reconocerá y lo dibujará según la definición del método. De la misma manera, cuando apliquemos el método mean() a un objeto «fdesnedecor» R no dará error y presentará el resultado tal y como hemos definido.

setMethod(f = «plot»,
          signature = «fdesnedecor»,
          definition = function(x, y, …) {
            xlim <- range(x@x)
            ylim <- range(x@y)            
            colores <- rainbow(dim(x@y)[2])            
            plot(x@x, x@y[,1], xlim = xlim,
                 ylim = ylim, col = colores[1], …)            
            for (i in 1:dim(x@y)[2]) {
              lines(x@x, x@y[,i], col = colores[i], …)
            }})

setMethod(f = «mean»,
          signature = «fdesnedecor»,
          definition = function(x, trim=0.0,  …) {
            return(
              list(x=mean(x@x,trim=trim),
                  y=apply(x@y,MARGIN=2,FUN=»mean»,trim=trim))
            )})
setMethod(f = «cor»,
          signature = «fdesnedecor»,
          definition = function(x) {
            cor(x@x,x@y)
          })

Aplicamos nuestros nuevos métodos a la variable fdsndcr que hemos creado con la clase «fdesnedecor». La salida del método plot() es una figura que podemos customizar pasando diferentes argumentos (xlab, ylab, main, …).

>plot(fdsndcr, type=»l», lwd=2, xlab=»Valores X», ylab= «Valores Y»)
>mean(fdsndcr)
$x
[1] 0.4695395
$y
[1] 0.26815532 0.16340600 0.41070006 0.06288825 0.46835713 -0.08951221
[7] 0.46360282 -0.05288027 0.48937064 -0.03022744
>mean(fdsndcr,trim=0.5)
$x
[1] 0.4986879
$y
[1] 0.20517952 0.10165574 0.52174545 0.48662299 0.83689841 0.07124269
[7] 0.47857533 -0.03292580 0.16320519 0.41816868
>cor(fdsndcr)
                          [,1]                  [,2]              [,3]                  [,4]                  [,5]                [,6]                   [,7]
[1,]    0.6196134 -0.1155048 0.393835 -0.5165105 -0.1371048 0.2734523 -0.2031487
                         [,8]                   [,9]            [,10]
[1,] -0.0321501 -0.2769156 0.410005