Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert an excel or spreadsheet column letter to its number in Pythonic fashion

Is there a more pythonic way of converting excel-style columns to numbers (starting with 1)?

Working code up to two letters:

def column_to_number(c):
    """Return number corresponding to excel-style column."""
    number=-25
    for l in c:
        if not l in string.ascii_letters:
            return False
        number+=ord(l.upper())-64+25
    return number

Code runs:

>>> column_to_number('2')
False
>>> column_to_number('A')
1
>>> column_to_number('AB')
28

Three letters not working.

>>> column_to_number('ABA')
54
>>> column_to_number('AAB')
54

Reference: question answered in C#

like image 868
paragbaxi Avatar asked Aug 31 '11 18:08

paragbaxi


3 Answers

There is a way to make it more pythonic (works with three or more letters and uses less magic numbers):

def col2num(col):
    num = 0
    for c in col:
        if c in string.ascii_letters:
            num = num * 26 + (ord(c.upper()) - ord('A')) + 1
    return num

And as a one-liner using reduce (does not check input and is less readable so I don't recommend it):

col2num = lambda col: reduce(lambda x, y: x*26 + y, [ord(c.upper()) - ord('A') + 1 for c in col])
like image 106
Sylvain Avatar answered Oct 09 '22 17:10

Sylvain


One-liners tested in Python 2.7.1 and 3.5.2

excel_col_num = lambda a: 0 if a == '' else 1 + ord(a[-1]) - ord('A') + 26 * excel_col_num(a[:-1])

excel_col_name = lambda n: '' if n <= 0 else excel_col_name((n - 1) // 26) + chr((n - 1) % 26 + ord('A'))

Multi-liners likewise

def excel_column_name(n):
    """Number to Excel-style column name, e.g., 1 = A, 26 = Z, 27 = AA, 703 = AAA."""
    name = ''
    while n > 0:
        n, r = divmod (n - 1, 26)
        name = chr(r + ord('A')) + name
    return name

def excel_column_number(name):
    """Excel-style column name to number, e.g., A = 1, Z = 26, AA = 27, AAA = 703."""
    n = 0
    for c in name:
        n = n * 26 + 1 + ord(c) - ord('A')
    return n

def test (name, number):
    for n in [0, 1, 2, 3, 24, 25, 26, 27, 702, 703, 704, 2708874, 1110829947]:
        a = name(n)
        n2 = number(a)
        a2 = name(n2)
        print ("%10d  %-9s  %s" % (n, a, "ok" if a == a2 and n == n2 else "error %d %s" % (n2, a2)))

test (excel_column_name, excel_column_number)
test (excel_col_name, excel_col_num)

All tests print

         0             ok
         1  A          ok
         2  B          ok
         3  C          ok
        24  X          ok
        25  Y          ok
        26  Z          ok
        27  AA         ok
       702  ZZ         ok
       703  AAA        ok
       704  AAB        ok
   2708874  EXCEL      ok
1110829947  COLUMNS    ok
like image 27
Devon Avatar answered Oct 09 '22 18:10

Devon


Here is one way to do it. It is a variation on code in the XlsxWriter module:

def col_to_num(col_str):
    """ Convert base26 column string to number. """
    expn = 0
    col_num = 0
    for char in reversed(col_str):
        col_num += (ord(char) - ord('A') + 1) * (26 ** expn)
        expn += 1

    return col_num


>>> col_to_num('A')
1
>>> col_to_num('AB')
28
>>> col_to_num('ABA')
729
>>> col_to_num('AAB')
704
like image 8
jmcnamara Avatar answered Oct 09 '22 18:10

jmcnamara