Here we author some R to download, parse and examine output from 2017 QLD state elections.

Download the data

ECQ provides the data in XML, wrapped in a zip file. This is trivial to download and injest into R. We print the time stamp on the file.

theURL <- "https://results.ecq.qld.gov.au/elections/state/state2017/results/public.zip"

library(curl)
tmpfile <- curl_download(theURL,destfile = tempfile(fileext = ".zip"))

library(rvest)
library(xml2)
doc <- read_xml(tmpfile)

parseDateTime <- function(str){
  as.POSIXct(as.POSIXlt(str,
                        tz="Australia/Brisbane",
                        format="%Y-%m-%dT%H:%M:%OS"))
}

getGenTime <- function(doc){
  require(xml2)
  string <- xml_text(xml_node(doc,xpath="//generationDateTime"))
  return(parseDateTime(string))
}

genTime <- getGenTime(doc)
print(genTime)
## [1] "2017-11-28 11:38:32 AEST"

State totals

## enrolment and total votes
getTotVote <- function(doc){
  e <- xml_node(doc,xpath = "/election")
  out <- as.data.frame(xml_attrs(e))
  names(out) <- "value"
  out$var <- rownames(out)
  
  out <- rbind(out,
               data.frame(var="votes",
                          value=xml_integer(xml_node(doc,xpath = "/election/totalVotes"))),
               data.frame(var="formal",
                          value=xml_integer(xml_node(doc,xpath = "/election/totalFormalVotes"))),
               data.frame(var="informal",
                          value=xml_integer(xml_node(doc,xpath = "/election/totalInformalVotes"))))
  return(out)
}

stotal <- getTotVote(doc)

## formatting
ok <- !is.na(as.numeric(stotal$value))
stotal$value[ok] <- format(as.numeric(stotal$value[ok]),
                           big.mark = ",",
                           drop0trailing = TRUE)

kable(stotal[,c("var","value")],
      row.names=FALSE,
      format.args = list(big.mark=","))
var value
id 526
name 2017 State General Election
type H
state QLD
date 2017-11-25
status Open
enrolment 3,229,536
percentRollCounted 76.21
boothCount 13,977
candidateCount 453
partyCount 6
xmlUpdateInterval 120
votes 2,461,347
formal 2,358,065
informal 103,282

Party totals

getPartyTotals <- function(doc){
  party <- xml_attr(xml_nodes(doc,xpath = "//party"),attr = "code")
  formal <- xml_integer(xml_nodes(doc,xpath = "//party/formalVotes/count"))
  out <- data.frame(party,formal)
  out$per <- out$formal/sum(out$formal)*100
  out$timeStamp <- getGenTime(doc)
  return(out)
}

kable(getPartyTotals(doc),format.args = list(big.mark=","),digits=2)
party formal per timeStamp
ALP 844,889 35.83 2017-11-28 11:38:32
LNP 792,397 33.60 2017-11-28 11:38:32
ONP 322,717 13.69 2017-11-28 11:38:32
GRN 228,215 9.68 2017-11-28 11:38:32
KAP 54,900 2.33 2017-11-28 11:38:32
CR 5,775 0.24 2017-11-28 11:38:32
ZZZ 109,172 4.63 2017-11-28 11:38:32

District level data, as a flat file

getDistrictTotals <- function(doc){
  out <- xml_attrs(xml_nodes(doc,xpath="//districts/district"))
  out <- lapply(out,
                  function(x){as.data.frame(as.list(x),stringsAsFactors=FALSE)})
  out <- dplyr::bind_rows(out)
  out$lastUpdated <- parseDateTime(out$lastUpdated)
  out$number <- NULL
  
  out$informalVotes <- xml_integer(xml_find_all(doc,xpath="//districts/district/informalVotes/count"))
  out$totalVotes <- xml_integer(xml_find_all(doc,xpath="//districts/district/totalVotes"))
  out$informalPer <- out$informalVotes/out$totalVotes * 100
  out$enrolment <- as.integer(out$enrolment)
  
  return(out)
}

ddata <- getDistrictTotals(doc)
kable(ddata %>% 
        arrange(desc(lastUpdated)),
      digits=2,
      format.args = list(big.mark=","))
