Una pregunta recurrente es cómo importar eficientemente en R datos climatológicos en formato ‘ancho’ (del inglés wide), como los proporcionados en ocasiones por la Agencia Española de Meteorología (AEMET). Este es un ejemplo de un fichero de datos de este tipo:
Estación Año Mes 1 2 3 ... 31
9245 1975 1 9 10 9 ... 12
9245 1975 2 15 15 15 ... NA
9245 1975 3 12 12 15 ... 16
9245 1975 4 5 6 10 ... NA
Se denomina formato ancho porque, como se puede apreciar, los datos correspondientes a los días se ubican en columnas. Cuando algún mes no tiene el día concreto (por ejemplo, febrero y abril no tienen el día 31), tenemos un valor faltante (NA). Se trata sin duda de un formato compacto, pero presenta dificultades si lo que queremos es tener los datos en formato largo, es decir como una serie de valores consecutivos en una sola columna, algo así:
Estación Año Mes Dia Valor
9245 1975 1 1 9
9245 1975 1 2 10
...
9245 1975 1 31 12
En realidad, pasar de un formato al otro es bastante sencillo. Vamos a ver una posible manera de hacerlo. El siguiente código se basa en un archivo de ejemplo con datos de temperatura para los años 1975 y 1976: T9245E_G.txt
.
En primer lugar vamos a leer los datos. Como en el fichero original los datos están separados por tabuladores emplearemos read.delim
. head
nos permitirá comprobar que hemos leído correctamente los datos.
> dat head(dat)
A continuación vamos a cambiar los nombres de las variables a algo más útil. En especial nos interesa que las columnas que se refieren a los días tengan nombres que luego puedan ordenarse fácilmente.
> names(dat) paste('d0',1:9,sep=''), paste('d',10:31,sep=''))
Resulta muy sencillo pasar los datos de formato ancho (wide) a formato largo (long) con la función melt
del paquete reshape
. Mediante el argumento id.vars
indicamos las variables que queremos utilizar como índices. Podemos utilizar el argumento variable_name
para indicar el nombre que queremos dar a la variable que vamos a pasar de formato ancho a largo; o, dicho de otro modo, de columnas a filas. En este caso es el día (‘day’). Finalmente, indicamos a la función que no queremos conservar los valores faltantes (NAs). De este modo eliminamos los días inexistentes en los meses de menos de 31 días.
> library(reshape)
> dat variable_name='day', na.rm=TRUE)
> head(dat)
Ya tenemos los datos en formato largo. El único problema es que han quedado ordenados por meses, luego por días y finalmente por años. Normalmente lo querremos ordenado por días/meses/años, así que hay que reordenar el objeto dat
. Para ello crearemos un vector auxiliar de texto combinando el año, el mes y el día, y luego utilizaremos ese mismo vector para reordenar dat
:
> f order(f)
> dat
Y ya tenemos los datos en el formato que queríamos, listos para utilizarlos.
[Editado]
El código anterior es correcto siempre y cuando no existan huecos (datos faltantes) en los datos. Frecuentemente este no es el caso, siendo normal encontrar incluso meses enteros faltantes en los registros. Eso no supone ningún problema, a menos que queramos mantener los registros vacíos, por ejemplo para rellenar los datos faltantes.
Es sencillo ‘expandir’ los datos para que aparezcan los huecos como NA, con unas pocas líneas de código. Empecemos por eliminar algunos registros de la tabla de datos recién importada para que nos sirva de ejemplo:
> dat
Para poder trabajar con fechas, nos será útil transformar los días eliminando la letra ‘d’ que tuvimos que poner para que melt
funcionara correctamente:
> dat$day
A continuación añadimos una nueva columna con la fecha completa de cada registro. Utilizamos, por ejemplo, la clase Date
. A continuación crearemos una serie de referencia conteniendo todas las fechas posibles entre las fechas mínima y máxima de nuestros datos:
> dat$date ref max(dat$date),'day'))
Finalmente, utilizamos la función merge
para fusionar nuestros datos con la serie de todas las fechas posibles. El argumento all.y=TRUE
garantiza que todos los elementos del objeto y (la serie de referencia, en este caso) se mantienen en el resultado final, rellenándose los elementos faltantes con valores NA
.
merge(dat,ref,by='date',all.y=TRUE)
Et voilá! Ah! El código funcionará aunque los datos de origen estén desordenados.
A continuación puedes copiar el código completo de este post.
# importar los datos dat <- read.delim('T9245E_G.txt', head=TRUE) names(dat) <- c('id', 'year', 'month', paste('d0',1:9,sep=''), paste('d',10:31,sep='')) library(reshape) dat <- melt(dat, id.vars=c('id','year','month'), variable_name='day', na.rm=TRUE) dat$month <- formatC(dat$month, width=3, flag="0") f <- paste(dat$year, dat$month, dat$day) dat <- dat[order(f),] # expandir los datos faltantes dat <- dat[-c(20,21,22),] dat$day <- gsub('d0?','',as.character(dat$day)) dat$date <- as.Date(paste(dat$year,dat$month,dat$day,sep='-'),'%Y-%m-%d') ref <- data.frame(date=seq.Date(min(dat$date),max(dat$date),'day')) merge(dat,ref,by='date',all.y=TRUE)
Hola Santi
estaba usando tu script para ordenar unos datos, y creo que tienes que hacer un update.
En concreto, a la hora de ordenar los datos con el vector auxiliar “f”, los meses los ordena así: 1, 10, 11, 12, 2, 3 etc… ya que es un vector de caracteres y entiende que el 10 va detrás del 1.
A mi se me ha ocurrido esta forma de solucionarlo (creando una columna nueva de meses, que permitan ser ordenados bien):
dat$meses <-ifelse (dat$month == 10 | dat$month == 11 | dat$month == 12 ‚dat$month, paste(0,dat$month,sep=“”))
y usar ya esa columna en tu vector f
que vaya bien! un abrazo!
Hola Enrique. Tienes razón en lo de la ordenación. La forma que yo suelo utilizar es la función
formatC
, que te permite especificar el formato de los números:dat$month - formatC(dat$month, width=3, flag='0')