Chapter 2: Time Series Graphics

Translating fpp3 Chapter 2 examples from R to Clojure using tablecloth, tablecloth.time, and plotje.

Reference: https://otexts.com/fpp3/graphics.html

Review figures: clj -M:notebooks -m plotje-review

(ns chapter-02-plotje
  (:require [tablecloth.api :as tc]
            [tech.v3.datatype.functional :as dfn]
            [scicloj.plotje.api :as pj]
            [tablecloth.time.api :as tct]))

2.1 — Loading data

(defn load-fpp3
  "Load one of the fpp3 datasets from CSV."
  [name]
  (tc/dataset (str "data/fpp3/" name ".csv") {:key-fn keyword}))
(def PBS (load-fpp3 "PBS"))
PBS

data/fpp3/PBS.csv [67596 9]:

:Month:Concession:Type:ATC1:ATC1_desc:ATC2:ATC2_desc:Scripts:Cost
1991-07-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS18228.067877.00
1991-08-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS15327.057011.00
1991-09-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS14775.055020.00
1991-10-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS15380.057222.00
1991-11-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS14371.052120.00
1991-12-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS15028.054299.00
1992-01-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS11040.039753.00
1992-02-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS15165.054405.00
1992-03-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS16898.061108.00
1992-04-01ConcessionalCo-paymentsAAlimentary tract and metabolismA01STOMATOLOGICAL PREPARATIONS18141.065356.00
...........................
2007-08-01GeneralSafety netZZZ250.03030.15
2007-09-01GeneralSafety netZZZ281.03822.94
2007-10-01GeneralSafety netZZZ374.04346.34
2007-11-01GeneralSafety netZZZ501.06930.19
2007-12-01GeneralSafety netZZZ655.07939.00
2008-01-01GeneralSafety netZZZ797.09604.00
2008-02-01GeneralSafety netZZZ135.01591.00
2008-03-01GeneralSafety netZZZ15.0276.00
2008-04-01GeneralSafety netZZZ11.0165.00
2008-05-01GeneralSafety netZZZ21.0278.00
2008-06-01GeneralSafety netZZZ57.0491.00

The a10 pipeline

In R:

PBS |>
  filter(ATC2 == "A10") |>
  select(Month, Concession, Type, Cost) |>
  summarise(TotalC = sum(Cost)) |>
  mutate(Cost = TotalC / 1e6) -> a10
