06 May 2009

Put figures etc. in MS Word without the "Figure" tag

One of the annoying things (at least for me) about Microsoft Word is that it wants all your figure numbers to have "Figure" prepended to them. It's all great if you always say "Figure 1" or "Figure 2" but it doesn't work if you like to say things such as "As we see in Figures 1 & 2" and not "As we see in Figure 1 and Figure 2." Furthermore, Word doesn't work well with things such as "Figure 4.12" or, my favorite, having Figure 1 followed by Example 2 followed by Table 3 -- I'm big into continuous numbering.

So here's what I finally came up with as a solution. Type whatever preface you want to appear in the caption of the document ("Figure ") and then run this macro and give your figure a short name with no spaces ("voiceRangesDunstaple" for instance), then two numbers will appear. Leave the first alone; It'll put a number after "Figure" ("Figure 12" for instance). Copy and cut the second number and then paste it in your text somewhere -- it's the reference number to the figure ("See figure 12" -- I like lowercase figure references). You can paste this number multiple places if you need multiple references. And a quick "Ctrl-A, F9" will update all the tables.

You'll need to give this macro a keyboard shortcut if you use it often.

---
Sub MakeFieldCrossRef()
'
' Macro recorded 3/25/2006 by Myke
'
Dim Message, Title, Default, macroName
Message = "Enter a short, unique name for the Xref (no spaces!)"
Title = "Create Crossref"
Default = ""
macroName = InputBox(Message, Title, Default)
Selection.Fields.Add Range:=Selection.Range, Type:=wdFieldEmpty, Text:= _
"AUTONUMLGL \e", PreserveFormatting:=False
Selection.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend
With ActiveDocument.Bookmarks
.Add Range:=Selection.Range, Name:=macroName
.DefaultSorting = wdSortByName
.ShowHidden = False
End With
Selection.MoveRight Unit:=wdCharacter, Count:=1
Selection.InsertCrossReference ReferenceType:="Bookmark", ReferenceKind:= _
wdContentText, ReferenceItem:=macroName, InsertAsHyperlink:=False, _
IncludePosition:=False
Call UpdateAllDocFields
End Sub

29 December 2008

Pachelbel lives on.

Great recent song by The Fratellis on their 2006 album Costello Music, "Ole Black 'N' Blue Eyes."

