Lithuania’s electoral system is complex with the Parliament (the Seimas) formed in part by a multi-member constituency elected by proportional representation, and by a nearly-equal-sized collection of single-member constituencies which are elected with two-round ballots.
It is proverbially difficult to predict, given little polling, large errors, a low threshold for entry into the multi-member constituency, a large number of active parties, rapid evolution (creation and dissolution) of parties and a short historical trend.
Lithuania uses a mix of a proportional representation and single-member constituencies with a run-off.
Advance polling might allow prediction of the make-up of the multi-member constituency, but polling errors are large compared to the threshold for winning any seats in the multi-member constituency and in 2020 there were surprises in the first round.
Results of the first round may give a heuristic indicator of the eventual outcomes in the run-offs, based on results in 2012, 2016 and 2020.
Past ambitions
In December 2020 I thought I might also look at trying to work from the election district results for the multimember constituency - which may indicate relative preferences which could be used for predicting how votes might redistribute among the top two candidates at the run-off. I also had vague ideas of trying to determine if I could quantify the constituency-by-constituency “lean” of each constituency (or even election district), but the boundaries change from election to election. Even with some investigation of trying to just work at how the different districts names changed from election to election, I eventually dropped these efforts.
Maybe for 2028?
Round I
After the first round of polling, seats are assigned in the multi-member constituency based on proportional representation with a 5% threshold which parties must reach to win any seats. If they pass this threshold, they will get at least 6 seats. These form the “floor” of a party’s eventual representation in Parliament. They cannot have fewer than the seats they get in the multi-member constituency.
A separate ballot in the first round of polling lets Lithuanians choose among candidates for their local constituency MP. If a candidate in a constituency wins more than 40% of the vote, they are elected to their seat. (Up until 2020, I think this threshold was 50%; in 2020 I could see that no one who won 40% of the first round vote lost in the second round, and it may be that this was also recognised and the law and threshold was changed.)
If no candidate gets 40% or more, the top two candidates compete in a run-off Round II, two weeks later.
Round II
Considering Round II, parties now have a “ceiling” on how many seats they can win. This isn’t just the number of seats they placed first in Round I, but also includes those where they were the first runner-up (“challenger”). * Most candidates who win in Round I win in Round II, but not all.
Previous years
In the single-member constituencies, if a candidate wins over 50% in the first round (40% beginning in 2024) they are elected. If no candidate meets this threshold, the top two candidates participate in a run-off election two weeks later.
2012
Candidate
Constituency
Party
Vote Share
VIRGINIJA BALTRAITIENĖ
Kėdainių
Darbo partija
57.4
LEONARD TALMONT
Vilniaus - Šalčininkų
Lietuvos lenkų rinkimų akcija
63.6
ALGIRDAS BUTKEVIČIUS
Vilkaviškio
Lietuvos socialdemokratų partija
68.7
2016
Candidate
Constituency
Party
Vote Share
POVILAS URBŠYS
Vakarinė
Išsikėlė pats
51.3
INGRIDA ŠIMONYTĖ
Antakalnio
Tėvynės sąjunga - Lietuvos krikščionys demokratai
53.3
RIMA BAŠKIENĖ
Kuršėnų-Dainų
Lietuvos valstiečių ir žaliųjų sąjunga
51.6
LEONARD TALMONT
Šalčininkų-Vilniaus
Lietuvos lenkų rinkimų akcija-Krikščioniškų šeimų sąjunga
67.9
ČESLAV OLŠEVSKI
Medininkų
Lietuvos lenkų rinkimų akcija-Krikščioniškų šeimų sąjunga
60.2
2020
Candidate
Constituency
Party
Vote Share
INGRIDA ŠIMONYTĖ
Antakalnio
Tėvynės sąjunga – Lietuvos krikščionys demokratai
62.6
BEATA PETKEVIČ
Šalčininkų–Vilniaus
Lietuvos lenkų rinkimų akcija - Krikščioniškų šeimų sąjunga
62.2
ČESLAV OLŠEVSKI
Medininkų
Lietuvos lenkų rinkimų akcija - Krikščioniškų šeimų sąjunga
55.8
2024
Candidate
Constituency
Party
Vote Share
INGRIDA ŠIMONYTĖ
Antakalnio
Tėvynės sąjunga-Lietuvos krikščionys demokratai
54.8
REMIGIJUS ŽEMAITAITIS
Kelmės–Šilalės
Politinė partija „Nemuno Aušra“
47.8
VIKTORAS FIODOROVAS
Kėdainių
NA
42.7
VAIDA ALEKNAVIČIENĖ
Žiemgalos vakarinė
Lietuvos socialdemokratų partija
41.1
JAROSLAV NARKEVIČ
Šalčininkų–Vilniaus
Lietuvos lenkų rinkimų akcija-Krikščioniškų šeimų sąjunga
48.7
RITA TAMAŠUNIENĖ
Nemenčinės
Lietuvos lenkų rinkimų akcija-Krikščioniškų šeimų sąjunga
# Set the thresholds for the heuristic we'll use - it's here so I can# refer to it in the body of the text later.VoteShareThreshold <-0.4VoteGapThreshold <-0.16
Candidates who led with more than 40% of the vote in the first round all won in the second round in 2012 and 2016, and then the law seems to have changed. But many candidates with less than 40% of the vote won in both rounds.
Candidates whose lead over the challenger was more than 16% also all won in the second round. Together these criteria account for around 20 seats in the single member constituencies.
Code
### Add in the first round results for 2024#for (thisYear inc(2024)) { first_round_results <- aggregate_round_I %>%group_by(year, constituency) %>%filter(max(perc)<0.4,year==thisYear) %>%arrange(desc(votes)) %>% dplyr::mutate(rank =row_number()) #%>%# Add a new row which contains a fake candidate for each constituency# who collects all the votes given to candidates who do not progress# to the second round## first_round_subtotals <- first_round_results %>%ungroup() %>%split(.$constituency) %>%map(~add_row( .,year = thisYear,given_name ="An",family_name ="Other",constituency =unique(.$constituency),constituency_number =unique(.$constituency_number),party ="Other",#eligible_voters = NA,votes =sum(.$votes, na.rm =TRUE),total =NA,perc =NA ) ) %>%bind_rows() %>%group_by(constituency) %>% dplyr::mutate(carry =if_else(rank <3, votes, 0)) %>% dplyr::mutate(carry =if_else(is.na(rank), votes -sum(carry, na.rm =TRUE), carry)) %>% dplyr::mutate(votes =if_else(is.na(rank), 0, votes)) linear_combined <- first_round_subtotals %>%rename(votes_I=votes,total_I=total,perc_I=perc) %>%mutate(votes_II=NA,total_II=NA, perc_I=NA) linear_wide <- linear_combined %>%select(-given_name,-family_name) %>%filter(carry >0) %>%pivot_wider(id_cols =c(year,constituency,constituency_number),names_from = rank,values_from =c(Acronym, carry, votes_II) ) %>%#dplyr::select(-party_NA, -votes_II_NA) %>% dplyr::rename(final_1 = votes_II_1, final_2 = votes_II_2) %>% dplyr::mutate(matchup =paste(Acronym_1, Acronym_2)) %>% dplyr::mutate(Acronym_1 =factor(Acronym_1, levels = ordered_party_list_all_years, ordered =TRUE),Acronym_2 =factor(Acronym_2, levels = ordered_party_list_all_years, ordered =TRUE)) %>% dplyr::mutate(flip =if_else((final_1 < final_2), TRUE, FALSE))# This used to test against is.na but that didn't workif (length(linear_wide_multiyear)==1) { linear_wide_multiyear <- linear_wide } else { linear_wide_multiyear <-bind_rows(linear_wide_multiyear, linear_wide) } }
In general, candidates who placed first in the first round win the second round, but the success rates vary from year to year. I refer to the candidate who won the most votes in the first round as the Leader, and the second-placed candidate as the Challenger.
85% of first round winners won the second round in 2012
69% of first round winners won the second round in 2016
84% of first round winners won the second round in 2020
This masks wide variation among parties and between years
TS-LKD retained their lead in 87.5% times in 2012 but only 40% in 2016
2012
year
Party
Won
Lost
Total
Success Rate (%)
2012
DP
8
4
12
66.67
2012
Independent
2
0
2
100.00
2012
LLRA
2
1
3
66.67
2012
LRLS
3
1
4
75.00
2012
LSDP
18
0
18
100.00
2012
LVŽS
1
0
1
100.00
2012
TS-LKD
21
3
24
87.50
2012
TT
3
1
4
75.00
2012
All
58
10
68
85.29
2016
year
Party
Won
Lost
Total
Success Rate (%)
2016
DP
2
1
3
66.67
2016
Independent
1
2
3
33.33
2016
LLRA
1
0
1
100.00
2016
LRLS
4
0
4
100.00
2016
LS
1
0
1
100.00
2016
LSDP
4
6
10
40.00
2016
LVŽS
20
0
20
100.00
2016
TS-LKD
10
12
22
45.45
2016
TT
3
0
3
100.00
2016
All
46
21
67
68.66
2020
year
Party
Won
Lost
Total
Success Rate (%)
2020
DP
1
0
1
100.00
2020
Independent
3
1
4
75.00
2020
LLRA
1
0
1
100.00
2020
LP
2
0
2
100.00
2020
LRLS
4
1
5
80.00
2020
LSDDP
2
0
2
100.00
2020
LSDP
3
1
4
75.00
2020
LT
1
0
1
100.00
2020
LVŽS
13
0
13
100.00
2020
LŽP
1
0
1
100.00
2020
TS-LKD
26
8
34
76.47
2020
All
57
11
68
83.82
We can look at the success rates of the different parties from year to year, which emphasises how unusual 2016 was compared with 2012 and 2020 (and particularly how bad it was for TS-LKD, LSDP and independent candidates). This does mask how many candidates are being considered at each point.
Let’s have a look and see what we can get from a very basic approach.
Code
# First round data in the single member constituenciesLTU_Elections_2024_Round_I <-read_excel("LTU Elections 2024 Round I.xlsx", skip =8)# Data in the multi member constituenciesLTU_Elections_2024_Multimember <-read_excel("LTU Elections 2024 Multimember.xlsx", skip =7)# But let's be quick and work from VRK summaries:First_round_results_2024_10_14_0849_cet_Rezultatai_vrk_lt <-read_excel("First round results 2024-10-14 0849 cet Rezultatai - vrk.lt.xlsx", skip =8)Multimember_results_2024_10_14_1830_cet_Rezultatai_vrk_lt <-read_excel("Multimember results 2024-10-14 1830 cet Rezultatai - vrk.lt.xlsx", sheet="data_lt",skip =11) %>%rename(Candidate="Kandidatas", "Electoral Constituency"="Apygarda", "Nominated by"="Partijos, koalicijos pavadinimas")
We can build a graph which shows the MPs elected to the Seimas, whether through the multi-member constituency off party lists in the first round, or by passing the threshold in the first round in single-member constituencies (8).
Code
parl_data <-bind_rows(MPs_elected_first_round, multimember_party_results) %>%rename(party_long=`Nominated by`, party_short=Acronym) %>%group_by(party_long, party_short) %>%tally(name="seats") first_round_parl_data <-parliament_data(election_data = parl_data,type="semicircle", party_seats = parl_data$seats,parl_rows =4)ggplot(first_round_parl_data, aes(x=x, y=y, colour=party_short)) +geom_parliament_seats(size=5) +theme_ggparliament() +scale_colour_manual(values = party_colours_names) +guides(colour =guide_legend(title=NULL)) +labs( title="MPs elected to the Seimas in the first round 2024")
Directly elected and leading
It’s more useful to have some indication of how this relates. This graph shows both those confirmed as elected and those leading in the first round in single-member constituencies.
I am also adding in the 5 seats where the vote gap is above 17%, counting them as confirmed.
This is somewhat useful and suggests that these seats are uncertain, but doesn’t reflect who the challenger is for each constituency.
Code
parl_data_leaders <- FirstRoundFractions_2024 %>%mutate("elected"=if_else(`Vote Gap`>VoteGapThreshold2024, TRUE, FALSE)) %>%select(party_short=Leader, elected) %>%mutate(party_long=party_short) %>%group_by(party_long, party_short, elected) %>%tally(name="seats")parl_data_with_leaders <-bind_rows(parl_data %>%mutate("elected"=TRUE), parl_data_leaders) %>%mutate(party_short =if_else(is.na(party_short), "Independent", party_short)) %>%mutate(party_long = party_short) %>%mutate(party_short =factor(party_short,levels=ordered_party_list_all_years)) %>%arrange(party_short,elected)first_round_parl_data_w_leaders <-parliament_data(election_data = parl_data_with_leaders,type="semicircle", party_seats = parl_data_with_leaders$seats,parl_rows =4)ggplot(first_round_parl_data_w_leaders, aes(x=x, y=y, colour=party_short)) +geom_parliament_seats(size=5) +theme_ggparliament() +scale_colour_manual(values = party_colours_names) +guides(colour =guide_legend(title=NULL)) +geom_emphasize_parliamentarians(elected, size=5) +labs( title="MPs elected and leading to the Seimas in the first round 2024")
Choosing success rates for simulation
When we looked at success rates year to year earlier, 2016 looked very odd. A key question for 2024 is whether we think 2016 was an outlier, or whether it reflects a broader range of how Lithuanian parties perform in elections.
In the end, I am going to treat 2016 as an outlier, and base calculations for party-by-party success rates on the data for 2012 and 2020.
# MaximumMaxLeaderSuccess <-80# Typical (for where we have no previous data)# This is what the figure was in the past, it is now being bumped up to 2/3#AverageLeaderSuccess <- 60AverageLeaderSuccess <-2/3*100
We’ll apply a maximum of 80% and assign a typical chance of success of 66.6666667% where we don’t have earlier data. For the Liberalų sąjūdis, I will treat them as a continuation of the LRLS. Note also that the DSVL is not running in any of the second round elections so the figures are irrelevant.
The second round of the election was simulated \(5\times 10^{4}\) times. Essentially a weighted coin was flipped for each constituency in each simulated election.
The probability of the leader winning is given by the adjusted probabilities we have calculated — if they lose, the challenger wins the seat.
The many simulations were then compiled to see how many seats on average the party will win — the “expected” number of seats.
Monte Carlo isn’t necessary here
I realise that because these are essentially the sum of many independent probability distributions, the calculations could be done without running the Monte Carlo. I have the Monte Carlo code ready and it’s (slightly) faster to rerun it than to put together the calculations which would assemble the expected values given the known independent distributions for each of my variables.
Summary_Part_B <- tallies %>%mutate(ExpectedSeats =if_else(is.na(ExpectedSeats), 0, ExpectedSeats)) %>%mutate(Rounded=round(ExpectedSeats))SummarySummaryPartB <- Summary_Part_B %>%summarise(Party="All", `ExpectedSeats`=sum(`ExpectedSeats`),`Rounded`=sum(`Rounded`))#kable(bind_rows(Summary_Part_B,SummarySummaryPartB), digits=2)total_expected <- first_round_parl_data %>%select(-x,-y,-row,-theta) %>%unique() %>%full_join(Summary_Part_B, by=c("party_short"="Party")) %>%left_join(Part_A_Parties_2024 %>%rename("Expected First Round Winners"=n), by=c("party_short"="Party")) %>%replace_na(list(party_long="Unknown", party_short="Independent","Expected First Round Winners"=0,seats=0, ExpectedSeats=0,Rounded=0)) %>%relocate("Expected First Round Winners", .before=ExpectedSeats) %>%filter(ExpectedSeats >0| seats >0) %>%rename("Party (Lithuanian)"=party_long, "Party"="party_short","First Round Seats"="seats", "Second Round Seats (expected)"=ExpectedSeats,"Second Round Seats (rounded)"= Rounded) %>%mutate(Total =`First Round Seats`+`Expected First Round Winners`+`Second Round Seats (rounded)`) %>%mutate(`Party (Lithuanian)`=case_when ( Party =="LP"~"Laisvės partija", Party =="LT"~"Partija „Laisvė ir teisingumas“",.default = Party ) ) %>%arrange(desc(Total))kable(total_expected, digits=2)
Party (Lithuanian)
Party
First Round Seats
Expected First Round Winners
Second Round Seats (expected)
Second Round Seats (rounded)
Total
LSDP
LSDP
20
2
18.52
19
41
TS-LKD
TS-LKD
18
3
19.27
19
40
PPNA
PPNA
15
0
5.89
6
21
DSVL
DSVL
8
0
3.05
3
11
LVŽS
LVŽS
6
0
4.80
5
11
LS
LS
8
0
1.28
1
9
Independent
Independent
1
0
2.92
3
4
LLRA
LLRA
2
0
1.03
1
3
Laisvės partija
LP
0
0
0.79
1
1
Partija „Laisvė ir teisingumas“
LT
0
0
0.46
0
0
Plot of expected outcome
Code
parl_data_expected <- total_expected %>%rename(party_long=`Party (Lithuanian)`, party_short=Party, seats=Total)expected_round_parl_data <-parliament_data(election_data = parl_data_expected,type="semicircle", party_seats = parl_data_expected$seats,parl_rows =5)ggplot(expected_round_parl_data, aes(x=x, y=y, colour=party_short)) +geom_parliament_seats(size=5) +theme_ggparliament() +scale_colour_manual(values = party_colours_names) +guides(colour =guide_legend(title=NULL)) +labs( title="Expected outcome of 2024 Seimas elections",subtitle="Probabilistic simulation based on past party results",caption="Calculations and graph by Richard Martin-Nielsen\nDetails at http://projects.martin-nielsen.ca/OneFortyOne")
Histograms of Seat Allocation
Histograms can show the distribution of the different numbers of seats for each party. The lump being further right means that the party is likely to win more seats. A narrow lump indicates that the party’s chances are limited in a tight range, usually because it is only competing for a limited number of seats in the second-round.
Lithuania’s governments are typically formed as a coalition among several parties, as in many other MMP systems. You can consider a few coalitions. LRT has set out some:
Code
CoalitionCounts <- TalliedTotalSeatsWide %>%transmute("CentreRight"= .$"TS-LKD"+.$LP+.$DSVL,"Existing"= .$"LS"+.$LP+.$"TS-LKD","CentreLeft"= .$LSDP+.$DSVL+.$"LVŽS","LeftWithNA"= .$LSDP+.$PPNA+.$"LVŽS","Liberals"= .$LS +.$LP, "RedBlue"= .$"TS-LKD"+ .$LSDP )CentreRightFreq <- CoalitionCounts %>%group_by(CentreRight) %>%tally() %>%mutate(Coalition="CentreRight", f = n/runs,Seats=CentreRight,LeadParty="TS-LKD")ExistingFreq <- CoalitionCounts %>%group_by(Existing) %>%tally() %>%mutate(Coalition="Existing", f = n/runs,Seats=Existing,LeadParty="TS-LKD")CentreLeftFreq <- CoalitionCounts %>%group_by(CentreLeft) %>%tally() %>%mutate(Coalition="CentreLeft", f = n/runs,Seats=CentreLeft,LeadParty="LSDP")LeftWithNAFreq <- CoalitionCounts %>%group_by(LeftWithNA) %>%tally() %>%mutate(Coalition="LeftWithNA", f = n/runs,Seats=LeftWithNA,LeadParty="LSDP")LiberalsFreq <- CoalitionCounts %>%group_by(Liberals) %>%tally() %>%mutate(Coalition="Liberals", f = n/runs,Seats=Liberals,LeadParty="LS")RedBlueFreq <- CoalitionCounts %>%group_by(RedBlue) %>%tally() %>%mutate(Coalition="RedBlue", f = n/runs,Seats=RedBlue,LeadParty="LSDP")AllFrequencies <-bind_rows(CentreRightFreq, CentreLeftFreq, ExistingFreq, LeftWithNAFreq, LiberalsFreq, RedBlueFreq)CoalitionDescriptions <-tribble(~`Label`, ~`Parties`, ~`Note`,"Centre Right", "TS-LKD, LP and DSVL", "","Existing", "TS-LKD, LS, LP", "The governing coalition since 2020 elections","Centre Left", "LSDP, LVŽS and DSVL", "","Left With Nemunas Dawn", "LSDP, PPNA and LVŽS", "Also includes Farmers & Greens","Liberals", "LS and LP", "The two Liberal parties","Red Blue", "TS-LKD and LSDP", "A \"grand coalition\" of the TS-LKD and LSDP")kable(CoalitionDescriptions)
Label
Parties
Note
Centre Right
TS-LKD, LP and DSVL
Existing
TS-LKD, LS, LP
The governing coalition since 2020 elections
Centre Left
LSDP, LVŽS and DSVL
Left With Nemunas Dawn
LSDP, PPNA and LVŽS
Also includes Farmers & Greens
Liberals
LS and LP
The two Liberal parties
Red Blue
TS-LKD and LSDP
A “grand coalition” of the TS-LKD and LSDP
When you look at the seat distributions expected for these possible coalitions individually, it’s quickly clear the Centre-Right is well-shy of the 71 seat margin to form a majority, and doesn’t differ much from the expected seat distribution for the current coalition. The Centre-Left coalition has a very slight chance of passing the 71 seat mark.
With the addition of the Nemunas Dawn (PPNA) and the Farmers and Greens (LVŽS), the LSDP would likely to be able to form a coalition government.
Code
AllFrequencies %>%filter(!Coalition =="Liberals") %>%ggplot() +facet_wrap(vars(Coalition)) +geom_col(aes(x=Seats,y=f, colour=Coalition,fill=Coalition),width=1) +# position = "dodge2") +# geom_line(aes(x=Seats,y=f, colour=Coalition,alpha=0.4)) +# geom_jitter(aes(x=Seats,y=f, colour=Coalition,alpha=0.4), width=0.02, height=0) +theme_light() +scale_y_continuous(labels=scales::percent) +scale_x_continuous(limits=c(0,90)) +labs(#title = paste(thisYear, "Seimas Elections: First to Second Round vote shift by first round leader"),#subtitle = "Arrows crossing the diagonal indicate a change in winner",x ="Seats",y ="Frequency" ) +scale_fill_manual(breaks =c( "CentreRight", "CentreLeft", "Existing", "LeftWithNA","Liberals", "RedBlue" ),values =c( "Blue", "Red", "Grey", "Orange", "Yellow", "Purple") )+scale_colour_manual(breaks =c( "CentreRight", "CentreLeft", "Existing", "LeftWithNA","Liberals", "RedBlue" ),values =c( "Blue", "Red", "Grey", "Orange", "Yellow", "Purple") )+guides(alpha="none") +#theme(legend.position ="none") +geom_vline(xintercept=70.5)
Superimposing these graphs doesn’t make the prospects of coalition-building much simpler. If coalition leaders choose not to include the Nemunas Dawn party, it makes it more difficult to pull together a group of MPs numbering 71 or more – which is why I have plotted the numbers for a grand coalition of the LSDP and the TS-LKD.
To be clear, I don’t think a grand coalition of the TS-LKD and the LSDP is likely; I’m not sure it is possible. It is nonetheless likely that the two parties together are likely to have enough seats that they could, if they wished, form a government without any other coalition partners.
Conclusions
It’s still about 24 hours early to conclude that this system will predict the outcome of the second round of the 2024 Parliamentary elections. (I’m writing this shortly after midnight, the morning of 27 October.)
I think this approach is likely to do fairly well again this year.
Four years ago I suggested different ways this system could be improved. Some of these are still open, and in a week we will have data for four elections, at which point it may be worth looking at how the different match-ups influence the chances of an upset in the second-round.