Calculating Indonesia House of Representative’s 2019 Election Result with Sainte-Laguë Method using R

They won’t be considered in the final count.

Now that we have slimmed down the race to 9 parties instead of 16, we have to count which party got how many seat.

This part is not done using the total of the vote on national level.

Instead, each constituency has an assigned number of seats proportional to their population; summing up to 575 of them in total.

The total vote for each party (only 9 of them) in each constituency will be the only deciding factor and will be calculated using the Sainte-Laguë method.

It would be a pain to perform the Sainte-Laguë method one by one for each of the 77 constituencies.

Fortunately, we can simulate it in R and get the result right away, quicker than you can make kopi tubruk.

Disclaimer:This simulation is meant to only show how the House of Representative would look like as it stands.

The data used in this simulation was obtained from the real count done by KPU on 11 May 2019, with 38.

7% of the total votes counted.

Now let’s get into the codes, shall we?Tidying upFirst thing first, we make dataframes that spark joy.

For this particular task, dplyr and tidyr packages come in handy.

We will also use electoral package to help us calculating the seat arrangement with the Sainte-Laguë method.

Lastly, we carry out the visualization using ggplot2 and ggparliament packages which would help us visualize the parliamentary seating arrangement.

library(dplyr)library(tidyr)# for calculating seat arrangement based on votes # with Webster/Sainte-Laguë methodlibrary(electoral)#for seat arrangement visuallibrary(ggplot2)library(ggparliament)Next, we simply import the files containing latest real count by KPU and seat allocation per constituency.

You can get ready-to-go files here.

# Seat allocation by constituencykursi_dapil <- read.


CSV”, header = TRUE, sep = ‘;’)# Latest vote count by constituencyhasil_dapil <- read.


csv”, header = TRUE, sep = ‘;’)hasil_dapilThe file mirrors KPU’s format which gives you the constituencies as observations and parties as measurements.

It would be easier for us to perform analysis if each parties are in rows instead of columns.

To do this, we use the gather function and save it to suara_dapil object as follows:suara_dapil <- gather(hasil_dapil, key = “PARTAI”, value = “SUARA”, -WILAYAH)head(suara_dapil)Now that the dataframe looks workable, we can get on the calculation.

The first barrier is to filter out parties that don’t make it through the 4% Parliamentary Threshold.

Thus, we count the total vote for each party and the corresponding percentage such as:# Get the nation-wide sum of votes for each partyhasil_partai <- suara_dapil %>% group_by(PARTAI) %>% summarise(JUMLAH_SUARA = sum(SUARA)) %>% mutate(PERSENTASE = round(JUMLAH_SUARA/sum(JUMLAH_SUARA)*100, 1))hasil_partaiWe now know which party gets how many percent of the total national vote.

The list of parties that make it through the threshold will be useful later.

To produce the list, we simply perform filter and get the value of the PARTAI column.

# Make the list of parties that make it through# the Parliamentary Thresholdpartai_lolos <- hasil_partai %>% filter(PERSENTASE >= 4)list_partai_lolos = partai_lolos$PARTAIlist_partai_lolosThe list will now be used to filter our original suara_dapil dataframe.

# Go back to suara_dapil dataframe # and filter out parties that don't make it through the# Parliamentary Thresholdpartai_lolos_dapil <- suara_dapil %>% filter(PARTAI %in% list_partai_lolos) %>% left_join(kursi_dapil, by = c(“WILAYAH” = “CONSTITUENCY”)) %>% arrange(WILAYAH)partai_lolos_dapilFor the next step, we are going to use the Sainte-Laguë method and work with two dataframes: kursi_dapil (containing constituencies and their respective seat allocation) and partai_lolos_dapil (containing constituencies, parties, and their respective count of vote and seat allocation).

In preparation, we create an empty list to house the result of seat allocation per constituency in each run.

We then use a for loop to calculate seat allocation per constituency using seats_ha function from electoral package and set the method to “webster” which will give you the divisor of 1,3,5,7,9,… in each calculation as the Sainte-Laguë method dictates.

