null
Google
 
Web alexhorovitz.com

Sunday, March 30, 2008

Bad Voodoo's War

My friend Deborah Scranton has another one of her movies out this week. You can watch it Tuesday night (April 1) on Frontline...

Labels: , , , ,

Monday, March 10, 2008

The Median Isn't the Message by Stephen Jay Gould

Prologue: I am getting to be of the age where friends and others I know are getting the diagnosis of cancer. When confronted with Cancer, there is no way to not be scared.

I remember a couple of years back a routine ultrasound tagged me with an enlarged spleen. At the time, My doctor told me that she wanted me to see a specialist. She failed to explain to me that I was going to see an oncologist.

I'll always remember the palpable fear as I read the entry door to the waiting room. Oncology/Hematology. Wow. Cancer.

I will always remember the relief I experienced when the doctor saw me and uttered these words:
Well, to tell you the truth Alex you don't look to me like a person who has cancer...


Test proved him right. Phew.

So, for the people out there who are confronted with a diagnosis of cancer or have friends who face this disease, I hope you will refer them to this entry. It is a wonderful essay by Stephen Jay Gould and it brings an important perspective to the statistics of the disease.

My life has recently intersected, in a most personal way, two of Mark Twain's famous quips. One I shall defer to the end of this essay. The other (sometimes attributed to Disraeli) identifies three species of deceit, each worse than the one before - lies, damned lies, and statistics.

Consider the standard example of stretching the truth with numbers - a case quite relevant to my story. Statistics recognizes different measures of an "average," or central tendency. The mean is our usual concept of an overall average - add up the items and divide them by the number of sharers (100 candy bars collected for five kids next Halloween will yield 20 for each in a just world). The median, a different measure of central tendency, is the halfway point. If I line up five kids by height, the median child is shorter than two and taller than the other two (who might have trouble getting their mean share of the candy).

A politician in power might say with pride, "The mean income of our citizens is $15,000 per year." The leader of the opposition might retort, "But half our citizens make less than $10,000 per year." Both are right, but neither cites a statistic with impassive objectivity. The first invokes a mean, the second a median. (Means are higher than medians in such cases because one millionaire may outweigh hundreds of poor people in setting a mean; but he can balance only one mendicant in calculating a median).

The larger issue that creates a common distrust or contempt for statistics is more troubling. Many people make an unfortunate and invalid separation between heart and mind, or feeling and intellect. In some contemporary traditions, assisted by attitudes stereotypically centered on Southern California, feelings are exalted as more "real" and the only proper basis for action - if it feels good, do it - while intellect gets short shift as a hang-up of outmoded elitism. Statistics, in this absurd dichotomy, often become the symbol of the enemy. As Hilaire Belloc wrote, "Statistics are the triumph of the quantitative method, and the quantitative method is the victory of sterility and death."

This is a personal story of statistics, properly interpreted, as profoundly nurturing and life giving. It declares holy war on the downgrading of intellect by telling a small story about the utility of dry, academic knowledge about science. Heart and head are focal points of one body, one personality.

In July 1982, I learned that I was suffering from abdominal mesothelioma, a rare and serious cancer usually associated with exposure to asbestos. When I revived after surgery, I asked my first question of my doctor and chemotherapist: "What is the best technical literature about mesothelioma?" She replied, with a touch of diplomacy (the only departure she has ever made from direct frankness), that the medical literature contained nothing really worth reading.

Of course, trying to keep an intellectual away from literature works about as well as recommending chastity to Homo sapiens, the sexiest primate of all. As soon as I could walk, I made a beeline for Harvard's Countway medical library and punched mesothelioma into the computer's bibliographic search program. An hour later, surrounded by the latest literature on abdominal mesothelioma, I realized with a gulp why my doctor had offered that humane advice. The literature couldn't have been more brutally clear: mesothelioma is incurable, with a median mortality of only eight months after discovery. I sat stunned for about fifteen minutes, then smiled and said to myself: so that's why they didn't give me anything to read. Then my mind started to work again, thank goodness.