name lastUpdated final enrolment percentRollCounted informalVotes totalVotes informalPer
Aspley 2017-11-28 11:35:11 NO 36,873 80.34 1,064 29,623 3.59
Buderim 2017-11-28 11:18:42 NO 34,822 76.28 897 26,562 3.38
Southern Downs 2017-11-28 11:18:02 NO 35,622 83.87 1,069 29,876 3.58
Woodridge 2017-11-28 11:12:46 NO 36,286 70.31 1,996 25,513 7.82
Pine Rivers 2017-11-28 11:05:14 NO 37,031 80.84 1,103 29,935 3.68
Warrego 2017-11-28 11:05:09 NO 29,615 76.43 898 22,635 3.97
Nicklin 2017-11-28 11:04:13 NO 32,813 78.48 1,132 25,752 4.40
Mundingburra 2017-11-28 11:03:08 NO 33,801 78.02 1,242 26,370 4.71
Cook 2017-11-28 11:01:29 NO 32,394 72.90 1,014 23,616 4.29
Gregory 2017-11-28 10:56:04 NO 24,862 74.82 575 18,602 3.09
Stretton 2017-11-28 10:53:20 NO 33,547 78.11 1,243 26,205 4.74
Capalaba 2017-11-28 10:52:14 NO 35,913 78.09 1,133 28,046 4.04
Callide 2017-11-28 10:51:30 NO 33,540 56.42 672 18,922 3.55
Toowoomba South 2017-11-28 10:47:22 NO 37,415 74.61 1,020 27,915 3.65
Oodgeroo 2017-11-28 10:05:42 NO 32,116 76.99 1,017 24,727 4.11
Mansfield 2017-11-28 10:05:06 NO 33,859 78.70 1,062 26,646 3.99
Lockyer 2017-11-28 10:01:04 NO 33,559 78.50 1,002 26,345 3.80
Kawana 2017-11-28 09:59:00 NO 34,844 79.62 1,294 27,743 4.66
Ipswich West 2017-11-28 09:58:24 NO 33,898 79.64 1,219 26,997 4.52
Burleigh 2017-11-28 09:54:31 NO 34,812 74.57 1,372 25,958 5.29
Bancroft 2017-11-28 09:52:09 NO 34,056 73.31 1,044 24,968 4.18
Clayfield 2017-11-28 09:49:46 NO 38,454 74.52 1,004 28,657 3.50
Morayfield 2017-11-28 09:43:21 NO 34,615 77.68 1,179 26,889 4.38
South Brisbane 2017-11-28 09:23:30 NO 34,460 70.77 836 24,386 3.43
Ninderry 2017-11-28 08:15:41 NO 35,706 73.81 1,255 26,356 4.76
Toohey 2017-11-28 08:00:09 NO 33,588 73.02 1,183 24,526 4.82
Stafford 2017-11-27 19:53:09 NO 37,641 75.02 1,025 28,239 3.63
Springwood 2017-11-27 19:52:38 NO 35,319 76.64 1,263 27,069 4.67
Southport 2017-11-27 19:51:56 NO 34,005 67.99 1,107 23,119 4.79
Mudgeeraba 2017-11-27 19:48:20 NO 36,268 74.73 1,333 27,102 4.92
Miller 2017-11-27 19:47:05 NO 34,567 75.45 655 26,080 2.51
Keppel 2017-11-27 19:32:54 NO 35,307 84.67 990 29,894 3.31
Kurwongbah 2017-11-27 19:29:26 NO 34,780 70.45 1,078 24,502 4.40
Hinchinbrook 2017-11-27 19:27:20 NO 33,488 78.98 1,070 26,448 4.05
Gympie 2017-11-27 19:26:32 NO 36,955 82.37 1,264 30,438 4.15
Cooper 2017-11-27 19:20:11 NO 36,850 78.24 635 28,831 2.20
Chatsworth 2017-11-27 19:18:21 NO 34,815 73.79 926 25,690 3.60
Caloundra 2017-11-27 19:17:11 NO 34,268 76.52 1,026 26,222 3.91
Burnett 2017-11-27 19:15:15 NO 34,015 81.01 1,056 27,556 3.83
Bulimba 2017-11-27 19:12:59 NO 37,824 75.56 893 28,581 3.12
Broadwater 2017-11-27 19:11:58 NO 33,511 70.08 884 23,483 3.76
Mirani 2017-11-27 19:06:49 NO 32,764 75.52 848 24,742 3.43
Mermaid Beach 2017-11-27 18:24:04 NO 34,479 73.00 1,631 25,168 6.48
Bundaberg 2017-11-27 18:12:28 NO 34,757 85.02 1,378 29,549 4.66
Glass House 2017-11-27 17:42:52 NO 33,365 79.12 875 26,397 3.31
Thuringowa 2017-11-27 17:33:11 NO 35,418 78.04 1,329 27,640 4.81
Inala 2017-11-27 17:33:10 NO 33,970 77.40 1,793 26,292 6.82
Macalister 2017-11-27 16:53:10 NO 34,898 76.67 1,742 26,755 6.51
Noosa 2017-11-27 16:45:26 NO 35,759 78.37 918 28,025 3.28
Toowoomba North 2017-11-27 16:35:40 NO 36,602 77.58 989 28,395 3.48
Traeger 2017-11-27 16:21:26 NO 26,132 71.87 758 18,782 4.04
Lytton 2017-11-27 16:19:42 NO 36,237 82.36 967 29,846 3.24
Bundamba 2017-11-27 16:12:52 NO 33,639 72.91 2,014 24,525 8.21
Townsville 2017-11-27 16:11:10 NO 34,926 75.22 975 26,270 3.71
Maiwar 2017-11-27 16:09:56 NO 37,608 75.52 648 28,403 2.28
Redcliffe 2017-11-27 16:08:15 NO 36,684 81.44 1,343 29,877 4.50
Maroochydore 2017-11-27 16:02:50 NO 34,346 68.40 852 23,491 3.63
Currumbin 2017-11-27 15:58:53 NO 34,409 71.82 1,067 24,713 4.32
Bonney 2017-11-27 15:55:39 NO 33,230 69.17 1,358 22,985 5.91
Cairns 2017-11-27 15:23:48 NO 36,569 73.09 1,071 26,729 4.01
Whitsunday 2017-11-27 15:19:49 NO 32,939 77.53 1,027 25,537 4.02
Waterford 2017-11-27 15:13:04 NO 33,029 72.11 1,380 23,817 5.79
Scenic Rim 2017-11-27 15:12:57 NO 36,245 77.17 1,012 27,972 3.62
Surfers Paradise 2017-11-27 15:10:41 NO 33,354 64.14 1,137 21,392 5.32
Rockhampton 2017-11-27 15:07:29 NO 35,801 84.29 1,260 30,178 4.18
Redlands 2017-11-27 15:07:14 NO 34,632 70.80 990 24,521 4.04
Nudgee 2017-11-27 15:06:16 NO 36,606 76.11 1,066 27,862 3.83
Maryborough 2017-11-27 15:05:01 NO 36,130 77.78 1,062 28,102 3.78
Mulgrave 2017-11-27 15:04:12 NO 34,190 78.46 1,323 26,824 4.93
Mount Ommaney 2017-11-27 15:03:40 NO 35,305 81.69 948 28,840 3.29
Moggill 2017-11-27 15:03:12 NO 34,904 80.87 745 28,226 2.64
Mackay 2017-11-27 15:01:11 NO 37,259 80.68 1,334 30,059 4.44
Logan 2017-11-27 15:00:11 NO 33,002 74.39 1,364 24,549 5.56
Hill 2017-11-27 14:59:07 NO 36,893 79.56 1,015 29,352 3.46
Nanango 2017-11-27 14:58:13 NO 36,217 77.98 922 28,241 3.26
Gladstone 2017-11-27 14:56:59 NO 32,216 80.62 848 25,974 3.26
Ferny Grove 2017-11-27 14:56:19 NO 35,461 75.03 734 26,607 2.76
Hervey Bay 2017-11-27 14:55:42 NO 37,620 81.40 1,204 30,621 3.93
Condamine 2017-11-27 14:53:36 NO 36,525 73.48 866 26,838 3.23
Burdekin 2017-11-27 14:51:52 NO 34,295 78.29 846 26,848 3.15
Theodore 2017-11-27 14:37:27 NO 33,243 71.15 1,285 23,651 5.43
Sandgate 2017-11-27 14:34:14 NO 36,123 83.30 1,025 30,089 3.41
Pumicestone 2017-11-27 14:31:43 NO 35,004 77.96 1,110 27,289 4.07
Murrumba 2017-11-27 14:27:19 NO 37,050 73.81 1,178 27,348 4.31
McConnel 2017-11-27 14:15:33 NO 35,013 67.99 802 23,804 3.37
Gaven 2017-11-27 13:47:55 NO 32,785 76.12 1,636 24,955 6.56
Jordan 2017-11-27 12:56:32 NO 33,336 77.90 1,635 25,970 6.30
Ipswich 2017-11-27 12:55:58 NO 31,774 78.40 1,281 24,911 5.14
Greenslopes 2017-11-27 12:52:34 NO 35,771 77.12 921 27,586 3.34
Everton 2017-11-27 12:49:19 NO 36,833 76.74 1,001 28,265 3.54
Coomera 2017-11-27 12:40:57 NO 37,009 73.17 1,565 27,079 5.78
Barron River 2017-11-27 12:26:08 NO 35,423 77.76 1,175 27,546 4.27
Algester 2017-11-27 12:20:51 NO 33,643 73.97 1,299 24,886 5.22