The first thing that jumped out at me was how closely the progression of the song mirrored the Pachelbel Canon in D's ground bass (though transposed to B-flat):
(Not sure if the right hand part matches the actual guitar parts used in the piece, but they're close enough for illustrative purposes).

But better than seeing similarities, I thought a little remix of the opening minute or so would show just how compatible the two pieces were. Enjoy.

And the original...

05 October 2008

Overwrite IE Favorites with Firefox Bookmarks


I love Firefox, but on Windows IE has one big advantage over it: it's made by the same people who made the OS. This lets IE access its favorites from all sorts of places, not just from the browser. I'm a task-oriented person rather than application-oriented: I tend to think "Gotta pay the bills now" not "Gotta open up the browser and then do things that require a browser, such as bill pay." IE 4.0+ (and Windows98) was oriented towards people like me, by putting the Favorites option in the start menu, every folder, etc. It never really caught on, and in fact the Favorites option was quietly removed from the start menu in either XP or Vista, though it can be re-enabled from the taskbar options.

There are a couple of ways to get your Firefox bookmarks in the Favorites. There are lots of bookmark synchronization programs, though they tend to need to be run manually--which is messy for me. There's also the PlainOlFavorites plug-in for Firefox which lets you just use IE favorites instead of Bookmarks. I loved this plug in for a few years, but I like FF's native Bookmark system enough that I wanted to make my own synchronization system.

Here's a little Python script I came up with. It copies YESTERDAY's Firefox backup over your Favorites (copying the current version would just have been too messy). It requires Python 2.6 or you'll need to install the "simplejson" module and replace all references to "json" with "simplejson" in the code. Do not run this if you use IE favorites: it will DELETE THEM ALL and overwrite them with Firefox favorites. You can comment out the "nukeFavorites()" line if you just want to add to your existing IE favorites. I plop this script into my Startup folder and it works great!


# bookmarks_json.py -- Michael Scott Cuthbert
import json
import copy
import sys
import os
import shutil

class BookmarkMaker(object):

startdir = os.environ["USERPROFILE"] + r'\Favorites'
internetShortcut = "[InternetShortcut]\nURL="

folderpath = os.environ["APPDATA"] + "\\Mozilla\\Firefox\\Profiles\\"
backuppath = r'\bookmarkbackups'

def nukeFavorites(self):
'''deletes everything in the Favorites dir -- do not use this if you update Favorites!'''
nukeem = os.listdir(self.startdir)
for thisfile in nukeem:
if thisfile != "desktop.ini":
thisFullFile = self.startdir + os.sep + thisfile
try:
os.remove(thisFullFile)
except OSError:
shutil.rmtree(thisFullFile)

def run(self):
stringFile = self.openRightFile()
self.parse(stringFile)

def openRightFile(self):
backupPath = self.backupPath()
theseFiles = os.listdir(backupPath);
theseFiles.sort(reverse=True)
for thisFile in theseFiles:
if thisFile.startswith('bookmarks-') and thisFile.endswith('.json'):
return self.openJson(backupPath + os.sep + thisFile)

def backupPath(self):
profile = self.mostRecentProfile(self.folderpath)
return profile + self.backuppath

def mostRecentProfile(self, profilesPath):
'''returns the most recently modified Profile in the profilesPath'''
newestName = ""
newestTime = 0

allDirs = os.listdir(profilesPath)
for thisDir in allDirs:
thisFullPath = profilesPath + os.sep + thisDir
try:
thisModTime = os.path.getmtime(thisFullPath + os.sep + "places.sqlite")
if thisModTime > newestTime:
newestTime = thisModTime
newestName = thisFullPath
except:
pass
return newestName

def openJson(self, jsonFile):
try:
myfile = open(jsonFile) #folderpath + '\\' + bookmarkfile)
except:
raise Exception("Could not open file " + jsonFile)
return myfile.read()

def parse(self, stringData):
self.jsonStuff = json.loads(stringData)
self.realBase = self.findRealBase(self.jsonStuff)
self.parseReally(self.realBase)

def findRealBase(self, searchIn):
return searchIn["children"][0]["children"]

def parseReally(self, startingPoint, currentDir = []):
for thing in startingPoint:
if thing.has_key("children"): ## Bookmark folder
tempdir = copy.copy(currentDir)
tempdir.append(thing["title"])
self.parseReally(thing["children"], tempdir)
elif thing.has_key("uri"): ## Bookmark
self.createFavorite(thing["uri"], os.sep.join(currentDir) + os.sep + thing["title"] + ".url")

def createFavorite(self, uri, filename):
shortcutOut = self.internetShortcut + uri
if not(filename.startswith(os.sep)):
filename = os.sep + filename
filename = self.startdir + filename
try:
self.recursiveMakeDir(filename)
filehandle = open(filename, "w")
filehandle.write(shortcutOut)
except:
pass

def recursiveMakeDir(self, filename):
if filename.count(os.sep) > 12:
raise Exception("Whoa, you sure you want to go so deep and make " + filename + "?")

parentDir = os.path.dirname(filename)
if os.path.exists(parentDir):
return True
else:
self.recursiveMakeDir(parentDir)
os.mkdir(parentDir)

if (__name__ == "__main__"):
bmm = BookmarkMaker()
bmm.nukeFavorites()
bmm.run()

20 September 2008

New mail script

I used to use this little program called "nfrm" (or frm -n) to tell me on log in to a unix system if I had new mail. Unfortunately what worked in the past no longer was keeping accurate lists in the world where I was also checking my mail via IMAP and the iPhone, where there's a lot of mail I've been informed (in some way) about, but have never actually read.

Here's a quick Perl script that I wrote which checks three folders (my inbox, a work-related box, and mailing lists) for new messages. It requires Mail::MboxParser (install via "cpan" then "install Mail::MboxParser" or have your sysadmin do it for you):

#!/usr/bin/perl

use Mail::MboxParser;
use Term::ReadKey;
use strict;

my ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize();
my $available_space = $wchar - 13;
my $from_length = int($available_space * .33);
my $subject_length = int($available_space * .67);

my @mailboxes = @ARGV;
if (scalar(@mailboxes) == 0) {
@mailboxes = (["**** ", $ENV{'MAIL'}],
["NBER ", "/homes/nber/cuthbert/mail/nber"],
["Lists", "/homes/nber/cuthbert/mail/lists"]);
}

my $parseropts = {
enable_cache => 1,
enable_grep => 1,
cache_file_name => '/tmp/msc-mbox-cache-file',
};

foreach my $box (@mailboxes) {
next unless -e $box->[1];
my $mb = Mail::MboxParser->new($box->[1],
decode => 'ALL',
parseropts => $parseropts);
while (my $msg = $mb->next_message) {
if ($msg->header->{status} !~ /R/) {
my $name = $msg->from->{name};
if (!$name) { $name = $msg->from->{email} }
$name =~ s/\"//g;
$name =~ s/^\s+//;
$name = substr($name, 0, $from_length);

my $subject = $msg->header->{subject};
$subject = substr($subject, 0, $subject_length);

printf("%-8s %-${from_length}s %-${subject_length}s\n", $box->[0], $name, $subject);
}
}
}

14 September 2008

Sexist ol' folks! (maybe not so much)


I was reading Matt Bai's most recent post on the presidential campaign; not as good as his usual missives, but still basically entertaining (and informative I didn't realize he was white; I guess I assumed Bai as an Asain name). But I focused mainly on a graphic that the New York Times placed in a sidebar showing the percentage of Americans who believe that a woman will be elected president during their lifetimes. Another statistical graphic without any exegesis; when will the Times learn? Based on the main gist of the article, I suppose we are to conclude that younger people are more open to the idea of a female president, and, therefore, more likely to conclude that a woman will be elected. Of the under-45 voters who expressed an opinion (95%), 17% believed that a woman would not be elected in their lifetimes. On the other hand, of the over-65 voters with an opinion (83%), 47% believed that no woman would be elected while they lived.