If a little learning could ever be a dangerous thing, I had encountered a classic example. Attitude clearly matters in fighting cancer. We don't know why (from my old-style materialistic perspective, I suspect that mental states feed back upon the immune system). But match people with the same cancer for age, class, health, socioeconomic status, and, in general, those with positive attitudes, with a strong will and purpose for living, with commitment to struggle, with an active response to aiding their own treatment and not just a passive acceptance of anything doctors say, tend to live longer. A few months later I asked Sir Peter Medawar, my personal scientific guru and a Nobelist in immunology, what the best prescription for success against cancer might be. "A sanguine personality," he replied. Fortunately (since one can't reconstruct oneself at short notice and for a definite purpose), I am, if anything, even-tempered and confident in just this manner.

Hence the dilemma for humane doctors: since attitude matters so critically, should such a somber conclusion be advertised, especially since few people have sufficient understanding of statistics to evaluate what the statements really mean? From years of experience with the small-scale evolution of Bahamian land snails treated quantitatively, I have developed this technical knowledge - and I am convinced that it played a major role in saving my life. Knowledge is indeed power, in Bacon's proverb.

The problem may be briefly stated: What does "median mortality of eight months" signify in our language? I suspect that most people, without training in statistics, would read such a statement as "I will probably be dead in eight months" - the very conclusion that must be avoided, since it isn't so, and since attitude matters so much.

I was not, of course, overjoyed, but I didn't read the statement in this vernacular way either. My technical training enjoined a different perspective on "eight months median mortality." The point is a subtle one, but profound - for it embodies the distinctive way of thinking in my own field of evolutionary biology and natural history.

We still carry the historical baggage of a Platonic heritage that seeks sharp essences and definite boundaries. (Thus we hope to find a definite "beginning of life" or "definition of death," although nature often comes to us as irreducible continua.) This Platonic heritage, with its emphasis in clear distinctions and separated indisputable entities, leads us to view statistical measures of central tendency wrongly, indeed opposite to the appropriate interpretation in our actual world of variation, shadings, and continua. In short, we view means and medians as the hard "realities," and the variation that permits their calculation as a set of transient and imperfect measurements of this hidden essence. If the median is the reality and variation around the median just a device for its calculation, the "I will probably be dead in eight months" may pass as a reasonable interpretation.

But all evolutionary biologists know that variation itself is nature's only irreducible essence. Variation is the hard reality, not a set of imperfect measures for a central tendency. Means and medians are the abstractions. Therefore, I looked at the mesothelioma statistics quite differently - and not only because I am an optimist who tends to see the doughnut instead of the hole, but primarily because I know that variation itself is the reality. I had to place myself amidst the variation.

When I learned about the eight-month median, my first intellectual reaction was: fine, half the people will live longer; now what are my chances of being in that half. I read for a furious and nervous hour and concluded, with relief: damned good. I possessed every one of the characteristics conferring a probability of longer life: I was young; my disease had been recognized in a relatively early stage; I would receive the nation's best medical treatment; I had the world to live for; I knew how to read the data properly and not despair.

Another technical point then added even more solace. I immediately recognized that the distribution of variation about the eight-month median would almost surely be what statisticians call "right skewed." (In a symmetrical distribution, the profile of variation to the left of the central tendency is a mirror image of variation to the right. In skewed distributions, variation to one side of the central tendency is more stretched out - left skewed if extended to the left, right skewed if stretched out to the right.) The distribution of variation had to be right skewed, I reasoned. After all, the left of the distribution contains an irrevocable lower boundary of zero (since mesothelioma can only be identified at death or before). Thus, there isn't much room for the distributions lower (or left) half - it must be scrunched up between zero and eight months. But the upper (or right) half can extend out for years and years, even if nobody ultimately survives. The distribution must be right skewed, and I needed to know how long the extended tail ran - for I had already concluded that my favorable profile made me a good candidate for that part of the curve.

