nominate 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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