Unchained Unchained Melodies and Unchained Guitars

The Telescoper has a link to a short YouTube clip of the original and excellent version of The Unchained Melody (subsequently made famous by the Righteous Brothers), which led me to a slightly less conventional version:

And at the suggested links at the end of that I was taken to guitarist Pierre Bensusan:

And while I was browsing guitarists, this from The Little Unsaid caught my attention:

Listening to the Pierre Bensusan tracks, I wondered if he had ever played with John Renbourn.  It seems he has, and that Renbourn (and Bert Jansch) were some of his earliest influences:

Interview Double Bill Concert with John Renbourn (2002)

Posted in Bach | Tagged , , , | 1 Comment

Python for VBA users – 2

This post will look in more detail at translating the VBA Quadratic function (described in Python for VBA users – 1) into a Python function, linked to Excel via PyXll.  For the purposes of this exercise I created a new Python module called quadratic2.py, and copied the VBA code into the blank module, using the PyScripter text editor.

After the first pass through of the procedure given in the previous Python post the code looked like this:

@xl_func("numpy_array CoeffA: numpy_array")
def py_Quadratic2(CoeffA):
# Calculate the zeros of the quadratic a*z**2+b1*z+c.
# The quadratic formula, modified to avoid overflow, is used to find the
# larger zero if the zeros are real and both zeros are complex.
# The smaller real zero is found directly from the product of the zeros c/a.
    if CoeffA.shape[0] == 1:
        CoeffA = transpose(CoeffA)
    A = CoeffA[0]
    B1 = CoeffA[1]
    C = CoeffA[2]
    if (A == 0.):
        NR = 1.
        SR = 0.
        if (B1 != 0):
            SR = -C / B1
        LR = 0.
        SI = 0.
        LI = 0.
    elif (C == 0.):
        NR = 1.
        SR = 0.
        LR = -B1 / A
        SI = 0.
        LI = 0.
    else:
        # Compute discriminant avoiding overflow
        B = B1 / 2.
        if (abs(B) >= abs(C)):
            E = 1. - (A / B) * (C / B)
            D = ((abs(E)) * abs(B))**0.5
        else:
            E = A
            if (C < 0.):
                 E = -A
             E = B * (B / abs(C)) - E
             D = ((abs(E)) * (abs(C))**0.5)**0.5
         if (E >= 0.):
            # Real zeros
            NR = 2
            if (B >= 0.):
                D = -D
            LR = (-B + D) / A
            SR = 0.
            if (LR <> 0.):
                SR = (C / LR) / A
            SI = 0.
            LI = 0.
        else:
            # complex conjugate zeros
            SR = -B / A
            LR = SR
            SI = abs(D / A)
            LI = -SI<br />
    if NR == 1:
        QuadA[1, 1] = SR
        QuadA[3, 1] = NR
    else:
        if LR < SR:
            QuadA[1, 1] = LR
            QuadA[2, 1] = SR
            QuadA[3, 1] = NR
            if NR == 0:
                QuadA[1, 2] = LI
                QuadA[2, 2] = SI
                QuadA[3, 2] = 2
        else:
            QuadA[1, 1] = SR
            QuadA[2, 1] = LR
            QuadA[3, 1] = NR
            if NR == 0:
                QuadA[1, 2] = SI
                QuadA[2, 2] = LI
                QuadA[3, 2] = 2
    return QuadA

Note that the function argument is designed to accept a 3 cell range, which may be either a single row or a single column.  The logic for dealing with this has been changed in the Python function, for reasons that will discussed in a later post.  For now just observe that the contents of the three cell input range (CoeffA) are copied into variables A, B1 and C.

After saving this code, and re-loading PyXll in Excel (Using Add-ins, PyXll, Reload PyXll), I entered three quadratic coefficients (a, b and c in ax^2 + bx + c = 0) in adjacent cells, and the function in a blank cell:

=py_Quadratic2(G10:I10)

This returned a Name? message. Before a new module can be used it must be added to the pyxll.cfg file in the PyXll folder.  After adding quadratic2 to the modules list in the cfg file, and re-loading PyXll in Excel I was still getting the Name? message.

The problem this time could be found in the PyXll log file, which showed:

  • 2013-09-28 15:58:00,308 – ERROR : Error importing ‘quadratic2’: name ‘xl_func’ is not defined

The PyXll library must be imported at the top of any code module where it is used.  In addition I would be using maths functions, and also numpy arrays and the transpose function from the numpy library, so the following lines were added to the top of the code module:

from pyxll import xl_func
from math import *
from numpy import *

Saving and re-loading the code, the function now displayed: #NUM! indicating progress, but not quite there yet.

Looking at the log file again, the last message was:

“2013-09-28 16:42:25,936 – ERROR : if NR == 1:
2013-09-28 16:42:25,936 – ERROR : UnboundLocalError: local variable ‘NR’ referenced before assignment.

Looking through the code, you will see that the variable NR is assigned a value 1 if either A or C are equal to zero, but if both are non-zero we arrive at the statement “if NR == 1:” before NR has been assigned any value.  This can be corrected by simply inserting NR = 0 near the top of the code.

Saving and re-loading, the function still shows #NUM!, and the log file now records:

2013-09-28 16:51:23,957 – ERROR : QuadA[1, 1] = SR
2013-09-28 16:51:23,959 – ERROR : NameError: global name ‘QuadA’ is not defined

We need to create the array QuadA before we can assign values to it.  We will look at the Python alternatives for storing arrays (or array-like objects) in a later post; for now we will just create a 3×2 array using the numpy “zeros” function:

QuadA = zeros((3,2))