Candidate level data

candidateProcess <- function(doc){
  cnodes <- xml_nodes(doc,xpath="//candidates/candidate")
  cdata <- xml_attrs(cnodes)
  
  d <- lapply(cnodes,xml_find_first,xpath="ancestor::district")
  d <- unlist(lapply(d,xml_attr,attr="name"))
  
  cdata <- lapply(cdata,function(x){as.data.frame(as.list(x),stringsAsFactors=FALSE)})
  cdata <- dplyr::bind_rows(cdata)
  cdata$district <- d
  return(cdata)
}

candidateShares <- function(doc){
  cnodes <- xml_find_all(doc,xpath = "//district/candidates/candidate/primaryVotes")
  votes <- xml_integer(xml_find_all(cnodes,xpath="count"))
  
  cinfo <- xml_attrs(xml_parent(cnodes))
  cinfo <- lapply(cinfo,
                  function(obj){
                    as.data.frame(as.list(obj),stringsAsFactors=FALSE)
                  })
  cinfo <- dplyr::bind_rows(cinfo)
  
  d <- lapply(cnodes,xml_find_first,xpath="ancestor::district")
  d <- unlist(lapply(d,xml_attr,attr="name"))
  
  cinfo$votes <- votes
  cinfo$district <- d
  
  return(cinfo)
}