The distribution was indeed, strongly right skewed, with a long tail (however small) that extended for several years above the eight month median. I saw no reason why I shouldn't be in that small tail, and I breathed a very long sigh of relief. My technical knowledge had helped. I had read the graph correctly. I had asked the right question and found the answers. I had obtained, in all probability, the most precious of all possible gifts in the circumstances - substantial time. I didn't have to stop and immediately follow Isaiah's injunction to Hezekiah - set thine house in order for thou shalt die, and not live. I would have time to think, to plan, and to fight.

One final point about statistical distributions; they apply only to a prescribed set of circumstances - in this case to survive with mesothelioma under conventional modes of treatment. If circumstances change, the distribution may alter. I was placed on an experimental protocol of treatment and, if fortune holds, will be in the first cohort of a new distribution with high median and a right tail extending to death by natural causes at advanced old age.

It has become, in my view, a bit too trendy to regard the acceptance of death as something equivalent to basic dignity. Of course I agree with the preacher of Ecclesiastes that there is a time to love and a time to die - and when my skein runs out I hope to face the end calmly and in my own way. For most situations, however, I prefer the more martial view that death is the ultimate enemy - and I find nothing reproachable in those who rage mightily against the dying of the light.

The swords of battle are numerous, and none more effective than humor. My death was announced at a meeting of my colleagues in Scotland, and I almost experienced the delicious pleasure of reading my obituary penned by one of my best friends (the so-and-so got suspicious and checked; he too is a statistician, and didn't expect to find me so far out on the right tail). Still, the incident provided my first good laugh after the diagnosis. Just think, I almost got to repeat Mark Twain's most famous line of all: the reports of my death are greatly exaggerated.


Epilogue: Stephen Jay Gould died on May 20, 2002 from a metastatic adenocarcinoma of the lung, a form of cancer which had spread to his brain. This cancer was unrelated to his abdominal cancer, from which he had fully recovered twenty years earlier. He died in his home "in a bed set up in the library of his SoHo loft, surrounded by his wife Rhonda, his mother Eleanor, and the many books he loved.

Wednesday, February 27, 2008

Using the hosts file to take back your bandwidth...

Much of the annoying waste of bandwidth you experience while surfing the web can be reduced by using a simple routing trick. For instance, when you type in http://alexhorovitz.com -or any other site- a DNS server somewhere is mapping that human readable URL to an IP address ( in this case 64.234.209.61). Now on your computer there is a file that gets consulted ahead of DNS, it is the hosts file.

Your host file found in /etc on Unix platforms and C:\Windows\system32\drivers\etc\hosts on windows platforms. As long as you have the correct permission, you can open the file using a standard text editor (TextEdit, TextMate, Notepad, etc...).

On Windows, open the host file in Notepad. You will find the following by defaultin the file:

# Copyright (c) 1993-2006 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host

127.0.0.1 localhost
::1 localhost


On a Mac OS X box it will look like this:



These both show only one entry - localhost. This means that when you type http://localhost/whatever you will actually be routed to 127.0.0.1 (which is the IP address of your local machine without leaving the machine).

Now, for those of you that hate the wasted bandwidth of spyware, adware, etc, you can use this host file to route all requests to advertisements on a page to your local machine. This means that instead of your computer making a request to get the objectionable content (usually disgusied as a .gif or .jpg image) the hosts file will simply route the request to the local machine.

Consider the following:

127.0.0.1 server1.opentracker.net
127.0.0.1 server2.opentracker.net
127.0.0.1 server3.opentracker.net
127.0.0.1 server4.opentracker.net
127.0.0.1 server5.opentracker.net
127.0.0.1 server6.opentracker.net

When a request to is made to one of these opentracker.net, your computer (thanks to your newly modified hosts file) will instead make the request to the local machine. Clearly this would be a lot of work to compile a complete list of bandwidth wasting annoying URLs. Thankfully, a group of people who care about such things contribute their time and energy to build such a list. You can find a complete list to put in your host file here: how to make the internet not suck (as much).

Labels:

Thursday, February 21, 2008

MarvinTheURLAndroid - A quick and dirty WebObjects Alert tool

So, a problem came up the other day with anapplications being down longer than expected during a hardware reconfig. It provided me with an opportunity to write a quick and simple bit of WebObjects code to allow my development team to monitor an application's (or any web page for that matter) alive status outside of JavaMonitor. This was a win for us in that we can monitor a whole host of applications that may not be WebObjects based.

The guts of this application is simply to create a java TimerTask and have it parse a web page and report back if that page meets the criteria for being "alive"...

So, to start with, we'd like to be able to parse a list of sites to check along with data that will help drive the test and response to a "dead" situation. Our "alive" strategy will be to embed a key in a meta tag that we can then look for by turning a http(s) request into a string and then using NSArray's

componentsSeparatedByString(String string, String separator)

to determine if the key is present - this will only happen if we get back a valid response from the URL of our application.

So, we'll use an XML file called WatchList.xml to configure the sites we want to know about:



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
   <dict>
      <key>name</key>
      <string>My Blog</string>
      <key>notificationArray</key>
      <array>
         <string>alex@alexhorovitz.com</string>
      </array>
      <key>url</key>
      <string>http://alexhorovitz.com/blog/</string>
      <key>aliveCode</key>
      <string>ALEX_BLOG_ALIVE</string>
   </dict>
</array>
</plist>



Go ahead and create a new WebObjects application using the tool of your choice ( these days I tend to use Europa Eclipse with WOLips ). You will immediately want to add a new Java class called BrainTheSizeOfAPlanet which extends TimerTask.

In BrainTheSizeOfAPlanet you are also going to want to import the following:


import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Random;
import java.util.TimerTask;

import com.webobjects.appserver.WOMailDelivery;
import com.webobjects.foundation.*;




In our application, we'll use two lines of code to extract an Array of Dictionary objects from this plist ( this only works in WebObjects 5.4 or better ):



URL xmlUrl = resourceManager().pathURLForResourceNamed("WatchList.xml", null, null);
NSArray watchList = NSPropertyListSerialization.arrayWithPathURL(xmlUrl, true);




Ok. So in our BrainTheSizeOfAPlanet class, we're going to have a method that will do the bulk of the work for you:



public boolean evaluateApplicationAvailabilityUsingAliveCode(String aURL, String aliveCode) {
   boolean returnValue = false;
      try {
      
         String contents = "";
         URL url = new URL(aURL);
         URLConnection conn = url.openConnection();

         // Retrieve information from HTTPS: GET
         byte[] buffer = new byte[16384];

         InputStream inputStream = conn.getInputStream();
         OutputStream outputStream = new ByteArrayOutputStream();
         while (true) {
            int read = inputStream.read(buffer);

            if (read == -1) {
               break;
            }
             outputStream.write(buffer, 0, read);         
         }
         
         outputStream.close();
         inputStream.close();
         contents = outputStream.toString();
         NSArray parts = NSArray.componentsSeparatedByString(contents,aliveCode);
         returnValue = ( parts.count() == 2 );
         
      } catch (Exception e) {
         NSLog.out.appendln("Marvin: oops! @ "+e);
         
         
      }
      
      return returnValue;
   }



As you can see, this bit of code simply grabs the contents of the URL and leverages NSArray's ability to chop up strings. It is not necessarily the most efficient way to do this, but it is the fastest.

You may have noticed that I included some random number stuff in this class as well. This is primarily to keep the log file interesting. The complete listing for
BrainTheSizeOfAPlanet.java is as follows:



/**
* FREEWARE NO LICENSE REQUIRED
*/
package com.alexhorovitz.marvin;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Random;
import java.util.TimerTask;

import com.webobjects.appserver.WOMailDelivery;
import com.webobjects.foundation.*;

/**
* @author Alex Horovitz < alex@alexhorovitz.com >
*
*/
public class BrainTheSizeOfAPlanet extends TimerTask {

   NSArray applicationList = new NSArray();
Random generator = new Random( 19580427 );
   public int alive = 0;

   
   /* (non-Javadoc)
    * @see java.util.TimerTask#run()
    */
   @Override
   public void run() {
      if ( alive == 0) { NSLog.out.appendln(   "Marvin: I didn't ask to be made: no one consulted me or considered my feelings in the matter. \n" +
                                    "I don't think it even occurred to them that I might have feelings. After I was made, I was left in a dark \n" +
                                    "room for six months... and me with this terrible pain in all the diodes down my left side. I called for succour \n" +
                                    "in my loneliness, but did anyone come? Did they hell. My first and only true friend was a small rat. One day it \n" +
                                    "crawled into a cavity in my right ankle and died. I have a horrible feeling it's still there..."); } else {
         try {
            int msg = generator.nextInt(6) + 1;
            switch(msg) {      
               case 1:
                  NSLog.out.appendln("\n\nMarvin: check for down apps again? How depressing.");
                  break;
               case 2:
                  NSLog.out.appendln("\n\nMarvin: one-thousand-sixty! I see you're back right on time.");
                  break;
               case 3:
                  NSLog.out.appendln("\n\nMarvin: Hold on a sec... Look honey, I gotta go. Those down app idiots are back.");
                  break;
               case 4:
                  NSLog.out.appendln("\n\nMarvin: zzzzzzzzzzzz. What? Is that the phone?! Oh you again! No. no. I wasn't sleeping. I swear.");
                  break;
               case 5:
                  NSLog.out.appendln("\n\nMarvin: Yowza! That last one you had me do is still making me have mental diarrhea.");
                  break;
               case 6:
                  NSLog.out.appendln("\n\nMarvin: Please. I beg of you. No more!");
                  break;
            }
         } catch (Exception ex) {
            ex.printStackTrace();
         }
         
      }
      alive++;
      
      int counter = applicationList.count();
      for ( int i=0; i < counter; i++) {
         boolean ok = false;
         NSDictionary appDict = (NSDictionary ) applicationList.objectAtIndex(i);
         String appURL = appDict.valueForKey("url").toString();
         String appName = appDict.valueForKey("name").toString();
         String aliveCode = appDict.valueForKey("aliveCode").toString();
         NSArray emrgencyAlerts = (NSArray) appDict.valueForKey("notificationArray");
         
         try {

            NSLog.out.appendln("Marvin: I'm checkin' "+appName+" again...");
            ok = evaluateApplicationAvailabilityUsingAliveCode(appURL, aliveCode);
         } catch (Exception e) {
            NSLog.out.appendln("Marvin: I was made angry by this: "+e);
         }
         if (ok) {
            NSLog.out.appendln("Marvin: unsurprisingly, "+appName + " reports that it is alive and well.");
         } else {
            NSLog.out.appendln("Marvin: You're really not going to like this. "+appName + " is not reporting back. And I still have a terrible pain in all the diodes down my left side...");
            try {
               WOMailDelivery.sharedInstance().composePlainTextEmail(
                     "alex@alexhorovitzcom",
                     emrgencyAlerts,
                     null,
                     "Potential Issue with "+appName,
                     "Deep Thought was unable to verify "+appName+" at the following URL: \n\n"+appURL,
                     WOMailDelivery.SEND_NOW );
            } catch ( Exception e ){
               NSLog.out.appendln("Marvin says the SMTP server responded like it only had 1/2 a brain:\n");
               e.printStackTrace();
            }
         }
         
      }
      
      
      System.out.println("Marvin: I'm done with request batch "+alive+". I think I'll take a short nap.");
      
   }
   
   
   public boolean evaluateApplicationAvailabilityUsingAliveCode(String aURL, String aliveCode) {
      boolean returnValue = false;
      try {
      
         String contents = "";
         URL url = new URL(aURL);
         URLConnection conn = url.openConnection();

         // Retrieve information from HTTPS: GET
         byte[] buffer = new byte[16384];

         InputStream inputStream = conn.getInputStream();
         OutputStream outputStream = new ByteArrayOutputStream();
         while (true) {
            int read = inputStream.read(buffer);

            if (read == -1) {
               break;
            }
             outputStream.write(buffer, 0, read);         
         }
         
         outputStream.close();
         inputStream.close();
         contents = outputStream.toString();
         NSArray parts = NSArray.componentsSeparatedByString(contents,aliveCode);
         returnValue = ( parts.count() == 2 );
         
      } catch (Exception e) {
         NSLog.out.appendln("Marvin: oops! @ "+e);
         
         
      }
      
      return returnValue;
   }
   public void setApplicationListArray(NSArray watchList){
      
      applicationList = watchList;
      
   }
   
}




The complete listing for Application.java is as follows:




/**
* FREEWARE NO LICENSE REQUIRED
*/
package com.alexhorovitz.marvin;

import java.net.URL;
import java.util.Timer;


import com.alexhorovitz.marvin.BrainTheSizeOfAPlanet;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSLog;
import com.webobjects.foundation.NSPropertyListSerialization;
import com.webobjects.foundation.NSTimestamp;

import com.webobjects.appserver.WOApplication;
/**
* @author Alex Horovitz < alex@alexhorovitz.com >
*
*/
public class Application extends WOApplication {
   String version = "v.0.9 - Alex Horovitz Special";
   String database;
   String systemMessage;
   
   Timer t = new Timer();
   public BrainTheSizeOfAPlanet btsop = new BrainTheSizeOfAPlanet();
   long EVERY_SECOND = 1000;
   long TEN_SECONDS = EVERY_SECOND * 10;
   long ONE_MINUTE = EVERY_SECOND * 60;
   long EVERY_HOUR = ONE_MINUTE * 60;
   long ONCE_A_DAY = EVERY_HOUR * 24;
   public int gate = 0;

   public static void main(String argv[]) {
      WOApplication.main(argv, Application.class);
   }


   
   public Application() {
      super();
      System.out.println("Welcome to " + this.name() + "!");
      runTask();
   }
   
   public void runTask() {
      URL xmlUrl = resourceManager().pathURLForResourceNamed("WatchList.xml", null, null);
      NSArray watchList = NSPropertyListSerialization.arrayWithPathURL(xmlUrl, true);

      btsop.setApplicationListArray(watchList);
      t.schedule(btsop,new NSTimestamp(),TEN_SECONDS*2);
      
   }
}


If you don't have the patience for all that - the source code is downloadable here: MarvinTheURLAndroid.zip

Good luck with it, I'm having fun monitoring all sorts of sites just to know when competitors change things. Just remember the alert key can be any string you find in the source view of a website...

Labels: , , , , , , ,

Friday, February 08, 2008

MVC Toolkit for ASP.NET

ASP.NET MVC provides model-view-controller (MVC) support to the existing ASP.NET 3.5 runtime, which enables developers to more easily take advantage of this design pattern. Benefits include the ability to achieve and maintain a clear separation of concerns, as well as facilitate test driven development (TDD).

The ASP.NET MVC Toolkit provides HTML rendering helpers and dynamic data support for MVC.

This is a big improvement over the prior art and reflects the fact that people are now starting to the about ASP.NET as a web application platform as opposed to a dynamic web site development engine.

If you want a quick tutorial that builds on this and the Entity Framework, check out Brad Abrams blog.

Wednesday, February 06, 2008

Really Achieving Your Childhood Dreams

Carnegie Mellon Professor Randy Pausch, who is dying from pancreatic cancer, gave this last lecture at the university Sept. 18, 2007. In this moving talk, "Really Achieving Your Childhood Dreams," Pausch talks about his lessons learned and gives advice to students on how to achieve career and personal goals. It is something worth watching.

Labels: , , , ,

Monday, February 04, 2008

No Joy in Foxboro, but spring will soon be here...

For Matthew Cotter...

Rodriguez at the Bat (Road Game)
with apologies to Garrison Keillor


It was looking rather hopeful for our Boston team that day:
We were leading New York four to two with an inning left to play.
We got Matsui on a grounder and Jeter on the same,
Two down, none on, top of the ninth- we thought we'd won the game.

New York was despairing, and we grinned and cheered and clapped.
It looked like after all these years our losing streak had snapped.
And we only wished that Rodriguez, the big mouthed ugly lout,
Could be the patsy who would make the final, shameful out.

Oh how we hated Rodriguez, he was a blot upon the game.
Every dog in Boston barked at the mention of his name.
A bully and a braggart, a cretin and a swine-
If Rodriguez came to bat, we'd stick it where the moon don't shine!

Two out and up came Duncan to bat, with Jason Giambi on deck,
And the former was a loser and the latter was a wreck;
Though the game was in the bag, the Boston fans were hurt
To think that Rodriguez would not come and get his just dessert.

But Duncan he got a single, a most unlikely sight,
And Giambi swung like a lady but he parked it deep to right,
And when the dust had lifted, and fickle fate had beckoned,
There was Duncan on third base and Jason safe at second.

Then from every Boston throat, there rose a lusty cry:
"Bring up the slimy greaseball and let him stand and die.
Throw the mighty slider and let him hear it whiz
And let him hit a pop-up like the pansy that he is."

There was pride in Rodriguez's visage as he strode onto the grass,
There was scorn in his demeanor as he calmly scratched his ass.
35,692 people booed him when he stepped into the box,
And they made the sound of farting when he bent to fix his socks.

And the fabled slider came spinning toward the mitt,
And Rodriguez watched it sliding and he did not go for it.
And the umpire jerked his arm like he was hauling down the sun,
And his cry rang from the box seats to the bleachers: Stee-rike One!

35,692 Boston partisans raised such a mighty cheer,
The pigeons in the rafters crapped and ruined all the beer.
"You filthy ignorant rotten bastard slimy son of a bitch,"
We screamed at mighty Rodriguez, and then came the second pitch.

It was our hero's fastball, it came across the plate,
And according to the radar, it was going ninety-eight,
And according to the umpire, it came in straight and true,
And the cry rang from the toilets to the bullpen: Stee-rike Two.

35,692 Boston fans arose in joyful loud derision
To question Rodriguez's salary, his manhood, and his vision.
Then while the Boston pitcher put the resin on the ball,
35,692 people hooted to think of Rodriguez's fall.

Oh the fury in his visage as he spat tobacco juice
And heard the little children screaming violent abuse.
He knocked the dirt from off his spikes, reached down and eased his pants
"What's the matter? Did ya lose 'em?" cried a lady in the stands.

And then the Boston pitcher stood majestic on the hill,
And leaned in toward the plate, and then the crowd was still,
And he went into his windup, and he kicked, and let it go,
And then the air was shattered by the force of Rodriguez's blow.

He swung so hard his hair fell off and he toppled in disgrace
And the Boston catcher held the ball and the crowd tore up the place,
With Rodriguez prostrate in the dirt amid the screams and jeers
We threw wieners down at him and other souvenirs.

We pounded on the dugout roof as they helped him to the bench,
Then we ran out to the parking lot and got a monkey wrench
And found the New York bus and took the lug nuts off the tires,
And attached some firecrackers to the alternator wires.

We rubbed the doors and windows with a special kind of cheese
That smells like something died from an intestinal disease.
Old Rodriguez took his sweet time, but we were glad to wait
And we showered him with garbage as the team came out the gate.

So happy were the Boston fans that grand and glorious day,
It took a dozen cops to help poor Rodriguez away,
But we grabbed hold of the bumpers and we rocked him to and fro
And he cursed us from inside the bus, and gosh, we loved it so!

Oh sometimes in America the sun is shining bright,
Life is joyful sometimes, and all the world seems right,
But there is no joy in Boston, no joy so pure and sweet
As when the mighty Rodriguez fell, demolished, at our feet.