But the young have something more important than open-mindedness that accounts for the statistical difference: life expectancy. Simply put, the under 45 voting crowd will witness many more elections, and, therefore many more chances of electing a female president. Let's plot this out. Given a probability X of a woman being elected in any election, the probability that a woman would NOT be elected after N elections (or 4N years) is:
(1-X)^N
The following chart gives the probability that a woman would not be elected given various values for X (left column) and N (top row):



Let's say that an "Under 45 voter" is 35 (a mean of 18-45 weighed toward the fact that younger people vote in lower percentages) and let's call an "Over 65 voter" 70 years old. It's obvious that a woman will not be elected president this year, so we'll say that the 70 year olds have, on average 3.5 more elections in their lifetime where a woman could be elected (I'm giving a them more longevity than you'd expect since there is some probability that McCain will be elected and die in his first term, giving a Mrs. President Palin. Though this poll was taken in June, after Hillary had dropped out but before McCain's choice of a female VP seemed likely). The 35-year-old voter has, on average, 10 more elections (I'm giving them a few fewer years on average to live because a 35 year old hasn't proved they won't die before 70). And we'll define "likely to become president" the standard way: over 50% chance. That is, if the voter thinking that it was likely had to bet with even odds, he or she would bet for a woman president being elected.

What can we extrapolate from the chart and formula? If a (rational) 35-year-old voter said that he thought it likely for a woman to be elected, he would need to believe that a woman has over a 7% chance of being elected in any given election, while a 70-year-old voter needs to believe that there's an 18% chance of a woman being elected.

A quick-and-dirty way of extrapolating from the results of the poll to the table is to say if 25% of people believe X is likely, we should look at the point in the table where X is 25% likely to happen. (There are better ways depending on what distribution of beliefs you think voters have, but this should work well enough for now). So since 47% of over-65 voters don't believe a woman will be elected in the next 3.5 elections, we can suppose that they believe women have a 20% chance of being elected any given election.

What about the younger crowds? 17% of the under-45 crowd believe that after the next 10 elections we still won't have a woman president. That works out to belief in a 16% chance of a woman being elected president any given election. So if anything, the young are less optimistic of women's chances of breaking that ultimate glass ceiling than older voters are.

What CBS (the poll takers) should have done is removed a variable by asking, "Do you think it is likely that a woman will be elected president in the next 30 years?" so that all their respondents were thinking along the same time frame. The woman vs. men answer (an apparent +5% optimism gap toward women) is similarly skewed by age and probably underestimates women's optimism about the topic. Since women live longer, they probably disproportionately make up the (more optimistic) over-65 audience. In fact, we may be able to explain the entire age difference in this response by the gender differences and women's propensity to live longer.

(I realize now that I have used the word "optimism" throughout this article, assuming, dear readers, that like me you'd view the possibility of a female president as a good thing. If for some reason you disagree, please comment with your reasons. Oh and the graphic's title should have been Madam President. No one said that the female president needed to be married.)