Nathan Grigg

Most common commands

I have been wanting to learn to use pyplot, but haven’t found the time. Last week I was inspired by Seth Brown’s post from last year on command line analytics, and I decided to make a graph of my most common commands.

I began using zshell on my home Mac about six months ago, and I have 15,000 lines of history since then:

$ wc -l ~/.zsh_history
   15273 /Users/grigg/.zsh_history

(Note it is also possible to get unlimited history in bash.)

I compiled a list of my top commands and made a bar chart using pyplot. Since git is never used by itself, I separated out the git subcommands. Here are the results:

top 20 commands

A couple of these are aliases: gis for git status and ipy for ipython. The lunchy command is a launchctl wrapper, j is part of autojump, and rmtex removes Latex log files.

Clearly, it is time to use bb as an alias for bbedit. I already have gic and gia set up as aliases for git commit and git add, but I need to use them more often.

Building the graph

The first step is parsing the history file. I won’t go into details, but I used Python and the Counter class, which takes a list and returns a dictionary-like object whose values are the frequency of each list item. After creating a list of commands, you count them like this:

from collections import Counter
top_commands = Counter(commands).most_common(20)

To make the bar chart, I mostly just copied the pyplot demo from the documentation. Here is what I did.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import matplotlib.pyplot as plt
import matplotlib
import numpy as np

width = 0.6
N = 20
ys = np.arange(N)

# change the font
matplotlib.rcParams['font.family'] = 'monospace'

# create a figure of a specific size
fig = plt.figure(figsize=(5, 5))

# create axes with grid
axes = fig.add_subplot(111, axisbelow=True)
axes.xaxis.grid(True, linestyle='-', color='0.75')

# set ymin, ymax explicitly
axes.set_ylim((-width / 2, N))

# set ticks and title
axes.set_yticks(ys + width / 2)
axes.set_yticklabels([x[0] for x in top_commands])
axes.set_title("Top 20 commands")

# put bars
axes.barh(ys, [x[1] for x in top_commands], width, color="purple")

# Without the bbox_inches, the longer labels got cut off
# 2x version. The fractional dpi is to make the pixel width even
fig.savefig('commands.png', bbox_inches='tight', dpi=160.1)

I still find pyplot pretty confusing. There are several ways to accomplish everything. Sometimes you use module functions and sometimes you create objects. Lots of functions return data that you just throw away. But it works!