nominate 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,382 @@
1
+ class Dwnominate
2
+ def initialize
3
+ @sessions = []
4
+ end
5
+
6
+ def add_session(session)
7
+ @sessions.push session
8
+ end
9
+
10
+ def sessions
11
+ @sessions
12
+ end
13
+
14
+ def dwnominate(prefix = 'dw_')
15
+ path = File.expand_path(File.dirname(__FILE__))
16
+ self.dw_check(path)
17
+ Dir.mkdir('nominate') unless Dir.exist?('nominate')
18
+ Dir.chdir('nominate')
19
+ self.check_ws()
20
+ self.write_dw(prefix)
21
+ system path + '/dw-nominate'
22
+ Dir.chdir('..')
23
+ end
24
+
25
+ def dw_check(path)
26
+ # compile DW-NOMINATE if needed
27
+ entries = Dir.entries(path)
28
+ if not entries.include?('dw-nominate')
29
+ puts 'You have not compiled DW-NOMINATE on this computer.'
30
+ puts 'DW-NOMINATE must be compiled to run.'
31
+ puts 'Would you like to compile DW-NOMINATE? (y/n)'
32
+ puts '(Requires sudo privileges.)'
33
+ answer = gets.chomp.downcase
34
+ if answer == 'y'
35
+ Dir.chdir(path) do
36
+ system 'sudo gfortran ' + path + '/DW-NOMINATE.FOR -w -o ' +
37
+ path + '/dw-nominate'
38
+ entries = Dir.entries(path)
39
+ if not entries.include?('dw-nominate')
40
+ puts 'Compiling failed.'
41
+ exit
42
+ else
43
+ puts ''
44
+ puts 'Code compiled successfully.'
45
+ end
46
+ end
47
+ else
48
+ exit
49
+ end
50
+ end
51
+ end
52
+
53
+ def check_ws()
54
+ @sessions.each_with_index do |session, i|
55
+ if session.prefix == nil
56
+ session.prefix = 'session_' + (i+1).to_s + '_'
57
+ self.check_for_session(session)
58
+ else
59
+ self.check_for_session(session)
60
+ end
61
+ end
62
+ end
63
+
64
+ def check_for_session(session)
65
+ if not Dir.entries(Dir.pwd).include?(session.prefix + 'legislators.csv')
66
+ Dir.chdir('..')
67
+ session.wnominate(session.prefix)
68
+ Dir.chdir('nominate')
69
+ end
70
+ end
71
+
72
+ def write_dw(prefix)
73
+ # write_vote_matrix()
74
+ # write_transposed_vote_matrix()
75
+
76
+ # 1) Rollcall data file
77
+
78
+ # setting variables
79
+ leg_session = 1
80
+ legs = {}
81
+ legNum = 1
82
+ stateNum = " 1"
83
+ district = " 0"
84
+ stName = " VERMONT"
85
+ parties = {}
86
+ party = 1
87
+ votes = { "Y" => "1", "N" => "6", 'M' => '9' }
88
+ final = []
89
+
90
+ # processing
91
+ @sessions.each do |session|
92
+ lines = IO.readlines(session.prefix + 'votes.csv')
93
+ lines.each_with_index do |line, i|
94
+ data = line.gsub("||", "| |").gsub("||", "| |").gsub("|\n", "| ").chomp.split("|")
95
+ legName = data[0]
96
+ legParty = data[1]
97
+ # making legislator IDs
98
+ if legs[legName] == nil
99
+ legs[legName] = " "*(6-legNum.to_s.length) + legNum.to_s
100
+ legNum += 1
101
+ end
102
+ # making party IDs
103
+ if parties[legParty] == nil
104
+ parties[legParty] = " "*(5-party.to_s.length) + party.to_s
105
+ party += 1
106
+ end
107
+ # converting votes to a numeric string
108
+ voteNums = ' '
109
+ data[2..-1].each do |vote|
110
+ begin
111
+ voteNums += votes[vote]
112
+ rescue
113
+ puts "Unrecognized vote: " + vote + ", from " + legName + ", " +
114
+ session + " " + legParty
115
+ end
116
+ end
117
+ shortName = legName.gsub(/[^A-Za-z -]/, '')[0..9] + " "*(10-legName.gsub(/[^A-Za-z -]/, '')[0..9].length)
118
+ if shortName.delete(' ') == ''
119
+ puts 'Blank legislator name-'
120
+ puts "Line #{(i+1).to_s}, '#{session.prefix}votes.csv'"
121
+ puts 'Legislators must have non-blank names to be included.'
122
+ puts ''
123
+ next
124
+ end
125
+ final.push sprintf("%4d", leg_session) + legs[legName] + stateNum +
126
+ district + stName + parties[legParty] + " " + shortName + voteNums
127
+ end
128
+ leg_session += 1
129
+ end
130
+ File.open(prefix + "rollcall_matrix.vt3", 'w') { |f1| f1.puts final }
131
+
132
+
133
+ # 2) Transposed rollcall data file
134
+
135
+ sessionNums = []
136
+ lines = IO.readlines(prefix + "rollcall_matrix.vt3")
137
+
138
+ lines.each do |line|
139
+ if not sessionNums.include? line[0..3]
140
+ sessionNums.push line[0..3]
141
+ end
142
+ end
143
+
144
+ final = []
145
+
146
+ sessionDatas = []
147
+ sessionNums.each do |session|
148
+ sessionData = []
149
+ lines.each do |line|
150
+ if line[0..3] == session
151
+ sessionData.push line[40..-1].chomp
152
+ end
153
+ end
154
+ sessionDatas.push sessionData
155
+ end
156
+
157
+ y = 0
158
+ sessionDatas.each do |sessionData|
159
+ x = 0
160
+ sessionData[0].each_char do |bill|
161
+ votes = ' '
162
+ sessionData.each do |legVotes|
163
+ begin
164
+ votes += legVotes[x]
165
+ rescue
166
+ puts "Session #{(y+1).to_s} , vote # #{(x+1).to_s}"
167
+ exit
168
+ end
169
+ end
170
+ final.push sessionNums[y] + " "*(5-(x+1).to_s.length) + (x+1).to_s + votes
171
+ x += 1
172
+ end
173
+ y += 1
174
+ end
175
+
176
+
177
+ File.open(prefix + "transposed_rollcall_matrix.vt3", 'w') do |f1|
178
+ final.each do |line|
179
+ f1.puts line
180
+ end
181
+ end
182
+
183
+
184
+ # 3) Legislator data file
185
+
186
+ final = []
187
+ final2 = []
188
+
189
+ legSession = 1
190
+ xpositives = []
191
+ ypositives = []
192
+ @sessions.each_with_index do |session, i|
193
+ flipx = false
194
+ flipy = false
195
+
196
+ # Leg file
197
+
198
+ sessionNum = " "*(4-legSession.to_s.length) + legSession.to_s
199
+ lines = IO.readlines(session.prefix + "legislators.csv")
200
+ lines.delete_at(0)
201
+
202
+ # deciding whether to flip numbers
203
+ xs = []
204
+ ys = []
205
+ if i != 0
206
+ lines.each do |line|
207
+ data = line.split("|")
208
+ name = data[0]
209
+ xs.push data[8] if xpositives.include? name
210
+ ys.push data[9] if ypositives.include? name
211
+ end
212
+ flipx = true if average(xs, i) < 0
213
+ flipy = true if average(ys, i) < 0
214
+ end
215
+
216
+ # writing the legislator file lines
217
+ lines.each_with_index do |line, i|
218
+ data = line.split("|")
219
+ name = data[0]
220
+ shortName = name.gsub(/[^A-Za-z -]/, '')[0..9] +
221
+ " "*(10-name.gsub(/[^A-Za-z -]/, '')[0..9].length)
222
+
223
+ if shortName.delete(' ') == ''
224
+ puts 'Vote is missing a name-'
225
+ puts "Line #{(i+1).to_s}, '#{session.prefix}legislators.csv'"
226
+ puts 'This vote will not be included.'
227
+ puts ''
228
+ next
229
+ end
230
+
231
+ # flipping numbers to orient each session in the same direction
232
+ xnum = data[8]
233
+ xnum = (-(xnum.to_f)).to_s if flipx
234
+ ynum = data[9]
235
+ ynum = (-(ynum.to_f)).to_s if flipy
236
+
237
+ if data[2] == 'NA' or data[3] == 'NA' or
238
+ data[4] == 'NA' or data[5] == 'NA'
239
+ numVotes = ' 0'
240
+ numErrors = ' 0'
241
+ else
242
+ voteTotal = data[2].to_i + data[3].to_i + data[4].to_i + data[5].to_i
243
+ numVotes = " "*(5-voteTotal.to_s.length) + voteTotal.to_s
244
+ errorTotal = data[3].to_i + data[4].to_i
245
+ numErrors = " "*(5-errorTotal.to_s.length) + errorTotal.to_s
246
+ end
247
+ begin
248
+ final.push sessionNum + legs[data[0]] + stateNum + district + stName +
249
+ parties[data[1]] + " " + shortName + " " + dw_format(xnum) +
250
+ dw_format(ynum) +
251
+ " 0.000 0.000 0.000 0.000 0.00000 0.00000" +
252
+ numVotes*2 + numErrors*2 + dw_format(data[6])*2
253
+ rescue
254
+ puts "Error:"
255
+ puts sessionNum
256
+ puts data[0]
257
+ puts legs[data[0]]
258
+ puts parties[data[1]]
259
+ puts format(data[8])
260
+ puts format(data[9])
261
+ puts numVotes*2
262
+ puts numErrors*2
263
+ puts format(data[6])*2
264
+ end
265
+ end
266
+
267
+ xpositives = []
268
+ ypositives = []
269
+ lines.each do |line|
270
+ data = line.split('|')
271
+ # keep track of legislator scores, to decide whether to flip numbers
272
+ # for the next session
273
+ xpositives.push data[0] if data[8].to_f > 0
274
+ ypositives.push data[0] if data[9].to_f > 0
275
+ end
276
+
277
+ # Bill file
278
+
279
+ sessionNum = " "*(4-legSession.to_s.length) + legSession.to_s
280
+ lines2 = IO.readlines(session.prefix + "rollcalls.csv")
281
+ lines2.delete_at(0)
282
+ lines2.each do |line|
283
+ data = line.split("|")
284
+ billNum = " "*(5-data[0].length) + data[0]
285
+ billx = data[8]
286
+ billx = (-(billx.to_f)).to_s if flipx
287
+ billy = data[10].chomp
288
+ billy = (-(billy.to_f)).to_s if flipy
289
+
290
+ final2.push sessionNum[1..3] + billNum + dw_format(data[7]) +
291
+ dw_format(data[9]) + dw_format(billx) + dw_format(billy)
292
+ end
293
+
294
+ legSession += 1
295
+ end
296
+
297
+ File.open(prefix + 'legislator_input.dat', 'w') do |f1|
298
+ final.each do |line|
299
+ f1.puts line
300
+ end
301
+ end
302
+
303
+ File.open(prefix + 'rollcall_input.dat', 'w') do |f1|
304
+ final2.each do |line|
305
+ f1.puts line
306
+ end
307
+ end
308
+
309
+
310
+ # 5) Session data file
311
+
312
+ final = []
313
+ legSession = 1
314
+ @sessions.each do |session|
315
+ sessionNum = " "*(3-legSession.to_s.length) + legSession.to_s
316
+ lines = IO.readlines(session.prefix + "rollcalls.csv")
317
+ rollcalls = " "*(5-(lines.length-1).to_s.length) + (lines.length-1).to_s
318
+ lines = IO.readlines(session.prefix + "legislators.csv")
319
+ legislators = " "*(4-(lines.length-1).to_s.length) + (lines.length-1).to_s
320
+ final.push sessionNum + rollcalls + legislators
321
+ legSession += 1
322
+ end
323
+
324
+ File.open(prefix + 'session_info.num', 'w') do |f1|
325
+ final.each do |line|
326
+ f1.puts line
327
+ end
328
+ end
329
+
330
+
331
+ # 6) DW-NOMSTART.DAT file
332
+
333
+ File.open('DW-NOMSTART.DAT', 'w') do |f1|
334
+ f1.puts prefix + 'rollcall_input.dat'
335
+ f1.puts prefix + 'rollcall_output.dat'
336
+ f1.puts prefix + 'legislator_input.dat'
337
+ f1.puts prefix + 'legislator_output.dat'
338
+ f1.puts prefix + 'session_info.num'
339
+ f1.puts prefix + 'rollcall_matrix.vt3'
340
+ f1.puts prefix + 'transposed_rollcall_matrix.vt3'
341
+ f1.puts 'NOMINAL DYNAMIC-WEIGHTED MULTIDIMENSIONAL UNFOLDING '
342
+ num_of_sessions = sessions.length.to_s
343
+ number_text = ' '*(5 - num_of_sessions.length) + num_of_sessions
344
+ f1.puts ' 2 1 1' + number_text + ' 2 5'
345
+ f1.puts ' 5.9539 0.3463'
346
+ end
347
+
348
+
349
+ puts ''
350
+ puts 'File formatting done.'
351
+ puts ''
352
+ puts ''
353
+
354
+ end
355
+
356
+ end
357
+
358
+
359
+
360
+
361
+ def dw_format(num)
362
+ if num[0] == '-'
363
+ return " " + num.to_f.round(3).to_s + "0"*(6-num.to_f.round(3).to_s.length)
364
+ elsif num == 'NA'
365
+ return dw_format('0')
366
+ else
367
+ return " " + num.to_f.round(3).to_s + "0"*(5-num.to_f.round(3).to_s.length)
368
+ end
369
+ end
370
+
371
+ def average(numbers, si)
372
+ if numbers.length == 0
373
+ puts 'No legislator names matched those of the previous session.'
374
+ puts 'You will not be able to run DW-NOMINATE on these two sessions.'
375
+ puts 'Put names from both sessions in the same format to correct the issue.'
376
+ puts "Sessions #{si.to_s} and #{(si+1).to_s}."
377
+ exit
378
+ end
379
+ sum = 0
380
+ numbers.each { |n| sum = sum + n.to_f }
381
+ sum / numbers.length
382
+ end
@@ -0,0 +1,34 @@
1
+ if( require("wnominate") == FALSE ) {
2
+ print("The R 'wnominate' package is not installed.")
3
+ print("Trying to install wnominate. (Requires sudo privileges.)")
4
+ install.packages("wnominate", repos='http://cran.rstudio.com/')
5
+ if( require("wnominate") ) {
6
+ print("wnominate is now installed.")
7
+ } else {
8
+ stop("Did not install wnominate.")
9
+ }
10
+ }
11
+
12
+ # performing W-NOMINATE analysis
13
+ data <- read.csv("votes.csv", header=F, sep="|", check.names = F, stringsAsFactors=F,
14
+ quote="", row.names = NULL)
15
+ names <- data[, 1]
16
+ legData <- matrix(data[, 2], length(data[, 2]), 1)
17
+ colnames(legData) <- "party"
18
+ data <- data[, -c(1, 2)]
19
+ rc <- rollcall(data, yea = c("Y"), nay = c("N"), missing = c('r'),
20
+ notInLegis = c("M"), legis.names = names, legis.data = legData,
21
+ desc = "NA", source = "NA")
22
+ result <- wnominate(rc, polarity = c(1, 1))
23
+
24
+ write.table(result$legislators, file = "legislators.csv", sep = "|", quote = F)
25
+ write.table(result$rollcalls, file = "rollcalls.csv", sep = "|", quote = F)
26
+ write.table(result$dimensions, file = "dimensions.csv", sep = "|", quote = F)
27
+ write.table(result$eigenvalues, file = "eigenvalues.csv", sep = "|", quote = F)
28
+ write.table(result$beta, file = "beta.csv", sep = "|", quote = F)
29
+ write.table(result$weights, file = "weights.csv", sep = "|", quote = F)
30
+ write.table(result$fits, file = "fits.csv", sep = "|", quote = F)
31
+
32
+ # png('result.png', width=700, height=500, res=100)
33
+ plot(result)
34
+ # dev.off()
@@ -0,0 +1,51 @@
1
+ class Wnominate
2
+ def initialize()
3
+ @legislators = {}
4
+ @rollcalls = []
5
+ # party is 'unknown' by default
6
+ @parties = Hash.new('unknown')
7
+ # variable for assigning each legislator a number, used for DW-NOMINATE
8
+ @x = 1
9
+ end
10
+ attr_accessor :parties
11
+ attr_accessor :prefix
12
+ def add_rollcall(rollcall_hash)
13
+ @rollcalls.push rollcall_hash
14
+ rollcall_hash.each_key do |name|
15
+ if not @legislators.has_key? name
16
+ @legislators[name] = @x
17
+ @x += 1
18
+ end
19
+ end
20
+ end
21
+ def wnominate(file_prefix = 'wnom_')
22
+ Dir.mkdir('nominate') unless Dir.exist?('nominate')
23
+ Dir.chdir('nominate')
24
+ @prefix = file_prefix
25
+ self.write_wnom(@legislators, @rollcalls, @parties)
26
+ path = File.expand_path(File.dirname(__FILE__))
27
+ was_good = system 'Rscript ' + path + '/nominate.R'
28
+ if was_good != true
29
+ puts ''
30
+ puts 'Something went wrong.'
31
+ puts 'If you have not installed R, please install R and try again.'
32
+ exit
33
+ else
34
+ files = ['votes.csv', 'legislators.csv', 'rollcalls.csv',
35
+ 'dimensions.csv', 'eigenvalues.csv', 'beta.csv', 'weights.csv',
36
+ 'fits.csv', 'Rplots.pdf']
37
+ files.each { |file| File.rename(file, file_prefix + file) }
38
+ end
39
+ Dir.chdir('..')
40
+ end
41
+ def write_wnom(legislators, rollcalls, parties)
42
+ final = []
43
+ # write output in format 'Bob Smith|party|Y|Y|N|Y|N|N|...'
44
+ legislators.each_key do |leg|
45
+ line = leg + '|' + parties[leg]
46
+ rollcalls.each { |hash| line << '|' + hash[leg] }
47
+ final.push line
48
+ end
49
+ File.open('votes.csv', 'w') { |f1| f1.puts final }
50
+ end
51
+ end