Mathematica Lab 9

MATH 220E Spring 2025

Instructions: Work through this notebook and complete the three problems below. Since this notebook contains code that you have to run before your solution code will work, you can put your solutions in this notebook — just make sure it’s clear where your solutions are (use headings and text cells) and explain your work. Alternately, you can write your solutions in a separate notebook. When finished, submit your notebook to the Mathematica Lab 9 assignment on Moodle.

Import Data

First we’ll import a data file. It’s a list of 100 Senators from the 116th United States Congress (2019-2021) and their voting records. We will use dot products to get a sense for how much different senators agree or disagree.

We will first import the data and then write some simple functions to help us access the data we want.

I have placed the data file on my website. Run the following code cell to download the file and import its data into Mathematica.

file=Import["https://mlwright.org/teaching/math220s25/files/senate_data.csv"]

If the import worked correctly, you should see an output that, if you click “show more,” becomes a giant list of senators and their voting records above.

Basically the data is stored as a matrix. The Dimensions function will tell us the size of the data we imported.

Dimensions[file]

The output {100, 723} tells us that there are 100 rows, corresponding to 100 senators. There are 723 columns. The first three columns contain senator names, parties, and states, while the remaining 720 columns contain vote data.

We can view a row corresponding to an individual senator like this:

file[[1]]

In the vote data, a 1 indicates a vote of “aye” (that is, “yes”), while a -1 indicates a vote of “nay” (that is, “no”)= "nay/no". A zero indicates “abstain” (that is, no vote).

In the next few code cells, we define some functions to help us access the data we want. First we’ll define a function senator that returns the name of a senator for an index that we supply.

senator[n_]:=file[[n]][[1]]

Try it out:

senator[1]

senator[2]

Next we’ll define a function senatorvote that returns the voting records for a senator.

senatorvote[n_]:=Drop[file[[n]],3](* Ignores the first three columns, since we just want the votes, not the other info. *)

Try it out:

senatorvote[1]

senatorvote[2]

Comparing Voting Records

We’re going to use the dot product of two voting records to determine the extent to which two senators agree. If two senators agree on a particular vote, the product of their values for that vote’s entry will be 1. If two senators disagree, the product will be -1. If one or both didn’t vote the product will be 0.

Thus, adding up all the products (which is the same as taking the dot product of two senators’ voting vectors) gives a sense of how much they agree or disagree. A large value for the dot product means lots of agreement, whereas a small value for the dot product means lots of disagreement.

The higher the number, the closer two senators’ records are. Since there are 720 recorded votes, the maximum dot product value is 720 (for two identical voting records) and the minimum is -720 (for two exactly opposite voting records). Let’s compute the dot product of the voting records for the first two senators:

senatorvote[1].senatorvote[2]

The result of -80 isn’t informative by itself; what we want to do is compare lots of different voting vectors. For example, Susan Collins is a moderate Republican senator from the state of Maine.  So we’ll find her voting record then see who was most like her and most different from her.

How do we find Collins’s so that we can compare others to her?  One way is to use the Position function to search our data matrix, like this:

Position[file,"Collins"]

So the name Collins occurs in row 98, column 1, meaning that Collins is the 98th senator in the list. Let’s check:

senator[98]

Now we’ll compare Collins’s voting record to all the others. That’s 100 dot products to compute. One way to do this easy computation is with the Table function. The Table function is extremely useful for computing a list of values.

Table[senatorvote[98].senatorvote[i],{i,1,100}]

In the computations below, we’ll find whose voting record is farthest away by finding the minimum value among all the dot products (with the Min function), then finding the position of that minimum. Once we find the position, we can just call up that senator with the senator function.

Min[Table[senatorvote[98].senatorvote[i],{i,100}]]

Position[Table[senatorvote[98].senatorvote[i],{i,100}],Min[Table[senatorvote[98].senatorvote[i],{i,100}]]]

senator[3]

Whose record is closest to Collins’s?  We’ll search for a max instead of a min.

Max[Table[senatorvote[98].senatorvote[i],{i,100}]]

Position[Table[senatorvote[98].senatorvote[i],{i,100}],Max[Table[senatorvote[98].senatorvote[i],{i,100}]]]

senator[98]

Oh yeah, of course Collins’s record is closest to Collins’s own record. We actually want the second closest so the answer is actually a different senator! Note the use of the number 2 in the RankedMax function below.

RankedMax[Table[senatorvote[98].senatorvote[i],{i,100}],2]

Position[Table[senatorvote[98].senatorvote[i],{i,100}],RankedMax[Table[senatorvote[98].senatorvote[i],{i,100}],2]]

senator[50]

We learned that Edward Markey, a fairly liberal Democrat from Massachusetts, voted quite differently from Susan Collins and Lisa Murkowski (a moderate Republican from Alaska) voted similarly.  No surprises, right?

Next let’s find out how the two Minnesota senators voted in comparison to Collins. First we have to find them. There are many ways, here’s one:

Position[file,"MN"]

“MN” appears in the two positions above. State information is always in the 3rd column (hence the 3’s), and names in the first column. So the MN senators are senator #53 and #88.  Let’s compute their dot product with Collins voting record and guess whether they are Republican or Democrat (even if you know).

senatorvote[53].senatorvote[98]

senatorvote[88].senatorvote[98]

Hmm. These distances are not very conclusive. Since Collins is Republican, it could be that senator #53 is Democrat. Senator #88 is also probably a Democrat, but maybe more moderate than #53 since they are a bit closer to Collins.

Let’s compare the Minnesota senators to #33 Bernie Sanders (a very liberal senator from Vermont) and to #7 Mitch McConnell (the Republican Minority Leader from Kentucky).

Position[file,"Sanders"][[1,1]]