Once we run through all of the constituencies, we bind the result together and we will be served with a complete dataframe containing constituencies, parties, votes, and how many seats they get in each constituency.

bagi_kursi = list() #empty datalistfor (x in 1:nrow(kursi_dapil)) {#Get a constituency for each rundapil = kursi_dapil$CONSTITUENCY[[x]]#Get the seat allocation by each constituencykursi = kursi_dapil$SEAT[[x]] df <- partai_lolos_dapil %>% filter(WILAYAH == dapil) %>% mutate(SEAT = seats_ha(PARTAI, SUARA, n_seats = kursi , method = “webster”))# Use method = “webster” to use 1,3,5,7,9,… as divisors bagi_kursi[[x]] <- df}hasil_akhir = do.

call(rbind, bagi_kursi) #Collect the resulthasil_akhirWe now know how many seats each party gets in a constituency.

Now we can quickly gather the information how many seats each party gets overall along with the percentage over 575 total available seats.

kursi_partai <- hasil_akhir %>% group_by(PARTAI) %>% summarise(KURSI = sum(SEAT)) %>% mutate(PERSENTASE = round(KURSI/sum(KURSI)*100))kursi_partaiWe can also add one more dimension such as which party supports the incumbent President of Indonesia, Joko Widodo, and which supports his challenger, Prabowo Subianto, in the 2019 election cycle.

kursi_partai <- kursi_partai %>% mutate(DUKUNGAN = c(“Oposisi”,”Oposisi”, “Petahana”, “Petahana”, “Oposisi”, “Petahana”, “Petahana”, “Oposisi”, “Petahana” )) %>% arrange(desc(DUKUNGAN),KURSI)kursi_partaiStop the numbers!.I want colors!Yes, yes.

Numbers can be boring.

It’s over, though.

We can now get into the visual now using ggplot2 and ggparliament.

The following workflow will give you the visual of seat allocation by parties:color_pal <- c("PPP" = "#1c5e1b", "NasDem" = "#3c447c", "PKB" = "#698670", "Golkar" = "#FBF501", "PDIP" = "#870e0f", "PAN" = "#ACC4DC", "Demokrat" = "#020197", "PKS" = "#030202", "Gerindra" = "#F3C916" )color_break <- c("PPP", "NasDem", "PKB", "Golkar", "PDIP", "PAN", "Demokrat","PKS", "Gerindra")kursi_dpr <- parliament_data(election_data = kursi_partai, type = “semicircle”, parl_rows = 10, party_seats = kursi_partai$KURSI)gg_dpr <- ggplot(kursi_dpr, aes(x, y, colour = PARTAI)) + geom_parliament_seats(size = 2.

5) + #set theme_ggparliament theme_ggparliament() + #other aesthetics scale_colour_manual(values = color_pal, breaks=color_break)gg_dpr9 Parties is can be too much of a full house.

We may be interested in how many seats the supporting parties of incumbent President gets versus their opposition.

The workflow for that one is as follows:color_pal_2 <- c(“Petahana” = “#7B52AC”, “Oposisi” = “#F47321”)petahana_oposisi <- parliament_data(election_data = kursi_partai, type = “semicircle”, parl_rows = 10, party_seats = kursi_partai$KURSI)gg_petahana_oposisi <- ggplot(kursi_dpr, aes(x, y, colour = DUKUNGAN)) + geom_parliament_seats(size = 2.

5) + #draw majority threshold draw_majoritythreshold(n = 288, label = TRUE, type = ‘semicircle’)+#set theme_ggparliament theme_ggparliament() + scale_colour_manual(values = color_pal_2)gg_petahana_oposisiNow you’re all set!.Have fun scribble around the code and visual!Should you have any feedback or want to discuss more on ideas, you can reach me through my email: baya.


comI’m pretty active on twitter too: @BayaInggas.. More details

Leave a Reply