idlwrap API

idlwrap helps you port IDL code to python by providing an IDL-like interface to numpy and scipy.

You do not need IDL to use idlwrap!
usage

An IDL function or procedure corresponds to a lowercased function in idlwrap:

FINDGEN   ->  idlwrap.findgen
POLY_FIT  ->  idlwrap.poly_fit

All idlwrap-specific functions end with an underscore. They have no directly corresponding IDL functions, they rather map special IDL syntax:

A # B
    ->  idlwrap.operator_(A, "#", B)

A[1:4,*] = 4
    ->  idlwrap.set_subset_(A, "[1:4,*]", 4)

FOR I=0, 32000 DO J = I
    ->  for i in idlwrap.range_(0, 32000): j = i
arrays

In python, array indices work differently from IDL. When you are used to IDL's array subscripts, idlwrap's subsetify_ function can be interesting for you.

Functions

findgen(*args)

Create a (multi-dimensional) range of float values.

Notes

Note that the shape of the output array is reversed compared to the arguments passed (e.g. indgen(2,3,4) → shape 4,3,2). For 3D cubes, the last argument to indgen is the number of frames, but the frame can be accessed directly with result[n] (first subset parameter.)

The keywords INCREMENT and START are not implemented.

Examples
FINDGEN(n)    ->  np.arange(n)

indgen(*shape)

Create a (multi-dimensional) range of integer values.

Notes

porting to python

If shape is of one dimension only, you can use np.arange(n). IDL accepts floats as dimension parameters, but applies int() before using them. While np.arange() also accepts floats, be careful, as the number of elements do not match any more!

INDGEN(5.2)         -> [0,1,2,3,4]
INDGEN(5)           -> [0,1,2,3,4]
np.arange(5.2)      -> [0,1,2,3,4,5] ; !!
np.arange(int(5.2)) -> [0,1,2,3,4]
np.arange(5)        -> [0,1,2,3,4]

dindgen(*shape)

Create a (multi-dimensional) range of double-precision float values.

fltarr(*shape)

Create a float array filled with zeros.

Parameters
*shape : (multiple) ints, NOT a list
the dimensions of the new array
dtype : np.dtype, optional
dtype object describing the tpe and precision of the values in the new array. numpy's default is float / np.float32
Notes
  • the flag /nozero was omitted.

Porting to python

The core numpy function is np.zeros. Pay attention when passing the value 1 to FLTARR (and its sister functions INTARR and DBLARR), as the resulting shape is slightly different: IDL ignores any final 1``s, so for IDL calling ``FLTARR(5, 1, 1, ...) is the same as FLTARR(5).

Examples
FLTARR(n)          -> np.zeros(n)
FLTARR(a, b)       -> np.zeros((b, a))
FLTARR(a, b, c)    -> np.zeros((c, b, a))
FLTARR(a, b, 1, 1) -> np.zeros((b, a))

FLTARR(n)+1        ->  np.ones(n)

intarr(*shape)

Create an integer array filled with zeros.

dblarr(*shape)

Create a double-precision float array filled with zeros.

shift(arr, *args)

WARNING

The Si arguments can be either a single array containing the shift parameters for each dimension, or a sequence of up to eight scalar shift values. For arrays of more than one dimension, the parameter Sn specifies the shift applied to the n-th dimension

while this implementation supports lists as arr argument, to match the style of IDL, the IDLpy bridge does not support lists, and returns it unchanged!

If SHIFT is used in combination with FFT, maybe you should look at np.fft.fftshift.

where(array_expression)

port of IDL's WHERE function.

Parameters
array_expression : ndarray / expression
see examples.
Returns
res : np.ndarray
List of 'good' indices. If no index was found, [-1] is returned.
Notes

see also np.put(a, ind, v), which is roughly equivalent to a.flat[ind]=v

porting to python

Most of the time, you will use WHERE for subsetting arrays. While this works only with indices in IDL (which are returned by WHERE), it work with both indices (idlwrap.where) and boolean masks (as returned by comparison operators like array_a < array_b). You can usually remove idlwrap.where entirely.

with 2d arrays a, b:

WHERE(a LT b)
    -> idlwrap.where(idlwrap.operator_(a, "LT", b))
    -> idlwrap.where(idlwrap.LT(a, b))
    -> idlwrap.where(a < b)
# ... in fact, it could even be replaced directly by ``a < b`` (which
# returns a boolean array in np), if WHERE is used as array index!
Examples
array = FINDGEN(100)
B = WHERE(array GT 20)
values = array[B]
array = idlwrap.findgen(100)
b = idlwrap.where(idlwrap.GT(array, 20))
# equivalent to `idlwrap.where(a > 20)`
values = array[b]

# or even:

values = array[array > 20]

size(arr)

Size and type information for arrays.

Parameters
arr : array_like
Returns
ndim : int
Number of dimensions.
*shape : ints
First, second, ... dimension.
dtype : int or np.dtype
Type of the array, as defined in the IDL Type Codes and Names, or as np.dtype object.
size : int
Total number of elements.

median(array, width=None, even=False)

Parameters
array : np.ndarray
The array to be processed. Array can have only one or two dimensions. If Width is not given, Array can have any valid number of dimensions.
width : np.ndarray
The size of the one or two-dimensional neighborhood to be used for the median filter. The neighborhood has the same number of dimensions as array.
even : bool, optional
If the EVEN keyword is set when Array contains an even number of points (i.e. there is no middle number), MEDIAN returns the average of the two middle numbers. The returned value may not be an element of Array . If Array contains an odd number of points, MEDIAN returns the median value. The returned value will always be an element of Array --even if the EVEN keyword is set--since an odd number of points will always have a single middle value.
Notes

porting to python

As long as /EVEN is passed to MEDIAN, and no WIDTH is present, it can safely be replaced with np.median().

mean(x)

Parameters
x : np.ndarray
Notes

The keyword parameters DIMENSION, DOUBLE and NAN are not implemented.

total(array, dimension=None, integer=False)

Parameters
array : ndarray
dimension : int, optional
integer : bool, optional
Notes

To force ndim >= 1:

if res.ndim == 0:
    return np.array([res])
else:
    return res

Implementation differences

not implemented: /CUMULATIVE, /DOUBLE, /NAN, /PRESERVE_TYPE

porting to python

TOTAL corresponds to ndarray.sum(). The parameters /DOUBLE and /INTEGER can be replicated through the dtype=... parameter. DIMENSION needs more attention, as the dimensions are reversed. If no DIMENSION is passed, just use np.sum().

TOTAL(array)  ->  np.sum(array)

todo Does IDL support a list as DIMENSION? What happens?

finite(x, infinity=False, nan=False, sign=0)

Identifies whether or not a given argument is finite.

Parameters
x : np.ndarray
A floating-point, double-precision, or complex scalar or array expression. Strings are first converted to floating-point. ????
infinity : bool, optional
nan : bool, optional
sign : int, optional
Only 0, the default behaviour, is implemented.
Returns
is_finite : bool / bool np.ndarray
If the result is finite.
Notes

SIGN is not implemented. One difficulty arrises from the fact that IDL distinguishes between -!VALUES.F_NAN and !VALUES.F_NAN. In python, there is no possibility to distinguish a negative from a positive np.nan:

a = -np.nan
b = np.nan

a == np.nan # -> False
b == np.nan # -> False

a < 0   # -> False
a > 0   # -> False
b < 0   # -> False
b > 0   # -> False

porting to python

if SIGN is not set:

FINITE(..., /NAN) -> np.isnan(...)
FINITE(..., /INF) -> np.isinf(...)
FINITE(...)       -> np.isfinite(...)

if SIGN is set:

???

fft(array, direction=-1, inverse=False)

Parameters
array : 2d (?????) np.ndarray
direction : integer, optional
Scalar indicating the direction fo the transform, which is negative by convention for the forward transform, and positive for the inverse transform. The value of direction is ignored if the inverse keyword is set.
inverse : boolean, optional
Set this keyword to perform an inverse transform. Setting this keyword is equivalent to setting the direction argument to a positive value. Note, however, that setting inverse results in an inverse transform even if direction is specified as negative.
Notes

A normalization factor of 1/N, where N is the number of points, is applied during the forward transform.

Implementation details

The parameters CENTER, DIMENSION, DOUBLE, OVERWRITE and the thread pool keywords are not implemented.

Examples

if you do not care about the normalization:

FFT(image2d, 1)  -> np.fft.ifft2(image2d)
FFT(image2d, -1) -> np.fft.fft2(image2d)

randomn(seed=None, *shape)

Normal-distributed random numbers.

Parameters
seed : int or 1-d array_like
seed for random generator.
*shape : list of int
dimension of the returned array
Notes

RANDOMN uses the Box-Muller method, based off of the gasdev algorithm (section 7.2 Numerical Recipies in C, 1992) . The uniform random numbers required for the Box-Miller method are generated using the Mersenne Twister algorithm. [from the IDL documentation]

Note that the random numbers generated by python differ from the ones from IDL, as the seed is handled differently and the algorithms differ too.

abs(x)

absolute value

round(x)

round to the nearest integer (-> int type).

Parameters
x : float or array
Returns
x : np.int64 or int64-ndarray
Notes

ROUND rounds to the nearest integer, unlike numpy's np.round / np.rint, which rounds to the nearest even value (defined in the standard IEEE 754)

https://stackoverflow.com/a/34219827/1943546

porting to python

No direct match. Use this workaround.

floor(x)

Parameters
x : float or array
Returns
x : np.int64 or int64-ndarray
Notes

The keyword L64 is not implemented.

porting to python

This is basically np.floor, but IDL returns integer types (e.g. used as array indices)

ceil(x)

Round upwards (towards infinity).

Returns
x : np.int64 or int64-ndarray

fix(expression)

Round to nearest integer towards zero.

Returns
x : np.int64 or int64-ndarray

complex(real, imaginary=0)

creates complex number. Same as idlwrap.dcomplex

Parameters
real : float or array or list
imaginary : float or array or list, optional

dcomplex(real, imaginary=0)

double-precision complex number

Parameters
real : float or array or list
Real part.
imaginary : float or array or list, optional
Imaginary part. Defaults to 0.
Returns
complex_number : ndarray
Notes

This always returns a numpy array. Beware of that if you call e.g. idlwrap.complex(1,2).real, which results in an 0-dimension np.ndarray.

The second signature type, with Expression, Offset, D1, D2, ... is not supported.

real_part(z)

Parameters
z : complex or ndarray
Notes

numpy .real works with complex numbers and ndarray.

imaginary(complex_expression)

imaginary part

conj(x)

complex conjugate

acos(x)

asin(x)

atan(x)

alog(x)

alog2(x)

alog10(x)

beta(z, w)

ibeta(a, b, z)

beselj(x, n)

Returns the J Bessel function of order N for the argument X.

Parameters
x
argument. A scalar or array specifying the values for which the Bessel function is required. IDL: Values for X must be in the range -108 to 108. If X is negative then N must be an integer (either positive or negative).
n
order. A scalar or array specifying the order of the Bessel function to calculate. Values for N can be integers or real numbers. If N is negative then it must be an integer.
Returns
my_return_parameter
Notes

The output keyword ITER, which returns the number of iterations, was omitted. For J Bessel functions, scipy's jn is just an alias for jv (which is not the case for the other Bessel functions, e.g. yn and yv)

porting to python

Replace BESELJ(x, n) with scipy.special.jv(n, x). Pay attention to the inversed order of the arguments.

keyword_set(kw)

only true if kw is defined AND different from zero.

here, None is used for non-defined keyword.

range_(init, limit, increment=1)

Behaves like IDL's FOR i=init, limit DO statement.

Parameters
init : int, float
limit : int, float
Notes

The endpoint stop is included (<= comparison instead of python's <). The increment is not implemented.

Examples
FOR I=0, 32000 DO J = I
    ->   for i in range_(0, 3200): j = i

FOR K=100.0, 1.0, -1 DO BEGIN
    PRINT, K
ENDFOR

    ->   for k in range_(100.0, 1.0, -1):
             print(k)

range_int_(*args)

Like range_, but returns integers which could then be used as list indices.

subsetify_(arr)

Transforms a numpy ndarray to an object which implements IDLs array subsetting. This is a convenient alternative to the subset_ and set_subset_ functions.

Returns
arr : object
This object is like an ndarray, but behaves differently when subsetting with a str.
Examples
# let's create a regular numpy ndarray:
>>> a = idlwrap.indgen(4, 5)
>>> a[2:3, 1:2]
array([[9]])

# transform b:
>>> b = idlwrap.subsetify_(a)

# b behaves like a regular numpy ndarray:
>>> b[2:3, 1:2]
array([[9]])
>>> b.mean()
9.5

# but when subsetting with a ``str``, it behaves like IDL's subset:
>>> b["2:3, 1:2"]
array([[ 6,  7],
      [10, 11]])
>>> b["*"]
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19])

# it also works for setting elements:
>>> b["1:2,1:3"] = 0
>>> b
array([[ 0,  1,  2,  3],
       [ 4,  0,  0,  7],
       [ 8,  0,  0, 11],
       [12,  0,  0, 15],
       [16, 17, 18, 19]])

subset_(arr, subset, debug=False)

Get a subset of an array.

Parameters
arr : ndarray
The input array.
subset : str
Subset as it would have been passed to IDL, as string. See examples.
Returns
res : ndarray
Notes

