James Tauber's Blog 2008/10


blog > 2008 >


Blogging Every Day in November

For perhaps the third time, I'm going to try to blog every day for a month, starting tomorrow. I thought I'd make this meta post today as making it my post for tomorrow seems like it would be cheating.

See you back here regularly!

by : Created on Oct. 31, 2008 : Last modified Oct. 31, 2008 : (permalink)


Pinax 0.5.0 Released

Well, I don't need to go into the history as you've all seen the video, but five months ago a project that had been brewing in my mind for a while started taking form and now I'm thrilled to announce the first official release of Pinax.

Thanks to Brian Rosner, Greg Newman, Jannis Leidel and Eric Florenzano—the core team—as well as the authors of additional third party apps we used and people who provided translations, bug reports, etc.

There'll hopefully be a number of big Pinax news items here over the next couple of months, but for now, you can join in the fun and download 0.5.0 from the Pinax Download Page.

by : Created on Oct. 28, 2008 : Last modified Oct. 28, 2008 : (permalink)


Guinea Pigs, Karaoke Machines and Minimum Sample Size

Yesterday I read a very strange quote:

2.86% of guinea pigs admitted to veterinary hospitals in the survey had been injured by karaoke machines

from Charlie Stross talking about an article in The Register.

It got me wondering what the minimum sample size would be to get 2.86%. It turns out, to the right number of significant digits, 1 in 35 gives 2.86% so it's possible it was only 1 case out of only 35 in the survey.

Here's some Python code for working out the minimum sample size that will result in a given decimal. Note that decimal is to be given as a string so significant trailing zeroes can be used. e.g. min_sample("0.1") == 7 whereas min_sample("0.10") == 10 as you would expect.

def min_sample(decimal): fl = float(decimal) assert 0 < fl < 1 sig_digits = len(decimal) - 2 for sample_size in range(1, (10 ** sig_digits) + 1): if round(round(fl * sample_size) / sample_size, sig_digits) == fl: return sample_size

So the next time you read 22% of X or 24% or Y or 2.86% of Z you can quickly work out the sample size could be as low as 9, 17 or 35 respectively.

P.S. I leave it as an exercise to the reader to rewrite using the decimal module and decide whether it's worth it.

by : Created on Oct. 18, 2008 : Last modified Oct. 18, 2008 : (permalink)


HSL Gradients

A few months ago, I wrote a post about Creating Gradients Programmatically in Python. The main use was to generate gradients for things like web site nav bars:

You might try to achieve the glassy look of the default nav button in the first example with a simple linear gradient from #516c7a to #7291a1 and then from #5f7f8f to #87a2af. The problem with dealing with RGB is that it's very difficult to know how to do a variation that differs in hue but has the same saturation and lightness. Or how to have more saturation but keep the hue constant.

So using the formulae from the Wikipedia page on HSL and HSV I implemented a version of gradient.py that takes HSL values instead of RGB.

Instead of specifying #516c7a to #7291a1 and #5f7f8f to #87a2af, this:

is achieved by specifying HSL (200°, 0.2, 0.40) to (200°, 0.2, 0.54) then (200°, 0.2, 0.47) to (200°, 0.2, 0.61). Note that this is much cleaner as the hue stays constant as does the saturation. Only the lightness varies to give the shiny look.

Just by increasing the saturation to 0.5 and keeping everything else the same, we get:

We can then change the hue from 200° to 0°:

With a little bit of experimentation, I found the button looked nicer if it had a slight glow, achieved by increasing the saturation by about 50% at the top and bottom. Here are the same three buttons with saturation 0.3 → 0.2 → 03 (instead of constant 0.2) and 0.7 → 0.5 → 0.7 (instead of constant 0.5):

In every case the lightness gradient was the same from one button to the next.

I found, though, that to get the shiny black, the original lightness didn't work:

Not even varying the lightness by a constant really worked, so the following is achieved with (0, 0.0, 0.40 - 0.25) to (0, 0.0, 0.54 - 0.20) and then from (0, 0.0, 0.47 - 0.25) (0, 0.0, 0.61 - 0.25):

Here is my HSL2RGB function and the reverse RGB2HSL:

[a commenter below has pointed out Python includes functions for this in the standard library already — oh well]

def RGB2HSL(R, G, B):
    fR = float(R / 255.0)
    fG = float(G / 255.0)
    fB = float(B / 255.0)
    ma = max(fR, fG, fB)
    mi = min(fR, fG, fB)
    if ma == mi:
        H = 0
    elif ma == fR:
        H = (60 * ((fG - fB)/(ma - mi))) % 360
    elif ma == fG:
        H = 120 + 60 * ((fB - fR)/(ma - mi))
    elif ma == fB:
        H = 240 + 60 * ((fR - fG)/(ma - mi))
    L = (ma + mi) / 2
    if ma == mi:
        S = 0
    elif L <= 0.5:
        S = (ma - mi) / (2 * L)
    elif L > 0.5:
        S = (ma - mi) / (2 - 2 * L)
    return H, S, L

def HSL2RGB(H, S, L):
    if L < 0.5:
        q = L * (1 + S)
    elif L >= 0.5:
        q = L + S - (L * S)
    p = 2 * L - q
    hk = H / 360.0
    tR = hk + 0.333333
    tG = hk
    tB = hk - 0.333333

    def color(t):
        if t < 0:
            t += 1
        if t > 1:
            t -= 1
        if t < 0.166666:
            c = p + ((q - p) * 6 * t)
        elif 0.166666 <= t < 0.5:
            c = q
        elif 0.5 <= t < 0.666667:
            c = p + ((q - p) * 6 * (0.666667 - t))
        else:
            c = p
        return c

    R = int(color(tR) * 255)
    G = int(color(tG) * 255)
    B = int(color(tB) * 255)
    return R, G, B

by : Created on Oct. 18, 2008 : Last modified Oct. 18, 2008 : (permalink)


London Python Meetup and FOWA

I'm in London this week and next for personal reasons but I'm taking the opportunity to attend FOWA tomorrow and Friday.

I'm also delighted to have a last minute gig talking about Pinax at the London Python Meetup tonight.

If you're going to be at the Python Meetup and/or FOWA, I look forward to catching up with you!

by : Created on Oct. 8, 2008 : Last modified Oct. 8, 2008 : (permalink)


Photo Meme

I'm late to this meme because I haven't been on my laptop (with camera) for ages. But now that I'm traveling, here I am:

This is at Logan airport on my way to London.

by : Created on Oct. 5, 2008 : Last modified Oct. 5, 2008 : (permalink)