1

I have a code snippet to format a 2D array to a specific string format. The code snippet works well with recent versions of NumPy (e.g. 1.9.2), but the same fails with NumPy 1.4.1, which is the current version for CentOS 6.6.

import numpy as np
cfmt = '%14.6E'
vfmt = np.vectorize(cfmt.__mod__)

Traceback (most recent call last):
  File "/usr/lib64/python2.6/site-packages/numpy/lib/function_base.py", line 1762, in __init__
    nin, ndefault = _get_nargs(pyfunc)
  File "/usr/lib64/python2.6/site-packages/numpy/lib/function_base.py", line 1694, in _get_nargs
    raise ValueError, 'failed to determine the number of arguments for %s' % (obj)
ValueError: failed to determine the number of arguments for <method-wrapper '__mod__' of str object at 0xb461e0>

From Numpy-discussion, this appears to be an issue with functools.partial, with a recommend workaround to use lambda. However, here are my failed attempts:

vfmt1 = np.vectorize(lambda x: cfmt.__mod__(x))
vfmt2 = np.vectorize(lambda x: cfmt % (x,), ['|S14'])  # attempt to specify otype

# A 2D array with shape (1, 3)
ar = np.array([[1.0e35, 9.999999, 10.0]])

print(vfmt1(ar))  # [['  1.0000' '  9.9999' '  1.0000']]
print(vfmt2(ar))  # [['  1.0000' '  9.9999' '  1.0000']]

These values appear to be strings clipped to 8 characters, and are essentially garbage.

The expected formatted result using a recent version of NumPy is:

array([['  1.000000E+35', '  9.999999E+00', '  1.000000E+01']], type='|S14')

Any suggestions on how to get the expected outputs using older versions of NumPy? Or generally how to vectorize formatting of numeric arrays to character arrays?

duplode
  • 33,731
  • 7
  • 79
  • 150
Mike T
  • 41,085
  • 18
  • 152
  • 203

1 Answers1

2

If your input array is one-dimensional, a simple list comprehension will work:

>>> ar
array([  1.00000000e+35,   9.99999900e+00,   1.00000000e+01])
>>> ar2 = np.array(["%14.6E" % v for v in ar])
>>> ar2
array(['  1.000000E+35', '  9.999999E+00', '  1.000000E+01'], 
      dtype='|S14')

For a 2-d array, a nested list comprehension will work. For example, here's a 2-d array x:

>>> x
array([[  1.00000000e+35,   9.99999900e+00,   1.00000000e+01],
       [  1.23450000e-04,   0.00000000e+00,   1.23000000e+02]])

Here's the nested list comprehension that creates a list of list of strings:

>>> [["%14.6E" % v for v in row] for row in x]
[['  1.000000E+35', '  9.999999E+00', '  1.000000E+01'], ['  1.234500E-04', '  0.000000E+00', '  1.230000E+02']]

If you need the result to be a numpy array, just put that in a call to np.array:

>>> np.array([["%14.6E" % v for v in row] for row in x])
array([['  1.000000E+35', '  9.999999E+00', '  1.000000E+01'],
       ['  1.234500E-04', '  0.000000E+00', '  1.230000E+02']], 
      dtype='|S14')
Warren Weckesser
  • 110,654
  • 19
  • 194
  • 214