The acquittal of the president in the Senate impeachment trial earlier this year led many observers to note the feat of “mental gymnastics” required by the Senators who voted to acquit. With the sole exception of Mitt Romney, Republican Senators managed to sit for days listening to the litany of offences committed by the president—only to walk away claiming that none of it rose to the level of the impeachable.

I thought I’d use this case to try to visualize what mental gymnastics as a process of meaning-making looks like in practice. How did U.S. Senators make sense of their votes in the impeachment trial? Seeking to answer this question, I first collected official statements from all Senators who released one following the trial. (You can find them here in case you’d like to analyze them yourself.) All but eleven Senators issued such a statement.

For the analysis, I used the latest release of my textnets package (version 0.4). This allows me to show not just how each individual Senator made sense of their decision, but also what their meaning-making processes look like in relation to each other. In other words, I can visualize the field of discursive positions Senators took.

(This also gives me a chance to show off some of the cool things you can do with textnets, since its documentation is not yet complete.)

In [1]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib
In [2]:
from collections import Counter

import pandas as pd
from textnets import Corpus, Textnet

First, I download the data from the repository linked above and prepare it for the creation of the textnet.

In [3]:
data = pd.read_csv('https://edu.nl/ffd4q', index_col='senator')
data['statement_date'] = pd.to_datetime(data['statement_date'])
In [4]:
corpus = Corpus(data['statement'])
In [5]:
tokens = corpus.tokenized(stem=False, 
                          lower=False,
                          remove=['R', 'D', 'WASHINGTON', 'D.C.', 'Washington',
                                  'February', '-30-',
                                  'released', 'following', 'statement', 
                                  'deliver', 'remark'])

I construct the textnet from the words that appear in at least 25 of the statements. The resulting data structure—the textnet—is a two-mode (bipartite) network with links between two sets of nodes: documents (Senators’ statements), and the words they contain.

In [6]:
tn = Textnet(tokens,
             doc_attrs=data[['party']].to_dict(),
             min_docs=25)
In [7]:
Counter(tn.graph.vs['type'])
Out[7]:
Counter({'doc': 89, 'term': 100})

There are 100 words that occur in at least 25 of the statements. Common stop words are excluded, as well as a few additional boilerplate words that occur in many statements. All 89 statements are included in the analysis.

In [8]:
tn.plot(label_doc_nodes=True,
        label_term_nodes=True,
        vertex_shape=['rectangle' if v['party'] is None 
                      else ('triangle-down' if v['party'] in ('Democrat', 'Independent') else 'triangle')
                      for v in tn.vs],
        vertex_color=['gold' if v['party'] is None 
                      else ('dodgerblue' if v['party'] == 'Democrat'
                      else ('lightgreen' if v['party'] == 'Independent' else 'indianred'))
                      for v in tn.vs],
        vertex_label_size=6)
Out[8]: