Plotting weather data using R

The Australian Bureau of Meteorology provides historical weather data, some of which can be freely downloaded. In this blog post, I will plot the weather data collected at two weather stations in Brisbane: the Brisbane Regional Office weather station (latitude 27.466 degrees south, longitude 153.0270 degrees east, and elevation of 38 metres) with data available from 1887 January until 1986 March and the Brisbane weather station (latitude 27.4808 degrees south, longitude 153.0389 degrees east, and elevation of 8 metres) with data available from 1999 December until 2020 January. I downloaded the monthly maximum temperature (the highest temperature recorded in the 24 hours from 9 am) and the monthly (the lowest temperature recorded in the 24 hours to 9 am). The ground minimum temperature (lowest overnight temperature measured at ground level.) is available but is not free to download. For convenience, I have provided the files on my web server.

Technically air temperature refers to:

The temperature of air is a measure of the average thermal energy of the molecules in the air – the higher the temperature, the higher the energy of the molecules. Historically, the most common instrument used has been a liquid-in-glass thermometer mounted in a Stevenson screen. Modern automatic weather stations generally use an electronic sensor. The daily maximum (highest) and minimum (lowest) temperature are generally recorded once each day at 9 am local time, while instantaneous measurements of air temperature may be made at various times throughout the day.

Let’s begin with loading some libaries and the data into R. The function read_data returns a data frame where the column names have been renamed to friendlier format (all lowercase and no spaces) and the months are converted from numbers into the the name of the months. The data contains a quality flag, where “Y” means completed quality control (QC) and acceptable; we will only keep entries that have an acceptable QC.

# we will be using functions from these libraries
library(tidyverse)
library(reshape2)
library(cowplot)
library(magrittr)

read_data <- function(file){
  read_csv(file) %>%
    rename_all(funs(replace(., values = c("product_code", "station_number", "year", "month", "mean_max", "quality")))) %>%
    mutate(month = factor(month.name[as.integer(month)], levels = month.name)) %>%
    filter(quality == "Y")
}

# load file directly from my web server
bris_reg_max <- read_data(file = "https://davetang.org/weather/IDCJAC0002_040214_Data1.csv")
bris_reg_min <- read_data(file = "https://davetang.org/weather/IDCJAC0004_040214_Data1.csv")
bris_max <- read_data(file = "https://davetang.org/weather/IDCJAC0002_040913_Data1.csv")
bris_min <- read_data(file = "https://davetang.org/weather/IDCJAC0004_040913_Data1.csv")

Temperature data prior to 1910 should be used with extreme caution as many stations, prior to that date, were exposed in non-standard shelters, therefore we will filter out entries prior to 1910. The %<>% pipes the object to the filter step and then updates the object after filtering.

bris_reg_max %<>% filter(year >= 1910)
bris_reg_min %<>% filter(year >= 1910)

Now let’s set some aesthetics for ggplot2. I like the theme_bw theme, so we will use that theme. I have also written a function to assign colours based on the the mean temperature of the month; coldest (blue) -> hottest (red). I use a lot of dplyr in this post, so if you are not familiar with this package, check out this tutorial.

theme_set(theme_bw())

get_colour <- function(df){
  colfunc <- colorRampPalette(c("blue", "red"))
  my_colour <- colfunc(12)
  
  df %>%
    group_by(month) %>%
    summarise(month_mean = mean(mean_max)) %>%
    arrange(month_mean) %>%
    pull(month) %>%
    as.integer() -> my_order
  
  my_colour[match(1:12, my_order)]
}

We will plot data from the Brisbane Regional Office weather station using ggplot2. I make use of plot_grid from the cowplot package, which makes it convenient to plot multiple plots. The NULL at the end of ggplot string is used for convenience when I was constructing the plot; having NULL at the end means I can end each part with a “+” sign. I also should have written a function instead of having four very similar ggplot calls.

my_colour <- get_colour(bris_reg_max)
a <- ggplot(bris_reg_max, aes(year, mean_max, colour = month)) +
  geom_point(size = 0.5) +
  geom_smooth(method = "loess") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
        axis.title.x = element_blank(),
        legend.position = "none") +
  scale_color_manual(values = my_colour) +
  labs(title = "Monthly mean maximum temperature", subtitle = "Brisbane Regional Office: January 1910 - March 1986", y = "Degrees Celsius") +
  facet_wrap(~month) +
  NULL

my_colour <- get_colour(bris_reg_min)
b <- ggplot(bris_reg_min, aes(year, mean_max, colour = month)) +
  geom_point(size = 0.5) +
  geom_smooth(method = "loess") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        legend.position = "none") +
  scale_color_manual(values = my_colour) +
  labs(title = "Monthly mean minimum temperature", subtitle = "Brisbane Regional Office: January 1910 - March 1986") +
  facet_wrap(~month) +
  NULL