In IDL, subset ranges are inclusive: [1:3] returns 3 elements, while it would only return 2 elements in python.

idlwrap.subsetify_ provides an alternative interface to the same functionality.

Examples
>>> a = idlwrap.indgen(4, 4)
>>> idlwrap.subset_(a, "*")
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])

>>> idlwrap.subset_(a, "[14]")
14

>>> idlwrap.subset_(a, "[1:2]")
array([1, 2])

>>> idlwrap.subset_(a, "[1:2,2:3]")
array([[ 9, 10],
       [13, 14]])

These are not yet implemented:

# idlwrap.subset_(a, "[-1]")   # negative subset
# idlwrap.subset_(a, "[1.5]")  # float-type subset

set_subset_(arr, subset, what)

Assign an array subset to a value. The arr is modified in place. An alternative interface to the same functionalities is provided by the idlwrap.subsetify_ function.

Parameters
arr : ndarray
The array to use.
subset : str
A string with the subset notation, as you would use it in IDL, e.g. "[1:4,*]". You can also omit the brackets [].
what : ndarray, numeric
The value(s) to assign to the selected subset.
Returns
None
Examples
a = idlwrap.indgen(10, 10)

idlwrap.set_subset(a, "[1:4]", 0)
idlwrap.set_subset(a, "*", 0)
idlwrap.set_subset(a, "2:5,2:5", 0)

# the following are valid IDL, but are not yet implemented in idlwrap:

# idlwrap.set_subset(a, "[1.5]", 0)  # float-type subset
# idlwrap.set_subset(a, "-1", 0)     # negative subset

matrix_multiply_(a, b, atranspose=False, btranspose=False)

H(a, b)

matrix multiplication ("hash"), corresponds to IDL A # B

Parameters
a : np.ndarray
supported shapes: (n,) or (n,m)
b : np.ndarray
supported shapes: (n,) or (n,m)
Returns
mat : ndarray
Multiplication of the arrays, as defined by the IDL documentation.
Notes

porting to python

Quite complicated, as the numpy function depends on the dimensions of the inputs. Look at the source code.

HH(a, b)

matrix multiplication, corresponds to IDL A ## B

Parameters
a : np.ndarray
supported shapes: (n,) or (n,m)
b : np.ndarray
supported shapes: (n,) or (n,m)
Returns
mat : ndarray
Multiplication of the arrays, as defined by the IDL documentation.
Notes

porting to python

Quite complicated, as the numpy function depends on the dimensions of the inputs. Look at the source code.

LE(a, b)

less-than-or-equal-to relational operator, corresponds to IDL a LE b

GE(a, b)

greater-than-or-equal-to relational operator, corresponds to IDL a GE b

LT(a, b)

less-than relational operator, corresponds to IDL a LT b

GT(a, b)

greater-than relational operator, corresponds to IDL a GT b

EQ(a, b)

equals-to relational operator, corresponds to IDL a EQ b

NE(a, b)

not-equal-to relational operator, corresponds to IDL a NE b

operator_(a, operator, b)

Special IDL operations.

Parameters
a : numeric or ndarray
operator : str

Operation. The following IDL operations are supported:

  • minimum and maximum operators:
    • ': minimum operator
    • '>': maximum operator
  • relational operators:
    • 'EQ': equal to
    • 'NE': not equal to
    • 'GE': greater than or equal to
    • 'GT': greater than
    • 'LE': less than or equal to
    • 'LT': less than
  • matrix operators:
    • '#': multiplies columns of a with rows of b. b must have the same number of columns as a has rows. The resulting array has the same number of columns as a and the same number of rows as b.
    • '##': multiplies rows of a with columns of b. b must have the same number of rows as a has columns. The resulting array has the same number of rows as a and the same number of columns as b.
Returns
res : numeric / ndarray
Notes

In idlwrap, the relational operators (EQ, NE, GE, GT, LE, LE) are also available as functions: EQ(a, b), ...

Porting to python

  • the relational operators can be replaced with its python equivalent
  • the minimum and maximum operators < and > can be replaced with np.minimum(a,b) and np.maximum(a,b), respectively a < b
  • the matrix operators are more complex. Please refer to the documentation of H and HH
Examples
A < B   ->  operator_(a, "<", b)
        ->  np.minimum(a, b)

A LE B  ->  operator_(a, "LE", b)
        ->  LE(a, b)
        ->  a <= b

A # B   ->  operator_(a, "#", b)
        ->  H(a, b)

A ## B  ->  operator_(a, "##", b)
        ->  HH(a, b)
Fork me on GitHub