配列

[IDL8] は IDL 8.0 で導入された機能、[IDL84] は IDL 8.4 で導入された機能であることを示す。

概要

添え字

配列の添え字

通常は0以上の数字で指定する。64bit整数型の範囲まで利用可能。小数を指定した場合、小数点以下は切り捨てられる。

負の添え字 [IDL8]

負の添え字は配列の末尾からの位置を示す。nを正整数とするとき、a[-n]a[n_elements(a)-n]と等価。負の添字をサポートした結果、コードによっては以前のバージョンのIDLで実行したときと異なる結果になる場合がある。以前ならエラーで止まってしまうケースが、エラーとならず想定外の計算結果を返してしまう。具体例はwhereの項目を参照。

配列の括弧

IDLで配列の添え字を指定するには、角括弧による A[1] と 丸括弧による A(1) の2通りの書き方がある。配列で丸括弧を利用すると関数呼び出しと一見区別がつかない。特に、同名の関数と配列が存在するとき、関数が既にコンパイル済みであるかどうかでプログラムの動作が変わってしまう。このため、角括弧を用いるほうがよい。compile_opt strictarr (compile_opt idl2 にも含まれる)で丸括弧による添え字の指定を禁止できる。

複数の一部分だけを引用 (部分配列)

配列の一部分だけを引用することができる (部分配列)。部分配列を指定するには、添え字として添え字三つ組やベクトル添え字を用いる。

  1. A[l:m:n]

A[l] から A[m] まで添え字が n 間隔の要素から構成される部分配列。mに*を指定すると配列の添え字の上限を指定したことになる。nは1以上の整数。A[l:m]のようにnを省略した時のデフォルトのnの値は1。m-l が n の倍数でないとき、部分配列にA[m]は含まれない。

  1. A[l1:m1:n1,l2:m2:n2]

多次元配列に対しても同様。

  1. A[l1:m1:n1,*]

ある次元については要素を全て選択するときには*を用いる。

1次元配列を添え字として使用することで、一つの配列の複数の要素を引用することができる(ベクトル添え字)。

  1. A[[1,2,3,5]]

A[1], A[2], A[3], A[5] から構成される部分配列。A[[1,3,1,5]] のように同じ要素を複数回指定してもよい。

ただし、ベクトル添え字を利用する場合、配列の範囲外を指し示す添え字が存在する場合でも、デフォルトでは範囲外を指さないようにクリップされてエラーにならない。

  1. A = [3, 5, 1, 2, 9]
  2. B = [1, 4, 100]
  3. PRINT, A[B] ; 結果は 5 9 9

COMPILE_OPT STRICTARRSUBS を指定すると、エラーにすることができる。

  1. COMPILE_OPT STRICTARRSUBS
  2. A = [3, 5, 1, 2, 9]
  3. B = [1, 4, 100]
  4. PRINT, A[B] ; エラー

部分配列は左辺値としても使用できる。代入の項を参照。

生成

指定したサイズで生成

make_array
多機能な配列作成関数。多数のオプションを持つ。配列を作成する関数は20個以上存在するが、そのほとんどは make_array でも実現できる。
intarr(n)
要素数nのinteger型の1次元配列を生成する。各要素は0で初期化される。make_array(n,/integer)と等価。初期化が不要な時は /nozero オプションを利用する。巨大な配列を作成する際に初期化の時間を節約できる。
dblarr(n,m,n) または dblarr([n,m,n])
各次元の要素数が (n, m, n) のdouble型の3次元配列を生成する。各次元の要素数を1次元配列であたえることもできる。make_array(n,m,n,/double)またはmake_array([n,m,n],/double)と等価。

型ごとに次のような個別の関数が用意されている。bytarr (byte型), intarr (integer型), lonarr (long型), lon64arr (64bit integer型), uintarr (符号無しinteger型), ulonarr (符号無しlong型), ulon64arr (符号無し64bit integer型), fltarr (float型), dblarr (double型), complexarr (complex型), dcomplexarr (倍精度complex型), strarr (string型)

等間隔の数列を生成