plot_grid(a, b)

Same plots as above but for the Brisbane weather station.

my_colour <- get_colour(bris_max)
a <- ggplot(bris_max, aes(year, mean_max, colour = month)) +
  geom_point(size = 0.5) +
  geom_smooth(method = "loess") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
        axis.title.x = element_blank(),
        legend.position = "none") +
  scale_color_manual(values = my_colour) +
  labs(title = "Monthly mean maximum temperature", subtitle = "Brisbane: December 1999 - November 2019", y = "Degrees Celsius") +
  facet_wrap(~month) +
  NULL

my_colour <- get_colour(bris_min)
b <- ggplot(bris_min, aes(year, mean_max, colour = month)) +
  geom_point(size = 0.5) +
  geom_smooth(method = "loess") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        legend.position = "none") +
  scale_color_manual(values = my_colour) +
  labs(title = "Monthly mean minimum temperature", subtitle = "Brisbane: December 1999 - November 2019") +
  facet_wrap(~month) +
  NULL

plot_grid(a, b)

Instead of plotting individual months, we will now plot means across the year; we simply take the mean of all maximum or minimum measurements across respective years. For the Brisbane Regional Office weather station, data collected in 1986 did not include all months, so we will exclude this year. In the plot_grid function, I included a NULL plot to increase the spacing between the two plots.

a <- bris_reg_max %>%
  filter(year != 1986) %>%
  group_by(year) %>%
  summarise(year_mean = mean(mean_max)) %>%
  ggplot(., aes(year, year_mean)) +
    geom_point() +
    geom_line() +
    geom_smooth(method = "loess") +
    theme(axis.title.x = element_blank(),
          legend.position = "none") +
    labs(title = "Annual mean maximum temperature", subtitle = "Brisbane Regional Office: 1910 - 1985", y = "Degrees Celsius") +
    NULL

b <- bris_reg_min %>%
  filter(year != 1986) %>%
  group_by(year) %>%
  summarise(year_mean = mean(mean_max)) %>%
  ggplot(., aes(year, year_mean)) +
    geom_point() +
    geom_line() +
    geom_smooth(method = "loess") +
    theme(axis.title.x = element_blank(),
          axis.title.y = element_blank(),
          legend.position = "none") +
    labs(title = "Annual mean minimum temperature", subtitle = "Brisbane Regional Office: 1910 - 1985") +
    NULL

plot_grid(a, NULL, b, nrow = 1, rel_widths = c(1, 0.05, 1))

Now for the Brisbane weather station; once again years 1999 and 2019 were incomplete, so I have excluded them.

a <- bris_max %>%
  filter(year != 1999, year != 2019) %>%
  group_by(year) %>%
  summarise(year_mean = mean(mean_max)) %>%
  ggplot(., aes(year, year_mean)) +
    geom_point() +
    geom_line() +
    geom_smooth(method = "loess") +
    theme(axis.title.x = element_blank(),
          legend.position = "none") +
    labs(title = "Annual mean maximum temperature", subtitle = "Brisbane: 2000 - 2018", y = "Degrees Celsius") +
    NULL

b <- bris_min %>%
  filter(year != 1999, year != 2019) %>%
  group_by(year) %>%
  summarise(year_mean = mean(mean_max)) %>%
  ggplot(., aes(year, year_mean)) +
    geom_point() +
    geom_line() +
    geom_smooth(method = "loess") +
    theme(axis.title.x = element_blank(),
          axis.title.y = element_blank(),
          legend.position = "none") +
    labs(title = "Annual mean minimum temperature", subtitle = "Brisbane: 2000 - 2018") +
    NULL

plot_grid(a, NULL, b, nrow = 1, rel_widths = c(1, 0.05, 1))

The trend seems to indicate that minimum temperatures are getting higher each year in Brisbane.

Sydney

Sydney (Observatory Hill) weather station (latitude 33.8607 degrees south, longitude 151.2050 degrees east, and elevation of 39 metres) has a continuous collection of data from 1859 until 2020. Repeat the above steps but now for Sydney data.

syd_max <- read_data(file = "https://davetang.org/weather/IDCJAC0002_066062_Data1.csv")
syd_min <- read_data(file = "https://davetang.org/weather/IDCJAC0004_066062_Data1.csv")
syd_max %<>% filter(year >= 1910)
syd_min %<>% filter(year >= 1910)

Monthly maximum and minimum.