cdata <- candidateShares(doc)

## add percentages
cdata <- cdata %>% 
  group_by(district) %>% 
  mutate(per=votes/sum(votes)*100)

We also rank order the candidates within districts.

cdata <- cdata %>% 
  group_by(district) %>%
  mutate(r = n() + 1 - rank(votes))

Seats won without preferences

kout <- inner_join(cdata,
                   cdata %>% 
                     filter(r==1 & per>50)) %>% 
  select(district,ballotName,party,per) %>%
  arrange(desc(per))

kable(kout)
district ballotName party per
Inala PALASZCZUK, Annastacia ALP 68.56606
Woodridge DICK, Cameron ALP 66.19467
Traeger KATTER, Robbie KAP 66.05637
Gladstone BUTCHER, Glenn ALP 65.00040
Surfers Paradise LANGBROEK, John-Paul LNP 63.99901
Kawana BLEIJIE, Jarrod LNP 56.61840
Bundamba MILLER, Jo-Ann ALP 53.73373
Nudgee LINARD, Leanne ALP 53.04523
Oodgeroo ROBINSON, Mark LNP 52.93547
Algester ENOCH, Leeanne ALP 52.44414
Everton MANDER, Tim LNP 52.17136
Stretton PEGG, Duncan ALP 50.92941
Burleigh HART, Michael LNP 50.29692
Sandgate HINCHLIFFE, Stirling ALP 50.02408

Plurality winners

foo <- filter(cdata,r==1) %>% select(party,district)
tab <- as.data.frame(xtabs(~party,data=foo))
kable(tab)
party Freq
ALP 47
KAP 2
LNP 43
ZZZ 1

Smallest pluralities, most entropy in vote shares

tmp <- full_join(cdata %>% group_by(district) %>%
                   summarise(StdDev=sd(per)) %>%
                   arrange(StdDev),
                 cdata %>% group_by(district) %>%
                   summarise(maxVoteShare=max(per)) %>%
                   arrange(maxVoteShare)) %>%
                   filter(between(row_number(),1,10))

kable(tmp,digits=2)
district StdDev maxVoteShare
Cairns 9.41 30.56
Rockhampton 9.67 31.91
Thuringowa 10.31 32.26
Hinchinbrook 10.60 30.09
Buderim 10.74 37.28
Greenslopes 11.24 42.41
Redlands 11.76 33.18
Mundingburra 11.77 31.65
Cook 11.89 39.25
Callide 11.94 31.24

Major party vote share

plotData <- bind_rows(cdata %>% 
                        group_by(district) %>%
                        summarise(x=per[party=="ALP"] + per[party=="LNP"],
                                  alp=per[party=="ALP"],
                                  lnp=per[party=="LNP"]) %>%
                        ungroup(),
                      cdata %>% 
                        ungroup() %>% 
                        summarise(alp0=sum(votes[party=="ALP"]),
                                  lnp0=sum(votes[party=="LNP"]),
                                  x=(alp0+lnp0)/sum(votes)*100,
                                  alp=alp0/sum(votes)*100,
                                  lnp=lnp0/sum(votes)*100,
                                  district="Statewide")) %>%
  arrange(x)

plotData$district <- ordered(plotData$district,levels=plotData$district)
plotData$Statewide <- factor(plotData$district=="Statewide")

library(reshape2)
plotData <- melt(plotData,id.vars = "district",measure.vars = c("alp","lnp","x"))

library(ggplot2)
ggplot(plotData,aes(y=district,x=value,color=variable)) + 
  geom_point() + 
  scale_color_manual(values=c("alp"="red","lnp"="blue","x"="black")) + 
  scale_x_continuous("Major party vote share") + 
  scale_y_discrete("") +
  theme(panel.border = element_blank(),
        legend.position = "none")

Two-candidate preferred

processTPP <- function(doc){
  require(xml2)
  nodes <- xml_find_all(doc,"//district/candidates/candidate/n2cpVotes")
 
  tpp <- data.frame(percentage=xml_double(
    xml_find_all(doc,"//district/candidates/candidate/n2cpVotes/percentage")),
    count=xml_integer(
      xml_find_all(doc,"//district/candidates/candidate/n2cpVotes/count"))
  )
  
  tpp$party <- unlist(lapply(nodes,
                             function(x){
                               xml_attr(xml_find_first(x,
                                                       "ancestor::candidate"),
                                        "party")
                             }))
  tpp$district <- unlist(lapply(nodes,
                                function(x){
                                  xml_attr(xml_find_first(x,
                                                          "ancestor::district"),
                                           "name")
                                }))
  return(tpp)
}

tpp <- processTPP(doc)

Maverick seats: no two-candidate preferred count

setdiff(ddata$name,tpp$district)
##  [1] "Burnett"        "Callide"        "Gladstone"      "Hervey Bay"    
##  [5] "Hinchinbrook"   "Jordan"         "Keppel"         "Logan"         
##  [9] "Morayfield"     "Mulgrave"       "Rockhampton"    "Scenic Rim"    
## [13] "Southern Downs" "Traeger"        "Warrego"        "Waterford"

Difference between TCP and 1st preferences

plotData <- inner_join(cdata,tpp,by=c("party","district"))
ggplot(subset(plotData,party %in% c("ALP","LNP","ONP")),
       aes(x=per,y=percentage)) +
  geom_abline(slope=1,intercept = 0) +
  geom_smooth(method = "lm",se=FALSE,col="blue") +
  geom_point() +
  scale_x_continuous("1st preferences") +
  scale_y_continuous("TCP") +
  facet_wrap(~party)

types of 2pp contests

tcp_type <- tpp %>% 
  group_by(district) %>% 
  arrange(desc(percentage)) %>% 
  summarise(p1=party[1],p2=party[2]) %>% 
  arrange(p1,p2)

kable(tcp_type)
district p1 p2
South Brisbane ALP GRN
Algester ALP LNP
Aspley ALP LNP
Bancroft ALP LNP
Barron River ALP LNP
Bulimba ALP LNP
Bundamba ALP LNP
Cairns ALP LNP
Capalaba ALP LNP
Cooper ALP LNP
Ferny Grove ALP LNP
Gaven ALP LNP
Greenslopes ALP LNP
Inala ALP LNP
Kurwongbah ALP LNP
Lytton ALP LNP
Macalister ALP LNP
Mackay ALP LNP
Maiwar ALP LNP
Mansfield ALP LNP
McConnel ALP LNP
Miller ALP LNP
Mount Ommaney ALP LNP
Mundingburra ALP LNP
Murrumba ALP LNP
Nudgee ALP LNP
Pine Rivers ALP LNP
Redcliffe ALP LNP
Redlands ALP LNP
Sandgate ALP LNP
Springwood ALP LNP
Stafford ALP LNP
Stretton ALP LNP
Toohey ALP LNP
Woodridge ALP LNP
Cook ALP ONP
Ipswich ALP ONP
Ipswich West ALP ONP
Maryborough ALP ONP
Thuringowa ALP ONP
Hill KAP LNP
Bonney LNP ALP
Broadwater LNP ALP
Bundaberg LNP ALP
Burdekin LNP ALP
Burleigh LNP ALP
Caloundra LNP ALP
Chatsworth LNP ALP
Clayfield LNP ALP
Coomera LNP ALP
Currumbin LNP ALP
Everton LNP ALP
Glass House LNP ALP
Kawana LNP ALP
Maroochydore LNP ALP
Mermaid Beach LNP ALP
Moggill LNP ALP
Mudgeeraba LNP ALP
Nicklin LNP ALP
Ninderry LNP ALP
Oodgeroo LNP ALP
Pumicestone LNP ALP
Southport LNP ALP
Surfers Paradise LNP ALP
Theodore LNP ALP
Toowoomba North LNP ALP
Toowoomba South LNP ALP
Townsville LNP ALP
Whitsunday LNP ALP
Buderim LNP ONP
Condamine LNP ONP
Gregory LNP ONP
Gympie LNP ONP
Lockyer LNP ONP
Nanango LNP ONP
Mirani ONP ALP
Noosa ZZZ LNP
tab <- xtabs(~p1+p2,data=tcp_type)
kable(tab,caption="1st TCP party tabbed against 2nd TCP party")
1st TCP party tabbed against 2nd TCP party
ALP GRN LNP ONP
ALP 0 1 34 5
KAP 0 0 1 0
LNP 28 0 0 6
ONP 1 0 0 0
ZZZ 0 0 1 0

ONP performance limited to where they ran

theDistricts <- filter(cdata, party=="ONP") %>%
  select(district)
onp_perf <- inner_join(cdata,theDistricts) %>% 
  group_by(party) %>% 
  summarise(total=sum(votes)) %>%
  mutate(per = total/sum(total)*100) %>%
  ungroup()

kable(onp_perf,digits=2,format.args = list(big.mark=",")) 
party total per
ALP 542,501 34.74
CR 1,930 0.12
GRN 118,235 7.57
KAP 24,733 1.58
LNP 483,878 30.99
ONP 322,717 20.67
ZZZ 67,626 4.33

Seats where ONP is 1st or 2nd (and no ties!)

theDistricts <- filter(cdata, r<2.5 & party=="ONP") %>% select(district)
print(theDistricts)
## # A tibble: 21 x 1
## # Groups:   district [21]
##        district
##           <chr>
##  1      Buderim
##  2      Burnett
##  3      Callide
##  4    Condamine
##  5         Cook
##  6    Gladstone
##  7      Gregory
##  8       Gympie
##  9 Hinchinbrook
## 10      Ipswich
## # ... with 11 more rows
foo <- inner_join(cdata,theDistricts) %>% 
  group_by(district) %>%
  summarise(p1=party[r==1],p2=party[r==2],p3=party[r==3],
            per1=per[r==1],per2=per[r==2],per3=per[r==3],
            onp=per[party=="ONP"]) %>%
  mutate(delta=per1-onp) %>%
  arrange(delta) %>%
  select(-onp)

kable(foo,digits=2)
district p1 p2 p3 per1 per2 per3 delta
Lockyer LNP ONP ALP 36.08 34.45 22.95 1.63
Callide LNP ONP ALP 31.24 25.99 22.95 5.25
Mirani ALP ONP LNP 37.32 31.66 26.85 5.67
Gympie LNP ONP ALP 37.75 29.66 22.36 8.09
Hinchinbrook LNP ONP KAP 30.09 21.93 21.13 8.15
Buderim LNP ONP ALP 37.28 28.75 22.13 8.53
Logan ALP ONP LNP 42.91 31.28 17.78 11.63
Scenic Rim LNP ONP ALP 41.60 27.38 21.34 14.22
Burnett LNP ONP ALP 42.93 26.37 25.41 16.56
Maryborough ALP ONP LNP 47.10 29.62 17.22 17.48
Condamine LNP ONP ALP 41.80 23.83 17.83 17.97
Ipswich West ALP ONP LNP 47.87 28.16 16.34 19.71
Cook ALP ONP LNP 39.25 18.89 17.89 20.36
Gregory LNP ONP ALP 44.93 24.08 21.74 20.85
Nanango LNP ONP ALP 48.33 27.09 19.47 21.23
Southern Downs LNP ONP ALP 41.38 20.14 16.88 21.23
Morayfield ALP ONP LNP 46.39 24.96 20.13 21.43
Jordan ALP ONP LNP 39.94 18.49 14.14 21.45
Ipswich ALP ONP LNP 48.55 26.31 13.79 22.24
Waterford ALP ONP LNP 48.64 20.74 19.75 27.90
Gladstone ALP ONP LNP 65.00 20.04 11.45 44.96

Seats where ONP is 3rd

theDistricts<- filter(cdata, r==3 & party=="ONP") %>% select(district)

foo <- inner_join(cdata,theDistricts) %>% 
  summarise(p1=party[r==1],p2=party[r==2],
            per1=per[r==1],per2=per[r==2],
            onp=per[party=="ONP"]) %>%
  arrange(desc(onp))

kable(foo,digits=2,format.args = list(big.mark=","))
district p1 p2 per1 per2 onp
Burdekin ALP LNP 36.00 31.62 29.59
Hervey Bay LNP ALP 37.76 29.30 25.27
Pumicestone ALP LNP 35.83 30.03 22.97
Glass House LNP ALP 35.74 26.58 22.48
Bundaberg LNP ALP 35.74 34.61 22.37
Mulgrave ALP LNP 48.23 22.92 22.34
Mackay ALP LNP 43.33 24.89 22.29
Caloundra LNP ALP 38.90 28.86 21.90
Broadwater LNP ALP 47.90 23.82 21.76
Kurwongbah ALP LNP 42.00 23.97 21.71
Rockhampton ALP ZZZ 31.91 23.67 21.24
Nicklin LNP ALP 32.05 25.50 20.91
Coomera LNP ALP 39.58 31.34 20.50
Thuringowa ALP LNP 32.26 21.08 20.23
Townsville ALP LNP 33.51 31.38 19.89
Whitsunday LNP ALP 32.42 31.38 19.83
Capalaba ALP LNP 43.25 25.36 19.43
Theodore LNP ALP 39.48 32.04 19.09
Murrumba ALP LNP 46.21 25.79 19.04
Bancroft ALP LNP 43.58 26.83 18.68
Algester ALP LNP 52.44 21.68 18.10
Ninderry LNP ALP 36.72 22.85 18.01
Mudgeeraba LNP ALP 46.47 23.21 17.79
Redlands ALP LNP 33.18 31.37 17.53
Mundingburra ALP LNP 31.65 26.01 16.78
Barron River ALP LNP 33.69 30.50 16.71
Toowoomba South LNP ALP 46.12 27.47 16.32
Maroochydore LNP ALP 45.08 25.50 15.96
Lytton ALP LNP 49.37 24.20 15.46
Sandgate ALP LNP 50.02 23.04 14.46
Toowoomba North LNP ALP 42.32 33.56 14.19
Pine Rivers ALP LNP 37.55 26.43 12.16
Stretton ALP LNP 50.93 28.56 12.03
Aspley LNP ALP 40.35 37.35 9.61
table(foo$p1,foo$p2)
##      
##       ALP LNP ZZZ
##   ALP   0  18   1
##   LNP  15   0   0

