r - ggplot - ggtitle position
Rosa de los vientos con ggplot(R)? (3)
¿Alguna vez ha probado la función windRose del paquete Openair? Es muy fácil y puedes establecer intervalos, estadísticas, etc.
windRose(mydata, ws = "ws", wd = "wd", ws2 = NA, wd2 = NA,
ws.int = 2, angle = 30, type = "default", bias.corr = TRUE, cols
= "default", grid.line = NULL, width = 1, seg = NULL, auto.text
= TRUE, breaks = 4, offset = 10, normalise = FALSE, max.freq =
NULL, paddle = TRUE, key.header = NULL, key.footer = "(m/s)",
key.position = "bottom", key = TRUE, dig.lab = 5, statistic =
"prop.count", pollutant = NULL, annotate = TRUE, angle.scale =
315, border = NA, ...)
pollutionRose(mydata, pollutant = "nox", key.footer = pollutant,
key.position = "right", key = TRUE, breaks = 6, paddle = FALSE,
seg = 0.9, normalise = FALSE, ...)
Estoy buscando un buen código R (o paquete) que use ggplot2 para crear rosas de viento que muestren la frecuencia, magnitud y dirección de los vientos.
Estoy particularmente interesado en ggplot2 ya que la construcción de la trama de esa manera me da la oportunidad de aprovechar el resto de la funcionalidad allí.
Datos de prueba
Descargue un año de datos meteorológicos del nivel de 80 m en la torre "M2" de National Wind Technology . Este enlace creará un archivo .csv que se descargará automáticamente. Necesita encontrar ese archivo (se llama "20130101.csv") y leerlo.
# read in a data file
data.in <- read.csv(file = "A:/drive/somehwere/20130101.csv",
col.names = c("date","hr","ws.80","wd.80"),
stringsAsFactors = FALSE))
Esto funcionaría con cualquier archivo .csv y sobrescribirá los nombres de las columnas.
Data de muestra
Si no desea descargar esa información, aquí hay 10 puntos de datos que usaremos para mostrar el proceso:
data.in <- structure(list(date = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L), .Label = "1/1/2013", clase = "factor"), hr = 1: 9, ws.80 = c (5, 7, 7, 51.9, 11, 12, 9, 11 , 17), wd.80 = c (30, 30, 30, 180, 180, 180, 269, 270, 271)), .Names = c ("date", "hr", "ws.80", " wd.80 "), row.names = c (NA, -9L), clase =" data.frame ")
Aquí está mi versión del código. Agregué etiquetas para las instrucciones (N, NNE, NE, ENE, E ....) e hice que la etiqueta y mostrara la frecuencia en porcentaje en lugar de conteos.
Haga clic aquí para ver la figura del viento Rose con instrucciones y frecuencia (%)
# WindRose.R
require(ggplot2)
require(RColorBrewer)
require(scales)
plot.windrose <- function(data,
spd,
dir,
spdres = 2,
dirres = 22.5,
spdmin = 2,
spdmax = 20,
spdseq = NULL,
palette = "YlGnBu",
countmax = NA,
debug = 0){
# Look to see what data was passed in to the function
if (is.numeric(spd) & is.numeric(dir)){
# assume that we''ve been given vectors of the speed and direction vectors
data <- data.frame(spd = spd,
dir = dir)
spd = "spd"
dir = "dir"
} else if (exists("data")){
# Assume that we''ve been given a data frame, and the name of the speed
# and direction columns. This is the format we want for later use.
}
# Tidy up input data ----
n.in <- NROW(data)
dnu <- (is.na(data[[spd]]) | is.na(data[[dir]]))
data[[spd]][dnu] <- NA
data[[dir]][dnu] <- NA
# figure out the wind speed bins ----
if (missing(spdseq)){
spdseq <- seq(spdmin,spdmax,spdres)
} else {
if (debug >0){
cat("Using custom speed bins /n")
}
}
# get some information about the number of bins, etc.
n.spd.seq <- length(spdseq)
n.colors.in.range <- n.spd.seq - 1
# create the color map
spd.colors <- colorRampPalette(brewer.pal(min(max(3,
n.colors.in.range),
min(9,
n.colors.in.range)),
palette))(n.colors.in.range)
if (max(data[[spd]],na.rm = TRUE) > spdmax){
spd.breaks <- c(spdseq,
max(data[[spd]],na.rm = TRUE))
spd.labels <- c(paste(c(spdseq[1:n.spd.seq-1]),
''-'',
c(spdseq[2:n.spd.seq])),
paste(spdmax,
"-",
max(data[[spd]],na.rm = TRUE)))
spd.colors <- c(spd.colors, "grey50")
} else{
spd.breaks <- spdseq
spd.labels <- paste(c(spdseq[1:n.spd.seq-1]),
''-'',
c(spdseq[2:n.spd.seq]))
}
data$spd.binned <- cut(x = data[[spd]],
breaks = spd.breaks,
labels = spd.labels,
ordered_result = TRUE)
# figure out the wind direction bins
dir.breaks <- c(-dirres/2,
seq(dirres/2, 360-dirres/2, by = dirres),
360+dirres/2)
dir.labels <- c(paste(360-dirres/2,"-",dirres/2),
paste(seq(dirres/2, 360-3*dirres/2, by = dirres),
"-",
seq(3*dirres/2, 360-dirres/2, by = dirres)),
paste(360-dirres/2,"-",dirres/2))
# assign each wind direction to a bin
dir.binned <- cut(data[[dir]],
breaks = dir.breaks,
ordered_result = TRUE)
levels(dir.binned) <- dir.labels
data$dir.binned <- dir.binned
# Run debug if required ----
if (debug>0){
cat(dir.breaks,"/n")
cat(dir.labels,"/n")
cat(levels(dir.binned),"/n")
}
# create the plot ----
p.windrose <- ggplot(data = data,
aes(x = dir.binned,
fill = spd.binned
,y = (..count..)/sum(..count..)
))+
geom_bar() +
scale_x_discrete(drop = FALSE,
labels = c("N","NNE","NE","ENE", "E",
"ESE", "SE","SSE",
"S","SSW", "SW","WSW", "W",
"WNW","NW","NNW")) +
coord_polar(start = -((dirres/2)/360) * 2*pi) +
scale_fill_manual(name = "Wind Speed (m/s)",
values = spd.colors,
drop = FALSE) +
theme(axis.title.x = element_blank()) +
scale_y_continuous(labels = percent) +
ylab("Frequencia")
# adjust axes if required
if (!is.na(countmax)){
p.windrose <- p.windrose +
ylim(c(0,countmax))
}
# print the plot
print(p.windrose)
# return the handle to the wind rose
return(p.windrose)
}
En aras de la argumentación, supondremos que estamos utilizando el marco de datos data.in
, que tiene dos columnas de datos y algún tipo de información de fecha / hora. Ignoraremos la información de fecha y hora inicialmente.
La función ggplot
He codificado la función a continuación. Estoy interesado en la experiencia o sugerencias de otras personas sobre cómo mejorar esto.
# WindRose.R
require(ggplot2)
require(RColorBrewer)
plot.windrose <- function(data,
spd,
dir,
spdres = 2,
dirres = 30,
spdmin = 2,
spdmax = 20,
spdseq = NULL,
palette = "YlGnBu",
countmax = NA,
debug = 0){
# Look to see what data was passed in to the function
if (is.numeric(spd) & is.numeric(dir)){
# assume that we''ve been given vectors of the speed and direction vectors
data <- data.frame(spd = spd,
dir = dir)
spd = "spd"
dir = "dir"
} else if (exists("data")){
# Assume that we''ve been given a data frame, and the name of the speed
# and direction columns. This is the format we want for later use.
}
# Tidy up input data ----
n.in <- NROW(data)
dnu <- (is.na(data[[spd]]) | is.na(data[[dir]]))
data[[spd]][dnu] <- NA
data[[dir]][dnu] <- NA
# figure out the wind speed bins ----
if (missing(spdseq)){
spdseq <- seq(spdmin,spdmax,spdres)
} else {
if (debug >0){
cat("Using custom speed bins /n")
}
}
# get some information about the number of bins, etc.
n.spd.seq <- length(spdseq)
n.colors.in.range <- n.spd.seq - 1
# create the color map
spd.colors <- colorRampPalette(brewer.pal(min(max(3,
n.colors.in.range),
min(9,
n.colors.in.range)),
palette))(n.colors.in.range)
if (max(data[[spd]],na.rm = TRUE) > spdmax){
spd.breaks <- c(spdseq,
max(data[[spd]],na.rm = TRUE))
spd.labels <- c(paste(c(spdseq[1:n.spd.seq-1]),
''-'',
c(spdseq[2:n.spd.seq])),
paste(spdmax,
"-",
max(data[[spd]],na.rm = TRUE)))
spd.colors <- c(spd.colors, "grey50")
} else{
spd.breaks <- spdseq
spd.labels <- paste(c(spdseq[1:n.spd.seq-1]),
''-'',
c(spdseq[2:n.spd.seq]))
}
data$spd.binned <- cut(x = data[[spd]],
breaks = spd.breaks,
labels = spd.labels,
ordered_result = TRUE)
# clean up the data
data. <- na.omit(data)
# figure out the wind direction bins
dir.breaks <- c(-dirres/2,
seq(dirres/2, 360-dirres/2, by = dirres),
360+dirres/2)
dir.labels <- c(paste(360-dirres/2,"-",dirres/2),
paste(seq(dirres/2, 360-3*dirres/2, by = dirres),
"-",
seq(3*dirres/2, 360-dirres/2, by = dirres)),
paste(360-dirres/2,"-",dirres/2))
# assign each wind direction to a bin
dir.binned <- cut(data[[dir]],
breaks = dir.breaks,
ordered_result = TRUE)
levels(dir.binned) <- dir.labels
data$dir.binned <- dir.binned
# Run debug if required ----
if (debug>0){
cat(dir.breaks,"/n")
cat(dir.labels,"/n")
cat(levels(dir.binned),"/n")
}
# deal with change in ordering introduced somewhere around version 2.2
if(packageVersion("ggplot2") > "2.2"){
cat("Hadley broke my code/n")
data$spd.binned = with(data, factor(spd.binned, levels = rev(levels(spd.binned))))
spd.colors = rev(spd.colors)
}
# create the plot ----
p.windrose <- ggplot(data = data,
aes(x = dir.binned,
fill = spd.binned)) +
geom_bar() +
scale_x_discrete(drop = FALSE,
labels = waiver()) +
coord_polar(start = -((dirres/2)/360) * 2*pi) +
scale_fill_manual(name = "Wind Speed (m/s)",
values = spd.colors,
drop = FALSE) +
theme(axis.title.x = element_blank())
# adjust axes if required
if (!is.na(countmax)){
p.windrose <- p.windrose +
ylim(c(0,countmax))
}
# print the plot
print(p.windrose)
# return the handle to the wind rose
return(p.windrose)
}
Prueba de concepto y lógica
Ahora comprobaremos que el código haga lo que esperamos. Para esto, usaremos el conjunto simple de datos de demostración.
# try the default settings
p0 <- plot.windrose(spd = data.in$ws.80,
dir = data.in$wd.80)
Esto nos da esta trama: Entonces: hemos ordenado correctamente los datos por dirección y velocidad del viento, y hemos codificado nuestros datos fuera de rango como se esperaba. ¡Se ve bien!
Usando esta función
Ahora cargamos los datos reales. Podemos cargar esto desde la URL:
data.in <- read.csv(file = "http://midcdmz.nrel.gov/apps/plot.pl?site=NWTC&start=20010824&edy=26&emo=3&eyr=2062&year=2013&month=1&day=1&endyear=2013&endmonth=12&endday=31&time=0&inst=21&inst=39&type=data&wrlevel=2&preset=0&first=3&math=0&second=-1&value=0.0&user=0&axis=1",
col.names = c("date","hr","ws.80","wd.80"))
o del archivo:
data.in <- read.csv(file = "A:/blah/20130101.csv",
col.names = c("date","hr","ws.80","wd.80"))
La manera rápida
La manera simple de usar esto con los datos M2 es simplemente pasar en vectores separados para spd
y dir
(velocidad y dirección):
# try the default settings
p1 <- plot.windrose(spd = data.in$ws.80,
dir = data.in$wd.80)
Lo que nos da esta trama:
Y si queremos contenedores personalizados, podemos agregarlos como argumentos:
p2 <- plot.windrose(spd = data.in$ws.80,
dir = data.in$wd.80,
spdseq = c(0,3,6,12,20))
Usar un marco de datos y los nombres de las columnas
Para que las tramas sean más compatibles con ggplot()
, también puede pasar un marco de datos y el nombre de las variables de velocidad y dirección:
p.wr2 <- plot.windrose(data = data.in,
spd = "ws.80",
dir = "wd.80")
Faceting por otra variable
También podemos trazar los datos por mes o año usando la capacidad de facetado de ggplot. Comencemos por obtener la marca de tiempo de la información de fecha y hora en data.in
, y la conversión a mes y año:
# first create a true POSIXCT timestamp from the date and hour columns
data.in$timestamp <- as.POSIXct(paste0(data.in$date, " ", data.in$hr),
tz = "GMT",
format = "%m/%d/%Y %H:%M")
# Convert the time stamp to years and months
data.in$Year <- as.numeric(format(data.in$timestamp, "%Y"))
data.in$month <- factor(format(data.in$timestamp, "%B"),
levels = month.name)
A continuación, puede aplicar facetas para mostrar cómo la rosa de los vientos varía por mes:
# recreate p.wr2, so that includes the new data
p.wr2 <- plot.windrose(data = data.in,
spd = "ws.80",
dir = "wd.80")
# now generate the faceting
p.wr3 <- p.wr2 + facet_wrap(~month,
ncol = 3)
# and remove labels for clarity
p.wr3 <- p.wr3 + theme(axis.text.x = element_blank(),
axis.title.x = element_blank())
Comentarios
Algunas cosas que debe tener en cuenta sobre la función y cómo se puede usar:
- Las entradas son:
- vectores de velocidad (
spd
) y dirección (dir
) o el nombre del marco de datos y los nombres de las columnas que contienen los datos de velocidad y dirección. - valores opcionales del tamaño del contenedor para velocidad del viento (
spdres
) y dirección (dirres
). -
palette
es el nombre de una paleta secuencial colorbrewer , -
countmax
establece el rango de la rosa de los vientos. -
debug
es un modificador (0,1,2) para habilitar diferentes niveles de depuración.
- vectores de velocidad (
- Quería poder establecer la velocidad máxima (
spdmax
) y el recuento (countmax
) para las tramas para que pueda comparar las mareas de viento de diferentes conjuntos de datos - Si hay velocidades de viento que exceden (
spdmax
), esas se agregan como una región gris (ver la figura). Probablemente debería codificar algo comospdmin
también, y regiones de código de color donde las velocidades del viento son menores que eso. - A raíz de una solicitud, implementé un método para usar contenedores de velocidad de viento personalizados. Se pueden agregar utilizando el
spdseq = c(1,3,5,12)
. - Puede eliminar las etiquetas de bin de grado utilizando los comandos ggplot habituales para borrar el eje x:
p.wr3 + theme(axis.text.x = element_blank(),axis.title.x = element_blank())
. - En algún momento recientemente ggplot2 cambió el orden de los contenedores, de modo que las tramas no funcionaron. Creo que esta era la versión 2.2. Pero, si sus tramas se ven un poco raras, cambie el código para que la prueba para "2.2" sea tal vez "2.1" o "2.0".