my_colour <- get_colour(syd_max)
a <- ggplot(syd_max, aes(year, mean_max, colour = month)) +
  geom_point(size = 0.5) +
  geom_smooth(method = "loess") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
        axis.title.x = element_blank(),
        legend.position = "none") +
  scale_color_manual(values = my_colour) +
  labs(title = "Monthly mean maximum temperature", subtitle = "Sydney: January 1910 - November 2019", y = "Degrees Celsius") +
  facet_wrap(~month) +
  NULL

my_colour <- get_colour(syd_min)
b <- ggplot(syd_min, aes(year, mean_max, colour = month)) +
  geom_point(size = 0.5) +
  geom_smooth(method = "loess") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        legend.position = "none") +
  scale_color_manual(values = my_colour) +
  labs(title = "Monthly mean minimum temperature", subtitle = "Sydney: January 1910 - November 2019") +
  facet_wrap(~month) +
  NULL

plot_grid(a, b)

a <- syd_max %>%
  filter(year != 2019) %>%
  group_by(year) %>%
  summarise(year_mean = mean(mean_max)) %>%
  ggplot(., aes(year, year_mean)) +
    geom_point() +
    geom_line() +
    geom_smooth(method = "loess") +
    theme(axis.title.x = element_blank(),
          legend.position = "none") +
    labs(title = "Annual mean maximum temperature", subtitle = "Sydney: 1910 - 2018", y = "Degrees Celsius") +
    NULL

b <- syd_min %>%
  filter(year != 2019) %>%
  group_by(year) %>%
  summarise(year_mean = mean(mean_max)) %>%
  ggplot(., aes(year, year_mean)) +
    geom_point() +
    geom_line() +
    geom_smooth(method = "loess") +
    theme(axis.title.x = element_blank(),
          axis.title.y = element_blank(),
          legend.position = "none") +
    labs(title = "Annual mean minimum temperature", subtitle = "Sydney: 1910 - 2018") +
    NULL

plot_grid(a, NULL, b, nrow = 1, rel_widths = c(1, 0.05, 1))

Similar trends as Brisbane and clearer increases in both mean maximum and minimum temperatures after 1950.

Perth

Perth Airport weather station (latitude 31.9275 degrees south, longitude 115.9764 degrees east, and elevation of 15 metres) has a continuous collection of data from 1944 until 2020. Repeat the above steps but now for Perth data.

per_max <- read_data(file = "https://davetang.org/weather/IDCJAC0002_009021_Data1.csv")
per_min <- read_data(file = "https://davetang.org/weather/IDCJAC0004_009021_Data1.csv")

Monthly maximum and minimum.

my_colour <- get_colour(per_max)
a <- ggplot(per_max, aes(year, mean_max, colour = month)) +
  geom_point(size = 0.5) +
  geom_smooth(method = "loess") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
        axis.title.x = element_blank(),
        legend.position = "none") +
  scale_color_manual(values = my_colour) +
  labs(title = "Monthly mean maximum temperature", subtitle = "Perth: June 1944 - November 2019", y = "Degrees Celsius") +
  facet_wrap(~month) +
  NULL

my_colour <- get_colour(per_min)
b <- ggplot(per_min, aes(year, mean_max, colour = month)) +
  geom_point(size = 0.5) +
  geom_smooth(method = "loess") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        legend.position = "none") +
  scale_color_manual(values = my_colour) +
  labs(title = "Monthly mean minimum temperature", subtitle = "Perth: June 1944 - November 2019") +
  facet_wrap(~month) +
  NULL

plot_grid(a, b)

a <- per_max %>%
  filter(year != 1944, year != 2019) %>%
  group_by(year) %>%
  summarise(year_mean = mean(mean_max)) %>%
  ggplot(., aes(year, year_mean)) +
    geom_point() +
    geom_line() +
    geom_smooth(method = "loess") +
    theme(axis.title.x = element_blank(),
          legend.position = "none") +
    labs(title = "Annual mean maximum temperature", subtitle = "Perth: 1945 - 2018", y = "Degrees Celsius") +
    NULL

b <- per_min %>%
  filter(year != 1944, year != 2019) %>%
  group_by(year) %>%
  summarise(year_mean = mean(mean_max)) %>%
  ggplot(., aes(year, year_mean)) +
    geom_point() +
    geom_line() +
    geom_smooth(method = "loess") +
    theme(axis.title.x = element_blank(),
          axis.title.y = element_blank(),
          legend.position = "none") +
    labs(title = "Annual mean minimum temperature", subtitle = "Perth: 1945 - 2018") +
    NULL

plot_grid(a, NULL, b, nrow = 1, rel_widths = c(1, 0.05, 1))

Print Friendly, PDF & Email



Creative Commons License
This work is licensed under a Creative Commons
Attribution 4.0 International License
.
One comment Add yours

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.