A = indgen(n)
要素数nのinteger型の1次元配列を生成する。各要素の値は添え字の値と等しい、つまり自然数列 (0, 1, 2, 3, ...)となる。オプションで他の型の配列も作ることができる。make_array(n,/integer,/index)と等価。
A = indgen(n,start=s) [IDL 8.2]
数列をsから始めることができる。つまり s, s+1, s+2, s+3, ... となる。
A = indgen(n,increment=i) [IDL 8.3]
数列の増分をiにできる。つまり 0, i, 2i, 3i, ... となる。
A = [s:f] または A = [s:f:i] [IDL8.3]
IDL 8.3で導入されたコロン演算子を用いることで、sから始まりfで終わる増分iの数列を生成する。増分を省略した時は、f>sのときは1、f<sのときは-1となる。indgen((f-s)/i+1, start=n, increment=i)と同じ結果になるが、新しい方法のほうがわかりやすい。s,f,iには小数も使えるが、浮動小数点の計算誤差のため要素数が期待されるよりも少なくなることがある。例: [0:1:0.001] の要素数は本来1001になることが期待されるが、実際には1000になる。
A = indgen(n,m) または A = indgen(n,m)
多次元配列の場合、各要素は、その配列を「1次元配列とみなして」アクセスするときの添え字の値と等しい。つまり、A[i,j]の値は n * j + i となる。

型ごとに次のような個別の関数も用意されている。bindgen (byte型), indgen (integer型 または任意の型), lindgen (long型), l64indgen (64bit integer型), uindgen (符号無しinteger型), ulindgen (符号無しlong型), ul64indgen (符号無し64bit integer型), findgen (float型), dindgen (double型), cindgen (complex型), dcindgen (倍精度complex型), sindgen (string型)

全要素の初期値を指定して生成

添え字を指定するための角括弧との混同に注意。

A = [43, 8, 75, 57, 19, 83]
integer型の1次元配列を作成する。
A = [41, 5.3d-1, 7, 313L, 4.3]
double型の1次元配列を作成する。初期値として指定した数列に異なる型が含まれているときは適切な型に格上げが行われる。
A = ['Jan', 'Feb', 'Mar']
string型の1次元配列を作成する。
A = [1, '2.2d0', 3.3]
文字列と数字が混在するときは、結果の型は数値のみで決定される。文字列はその型に変換される。この例では、float型の1次元配列を作成する。
A = [[0,4,1],[2,1,3]]
3x2の2次元配列を作成する。なお、B=[0,4,1] & C=[2,1,3] & A=[B,C] とすると、BCが単純に連結され、要素数6の1次元配列となることに注意。同じ結果を得るには、最後にA=[[B],[C]]とする必要がある。

指定した要素をコピーして生成

array=replicate(r, 次元1, 次元2, ...)
rを各要素の値とする配列を作成する。rは任意の型のスカラ値、任意の型の長さ1の配列、構造体のいずれか。配列の要素の型はrと同じになる。make_array(次元1, 次元2, ... , value=r) と等価。

空の配列・!NULL [IDL8]

array=[] または array=!NULL
空の配列 []!NULL と等価である。
変数に[]!NULL を代入すると、変数が使用していたメモリが解放される。!NULLの項目も参照。

多次元配列

IDLでは最高で8次元までの多次元配列が利用可能。添え字はカンマで区切る(A[1,5,4])。

多次元配列のメモリ内での順番は列主導(Column Major)。一番左の添え字がもっとも急速に変化する。つまりFortran、MATLABなどと同じ。例えば2次元配列の場合は、a[0, 0], a[1, 0], a[2, 0], ... , a[0, 1], a[1, 1], a[2, 1], ... の順になる。行主導(Row Major)の配列を採用している他の言語(Cなど)とデータをやりとりするときには、場合によってはtranspose関数を用いて次元の順序を入れ替える必要がある。

多次元配列の添字を計算する

多次元配列の各要素は、その配列を「1次元配列とみなして」アクセスすることができる。つまり、A = intarr(3,2)のとき、a[i,j] は 2 * j + i 番目の要素であるから、a[2 * j + 1]としても参照できる。このことは、多次元配列の各要素に対して配列中での位置に依存しない処理を行うとき、ループのネストが必要ないことを意味する。