Seats where GRN is 1st or 2nd

theDistricts <- filter(cdata, r<3 & party=="GRN") %>% select(district)

foo <- inner_join(cdata,theDistricts) %>% 
  summarise(p1=party[r==1],p2=party[r==2],p3=party[r==3],
            per1=per[r==1],per2=per[r==2],per3=per[r==3],
            grn=per[party=="GRN"]) %>%
  mutate(delta=per1-grn) %>%
  arrange(delta) %>%
  select(-grn)

kable(foo,
      digits=2,
      format.args = list(big.mark=","))
district p1 p2 p3 per1 per2 per3 delta
South Brisbane ALP GRN LNP 36.34 34.61 23.72 1.73
Maiwar LNP GRN ALP 42.24 27.62 27.55 14.62
foo <- inner_join(cdata,theDistricts) %>% 
  filter(r==1) %>% 
  select(party,district)

Seats where GRN has finished 3rd

theDistricts<- filter(cdata, r==3 & party=="GRN") %>% select(district)

foo <- inner_join(cdata,theDistricts) %>% 
  summarise(p1=party[r==1],p2=party[r==2],
            per1=per[r==1],per2=per[r==2],
            grn=per[party=="GRN"]) %>%
  arrange(desc(grn))

kable(foo,digits=2,format.args = list(big.mark=","))
district p1 p2 per1 per2 grn
McConnel LNP ALP 36.45 33.78 27.18
Miller ALP LNP 38.49 36.47 21.81
Greenslopes ALP LNP 42.41 36.82 20.77
Moggill LNP ALP 49.08 26.57 20.71
Cooper ALP LNP 41.14 35.90 20.22
Clayfield LNP ALP 48.43 33.04 18.53
Stafford ALP LNP 48.57 33.61 17.82
Burleigh LNP ALP 50.30 34.22 15.48
Ferny Grove ALP LNP 40.61 40.31 15.36
Toohey ALP LNP 45.23 30.02 14.26
Oodgeroo LNP ALP 52.94 33.81 13.26
Mount Ommaney ALP LNP 43.13 36.77 13.03
Nudgee ALP LNP 53.05 28.44 12.73
Bulimba ALP LNP 49.84 34.41 12.58
Everton LNP ALP 52.17 35.84 11.99
Chatsworth LNP ALP 49.88 38.54 11.58
Currumbin LNP ALP 47.37 36.45 11.45
Inala ALP LNP 68.57 20.31 11.13
Mansfield LNP ALP 40.38 39.70 10.74
Bundamba ALP LNP 53.73 14.61 10.67
Gaven LNP ALP 46.34 43.56 10.09
Kawana LNP ALP 56.62 25.47 10.08
Bonney LNP ALP 43.69 36.36 9.48
Surfers Paradise LNP ALP 64.00 22.38 8.71
Springwood ALP LNP 44.87 40.43 8.69
Redcliffe ALP LNP 45.47 37.55 7.51
table(foo$p1,foo$p2)
##      
##       ALP LNP
##   ALP   0  13
##   LNP  13   0

One Nation dotplot

plotData <- inner_join(cdata,
                       filter(cdata,party=="ONP") %>%
                         select(district)) %>%
  filter(party %in% c("ALP","LNP","ONP","GRN","KAP"))

levs <- filter(plotData,party=="ONP") %>% arrange(desc(per)) %>% select(district)
plotData$district <- ordered(plotData$district,levels=rev(levs$district))

library(ggplot2)
ggplot(plotData,aes(y=district,x=per,color=party,shape=party)) + 
  geom_point() +
  scale_x_continuous("1st preference vote share") + 
  scale_y_discrete("") + 
  scale_color_manual(values=c("ALP"="red","LNP"="blue","ONP"="black","GRN"="green","KAP"="orange")) +
  scale_shape_manual(values=c("ALP"=1,"LNP"=1,"ONP"=15,"GRN"=1,"KAP"=1)) +
  theme(panel.border = element_blank(),
        legend.position = "top")

Green dotplot

plotData <- inner_join(cdata,
                       filter(cdata,party=="GRN") %>%
                         select(district)) %>%
  filter(party %in% c("ALP","LNP","ONP","GRN","KAP"))

levs <- filter(plotData,party=="GRN") %>% arrange(desc(per)) %>% select(district)
plotData$district <- ordered(plotData$district,levels=rev(levs$district))