Position[file,"Mcconnell"][[1,1]]

senatorvote[53].senatorvote[33]

senatorvote[88].senatorvote[33]

senatorvote[53].senatorvote[7]

senatorvote[88].senatorvote[7]

The 219 and 203 above make it more clear that senators #53 and #88 are Democrats, since they match Sanders better than Collins. In fact, senator #53 is Amy Klobuchar and senator #88 is Tina Smith, both Democrats (and still the two senators from Minnesota).

senator[53]

senator[88]

We can get all of the information on Klobuchar with the code file[[53]], or we can focus on her name, party, and state that are in columns 1-3.

file[[53]]

file[[53,1;;3]]

Problem 1

(a) Pick a state other than Minnesota and find the names of the two senators. Find their distance from Collins (or Sanders or McConnell) and guess whether they are Republican or Democrat.
(b) Determine their party affiliations. These data are stored in file as we saw with Klobuchar above.
(c) Identify which senators are closest and farthest away from your two: find the max/min values and find the names of the corresponding senators.

Work on your solution here. Use formatting (such as background colors) to distinguish your solution from all of the other cells in this notebook. Explain your work using text cells and code comments.

Compare All Voting Records

We’ve found the senators closest to and farthest from Collins and your two senators. Next we want to be able to do this for all 100 senators in the data and display the results.

Rather than use the Table function to do a bunch of computations related just to Collins, we’ll use For loops to create closest and farthest functions, and then display the results.

closestvoter[n_]:=Module[
(* initialize local variables *) 
{max=0,close=0,k,i},

(* loop *)
For[i=1,i<101,i++,
(* compute dot product *) 
k=senatorvote[n].senatorvote[i];

(* if k is the current maximum, then store k and i *) 
If[k≥max && i≠n, 
max=k;
close=i;
];
];

(* return the index of the closest senator *) 
close]

The closestvoter function above finds the closest voter to senator n. Here’s what the different pieces mean:

Module: A module defines a block of code that we can call by name.

{max=0, close=0, k, i}: We’re going to track the maximum dot product as we go along, but here we start it at 0. We’re also going to track the closest senator as we go along, and here we set it to senator 0 (which doesn’t exist, but that’s ok). Variables k and i are indexes that we will use in our loop, and we declare them here so that they don’t conflict with other variables named k or i that might exist in Mathematica’s memory.

For[i=1, i<101, i++, ...]: This is the usual For loop syntax, where i starts at 1 and continues as long as it is less than 101, advancing by 1 each time.

k=senatorvote[n].senatorvote[i]: We compute the dot product and set it equal to k.

If[k≥max && i≠n, max=k; close=i]: Each time we go through the loop, Mathematica will check to see if the dot product is bigger than the dot products we computed previously. If so, the new max is set equal to k and the new closest senator is set equal to i.  We need i not equal to n, otherwise the closest senator to senator n would always be themself.

Problem 2

Define a new function called farthestvoter that finds the senator whose voting record is farthest away from senator n. Make appropriate changes to the function above, making sure to use variables like “min” and “far” rather than “max” and “close.”  Check that farthestvoter[98] returns the value 3.

Work on your solution here. Use formatting (such as background colors) to distinguish your solution from all of the other cells in this notebook. Explain your work using text cells and code comments.

Table of closest voters

Now we want to compute and display the closet voter to each senator. One way to do this is to use Table to compute all of the closest voters, and use Grid to display the data, like this:

Grid[
Table[{n,closestvoter[n]},{n,1,100}],
Frame->All
]

That’s good data, but it would be better to have the names of the senators and their party affiliations rather than just their numbers. Also, we’ll throw in the dot products just for fun. Remember that the senator[n] function displays a vector of info for a senator, of which the first entry is the name. Going back to the original data, the 2nd entry of file[[n]] gives party affiliation. We’ll use the Table function to assemble the data we want for each senator, just listing the first ten rows for now. We can display the data in a nice table with headers using TableForm, as shown here:

(* get data on the fist ten senators *) 
data=Table[{senator[n],file[[n]][[2]],senator[closestvoter[n]],file[[closestvoter[n]]][[2]],senatorvote[n].senatorvote[closestvoter[n]]},{n,1,10}];
(* display the data in a table with headers *)
TableForm[data, TableHeadings->{Range[10],{"Name","Party","Closest","Party","dot prod"}}]

Problem 3

Make a copy of the previous code cell and modify the data line so that the farthest voter information is included. Also add a heading for the “Farthest” column in the table. You can test your adjustments with just 10 senators, but then make sure you have a final grid with all 100. Once you have the full table, what conclusions can you draw from the information? What other questions might you ask? (e.g., Which Democrat is closest to Collins? Which pair of senators is closest of all? farthest of all?)

Work on your solution here. Use formatting (such as background colors) to distinguish your solution from all of the other cells in this notebook. Explain your work using text cells and code comments.

Extra credit

This section is optional. The table you just created has good info, but there can be many senators that are closest/farthest to a given senator. Here’s another quick way to get closest info:

closest[n_]:=Position[Table[senatorvote[n].senatorvote[i],{i,100}],RankedMax[Table[senatorvote[n].senatorvote[i],{i,100}],2]]

For[i=1,i<20,i++,Print[closest[i]]]

closest[14]

Notice that senators #5 and #48 are equally close to senator #14. We didn’t get that info from the closestvoter function:

closestvoter[14]

For extra credit, improve the closestvoter and farthestvoter functions so that they return all closest and farthest voters, not just one. Then re-create the data table from Problem 3 to include this information in the table.

Work on your solution here. Use formatting (such as background colors) to distinguish your solution from all of the other cells in this notebook. Explain your work using text cells and code comments.