逆に、WHERE、MAX、MIN関数など、1次元配列とみなしたときの添字を返すような関数の結果を、実際の多次元配列での添字に変換するには、ARRAY_INDICES 関数を使う。(割り算して自分で求めることもできるが、こちらのほうが楽)

多次元配列の次元を変更

配列の要素数を変えず、メモリー内に格納されている順序も変更しないで、次元のみを変えるときは reform 関数を用いる。/overwrite キーワードをつけると、データのコピーは行わず、次元の変更のみが行われる。reform関数は、次元を指定しない場合には、サイズが1の次元のみを除去する。

代入・コピー

a=b
配列bを配列aに代入する。aの型・次元はbと等しくなる。
a[*]=1
配列aの全要素に1が代入される。
a=1
配列aは削除され、ただの整数スカラーの1となる。配列が使用してたメモリーを解放するのにも有効。
a=temporary(b)
メモリーのコピーを行わずに配列bを配列aに代入する。代入後bは未定義となる。時間とメモリー使用量を節約できる。
a=a+2
配列aの全ての要素にそれぞれ2を加算した結果を配列aに代入する。このとき、aのサイズと同じ大きさの作業用の領域が用いられる。
a=temporary(a)+2
配列aの全ての要素にそれぞれ2を加算した結果を配列aに代入する(書き戻す)。作業用の領域を用いないため、時間とメモリー使用量を節約できる。演算前後でaの型が変わる場合には、temporaryを使う意味はない。
a+=2
加算代入演算子を用いた場合でも、作業用の領域を用いないため、時間とメモリー使用量を節約できる。

部分配列は左辺値としても使用できる。

A[[1,7,2]] = [10,9,8]
A[1] = 10, A[7] = 9, A[2] = 8 と代入される。
A[1:*:2] = A[0:*:2]
添え字が 2n となる要素を添え字が 2n+1 の要素にコピーする。(ただし、Aの要素数は偶数)
A[2:4] = [4,5,6]
A[2] から A[4] までに右辺の要素数3の配列を代入する。(配列の一部を別の配列で置き換える)
A[2] = [4,5,6]
代入の終了位置(この場合4)は省略可能。この場合でもA[2] から A[4] までに右辺の要素数3の配列を代入する。

配列の全部または一部にスカラーを代入するには、REPLICATE_INPLACEプロシージャも使える。うまく使えば高速化に有効。

連結

b=[a,1,2,3]
配列(またはスカラー値)aの末尾に要素を3つ追加した配列を作る。
c=[a,b]
配列(またはスカラー値)abを連結した配列を作る。なお、abが多次元配列の場合は、最も左側の次元(first dimension)について連結する。このときabは最も左側の次元をのぞいて、サイズが同じでなければならない。次元[i,k,m]と[j,k,m]の3次元配列を連結すると、次元は[i+j,k,m]となる。
c=[[a],[b]]
配列(またはスカラー値)abを連結した配列を作る。左から2番目の次元(Second dimension )について連結する。このときab左から2番目の次元をのぞいて、サイズが同じでなければならない。[i,j,m]と[i,k,m]の3次元配列を連結すると、次元は[i,j+k,m]となる。なお、abがスカラー値または1次元配列(つまり左から2番目の次元は1だとみなせる)の場合は、結果の次元が[n_elements(a),2]となる。
c=[[[a]],[[b]]]
配列(またはスカラー値)abを連結した配列を作る。左から3番目の次元(Third dimension)について連結する。このときabは左から3番目の次元をのぞいて、サイズが同じでなければならない。[i,j,k]と[i,j,l]の3次元配列を連結すると、次元は[i,j,k+l]となる。なお、abの次元が2次元以下(つまり左から3番目の次元は1だとみなせる)の場合は、結果の次元が[aのfirst dimension size, aのsecond dimension size, 2]となる。

[ ] のネスト数の最大は、IDL 8.8.x までは3個、IDL 8.9以降は8個。

配列の状態を調べる