library(ggplot2)
ggplot(plotData,aes(y=district,x=per,color=party,shape=party)) + 
  geom_point() +
  scale_x_continuous("1st preference vote share") + 
  scale_y_discrete("") + 
  scale_color_manual(values=c("ALP"="red","LNP"="blue","ONP"="black","GRN"="green","KAP"="orange")) +
  scale_shape_manual(values=c("ALP"=1,"LNP"=1,"ONP"=1,"GRN"=15,"KAP"=1)) +
  theme(panel.border = element_blank(),
        legend.position = "top")

Informality dotplot

plotData <- ddata
plotData$district <- plotData$name
levs <- plotData %>% arrange(desc(informalPer)) %>% select(district)
plotData$district <- ordered(plotData$district,levels=rev(levs$district))

library(ggplot2)
ggplot(plotData,aes(y=district,x=informalPer)) + 
  geom_point() +
  scale_x_continuous("Informality rate") + 
  scale_y_discrete("") + 
  theme(panel.border = element_blank())

Interesting seats

theOnes <- c("Hinchinbrook","Maiwar","Mirani","Mundingburra",
             "Maryborough","Callide","Jordan","Noosa")
tabFunc <- function(theDistrict){
  cat(paste("###",theDistrict))
  kout <- kable(cdata %>% 
                  filter(district==theDistrict) %>% 
                  ungroup() %>%
                  arrange(r) %>%
                  select(-ballotOrderNumber,-sitting) %>%
                  select(-district),
                digits=2,
                format="markdown",
                format.args = list(big.mark=","))
  return(kout)
}

for(theDistrict in sort(theOnes)){
  print(tabFunc(theDistrict))
}

Callide

ballotName party gender votes per r
BOYCE, Colin LNP M 5,701 31.24 1
LOHSE, Sharon ONP F 4,743 25.99 2
BLACKWOOD, Darren ALP M 4,189 22.95 3
RADEL, Robbie KAP M 2,445 13.40 4
BAKER, Jaiben GRN M 695 3.81 5
ANDERSON, Sandra ZZZ F 477 2.61 6

Hinchinbrook

ballotName party gender votes per r
CRIPPS, Andrew LNP M 7,635 30.09 1
BELL, Margaret ONP F 5,566 21.93 2
DAMETTO, Nick KAP M 5,363 21.13 3
JACOB, Paul ALP M 4,840 19.07 4
RAFFLES, Peter ZZZ M 1,207 4.76 5
BURNESS, Lyle GRN M 767 3.02 6

Jordan

ballotName party gender votes per r
MULLEN, Charis ALP F 9,719 39.94 1
PUCCI, Michael ONP M 4,500 18.49 2
MURRAY, Duncan LNP M 3,441 14.14 3
CUTCLIFFE, Phil ZZZ M 2,469 10.15 4
HODGSON, Steve ZZZ M 2,072 8.51 5
PURCELL, Steven GRN M 1,747 7.18 6
ERVIK, Peter CR M 387 1.59 7

Maiwar

ballotName party gender votes per r
EMERSON, Scott LNP M 11,725 42.24 1
BERKMAN, Michael GRN M 7,666 27.62 2
KING, Ali ALP F 7,647 27.55 3
DIAMOND, Anita ZZZ F 717 2.58 4

Maryborough

ballotName party gender votes per r
SAUNDERS, Bruce ALP M 12,735 47.10 1
HANSEN, James ONP M 8,009 29.62 2
KINGSTON, Richard LNP M 4,657 17.22 3
ARMSTRONG, Craig GRN M 889 3.29 4
CURRIE, Roger M ZZZ M 750 2.77 5

Mirani

ballotName party gender votes per r
PEARCE, Jim ALP M 8,918 37.32 1
ANDREW, Stephen ONP M 7,564 31.66 2
LATTER, Kerry LNP M 6,415 26.85 3
CARLISLE, Christine GRN F 997 4.17 4

Mundingburra

ballotName party gender votes per r
O’ROURKE, Coralee ALP F 7,954 31.65 1
DERLAGEN, Matthew LNP M 6,535 26.01 2
CHARLWOOD, Mal ONP M 4,217 16.78 3
ABRAHAM, Mike KAP M 3,542 14.10 4
BROWN, Jenny GRN F 1,862 7.41 5
EASZON, Dennis ZZZ M 392 1.56 6
VIRGO, Geoff ZZZ M 319 1.27 7
BIRRELL, Alan R ZZZ M 307 1.22 8

Noosa

ballotName party gender votes per r
BOLTON, Sandy ZZZ F 8,684 32.04 1
ELMES, Glen LNP M 8,087 29.83 2
DENHAM, Mark ALP M 3,250 11.99 3
WHITESIDE, Eve-Marie ONP F 3,174 11.71 4
JENKINS, Phillip GRN M 3,071 11.33 5
WHITE, Aaron ZZZ M 564 2.08 6
BRISTOW, Robin ZZZ M 277 1.02 7