(def a10
  (-> PBS
      (tc/select-rows #(= "A10" (:ATC2 %)))
      (tc/select-columns [:Month :Concession :Type :Cost])
      (tc/group-by [:Month])
      (tc/aggregate {:TotalC #(dfn/sum (% :Cost))})
      (tc/add-column :Cost #(dfn// (% :TotalC) 1e6))))
a10

_unnamed [204 3]:

:Month:TotalC:Cost
1991-07-013.52659100E+063.52659100
1991-08-013.18089100E+063.18089100
1991-09-013.25222100E+063.25222100
1991-10-013.61100300E+063.61100300
1991-11-013.56586900E+063.56586900
1991-12-014.30637100E+064.30637100
1992-01-015.08833500E+065.08833500
1992-02-012.81452000E+062.81452000
1992-03-012.98581100E+062.98581100
1992-04-013.20478000E+063.20478000
.........
2007-08-012.39302035E+0723.93020353
2007-09-012.29303569E+0722.93035694
2007-10-012.32633399E+0723.26333992
2007-11-012.52500302E+0725.25003022
2007-12-012.58060900E+0725.80609000
2008-01-012.96653560E+0729.66535600
2008-02-012.16542850E+0721.65428500
2008-03-011.82649450E+0718.26494500
2008-04-012.31076770E+0723.10767700
2008-05-012.29125100E+0722.91251000
2008-06-011.94317400E+0719.43174000

ansett — Ansett airlines weekly passenger data

(def ansett (load-fpp3 "ansett"))
ansett

data/fpp3/ansett.csv [7407 4]:

:Week:Airports:Class:Passengers
1989-07-10ADL-PERBusiness193.0
1989-07-17ADL-PERBusiness254.0
1989-07-24ADL-PERBusiness185.0
1989-07-31ADL-PERBusiness254.0
1989-08-07ADL-PERBusiness191.0
1989-08-14ADL-PERBusiness136.0
1989-08-21ADL-PERBusiness0.0
1989-08-28ADL-PERBusiness0.0
1989-09-04ADL-PERBusiness0.0
1989-09-11ADL-PERBusiness0.0
............
1992-09-07SYD-PERFirst176.0
1992-09-14SYD-PERFirst191.0
1992-09-21SYD-PERFirst204.0
1992-09-28SYD-PERFirst183.0
1992-10-05SYD-PERFirst220.0
1992-10-12SYD-PERFirst234.0
1992-10-19SYD-PERFirst203.0
1992-10-26SYD-PERFirst137.0
1992-11-02SYD-PERFirst161.0
1992-11-09SYD-PERFirst155.0
1992-11-16SYD-PERFirst188.0

aus_production — Australian quarterly production

(def aus-production (load-fpp3 "aus_production"))
aus-production

data/fpp3/aus_production.csv [218 7]:

:Quarter:Beer:Tobacco:Bricks:Cement:Electricity:Gas
1956-01-01284.05225.0189.0465.03923.05.0
1956-04-01213.05178.0204.0532.04436.06.0
1956-07-01227.05297.0208.0561.04806.07.0
1956-10-01308.05681.0197.0570.04418.06.0
1957-01-01262.05577.0187.0529.04339.05.0
1957-04-01228.05651.0214.0604.04811.07.0
1957-07-01236.05317.0227.0603.05259.07.0
1957-10-01320.06152.0222.0582.04735.06.0
1958-01-01272.05758.0199.0554.04608.05.0
1958-04-01233.05641.0229.0620.05196.07.0
.....................
2007-10-01473.02562.056411.0205.0
2008-01-01420.02183.059118.0194.0
2008-04-01390.02558.056660.0229.0
2008-07-01410.02612.064067.0249.0
2008-10-01488.02373.059045.0203.0
2009-01-01415.01963.058368.0196.0
2009-04-01398.02160.057471.0238.0
2009-07-01419.02325.058394.0252.0
2009-10-01488.02273.057336.0210.0
2010-01-01414.01904.058309.0205.0
2010-04-01374.02401.058041.0236.0

vic_elec — Victorian half-hourly electricity demand

Timestamps are stored in UTC; convert them to Melbourne time so years and seasons follow the local clock.

(def vic-elec
  (-> (load-fpp3 "vic_elec")
      (tc/convert-types :Time [:local-date-time "yyyy-MM-dd HH:mm:ss"])
      (tct/replace-time-zone :Time "UTC")
      (tct/convert-time-zone :Time "Australia/Melbourne")))
vic-elec

data/fpp3/vic_elec.csv [52608 5]:

:Time:Demand:Temperature:Date:Holiday
2012-01-01T00:00+11:00[Australia/Melbourne]4382.82517421.402012-01-01True
2012-01-01T00:30+11:00[Australia/Melbourne]4263.36552621.052012-01-01True
2012-01-01T01:00+11:00[Australia/Melbourne]4048.96604620.702012-01-01True
2012-01-01T01:30+11:00[Australia/Melbourne]3877.56333020.552012-01-01True
2012-01-01T02:00+11:00[Australia/Melbourne]4036.22974620.402012-01-01True
2012-01-01T02:30+11:00[Australia/Melbourne]3865.59724420.252012-01-01True
2012-01-01T03:00+11:00[Australia/Melbourne]3694.09766420.102012-01-01True
2012-01-01T03:30+11:00[Australia/Melbourne]3561.62368619.602012-01-01True
2012-01-01T04:00+11:00[Australia/Melbourne]3433.03535219.102012-01-01True
2012-01-01T04:30+11:00[Australia/Melbourne]3359.46800018.952012-01-01True
...............
2014-12-31T18:30+11:00[Australia/Melbourne]4244.46553023.402014-12-31False
2014-12-31T19:00+11:00[Australia/Melbourne]4125.98825222.302014-12-31False
2014-12-31T19:30+11:00[Australia/Melbourne]4013.26284820.902014-12-31False
2014-12-31T20:00+11:00[Australia/Melbourne]3924.59343420.302014-12-31False
2014-12-31T20:30+11:00[Australia/Melbourne]3893.86797420.302014-12-31False
2014-12-31T21:00+11:00[Australia/Melbourne]3927.75308820.302014-12-31False
2014-12-31T21:30+11:00[Australia/Melbourne]3873.44871419.002014-12-31False
2014-12-31T22:00+11:00[Australia/Melbourne]3791.63732218.502014-12-31False
2014-12-31T22:30+11:00[Australia/Melbourne]3724.83566617.702014-12-31False
2014-12-31T23:00+11:00[Australia/Melbourne]3761.88685417.302014-12-31False
2014-12-31T23:30+11:00[Australia/Melbourne]3809.41458617.102014-12-31False

tourism — Australian quarterly overnight trips

(def tourism (load-fpp3 "tourism"))
tourism

data/fpp3/tourism.csv [24320 5]:

:Quarter:Region:State:Purpose:Trips
1998-01-01AdelaideSouth AustraliaBusiness135.0776903
1998-04-01AdelaideSouth AustraliaBusiness109.9873160
1998-07-01AdelaideSouth AustraliaBusiness166.0346866
1998-10-01AdelaideSouth AustraliaBusiness127.1604643
1999-01-01AdelaideSouth AustraliaBusiness137.4485333
1999-04-01AdelaideSouth AustraliaBusiness199.9125861
1999-07-01AdelaideSouth AustraliaBusiness169.3550898
1999-10-01AdelaideSouth AustraliaBusiness134.3579372
2000-01-01AdelaideSouth AustraliaBusiness154.0343980
2000-04-01AdelaideSouth AustraliaBusiness168.7763637
...............
2015-04-01Yorke PeninsulaSouth AustraliaVisiting35.5787294
2015-07-01Yorke PeninsulaSouth AustraliaVisiting8.0412485
2015-10-01Yorke PeninsulaSouth AustraliaVisiting24.1803395
2016-01-01Yorke PeninsulaSouth AustraliaVisiting40.5119543
2016-04-01Yorke PeninsulaSouth AustraliaVisiting26.8346812
2016-07-01Yorke PeninsulaSouth AustraliaVisiting22.7588087
2016-10-01Yorke PeninsulaSouth AustraliaVisiting33.6721508
2017-01-01Yorke PeninsulaSouth AustraliaVisiting46.2230139
2017-04-01Yorke PeninsulaSouth AustraliaVisiting50.5828371
2017-07-01Yorke PeninsulaSouth AustraliaVisiting27.7667276
2017-10-01Yorke PeninsulaSouth AustraliaVisiting46.2815777

2.2 — Time plots

Ansett airlines: Melbourne-Sydney economy class

R: autoplot(melsyd_economy, Passengers)

(def melsyd-economy
  (-> ansett
      (tc/select-rows #(and (= "MEL-SYD" (:Airports %))
                            (= "Economy" (:Class %))))
      (tc/add-column :Passengers-000s #(dfn// (% :Passengers) 1000))))
(def melsyd-economy-plot
  (-> melsyd-economy
      (pj/lay-line :Week :Passengers-000s)
      (pj/options {:title "Ansett airlines economy class: Melbourne-Sydney"})))
melsyd-economy-plot
Ansett airlines economy class: Melbourne-SydneyPassengers 000sWeek19881989199019911992051015202530

Notable features visible in the plot:

  • 1989: No passengers (industrial dispute)
  • 1992: Reduced load (economy seats replaced by business class trial)
  • Late 1991: Large increase in passenger load
  • Start of each year: Holiday-effect dips

Antidiabetic drug sales (a10)

R: autoplot(a10, Cost)

(def a10-plot
  (-> a10
      (pj/lay-line :Month :Cost)
      (pj/options {:title "Australian antidiabetic drug sales"
                   :y-label "$ (millions)"})))
a10-plot
Australian antidiabetic drug sales$ (millions)Month19921994199619982000200220042006200851015202530

Clear increasing trend with strong seasonality that grows proportionally. The January spike each year is from stockpiling before year-end subsidies.

2.3 — Time series patterns

Three fundamental components:

  • Trend: long-term increase/decrease
  • Seasonal: fixed, known period (calendar-linked)
  • Cyclic: rises/falls of variable, non-fixed frequency

Australian quarterly beer production (seasonal + no trend)

(def recent-beer
  (-> aus-production
      (tct/add-time-columns :Quarter {:year :Year})
      (tc/select-rows #(>= (:Year %) 2000))
      (tc/select-columns [:Quarter :Beer])))
(def recent-beer-plot
  (-> recent-beer
      (pj/lay-line :Quarter :Beer)
      (pj/options {:title "Australian quarterly beer production"})))
recent-beer-plot
Australian quarterly beer productionBeerQuarter200020022004200620082010380400420440460480500

Google stock — daily changes (no pattern, random walk)

(def gafa (load-fpp3 "gafa_stock"))
(def google-2015
  (-> gafa
      (tct/add-time-columns :Date {:year :Year})
      (tc/select-rows #(and (= "GOOG" (:Symbol %))
                            (= (:Year %) 2015)))))

Daily closing price

(def google-2015-close-plot
  (-> google-2015
      (pj/lay-line :Date :Close)
      (pj/options {:title "Google daily closing stock price (2015)"})))
google-2015-close-plot
Google daily closing stock price (2015)CloseDateMar 2015May 2015Jul 2015Sep 2015Nov 2015500550600650700750

Daily change in closing price; the first day has no previous close.

(def google-2015-change
  (-> google-2015
      (tct/add-lag :Close 1 :Close-lag1)
      (tc/drop-missing [:Close-lag1])
      (tc/add-column :Change #(dfn/- (% :Close) (% :Close-lag1)))
      (tc/select-columns [:Date :Change])))
(def google-2015-change-plot
  (-> google-2015-change
      (pj/lay-line :Date :Change)
      (pj/options {:title "Google daily change in closing stock price (2015)"})))
google-2015-change-plot
Google daily change in closing stock price (2015)ChangeDateMar 2015May 2015Jul 2015Sep 2015Nov 2015-40-20020406080

2.4 — Seasonal plots

fpp3 colours each season by year on a continuous scale. Plotje cannot yet colour lines by a numeric column, so years are categories, each given a shade from dark (earliest) to light (latest) blue.

(defn year-colors
  "Map each year string to a shade of blue, darkest first."
  [years]
  (let [ys (sort (distinct years))
        n (max 1 (dec (count ys)))
        lerp (fn [a b t] (int (+ a (* t (- b a)))))]
    (into {}
          (map-indexed (fn [i y]
                         (let [t (/ i n)]
                           [y (format "#%02x%02x%02x"
                                      (lerp 0x13 0x56 t)
                                      (lerp 0x2b 0xb1 t)
                                      (lerp 0x43 0xf7 t))]))
                       ys))))
(def month-names ["Jan" "Feb" "Mar" "Apr" "May" "Jun"
                  "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"])

Antidiabetic drug sales (a10)

R: gg_season(a10, Cost, labels = "both")

(def a10-seasonal
  (-> a10
      (tct/add-time-columns :Month {:year-string :Year :month :MonthNum})
      (tc/order-by [:Year :MonthNum])))
(def a10-seasonal-plot
  (-> a10-seasonal
      (pj/lay-line :MonthNum :Cost {:color :Year})
      (pj/scale :x {:breaks (range 1 13) :tick-labels month-names})
      (pj/options {:title "Seasonal plot: Antidiabetic drug sales"
                   :x-label "Month"
                   :y-label "$ (millions)"
                   :color-label "Year"
                   :color-values (year-colors (a10-seasonal :Year))})))
a10-seasonal-plot
Seasonal plot: Antidiabetic drug sales$ (millions)MonthYear199119921993199419951996199719981999200020012002200320042005200620072008JanFebMarAprMayJunJulAugSepOctNovDec51015202530
  • January spike (stockpiling)
  • Generally higher in the second half of the year
  • The whole pattern shifts upward year over year (trend)

Multiple seasonal periods — vic_elec

R: gg_season(Demand, period = "day" | "week" | "year") Each period gets its own x position and its own line grouping.

(def vic-elec-seasonal
  (-> vic-elec
      (tct/add-time-columns :Time {:year-string :Year
                                   :date-string :Day
                                   :day-of-week :DayOfWeek
                                   :day-of-year :DayOfYear
                                   :hour-fractional :Hour
                                   :weekly-phase :WeeklyPhase})
      (tc/add-column :DayOfWeekFrac #(dfn/* (% :WeeklyPhase) 7))
      (tc/add-column :DayOfYearFrac #(dfn/+ (dfn/- (% :DayOfYear) 1) (dfn// (% :Hour) 24)))
      (tc/add-column :WeekStart
                     #(map (fn [day dow]
                             (str (.minusDays (java.time.LocalDate/parse day) (dec dow))))
                           (% :Day) (% :DayOfWeek)))
      (tc/order-by [:Time])))
(def vic-elec-year-colors (year-colors (vic-elec-seasonal :Year)))

Daily: one line per day.

(def vic-elec-daily-plot
  (-> vic-elec-seasonal
      (pj/lay-line :Hour :Demand {:color :Year :group :Day :size 0.3 :alpha 0.5})
      (pj/scale :x {:breaks (range 0 25 3)})
      (pj/options {:title "Electricity demand: Victoria (daily pattern)"
                   :x-label "Hour of day"
                   :y-label "MWh"
                   :color-label "Year"
                   :color-values vic-elec-year-colors
                   :width 800})))
vic-elec-daily-plot
Electricity demand: Victoria (daily pattern)MWhHour of dayYear201220132014036912151821243000400050006000700080009000

Weekly: one line per Monday-to-Sunday week.

(def vic-elec-weekly-plot
  (-> vic-elec-seasonal
      (pj/lay-line :DayOfWeekFrac :Demand {:color :Year :group :WeekStart
                                           :size 0.3 :alpha 0.5})
      (pj/scale :x {:breaks (range 7)
                    :tick-labels ["Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"]})
      (pj/options {:title "Electricity demand: Victoria (weekly pattern)"
                   :x-label "Day of week"
                   :y-label "MWh"
                   :color-label "Year"
                   :color-values vic-elec-year-colors
                   :width 800})))
vic-elec-weekly-plot
Electricity demand: Victoria (weekly pattern)MWhDay of weekYear201220132014MonTueWedThuFriSatSun3000400050006000700080009000

Yearly: one line per year, ticked at the first day of each month.

(def vic-elec-yearly-plot
  (-> vic-elec-seasonal
      (pj/lay-line :DayOfYearFrac :Demand {:color :Year :size 0.3})
      (pj/scale :x {:breaks (map #(dec (.getDayOfYear (java.time.LocalDate/of 2013 (int %) 1)))
                                 (range 1 13))
                    :tick-labels month-names})
      (pj/options {:title "Electricity demand: Victoria (yearly pattern)"
                   :x-label "Day of year"
                   :y-label "MWh"
                   :color-label "Year"
                   :color-values vic-elec-year-colors
                   :width 800})))
vic-elec-yearly-plot
Electricity demand: Victoria (yearly pattern)MWhDay of yearYear201220132014JanFebMarAprMayJunJulAugSepOctNovDec3000400050006000700080009000

2.5 — Seasonal subseries plots

R: gg_subseries(a10, Cost) — for each month, show values across years with the mean as a horizontal line.

Each row carries its month's mean in :MeanCost, so the mean draws as a flat line in every panel. Facets follow the order rows first appear in, so rows are sorted by month before faceting.

(def a10-subseries
  (-> a10
      (tct/add-time-columns :Month {:year :Year :month :MonthNum})
      (tc/order-by [:MonthNum :Year])
      (tc/group-by [:MonthNum])
      (tc/add-column :MeanCost #(repeat (tc/row-count %) (dfn/mean (% :Cost))))
      (tc/ungroup)
      (tc/add-column :MonthName #(map (comp month-names dec) (% :MonthNum)))))
(def a10-subseries-plot
  (-> a10-subseries
      pj/overlay
      (pj/lay-line :Year :Cost)
      (pj/lay-point :Year :Cost)
      (pj/lay-line :Year :MeanCost {:color "#1f77b4"})
      (pj/facet :MonthName)
      (pj/options {:title "Subseries: Australian antidiabetic drug sales"
                   :y-label "$ (millions)"
                   :width 1200
                   :height 350})))
a10-subseries-plot
Subseries: Australian antidiabetic drug sales$ (millions)Year20005101520253020002000200020002000200020002000200020002000JanFebMarAprMayJunJulAugSepOctNovDec

The means show January's stockpiling peak, a February drop, then a slow climb to December. Every month trends upward across the years.

Australian tourism: holiday trips by state

R: tourism |> filter(Purpose == "Holiday") |> group_by(State) |> summarise(Trips = sum(Trips))

(def holidays
  (-> tourism
      (tc/select-rows #(= "Holiday" (:Purpose %)))
      (tc/group-by [:Quarter :State])
      (tc/aggregate {:Trips #(dfn/sum (% :Trips))})
      (tct/add-time-columns :Quarter {:year-string :Year :quarter :QuarterNum})
      (tc/order-by [:State :Quarter])))

R: autoplot(holidays, Trips)

(def holidays-plot
  (-> holidays
      (pj/lay-line :Quarter :Trips {:color :State})
      (pj/options {:title "Australian domestic holidays"
                   :y-label "Overnight trips ('000)"
                   :width 800})))
holidays-plot
Australian domestic holidaysOvernight trips ('000)QuarterStateACTNew South WalesNorthern TerritoryQueenslandSouth AustraliaTasmaniaVictoriaWestern Australia199820002002200420062008201020122014201605001000150020002500300035004000

R: gg_season(holidays, Trips) — one panel per state, each with its own y scale, one line per year.

(def holidays-seasonal-plot
  (-> holidays
      (pj/lay-line :QuarterNum :Trips {:color :Year})
      (pj/facet :State :row)
      (pj/scale :x {:breaks (range 1 5) :tick-labels ["Q1" "Q2" "Q3" "Q4"]})
      (pj/options {:title "Australian domestic tourism: Holiday trips"
                   :x-label "Quarter"
                   :y-label "Overnight trips ('000)"
                   :color-label "Year"
                   :color-values (year-colors (holidays :Year))
                   :scales :free-y
                   :line-width 1
                   :width 600
                   :height 1100})))
holidays-seasonal-plot
Australian domestic tourism: Holiday tripsOvernight trips ('000)QuarterYear199819992000200120022003200420052006200720082009201020112012201320142015201620171002003003000400020040015002000250060080050020003000Q1Q2Q3Q41000ACTNew South WalesNorthern TerritoryQueenslandSouth AustraliaTasmaniaVictoriaWestern Australia

Southern states (Victoria, Tasmania) peak in summer (Q1); northern ones (Queensland, Northern Territory) in winter and spring (Q3).

2.6 — Scatterplots

Electricity demand and temperature in Victoria, 2014

(def vic-elec-2014
  (-> vic-elec
      (tct/add-time-columns :Time {:year :Year})
      (tc/select-rows #(= 2014 (:Year %)))
      (tc/add-column :Demand-GW #(dfn// (% :Demand) 1000))))

R: autoplot(Demand)

(def vic-elec-demand-plot
  (-> vic-elec-2014
      (pj/lay-line :Time :Demand-GW {:size 0.5})
      (pj/options {:title "Half-hourly electricity demand: Victoria"
                   :y-label "GW"})))
vic-elec-demand-plot
Half-hourly electricity demand: VictoriaGWTimeJan 2014Mar 2014May 2014Jul 2014Sep 2014Nov 20143456789

R: autoplot(Temperature)

(def vic-elec-temperature-plot
  (-> vic-elec-2014
      (pj/lay-line :Time :Temperature {:size 0.5})
      (pj/options {:title "Half-hourly temperatures: Melbourne, Australia"
                   :y-label "Degrees Celsius"})))
vic-elec-temperature-plot
Half-hourly temperatures: Melbourne, AustraliaDegrees CelsiusTimeJan 2014Mar 2014May 2014Jul 2014Sep 2014Nov 2014051015202530354045

R: ggplot(aes(x = Temperature, y = Demand)) + geom_point()

(def demand-vs-temperature-plot
  (-> vic-elec-2014
      (pj/lay-point :Temperature :Demand-GW {:size 1 :alpha 0.5})
      (pj/options {:title "Electricity demand vs temperature: Victoria, 2014"
                   :x-label "Temperature (degrees Celsius)"
                   :y-label "Electricity demand (GW)"})))
demand-vs-temperature-plot
Electricity demand vs temperature: Victoria, 2014Electricity demand (GW)Temperature (degrees Celsius)0510152025303540453456789

Demand is high for both cold (heating) and hot (air conditioning) days. A correlation coefficient misses this non-linear shape; plot first.

Scatterplot matrix — Australian tourism by state

R: pivot_wider(values_from = Trips, names_from = State) |> GGally::ggpairs(columns = 2:9)

(def visitors-by-state
  (-> tourism
      (tc/group-by [:Quarter :State])
      (tc/aggregate {:Trips #(dfn/sum (% :Trips))})
      (tc/pivot->wider :State :Trips)))
visitors-by-state

_unnamed [80 9]:

:QuarterSouth AustraliaNorthern TerritoryWestern AustraliaVictoriaNew South WalesQueenslandACTTasmania
1998-01-011735.4384181181.44882341641.08949456010.42449058039.79479544041.3701591551.0019215981.6291663
1998-04-011394.6383194313.93615151576.32653374795.24675527166.01380533967.9046526416.0256229693.2882267
1998-07-011213.3307229528.43685921588.29369224316.84516976747.93578964593.8939910436.0290111401.8752750
1998-10-011452.5699692247.70281731839.71699044674.82911777282.08237124202.8291407449.7984449680.6010392
1999-01-011541.1817910184.88959231835.68757295304.33419547584.77683894332.4908503378.5728168925.4197220
1999-04-011636.1154606366.09278581836.96429034561.71090097054.03870514824.4804518558.1781421620.7925480
1999-07-011282.9490168501.43128411725.37486813783.60103936723.67714615018.0345042448.9011959430.2234538
1999-10-011386.9430724248.43029961518.94269724201.42247797135.76694544349.8358766594.8254416591.7588297
2000-01-011832.8466955206.05322001635.52501815566.85728767295.98663874413.2262339599.6685405789.1311444
2000-04-011415.4842932359.76192461808.32861604501.90428226445.22016774344.1592770557.1350565509.0698648
...........................
2015-04-011561.2069129533.02541612394.37403645284.47087757375.20853235555.8301970516.8703428577.9280518
2015-07-011383.9080928682.55182372467.06424634981.16882946961.25020335866.0704509688.2031884455.5288199
2015-10-011700.3825231423.20251642743.44906485550.82370347760.01902215532.2106436597.2455694832.8281787
2016-01-011871.4572503352.09849582871.17187476599.70048308287.87208045042.1537503625.14163441011.0421206
2016-04-011660.1266358469.13653562469.39428095335.22973697660.57962065446.5536505592.6084987651.3987982
2016-07-011534.2342547713.82074992317.04454375221.88050977326.30467075939.3346446572.4370928566.2636651
2016-10-011635.4677141340.69592742656.33070066113.01641297995.50219206106.3838529667.2141410832.9900323
2017-01-011815.4121833298.16602642570.91168927269.52701368320.70346235451.9871280634.36874691135.3127709
2017-04-011660.1320376621.44139222438.48793875901.38654738285.02627955638.4735354748.2904309820.3685463
2017-07-011514.7861308597.65839692493.95499955817.97154378298.25741736533.8369322631.7599043618.0893828
2017-10-011869.1069854346.06193842635.75429616865.39885118542.49060735813.9036668720.3293701800.5084986
(def states (sort (remove #{:Quarter} (tc/column-names visitors-by-state))))

pj/cross pairs every state with every other; the diagonal shows each state's distribution.

(def tourism-splom-plot
  (-> visitors-by-state
      (pj/pose (pj/cross states states) {:size 1.5})
      (pj/options {:width 1250
                   :height 1000})))
tourism-splom-plot
010600080005004000600015005001000400060004006002000600080005004000600015005001000400060002000ACTNew South WalesNorthern TerritoryQueenslandSouth AustraliaTasmaniaVictoriaWestern AustraliaACTNew South WalesNorthern TerritoryQueenslandSouth AustraliaTasmaniaVictoriaWestern Australia

2.7 — Lag plots

R: recent_production |> gg_lag(Beer, geom = "point") Each panel plots beer production y_t against y_{t-k}, coloured by quarter.

(def lags (range 1 10))
(def beer-lags
  (-> recent-beer
      (tct/add-time-columns :Quarter {:quarter :Q})
      (tc/add-column :Quarter-of-year #(map (partial str "Q") (% :Q)))))
(defn lag-panel
  [k]
  (let [lagged (-> beer-lags
                   (tct/add-lag :Beer k :Beer-lagged)
                   (tc/drop-missing [:Beer-lagged])
                   (tc/order-by [:Q]))
        diagonal (tc/dataset {:from [(dfn/reduce-min (recent-beer :Beer))]
                              :to [(dfn/reduce-max (recent-beer :Beer))]})]
    (-> lagged
        (pj/lay-point :Beer-lagged :Beer {:color :Quarter-of-year})
        (pj/lay-segment :from :from {:data diagonal
                                     :x-end :to
                                     :y-end :to
                                     :stroke-dash :dashed
                                     :alpha 0.5
                                     :overlay true})
        (pj/options {:title (str "lag " k)
                     :x-label "lag(Beer, k)"
                     :color-label "Quarter"}))))
(def beer-lag-plot
  (pj/arrange (map lag-panel lags)
              {:cols 3
               :share-scales #{:x :y}
               :width 900
               :height 900}))
beer-lag-plot
lag 1Beerlag(Beer, k)400450500380400420440460480500lag 2Beerlag(Beer, k)400450500380400420440460480500lag 3Beerlag(Beer, k)400450500380400420440460480500lag 4Beerlag(Beer, k)400450500380400420440460480500lag 5Beerlag(Beer, k)400450500380400420440460480500lag 6Beerlag(Beer, k)400450500380400420440460480500lag 7Beerlag(Beer, k)400450500380400420440460480500lag 8Beerlag(Beer, k)400450500380400420440460480500lag 9Beerlag(Beer, k)400450500380400420440460480500QuarterQ1Q2Q3Q4

Lags 4 and 8 sit on the diagonal: each quarter matches the same quarter a year earlier. Lags 2 and 6 are negative: Q4 peaks pair with Q2 troughs.

Figures exported by plotje-review, keyed by output file name.

(def figures
  {"a10" a10-plot
   "melsyd-economy" melsyd-economy-plot
   "recent-beer" recent-beer-plot
   "google-2015-close" google-2015-close-plot
   "google-2015-change" google-2015-change-plot
   "a10-seasonal" a10-seasonal-plot
   "vic-elec-daily" vic-elec-daily-plot
   "vic-elec-weekly" vic-elec-weekly-plot
   "vic-elec-yearly" vic-elec-yearly-plot
   "a10-subseries" a10-subseries-plot
   "holidays" holidays-plot
   "holidays-seasonal" holidays-seasonal-plot
   "vic-elec-demand-2014" vic-elec-demand-plot
   "vic-elec-temperature-2014" vic-elec-temperature-plot
   "demand-vs-temperature" demand-vs-temperature-plot
   "tourism-splom" tourism-splom-plot
   "beer-lag" beer-lag-plot})
source: notebooks/chapter_02_plotje.clj