IDL/배열 생성 및 처리

배열 인덱싱 관련 예제

이상우_idl 2018. 1. 30. 11:52
728x90
반응형

오늘은 배열의 인덱싱(indexing)과 관련하여 한가지 흥미로운 주제를 다뤄볼까 합니다. 최근에 제가 봤던 IDL 관련 질문의 내용을 바탕으로 한 것인데, 여기서 다뤄보면 좋을 것 같다는 생각이 들었습니다. 주제는 어떤 2차원 배열에서 통상적인 값들 외에 -999와 같은 특이한 값들이 군데군데 위치해 있는데, 이러한 위치 각각은 포인트 하나이지만 이를 5x5의 크기로 확대하려면 어떻게 해야 하는가입니다.말로만 하면 이해가 힘들 것 같으므로 이 상황에 해당되는 그림을 보면 다음과 같습니다.



이 그림에서 표출된 예제 2차원 데이터에서는 통상적인 값들은 0~300의 범위를 갖지만, 군데군데 불규칙적으로 -999인 값들이 분포해 있습니다. 이러한 지점들은 검정색 포인트로 보입니다. 그런데 각각의 포인트는 크기로 굳이 따진다면 1x1이 되는데 이 크기를 5x5로 확대하고 그 안의 값들 역시 모두 -999가 되게 하려면 어떻게 해야 하는가의 문제입니다. 즉 다음 그림과 같은 결과를 얻으려면 어떻게 해야 할 것인가의 문제입니다.



물론 뭐 제가 지금 이 그림을 보여드리고 있다는 것은 이미 문제를 해결했다는 의미라고 보셔도 됩니다. 다만 어떻게 해결했느냐가 문제인데요. 두가지 정도의 방법을 생각해볼 수 있을 것 같습니다. 어차피 배열 기반의 작업일 수밖에 없는데, 일단 다음과 같이 예제 데이터를 먼저 만들어봅시다.


data = HANNING(100, 100)*300

ww = FIX(RANDOMU(-1, 20)*(100L*100-1))

data[ww] = -999.


처음에 제시한 그림에서 표출된 2차원 데이터인데, 100x100의 구조를 갖고 있고 값의 범위는 0~300입니다. 다만 임의의 위치에 의도적으로 -999라는 값을 넣었습니다. 그리고 다음 과정에 의하여 데이터를 이미지 형태로 표출하면, 앞서 처음에 제시했던 것과 같은 그림을 얻게 됩니다.


win0 = WINDOW(DIMENSIONS=[600, 600], /NO_TOOLBAR)

im0 = IMAGE(data, MARGIN=0, /CURRENT)


그러면 본격적인 처리 과정으로 넘어가서, 먼저 값이 -999인 위치들을 찾아봅시다. 어떤 배열에서 특정 조건을 만족하는 원소값들의 위치를 알아내기 위해서는 WHERE 함수를 다음과 같이 사용하면 됩니다.


ww = WHERE(data EQ -999.)


여기서는 값이 -999인 위치를 모두 찾아내야 하므로 위와 같이 WHERE 함수를 사용하였습니다. 현재 사용중인 data 배열 내에는 -999의 값이 총 20개가 존재합니다. 따라서 ww는 총 20개의 값들로 구성된 1차원 배열로 얻어집니다. 다만 이 20개의 값들 하나하나가 위치 좌표이긴 한데, 우리가 알기 쉬운 2차원의 모습은 아닙니다. 즉 (x, y)와 같은 형태가 아니라 그냥 숫자 하나로 주어집니다. 우리가 알아보기 쉬운 2차원 좌표의 형태로 결과를 얻으려면 다음과 같이 ARRAY_INDICES 함수를 사용해야 합니다.


arr = ARRAY_INDICES(data, ww)

HELP, arr

xp = REFORM(arr[0, *])

yp = REFORM(arr[1, *])

HELP, xp, yp


자세한 과정은 위와 같습니다. 사실 ARRAY_INDICES 함수로 얻어지는 결과는 2x20의 배열인데 이것은 각 위치의 X, Y 좌표값들이 한꺼번에 묶인 상태입니다. 따라서 X좌표값들만으로 구성된 xp라는 배열과 Y좌표값들만으로 구성된 yp라는 배열을 따로 추출하는 과정도 필요하여 추가하였습니다. 다음 과정에서 이 xp, yp가 사용됩니다.


