Importar datos AEMET en formato ancho

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)

2 Comments

  • 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!

  • sbegueria wrote:

    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')

Leave a Reply

Your email is never shared.Required fields are marked *