配列aの要素数を調べる。変数が未定義の場合は0を返す。変数がスカラーの場合1を返すため、この方法では「要素数1の配列」とスカラーの区別はできない。

  1. PRINT, N_ELEMENTS(a)
  2. PRINT, SIZE(a ,/N_ELEMENTS)
  3. PRINT, a.length ; [IDL84]
配列aの次元の数を調べる。また、変数aが配列かスカラーかを判別する。次元の数が1以上なら配列、0ならスカラー(または未定義)。ただし、構造体・ハッシュ・リストは常に配列であるとみなされる。
size(変数,/n_dimensions)は配列の次元を返す。これが0ならスカラー(または未定義)、1以上なら配列。ただし、構造体・ハッシュ・リストは、たとえ要素が1個であったとしても、常に配列であるとみなされる。
変数.ndim ; [IDL84] も次元を返す。
各次元のサイズ
size(変数,/dimensions)
変数.dim ; [IDL84]
size(変数,/type)type code (0-15の整数)size(変数,/tname)で型の名前を文字列で返す。
[IDL84] 変数.typecodetype code (0-15の整数)変数.typenameで型の名前を文字列で返す。

値がある条件を満たす要素を操作する

where関数を使う。例えば、a が数値の配列のとき、where(a lt 0) で 0 よりも小さな要素の位置を配列で返す。条件を満たす要素が存在しないときには -1 を返す。

IDL 8以降では配列の負の添字がサポートされたため、a[where(a lt 0)] = 0 などとすると、aに負の値が含まれていないとき、where(a lt 0) の結果は -1 となり、a の最後の要素が0になってしまう。これを回避するには、a[where(a lt 0, /null)] = 0 とする。

最小値や最大値でクリップするには >や< 演算子も使える。例えば、a >= 0a[where(a lt 0, /null)] = 0 はどちらも0より小さい値を0に置き換える。

[IDL84] IDL_Variable::Filter を用いると、自作の関数やlambda関数をフィルタとして用いて、これらの関数が真を返す要素のみを抽出することができる。

サブルーチンの引数

部分配列をサブルーチンの引数として渡すときは、値渡しとなることに注意。

要素数を変更せずに次元を変更

reform

拡大・縮小

rebin, expand, congrid

回転・行列を入れ替え

transpose, rot, rotate

シフト

shift, shift_diff

順序逆転・ソート・重複除去

reverse, sort, uniq

比較

array_equal, IDL_Variable::Compare, IDL_Variable::Diff, IDL_Variable::Equals

総和

total

多数の要素を加算するときや、要素の値の大小差が非常に大きいときには誤差に注意。IDLのtotal関数は配列の要素を最初から順に足しあわせるだけである。並列化の有無(デフォルトでは並列化を行うが、/TPOOL_NO をつけると並列化を行わない)でも計算順序が変わるため結果が変わる。例えば、a = FINDGEN(100000) & PRINT, TOTAL(a, /TPOOL_NO) - TOTAL(REVERSE(a), /TPOOL_NO) の結果は -96768.0 となる。入力をDOUBLE型にするか、totalに /DOUBLEキーワードをつけて倍精度で計算するようにする。

状況によってはカハンの加算アルゴリズムの使用を検討する。

  1. function kahansum, input
  2. sum = 0d
  3. c = 0d
  4. for i = 0, n_elements(input) -1 do begin
  5. y = input[i] - c
  6. t = sum + y
  7. c = (t - sum) - y
  8. sum = t
  9. endfor
  10. return, sum
  11. end

total(replicate(0.1d, 1000000),/TPOOL_NO) の結果は 100000.00000133288 になるが、kahansum(replicate(0.1d, 1000000)) の結果は 100000.00000000000 になる。

総乗

product

/INTEGER キーワードをつけると、64-bit 整数型で計算を行う。

要素の最大・最小値

min, max関数

平均・標準偏差・分散・歪度・尖度

mean, stddev, variance, skewness, kurtosis

単位行列を得る

identity

行列積

#, ##, matrix_multiply, matrix_power

逆行列

invert, la_invert

西田圭佑 (NISHIDA Keisuke)
nishida at kwasan.kyoto-u.ac.jp