이제 해야 할 작업은 data 배열 내에서 값이 -999인 모든 위치 포인트의 크기를 1x1에서 5x5로 확대하는 작업이라고 볼 수 있습니다. 제가 아까 이를 위한 두가지 해결방법이 있다고 언급했는데, 그 중 첫번째 방법은 다음과 같습니다.


FOR j = 0, N_ELEMENTS(xp)-1 DO BEGIN

  data[xp[j]-2>0:xp[j]+2<99, yp[j]-2>0:yp[j]+2<99] = -999.

ENDFOR


이와 같이 반복문을 사용하여 모든 포인트들에 대하여 작업을 해주는 방식입니다. 여기서는 값이 -999인 어떤 포인트의 위치 좌표에 대하여 그 포인트를 중심으로 X 및 Y 방향으로 5x5 크기의 부분 배열에 대하여 값들이 모두 -999가 되도록 하였습니다. 포인트 하나씩만 따지면 그 X좌표는 xp[j]가 되고 Y좌표 yp[j]가 되겠지만 각각에 대하여 -2부터 +2 인덱스까지 취하는 방식입니다. 다만 -2부터 +2의 위치좌표를 계산하다보면, 이미지 가장자리에 있는 포인트의 경우 좌표값이 한계를 넘는 경우가 발생하므로 위와 같이 >0 또는 <99와 같은 처리를 하였습니다. 어쨌든 이와 같은 방식으로 data 배열의 값을 처리한 후 다음과 같이 별도의 그래픽창에 표출해보면 그 모습은앞서 두번째로 제시했던 그림과 같아집니다.


win1 = WINDOW(DIMENSIONS=[600, 600], /NO_TOOLBAR)

im1 = IMAGE(data, MARGIN=0, /CURRENT)


그 다음 두번째 방법은 어차피 첫번째와 같이 반복문의 형태를 띄게 되는 것은 맞는데 내용은 다음과 같이 약간 다릅니다. 위의 반복문 부분을 다음 내용으로 대체하면 됩니다.


FOR j = -2, 2 DO BEGIN

  FOR i = -2, 2 DO BEGIN

    data[xp+i, yp+j] = -999.

  ENDFOR

ENDFOR


이 내용을 보면 2중 반복문의 형태인 것을 볼 수 있는데, 반복인자가 아까는 값이 -999인 포인트들의 갯수였던 반면 지금은 -2에서 2까지입니다. 대신에 xp, yp는 반복문 내부 블록에 들어가 있습니다.  사실 xp, yp 각각은 배열입니다. 따라서 xp+i, yp+j 모두 배열입니다. 예를 들어 i가 1이고 j가 -1인 경우라면 data[xp+1, yp-1] = -999.과 같은 내용이 실행되는 셈입니다. 즉 xp+1, yp-1 모든 좌표값들에 대하여 -999를 대입하는 내용이고, 이와 유사한 작업들이 각 좌표을 중심으로 -2~+2의 인덱스 범위로 진행되는 것이라고 보면 됩니다. 조금 설명이 두서없긴 하지만, 잘 생각해보시면 그 의미를 이해할 수 있으리라 생각됩니다.


제가 두가지 방법을 소개해드렸는데, 둘 중 어느방법이 좀 더 효율적일까요? 만약 데이터의 크기가 크고 특정값에 해당되는 포인트들의 갯수도 꽤 많은 편이라면, 아마도 두번째 방법이 좀 더 효율적이고 빠를 것 같다는 생각은 듭니다. 물론 저도 실제 상황을 테스트해본 것은 아니라서 100% 확신은 없지만, 기법의 특성상 두번째 방법이 좀 더 빠를 것 같습니다. 혹시 여러분들이 이러한 테스트를 해보실 수 있다면 한번 확인해보시는 것도 좋을 것 같습니다. 물론 컴퓨터의 사양이 매우 좋다면 아마 두 방법 사이의 차이가 크게 나지 않을 수도 있습니다.

반응형