Note the double brackets required for a 2D array, and that zeros uses () rather than []. Also remember that all Python arrays (and lists, etc.) are zero based, whereas our VBA arrays were dimensioned as base 1.  All the array indices therefore should be reduced by 1:

    if NR == 1:
        QuadA[0, 0] = SR
        QuadA[2, 0] = NR
    else:
        if LR < SR:
            QuadA[0, 0] = LR
            QuadA[1, 0] = SR
            QuadA[2, 0] = NR
            if NR == 0:
                QuadA[0, 1] = LI
                QuadA[1, 1] = SI
                QuadA[2, 1] = 2
...

After making all the required changes, saving the module, and re-loading in Excel, we finally have a function that works:

py_quad

Posted in Excel, Link to Python, NumPy and SciPy, UDFs, VBA | Tagged , , , , , , | 11 Comments

Commenting a block of code in VBA

One way is to manually place an apostrophe at the start of every line in the block.

A much easier way (I just found out today) is:

  • Go to View-Toolbars-Customise
  • Select the Command tab
  • Select the Edit Category on the left
  • Drag the “Comment Block” and “Uncomment Block” icons onto your toolbar.

Now you can comment (or uncomment) as many lines as you like with a single click.

Posted in Excel, VBA | Tagged , , | 9 Comments

Python for VBA users – 1

Following my recent posts on some preliminary experimentation with linking Excel to Python, this will be the first of a more structured series, mainly as a reminder to myself of lessons learnt on the way, but also I hope of use to others starting out with Python.  The focus will be on connecting to Python from within Excel, using the PyXll add-in, combined with the numpy and scipy maths and scientific libraries.  See the link above for suggestions for installation of Python and PyXll.

This post will look at the procedure for transferring a VBA User Defined Function (UDF) to Python.  The basic procedure for creating a Python UDF in PyXll is as follows:

  • Create a Python module to hold the Python code.  A Python module is analogous to a code module in the Visual Basic Editor, but consists of a text file with the extension .py.
  • Add the Python code for the required function.
  • Add an “xl_func” decorator at the top of the Python function code, to expose the function to Excel.
  • Add the name of the Python module to the modules list in the file pyxll.cfg in the main PyXll folder.
  • The Python function will now be available from any Excel file, so long as the PyXll add-in is active.

For the purposes of illustration of this procedure I have chosen to translate VBA functions to solve Quadratic and Cubic equations to Python.  In fact functions to solve polynomial equations can be found in the numpy library, but translating the code is a good exercise, and provides a function that can be easily checked.

The procedure I used to translate the VBA code to Python was:

  • Ensure that the VBA code is properly indented.  In VBA indentation is solely for ease of reading, but in Python the indentation is essential to the logic of the code.  I use the excellent and free Smart Indenter, for automatic indentation of my VBA code.
  • Copy and paste the VBA code to your text editor of choice.  I am currently using PyScripter, a free and open-source Integrated Development Environment.
  • Edit the VBA code to follow Python requirements:
    • Remove all Dim statements.
    • Replace all VBA comment markers (‘) with the Python equivalent (#).
    • Create any global variables, lists or arrays required, and ensure that bracketed index values to arrays are in the correct format (more details in a later post).
    • Modify all If, Do, and similar loops to Python format.  Note that Select Case loops must be replaced with a series of if … elif statements.  Also note that Python is case sensitive, and that VBA statements in Proper Case must be converted to all lower case.
    • Where the VB editor has added a # to integer values this must be removed, since it is the comment character in Python.
    • Where integer fractions are required to be treated as floating point values (e.g. 1/3), at least one of the values must be entered as a decimal: 1.0/3.
    • Replace any VBA exponentiation symbols (^) with the Python equivalent (**).
    • Check the correct spelling for any built-in functions used, and remember to change initial upper case letters to lower case.
    • Comparison operators: <, <=, >, and >= may remain unchanged, but = must be replaced with == (when used for comparison, rather than assignment), and != is preferred to <>.
    • Revise any code segments where the methods used in VBA will not work in Python.
    • Create a return value or array in a format suitable for transfer to Excel.
    • Replace the VBA style return value statement:
      FunctionName  = x
      with the Python:
      return x
      and delete the End Function statement.
    • At the first line, replace VBA:
      Function FunctionName(Parameter as Type, …) as Type
      with Python:
      def FunctionName(Parameter):
    • Add the xl_func “decorator”:
      @xl_func(“type Parameter, … : return type”)

The two screen-shots below show a section of code as pasted from the VBA editor, and as translated to VBA format.  In the next post in this series I will look at this process in more detail, including some of the catches for the unwary.

VBA code pasted to Python editor (Dim statements deleted)

VBA code pasted to Python editor (Dim statements deleted)

VBA2Python1-2

Final Python Code

Posted in Excel, Link to Python, UDFs, VBA | Tagged , , , , , | 2 Comments

Sorting with Python

A previous post provided a VBA Sort function that could be used as a User Defined Function (UDF) to provide a sorted list on the spreadsheet, or called from another VBA routine to sort an array.

I have just written a Python/PyXll routine to provide the same functionality, including sorting on any specified column, and sorting in ascending or descending order.  Here is the code in full:

from operator import itemgetter
@xl_func("var srange, int sortcol, bool rev : var")
def py_Sort(srange, sortcol, rev):
    return sorted(srange, key = itemgetter(sortcol), reverse = rev)

The py_Sort function requires Python and PyXll to be installed, but otherwise works the same as the VBA function, and a quick test indicates that it is about twice as fast.  The screen-shot below shows output from the VBA and Python function:

VBA and Python Sort Functions

VBA and Python Sort Functions

Posted in Arrays, Excel, Link to Python, UDFs | Tagged , , , , , | 5 Comments