# Copyright Eric Stansifer 2020 # This code is pretty messy... you don't want to use it import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.patches as mp # 1976-2018-senate.tab # from: https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/PEJ5QU # legislators-current.json and legislators-historical.json from theunitedstates.io # List of US senators from # https://www.congress.gov/search?searchResultViewType=expanded&q={%22congress%22:%22all%22,%22source%22:%22members%22,%22chamber%22:%22Senate%22}&pageSize=100&page=4 # Votes on scotus nominations from # https://www.senate.gov/legislative/nominations/SupremeCourtNominations1789present.htm # approximate, used for sorting purposes def last_name(name): parts = name.split() while parts[-1].lower() in ['jr', 'sr', 'jr.', 'sr.', 'i', 'ii', 'iii', 'iv']: parts = parts[:-1] l = parts[-1] if l[-1] == ',': return l[:-1] else: return l class Election: def __init__(self, state, year, state_abbr, off_schedule = False): self.state = state self.state_abbr = state_abbr self.year = year self.off_schedule = off_schedule self.total_votes = None self.name2votes = {} self.winner_name = None def add_candidate(self, name, votes, total_votes): if self.total_votes is None: self.total_votes = total_votes else: if self.total_votes != total_votes: print(self.state, self.year, self.total_votes, total_votes) assert self.total_votes == total_votes if name in self.name2votes: self.name2votes[name] += votes else: self.name2votes[name] = votes def postprocess(self): # The 2018 Minnesota and Mississippi vote totals are incorrect # as they combine the regular and special elections if self.total_votes != sum(self.name2votes.values()): self.total_votes = sum(self.name2votes.values()) # print(self.state, self.year, self.total_votes) # print(sum(self.name2votes.values())) # print(self.name2votes) name = None votes = None for name_ in self.name2votes: if (votes is None) or self.name2votes[name_] > votes: name = name_ votes = self.name2votes[name] self.winner_name = name def winner_last_name(self): return last_name(self.winner_name) def winner_id(self): return '{} (?-{})'.format(last_name(self.winner_name), self.state_abbr) def winner_votes(self): return self.name2votes[self.winner_name] class Elections: def __init__(self): self.elections = {} self.year2senate = {} # year -> [Election] self.state2election = {} # state -> [Election] def read_from_file(infile = '1976-2018-senate.tab'): self = Elections() with open(infile, 'r') as f: f.readline() for line in f: line = line.strip() parts = line.split('\t') if parts[8] != '"gen"': continue # For standard elections, senators start serving # the year after the election on Jan 3 year = int(parts[0]) + 1 state = parts[1][1:-1] state_abbr = parts[2][1:-1] special = (parts[9] == '"TRUE"') name = parts[10][1:-1] party = parts[11][1:-1].lower() votes = int(parts[14]) total_votes = int(parts[15]) key = (state, year, special) if key not in self.elections: self.elections[key] = Election(state, year, state_abbr) if key == ('Rhode Island', 1989, False): if name == 'John H. Chaffee': name = 'John H. Chafee' if key == ('Wyoming', 1979, False): if name == 'Raymond B. Whitaker': votes = 50456 if name == 'Alan Kooi Simpson': votes = 82908 self.elections[key].add_candidate(name, votes, total_votes) self.add_missing_data() for e in self.elections.values(): e.postprocess() self.precompute_senate() return self def add_missing_data(self): special_elections = [ ('New Hampshire', 'NH', 'John A. Durkin (D)', 'September 16, 1975'), # ('Minnesota', 'David Durenberger (R)', 'November 7, 1978'), # ('Alabama', 'Donald Stewart (D)', 'November 7, 1978'), # ('Washington', 'WA', 'Dan Evans (R)', 'November 8, 1983'), ('Washington', 'WA', 'Dan Evans (R)', 'January 3, 1984'), # Actually Evans appointed Sept 1983, and elected Nov 1983, but ran into problems with Jackson # being elected in 1982. # ('North Carolina', 'Terry Sanford (D)', 'November 4, 1986'), # ('Indiana', 'Dan Coats (R)', 'November 6, 1990'), # ('Hawaii', 'Daniel Akaka (D)', 'November 6, 1990'), ('Pennsylvania', 'PA', 'Harris Wofford (D)', 'November 5, 1991'), # ('California', 'Dianne Feinstein (D)', 'November 3, 1992'), ('North Dakota', 'ND', 'Kent Conrad (D)', 'December 4, 1992'), ('Texas', 'TX', 'Kay Bailey Hutchison (R)', 'June 5, 1993'), # ('Tennessee', 'Fred Thompson (R)', 'November 8, 1994'), # ('Oklahoma', 'Jim Inhofe (R)', 'November 8, 1994'), ('Oregon', 'OR', 'Ron Wyden (D)', 'January 30, 1996'), # ('Kansas', 'Sam Brownback (R)', 'November 5, 1996'), # ('Georgia', 'Zell Miller (D)', 'November 7, 2000'), # ('Missouri', 'Jim Talent (R)', 'November 5, 2002'), # ('Wyoming', 'John Barrasso (R)', 'November 4, 2008'), # ('Mississippi', 'Roger Wicker (R)', 'November 4, 2008'), ('Massachusetts', 'MA', 'Scott Brown (R)', 'January 19, 2010'), # ('Illinois', 'Mark Kirk (R)', 'November 2, 2010'), # ('Delaware', 'Chris Coons (D)', 'November 2, 2010'), # ('New York', 'Kirsten Gillibrand (D)', 'November 2, 2010'), # ('West Virginia', 'Joe Manchin (D)', 'November 2, 2010'), ('Massachusetts', 'MA', 'Ed Markey (D)', 'June 25, 2013'), ('New Jersey', 'NJ', 'Cory Booker (D)', 'October 16, 2013'), # ('South Carolina', 'Tim Scott (R)', 'November 4, 2014'), # ('Hawaii', 'Brian Schatz (D)', 'November 4, 2014'), # ('Oklahoma', 'James Lankford (R)', 'November 4, 2014'), # ('Alabama', 'Doug Jones (D)', 'December 12, 2017') ('Alabama', 'AL', 'Doug Jones (D)', 'January 3, 2018') # Elected Dec 12 2017, joined senate on Jan 3 2018 ] for state, state_abbr, name, date in special_elections: year = int(date[-4:]) e = Election(state, year, state_abbr, off_schedule = True) e.add_candidate(name[:-4], 1, 1) self.elections[(state, year, True)] = e appointees = [ ('North Carolina', 'NC', 'Jim Broyhill (R)', '1986', 0, 0), ('Nebraska', 'NE', 'David Karnes (R)', 'March 11, 1987', 0, 0), ('Maine', 'ME', 'George J. Mitchell (D)', 'May 17, 1980', 0, 0), ('Mississippi', 'MS', 'Cindy Hyde-Smith (R)', 'April 9, 2018', 0, 0), ('Minnesota', 'MN', 'Tina Smith (D)', 'January 3, 2018', 0, 0), ('Alabama', 'AL', 'Luther Strange (R)', 'February 9, 2017', 0, 0), ('Arizona', 'AZ', 'Jon Kyl (R)', 'September 5, 2018', 0, 0), ('Colorado', 'CO', 'Michael Bennet (D)', 'January 21, 2009', 0, 0), ('Illinois', 'IL', 'Roland Burris (D)', 'January 12, 2009', 0, 0), ('New York', 'NY', 'Kirsten Gillibrand (D)', 'January 26, 2009', 0, 0), ('West Virginia', 'WV', 'Carte Goodwin (D)', 'July 16, 2010', 0, 0), ('Delaware', 'DE', 'Ted Kaufman (D)', 'January 15, 2009', 0, 0), # ('Florida', 'FL', 'George LeMieux (R)', 'September 9, 2009', 0, 0), ('Florida', 'FL', 'George LeMieux (R)', 'actually 2009, 2010', 0, 0), ('New Jersey', 'NJ', 'Bob Menendez (D)', 'January 17, 2006', 0, 0), ('Tennessee', 'TN', 'Harlan Mathews (D)', 'January 2, 1993', 0, 0), ('Hawaii', 'HI', 'Daniel K. Akaka (D)', 'May 16, 1990', 0, 0), ('California', 'CA', 'John Seymour (R)', 'January 7, 1991', 0, 0), ('Indiana', 'IN', 'Dan Coats (R)', 'January 3, 1989', 0, 0), ('Georgia', 'GA', 'Kelly Loeffler (R)', 'January 6, 2020', 0, 0), ('Arizona', 'AZ', 'Martha McSally (R)', 'January 2, 2019', 0, 0) ] other_missing = [ ('North Dakota', 'ND', 'Mark Andrews (R)', '1981', 210347, 299272) ] for state, state_abbr, name, date, votes, total_votes in (appointees + other_missing): year = int(date[-4:]) e = Election(state, year, state_abbr) e.add_candidate(name[:-4], votes, total_votes) self.elections[(state, year, True)] = e def precompute_senate(self): # [(year, senA, senB)] # list of instances where senB replaced senA and senA was the most recently # elected. year is the year of senA's election. exceptions = [ # ('Washington', 'Henry M. Jackson', 'Dan Evans'), # ('Arizona', 'John McCain', 'Jon Kyl'), # ('Minnesota', 'Al Franken', 'Tina Smith'), # ('Alabama', 'Luther Strange', 'Doug Jones'), # ('Alabama', 'Jeff Sessions', 'Luther Strange'), # ('Mississippi', 'Thad Cochran', 'Cindy Hyde-Smith') (1983, 'Henry M. Jackson', 'Dan Evans'), (2017, 'John McCain', 'Jon Kyl'), (2015, 'Al Franken', 'Tina Smith'), (2017, 'Luther Strange', 'Doug Jones'), (2015, 'Jeff Sessions', 'Luther Strange'), (2015, 'Thad Cochran', 'Cindy Hyde-Smith'), (2007, 'Hillary Rodham Clinton', 'Kirsten Gillibrand'), (1991, 'Albert Gore Jr.', 'Harlan Mathews'), (1989, 'Spark Matsunaga', 'Daniel K. Akaka'), (1989, 'Pete Wilson', 'John Seymour'), (1989, 'John Heinz', 'Harris Wofford'), (1987, 'David Karnes', 'Bob Kerrey'), (1986, 'Jim Broyhill', 'Terry Sanford'), (2017, 'Johnny Isakson', 'Kelly Loeffler') ] y0 = 1977 y1 = 2020 self.year2senate = {} for year in range(y0, y1 + 1): self.year2senate[year] = [] # state -> [Election] self.state2election = {} for e in self.elections.values(): if not (e.state in self.state2election): self.state2election[e.state] = [] self.state2election[e.state].append(e) for state in self.state2election: self.state2election[state].sort(key = lambda e : e.year) for state in self.state2election: es = self.state2election[state] for y in self.year2senate: names = [] senators = [] for e in sorted(es, reverse = True, key = lambda e_ : e_.year): if e.year > y: continue if e.winner_name in names: continue if len(names) == 1 and ((e.year, e.winner_name, names[0]) in exceptions): continue names.append(e.winner_name) senators.append(e) if len(senators) == 2: break self.year2senate[y].extend(senators) for year in self.year2senate: self.year2senate[year].sort(key = lambda e : e.winner_last_name()) def get_election(self, year, name_id): name = last_name(name_id[:-7]) state_abbr = name_id[-3:-1] for e in self.year2senate[year]: if e.state_abbr == state_abbr and e.winner_last_name() == name: return e sens = [] for e in self.year2senate[year]: if e.state_abbr == state_abbr: sens.append(e.winner_name) print("Missing senator:", year, name_id, ' found:', sens[0], ', ', sens[1]) print('@', last_name(sens[0]), '@') print('@', name, '@') return None def compute_senator_votes(es, infile = 'nomination_votes', print_appointees = True): with open(infile, 'r') as f: lines = f.readlines() if print_appointees: print('|Justice|Year|Senator|State|Vote|') print('|:-|:-|:-|:-|:-|') result = [] ind = 0 while ind < len(lines): justice = lines[ind].strip() ind += 1 party, nominator = lines[ind].strip().split() ind += 1 nomination_time = lines[ind].strip() year = int(nomination_time.split(',')[1].strip()) ind += 1 votes = [0, 0, 0, 0, 0, 0, 0] # Yeas, Nays, others # votes for Yeas, votes for Nays, votes for others while (ind < len(lines)) and (',' in lines[ind]): name_id, vote = lines[ind].split(',', maxsplit = 1) ind += 1 vote = vote.strip() e = es.get_election(year, name_id) if vote == 'Yea': votes[0] += 1 votes[3] += e.winner_votes() elif vote == 'Nay': votes[1] += 1 votes[4] += e.winner_votes() else: votes[2] += 1 votes[5] += e.winner_votes() if e.winner_votes() == 0 and print_appointees: print('|{}|{}|{}|{}|{}|'.format( last_name(justice), year, e.winner_name, e.state, vote)) while ind < len(lines) and len(lines[ind].strip()) == 0: ind += 1 result.append((justice, year, party, nominator, nomination_time, votes)) return result def plot_confirmations(outfile, confirmation_votes, nominator_votes): nom_pct = [100 * v[3] / (2 * v[3] - v[4]) for v in nominator_votes] nom_graph_xs = [] nom_graph_ys = [] for i in range(len(nominator_votes)): nom_graph_xs.append(nominator_votes[i][0]) nom_graph_xs.append(nominator_votes[i][0] + 4) nom_graph_ys.append(nom_pct[i]) nom_graph_ys.append(nom_pct[i]) plt.clf() x0 = 1981 x1 = 2021 # plt.plot([x0, x1], [0, 0], color = 'black', linewidth = 3) plt.plot([x0, x1], [50, 50], color = 'black', linestyle = 'dotted', linewidth = 1) # plt.plot([x0, x1], [100, 100], color = 'black', linewidth = 3) plt.gca().add_patch(mp.Rectangle((1981, 0), 12, 100, facecolor=(1, 0, 0, 0.2), zorder = 0)) plt.gca().add_patch(mp.Rectangle((1993, 0), 8, 100, facecolor=(0, 0, 1, 0.2), zorder = 0)) plt.gca().add_patch(mp.Rectangle((2001, 0), 8, 100, facecolor=(1, 0, 0, 0.2), zorder = 0)) plt.gca().add_patch(mp.Rectangle((2009, 0), 8, 100, facecolor=(0, 0, 1, 0.2), zorder = 0)) plt.gca().add_patch(mp.Rectangle((2017, 0), 4, 100, facecolor=(1, 0, 0, 0.2), zorder = 0)) plt.plot(nom_graph_xs, nom_graph_ys, color = 'black', zorder = 1) xs = [] ys_nominator = [] ys_senate = [] colors = [] for confirmation_vote in confirmation_votes: justice, year, party, nominator, nomination_time, senate_votes = confirmation_vote yeas = senate_votes[0] nays = senate_votes[1] yeas_votes = senate_votes[3] nays_votes = senate_votes[4] if party == 'D': colors.append((0, 0, 1)) else: colors.append((1, 0, 0)) xs.append(year + 0.5) if yeas == 0: pct = -1 else: pct = 100 * yeas_votes / (yeas_votes + nays_votes) ys_senate.append(pct) for i in range(len(nominator_votes)): nom_year = nominator_votes[i][0] if (nom_year <= year) and (year < nom_year + 4): ys_nominator.append(nom_pct[i]) # plt.text(year + 0.2, pct - 4, '{} - {}'.format(yeas, nays), fontsize = 7) if yeas > 0: plt.text(year + 0.3, pct - 4, str(yeas), fontsize = 7) plt.scatter(xs, ys_nominator, s = 16, c = colors, marker = 's', edgecolors = 'black', zorder = 2) plt.scatter(xs, ys_senate, s = 16, c = colors, marker = 'o', edgecolors = 'black', zorder = 3) for i in [5, 7, 8, 10, 12, 14]: plt.text(nominator_votes[i][0] + 0.3, 1, last_name(nominator_votes[i][2]), fontsize = 6) plt.ylim(0, 100) plt.xlim(x0, x1) # plt.savefig('fig_scotus_confirmations1.png', dpi = 200) plt.savefig(outfile, dpi = 200) return ys_nominator def run(): # year, name, votes, difference with runner up nominator_votes = [ (1961, 'D', 'John F. Kennedy', 34220984, 112827), (1965, 'D', 'Lyndon Johnson', 43127041, 15951287), (1969, 'R', 'Richard Nixon', 31783783, 511944), (1973, 'R', 'Richard Nixon', 47168710, 17995488), (1977, 'D', 'Jimmy Carter', 40831881, 1683247), (1981, 'R', 'Ronald Reagan', 43903230, 8423115), (1985, 'R', 'Ronald Reagan', 54455472, 16878120), (1989, 'R', 'George H. W. Bush', 48886597, 7077121), (1993, 'D', 'Bill Clinton', 44909806, 5805256), (1997, 'D', 'Bill Clinton', 47400125, 8201370), (2001, 'R', 'George W. Bush', 50460110, -543816), (2005, 'R', 'George W. Bush', 62040610, 3012171), (2009, 'D', 'Barack Obama', 69498516, 9550193), (2013, 'D', 'Barack Obama', 65915795, 4982291), (2017, 'R', 'Donald Trump', 62984828, -2868686) ] es = Elections.read_from_file() confirmation_votes = compute_senator_votes(es) plot_confirmations('fig_scotus_confirmations1.png', confirmation_votes[1:], nominator_votes) ys_nominator = plot_confirmations('fig_scotus_confirmations2.png', confirmation_votes, nominator_votes) print_table = True if print_table: cur_justices = [1991, 1994, 2005, 2006, 2009, 2010, 2017, 2018, 2020] print('') print('') print('|Justice|Year|Nominator|Senate|\'Yea\'|\'Nay\'|\'Yea\'%|') print('|:-' * 7 + '|') line = [None] * 7 lines = [] idx = 0 for i, confirmation_vote in enumerate(confirmation_votes): justice, year, party, nominator, nomination_time, senate_votes = confirmation_vote yeas = senate_votes[0] nays = senate_votes[1] others = senate_votes[2] yeas_votes = senate_votes[3] nays_votes = senate_votes[4] other_votes = senate_votes[5] line[0] = last_name(justice) # line[1] = '{} {}'.format(year, nomination_time.split(',')[0]) line[1] = str(year) line[2] = '{} {:.2f}%'.format(last_name(nominator), ys_nominator[i]) line[3] = '{} - {}'.format(yeas, nays) n = last_name(justice) if year == 1967 and n == 'Marshall': line[3] = '69 - 11' if year == 1969 and n == 'Burger': line[3] = '74 - 3' if year == 1970 and n == 'Blackmun': line[3] = '94 - 0' if year == 1971 and n == 'Powell': line[3] = '89 - 1' if year == 1971 and n == 'Rehnquist': line[3] = '68 - 26' if year == 1975 and n == 'Stevens': line[3] = '98 - 0' if yeas == 0: line[4] = '-' line[5] = '-' line[6] = '-' else: line[4] = str(yeas_votes) line[5] = str(nays_votes) # line[6] = str(other_votes) line[6] = '{:.2f}%'.format(100 * yeas_votes / (yeas_votes + nays_votes)) if year in cur_justices: for i in range(len(line)): line[i] = '**' + line[i] + '**' lines.append('|' + '|'.join(line) + '|') for l in reversed(lines): print(l) if False: es = Elections.read_from_file() votes = compute_senator_votes(es) for a, b, c, d, e in votes: print(a, ',', c, d) print(e[:4]) if False: es = Elections.read_from_file() for e in es.state2election['Washington']: print(e.year, e.winner_name) for e in es.year2senate[1986]: if e.state == 'Washington': print(e.state, e.year, e.winner_name) if False: es = Elections.read_from_file() noms = [ ('Anthony M. Kennedy', 'R Reagan', 'February 3, 1988'), ('Antonin Scalia', 'R Reagan', 'September 17, 1986'), ('William H. Rehnquist', 'R Reagan', 'September 17, 1986'), ('Sandra Day O\'Connor', 'R Reagan', 'September 21, 1981') ] for name, nominator, date in noms: year = int(date[-4:]) print('') print(name) print(nominator) print(date) for e in es.year2senate[year]: print(e.winner_id() + ', Yea') # print(len(es.year2senate[1988])) # print(len(es.year2senate[1986])) # print(len(es.year2senate[1986])) # print(len(es.year2senate[1981])) if __name__ == "__main__": run()