3. 넘파이 어레이#

넘파이numpy는 NUMerical PYthon의 줄임말이며, 파이썬 데이터 사이언스에서 가장 유용하게 활용되는 도구를 제공하는 라이브러리다. 넘파이가 제공하는 핵심 요소는 아래 두 가지이다.

  • 다차원 어레이(배열)

  • 메모리 효율적이며 빠른 어레이 연산

기본 설정

numpy 라이브러리는 관습적으로 별칭 np로 불러온다.

import numpy as np

3.1. 다차원 어레이#

리스트와는 달리 넘파이 어레이는 항목으로 사용된 값들 뿐만 아니라, 어레이의 모양, 항목들의 자료형 등에 대한 정보도 함께 포함한다. 어레이의 모양에 따라 차원이 결정되며, 어레이의 모든 항목은 동일한 자료형을 가져야 한다.

3차원, 4차원 등의 고차원 어레이도 데이터 분석에서 다루지만 여기서는 가장 많이 활용되는 1차원과 2차원 어레이의 사용법을 소개한다.

3.1.1. 1차원 어레이#

1차원 어레이는 중첩이 없는 리스트와 동일한 모양을 가지며, 리스트 등에 np.array() 함수를 적용하여 생성할 수 있다. 1차원 어레이는 벡터vector로도 불리며, 한 개의 axis을 갖는다.

data_1D = [6, 7.5, 8, 0, 1]

arr_1D = np.array(data_1D)
arr_1D
array([6. , 7.5, 8. , 0. , 1. ])

ndarray 자료형

넘파이 어레이의 자료형은 numpy.ndarray다.

type(arr_1D)
numpy.ndarray

3.1.2. 2차원 어레이#

2차원 어레이의 모든 항목은 동일한 크기의 1차원 어레이어야 한다. 따라서 동일한 길이의 리스트를 항목으로 갖는 중첩 리스트를 2차원 어레이로 변환할 수 있다. 아래 코드는 길이 4인 두 개의 리스트를 항목으로 갖는 중첩 리스트 data_2D를 2차원 어레이 arr_2D로 변환한다.

data_2D = [[1, 2, 3, 4],
         [5, 6, 7, 8]]

arr_2D = np.array(data_2D)
arr_2D
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])

2차원 어레이는 rowcolumn 두 개의 축을 가지며, 행은 0번 축, 열은 1번 축이라 부른다. 이런 의미에서 행렬matrix로 불린다. 예를 들어, arr_2D는 2개의 행과 4개의 열을 갖는 아래 행렬에 대응한다.

\[\begin{split} \begin{bmatrix} 1 & 2 & 3 & 4\\ 5 & 6 & 7 & 8 \end{bmatrix} \end{split}\]

아래 list_ragged처럼 중첩 리스트의 항목이 서로 길이가 다른 리스트로 이루어져 있으면 어레이로의 변환이 허용되지 않음에 주의한다.

list_ragged = [[1, 2, 3, 4], [5, 6, 7], [8, 9]]
list_ragged
[[1, 2, 3, 4], [5, 6, 7], [8, 9]]
np.array(list_ragged)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[6], line 1
----> 1 np.array(list_ragged)

ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (3,) + inhomogeneous part.

3.1.3. np.arange() 함수#

np.arange() 함수는 2.2절에서 살펴본 range() 함수와 유사한 역할을 하지만, 반환값이 리스트가 아닌 넘파이 어레이이다.

np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

range() 함수와는 다르게 부동소수점을 스텝으로 사용할 수도 있다. 예를 들어 아래 코드는 0부터 1까지 스텝 0.1씩 증가시켜 1차원 어레이를 생성한다. 시작점 0은 어레이의 항목이 되지만, 오른쪽 끝값인 1은 어레이의 항목이 아님에 유의한다.

np.arange(0, 1, 0.1)
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])

3.2. 어레이 속성#

넘파이 어레이 객체는 자신에 대한 정보를 다양한 속성으로 저장한다. 여기서는 가장 중요한 속성 세 가지를 소개한다.

3.2.1. shape 속성#

shape 속성은 어레이의 모양을 저장한다. 예를 들어, 위에서 생성한 2차원 어레이 arr_2Dshape은 행과 열의 개수로 구성된 튜플 (2, 4)이다.

arr_2D.shape
(2, 4)

1차원 어레이, 즉 벡터의 모양은 벡터에 포함된 항목의 개수로 구성된 길이가 1인 튜플로 표현된다.

arr_1D.shape
(5,)

참고로 길이가 1인 튜플은 반드시 쉼표를 포함해야 함에 주의한다. 쉼표를 표시하지 않으면 튜플이 아니다.

type((5,))
tuple
type((5))
int

3.2.2. ndim 속성#

어레이의 차원은 ndim 속성에 저장되며, shape에 저장된 튜플의 길이와 동일하다. 2차원 어레이 arr_2Dndim은 2이고, 1차원 어레이 arr_1Dndim은 1이다.

arr_2D.ndim
2
arr_1D.ndim
1

3.2.3. dtype 속성#

넘파이 어레이의 dtype 속성은 어레이 항목의 자료형을 담고 있는데, 저장된 자료형은 파이썬 표준 라이브러리에서 제공하는 int, float, str 등을 보다 세분화한 버전이다.

부동소수점 자료형

예를 들어, 변수 arr_1D가 가리키는 어레이에 포함된 항목의 자료형은 64비트 형식의 부동소수점 자료형인 float64다.

arr_1D.dtype
dtype('float64')

arr_1D의 선언에 사용된 data_1D 리스트에는 정수와 부동소수점이 포함되어 있는데 앞서 언급한 대로 하나의 자료형으로 통일되어야 하기에 float64ndim 속성이 지정되었다.

data_1D
[6, 7.5, 8, 0, 1]

정수 자료형

반면에 arr_2Dndim은 64비트 형식의 정수 자료형인 int64다. 이유는 어레이 생성에 사용된 data_2D 리스트에 정수만 포함되었기 때문이다.

arr_2D.dtype
dtype('int64')

파이썬은 기본적으로 정수와 부동소수점을 64비트로 처리하지만, 어레이 객체는 필요에 따라 astype() 메서드를 이용하여 32비트, 16비트, 8비트 형식으로 ndim 속성을 지정할 수 있다.

예를 들어, 아래 코드는 astype() 메서드를 이용해 16비트 형식의 부동소수점으로 항목들의 자료형을 지정한다. 소수점 이하의 자릿수를 줄이므로 메모리 사용량을 절약하는 데에 사용되곤 한다.

arr_1D = np.array(data_1D).astype('float16')
arr_1D.dtype
dtype('float16')

아래 코드는 16비트 형식의 정수로 구성된 2차원 어레이를 생성한다.

arr_2D = np.array(data_2D).astype('int16')
arr_2D.dtype
dtype('int16')

문자열 자료형

문자열은 기본적으로 유니코드로 처리되며,크기는 최장 길이의 문자열 항목에 맞춰 결정된다. 예를 들어 아래 코드는 어레이에 포함된 가장 긴 문자열 'python' 의 길이가 6이기에 <U6가 자료형으로 지정됨을 보여준다.

arr_string = np.array(['python', 'data'])
arr_string.dtype
dtype('<U6')

반면에 아래 어레이의 자료형은 <U4인데, 문자열 '1.25'가 길이가 가장 긴 항목이기 때문이다.

numeric_strings = np.array(['1.25', '-9.6', '42'])
numeric_strings.dtype
dtype('<U4')

3.3. 어레이 연산#

넘파이 어레이 연산은 기본적으로 항목별로 이루어진다. 즉, 지정된 연산을 동일한 위치의 항목끼리 실행하여 새로운, 동일한 모양의 어레이를 생성한다.

3.3.1. 1차원 어레이 사칙연산#

모양이 동일한 아래 1차원 어레이 두 개를 이용하여 어레이 사칙연산의 작동법을 보여준다.

arr1 = np.array([1.0, 2, 3.2])
arr2 = np.array([1, 2, 3])
arr1 + arr2
array([2. , 4. , 6.2])
arr1 - arr2
array([0. , 0. , 0.2])
arr1 * arr2
array([1. , 4. , 9.6])
arr1 / arr2
array([1.        , 1.        , 1.06666667])

3.3.2. 2차원 어레이 사칙연산#

2차원 어레이 연산도 동일한 방식으로 진행된다.

arr3= np.array([[1., 2., 3.], [4., 5., 6.]])
arr3
array([[1., 2., 3.],
       [4., 5., 6.]])
arr4 = np.array([[3., 2., 1.], [4., 2., 12.]])
arr4
array([[ 3.,  2.,  1.],
       [ 4.,  2., 12.]])
arr3 + arr4
array([[ 4.,  4.,  4.],
       [ 8.,  7., 18.]])
arr3 - arr4
array([[-2.,  0.,  2.],
       [ 0.,  3., -6.]])
arr3 * arr4
array([[ 3.,  4.,  3.],
       [16., 10., 72.]])
arr3 / arr4
array([[0.33333333, 1.        , 3.        ],
       [1.        , 2.5       , 0.5       ]])

3.3.3. 비교 연산#

모양이 동일한 두 어레이의 값 비교도 항목별로 수행되며, 결과물로 생성되는 어레이는 모든 항목이 bool 자료형, 즉 True 또는 False이다.

arr4 > arr3
array([[ True, False, False],
       [False, False,  True]])
arr4 <= arr3
array([[False,  True,  True],
       [ True,  True, False]])
arr3 != arr4
array([[ True, False,  True],
       [False,  True,  True]])
arr3 == arr3
array([[ True,  True,  True],
       [ True,  True,  True]])
1.2 < arr3
array([[False,  True,  True],
       [ True,  True,  True]])
1.2 >= arr4
array([[False, False,  True],
       [False, False, False]])

3.3.4. 논리 연산#

어레이를 대상으로 사용 가능한 논리 연산은 아래 세 가지이며, 논리 연산 또한 항목별로 실행되어 동일한 모양의 어레이를 생성한다.

Table 3.1 어레이 논리 연산자#

기호

기능

~

부정(not) 연산자

&

논리곱(and) 연산자

|

논리합(or) 연산자

~(arr3 == arr3)
array([[False, False, False],
       [False, False, False]])
(arr3 == arr3) & (arr4 == arr4)
array([[ True,  True,  True],
       [ True,  True,  True]])
~(arr3 == arr3) | (arr4 == arr4)
array([[ True,  True,  True],
       [ True,  True,  True]])

3.4. 어레이 모양 변형#

어레이의 항목을 그대로 유지하면서 모양만 변형시키는 방식과 활용법을 소개한다.

reshape() 메서드

reshape() 메서드는 어레이의 모양을 변형하는데 쓰인다. 단, 전체 항목의 수가 변하지 않도록 모양을 지정해야 한다. 예를 들어, 길이 8인 1차원 어레이는 (4, 2) 모양의 2차원 어레이로 변형할 수 있지만, (4, 3) 모양의 어레이로 변형하려 하면 오류를 발생시킨다.

arr_arange8 = np.arange(8)
arr_arange8
array([0, 1, 2, 3, 4, 5, 6, 7])
arr_arange8.reshape((4, 2))
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7]])
arr_arange8.reshape((4, 3))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[44], line 1
----> 1 arr_arange8.reshape((4, 3))

ValueError: cannot reshape array of size 8 into shape (4,3)

-1의 역할

어레이의 모양을 지정하는 튜플의 특정 위치에 -1을 사용하면, 그 위치의 값은 어레이의 전체 항목 수와 튜플의 다른 항목에 의해 자동으로 결정된다. 예를 들어, 아래 코드에서 -1은 4로 결정된다. 20개의 항목을 5개의 행을 가진 2차원 어레이로 지정하려면 열의 개수가 4가 되기 때문이다.

arr_arange20 = np.arange(20)
arr_arange20.reshape((5, -1))
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19]])
arr_arange20.reshape((5, 4))
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19]])

마찬가지로, 아래 코드에서 -1은 5를 의미한다.

arr_arange20.reshape((-1, 4))
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19]])

3.5. 브로드캐스팅#

브로드캐스팅broadcasting 기능은 모양이 서로 다른 두 어레이가 주어졌을 때 두 모양을 통일시킬 수 있다면 두 어레이의 연산이 가능하도록 도와주는 기능이다. 설명을 위해 하나의 어레이와 하나의 정수의 곱셈이 작동하는 과정을 살펴본다.

arr_623 = np.arange(6).reshape((2,3))
arr_623
array([[0, 1, 2],
       [3, 4, 5]])

위 어레이에 4를 곱한 결과는 다음과 같다.

arr_623 * 4
array([[ 0,  4,  8],
       [12, 16, 20]])

결과가 항목별로 곱해지는 이유는 arr * 4 가 아래 어레이의 곱셈과 동일하게 작동하기 때문이다. 즉, 정수 4로 채워진 동일한 모양의 어레이를 먼저 생성한 후에 항목별 곱셈을 진행한 결과와 동일하다.

이와 같이 어레이의 모양을 확장하여 항목별 연산을 가능하게 해주는 브로드캐스팅 기능은 두 어레이의 모양이 항목별 연산을 일괄적으로 적용시켜도 무방한 경우에는 자동 적용된다.

아래 예제는 (4, 3) 모양의 2차원 어레이와 (3,) 모양의 1차원 어레이의 덧셈을 실행해서 브로드캐스팅 기능이 어떻게 작용하는지를 보여준다. 먼저 1차원 어레이를 (4,1) 모양의 2차원 어레이로 변형한 후, (4,3) 모양의 어레이로 확장시킨다.

arr_arange4_2D = np.array([[0, 0, 0],
                           [1, 1, 1],       
                           [2, 2, 2],
                           [3, 3, 3]])
arr_arange4_2D
array([[0, 0, 0],
       [1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

또다른 어레이는 (3,) 모양으로 생성한 후, 앞의 어레이와 덧셈을 실행해 본다.

arr_arange14 = np.arange(1, 4)
arr_arange14
array([1, 2, 3])
arr_arange4_2D + arr_arange14
array([[1, 2, 3],
       [2, 3, 4],
       [3, 4, 5],
       [4, 5, 6]])

위 연산이 작동하는 이유는 아래 그림을 통해 이해할 수 있다.

아래 코드는 (4,3) 모양의 어레이와 (4,1) 모양의 어레이의 덧셈에도 브로드캐스팅 기능이 마찬가지로 자동 적용됨을 보여준다.

arr_arange15_2D = np.arange(1, 5).reshape((4,1))
arr_arange15_2D
array([[1],
       [2],
       [3],
       [4]])
arr_arange4_2D + arr_arange15_2D
array([[1, 1, 1],
       [3, 3, 3],
       [5, 5, 5],
       [7, 7, 7]])

아래 그림이 위 연산이 작동하는 이유를 설명한다.

하지만 (4,3) 모양의 어레이와 (1,4) 모양의 어레이의 항목별 연산은 정의될 수가 없으므로, 브로드캐스팅이 가능하지 않고 오류가 발생한다.

arr_arange15_reshaped = arr_arange15_2D.reshape(1,4)
arr_arange15_reshaped
array([[1, 2, 3, 4]])
arr_arange4_2D + arr_arange15_reshaped
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[56], line 1
----> 1 arr_arange4_2D + arr_arange15_reshaped

ValueError: operands could not be broadcast together with shapes (4,3) (1,4) 

아래 그림이 이유를 설명해준다.

<그림 출처: NumPy: Broadcasting>

3.6. 인덱싱과 슬라이싱#

1차원 어레이 인덱싱과 슬라이싱

1차원 어레이의 인덱싱과 슬라이이싱은 리스트와 거의 동일하다.

arr_1D = np.arange(10)
arr_1D
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
arr_1D[5]
5
arr_slice = arr_1D[5:8]
arr_slice
array([5, 6, 7])

슬라이싱의 뷰 기능

넘파이 어레이의 슬라이싱은 리스트와 다르게, 지정된 구간의 객체를 새로 생성하는 게 아니라 구간의 정보를 활용하기만 한다. 이러한 기능을 view라고 부르는데, 슬라이싱을 포함하여 넘파이 어레이와 관련된 많은 함수가 뷰 기능을 사용한다.

예를 들어, 아래 코드에서 arr_slice의 항목을 변경하면 arr_1D의 항목도 함께 달라진다. 즉, 슬라이싱이 새로운 어레이를 생성하는 것이 아님이 확인된다.

arr_slice[1] = 3450
arr_1D
array([   0,    1,    2,    3,    4,    5, 3450,    7,    8,    9])

copy() 메서드

따라서 슬라이싱할 때, 원본을 그대로 유지하려면 copy() 메서드로 사본을 만들어 활용할 것을 권장한다.

arr_slice2 = arr[5:8].copy()
arr_slice2
array([   5, 3450,    7])

arr_slice2를 변경해도 arr은 영향받지 않는다.

arr_slice2[1] = 12
arr_slice2
array([ 5, 12,  7])
arr
array([   0,    1,    2,    3,    4,    5, 3450,    7,    8,    9])

2차원 어레이 인덱싱과 슬라이싱

2차원 이상의 다차원 어레이는 리스트보다 훨씬 다양한 인덱싱, 슬라이싱 기능을 제공한다.

arr_2D = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr_2D
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

우선 리스트의 인덱싱을 그대로 사용할 수 있다.

arr_2D[0]
array([1, 2, 3])
arr_2D[0][2]
3

하지만 어레이는 다음과 같이 축별 인덱스를 활용한 좌표 형식의 인덱싱도 허용한다.

arr_2D[0, 2]
3

슬라이싱 또한 리스트 슬라이싱 방식을 동일하게 적용할 수 있다.

  • 1번 인덱스 이전까지

arr_2D[:1]
array([[1, 2, 3]])
  • 2번 인덱스 이전까지

arr_2D[:2]
array([[1, 2, 3],
       [4, 5, 6]])
  • 전체 항목 슬라이싱

arr_2D[:3]
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

행과 열을 함께 슬라이싱하려면 행과 열에 대한 슬라이싱을 동시에 지정한다.

  • 행 기준: 2번 행 이전까지

  • 열 기준: 1번 열부터 끝까지

arr_2D[:2, 1:]
array([[2, 3],
       [5, 6]])

인덱싱과 슬라이싱이 행과 열 각각에 대해 독립적으로 사용될 수 있다.

  • 행 기준: 1번 행 인덱싱

  • 열 기준: 2번 열 이전까지

arr_2D[1, :2]
array([4, 5])

주의사항:

슬라이싱 결과물의 차원은 기존 어레이의 차원보다 인덱싱을 사용하는 축의 개수만큼 줄어든다. 따라서 동일한 항목을 얻는 슬라이싱일지라도, 인덱싱을 사용할 때와 아닐 때의 결과물은 다른 모양이 된다. 예를 들어, 아래 코드는 0번 축에 대해 인덱싱을 사용하므로, 슬라이싱의 결과물은 1차원 어레이이다.

arr_2D[1, :2]
array([4, 5])
arr_2D[1, :2].shape
(2,)

반면에, 아래 코드는 모든 축에 대해 슬라이싱을 적용하였기에 차원이 그대로 유지된 (1,2) 어레이가 된다.

arr_2D[1:2, :2]
array([[4, 5]])
arr_2D[1:2, :2].shape
(1, 2)

3.7. 예제#

예제 1

아래 모양의 2차원 어레이를 지정된 단계를 따라 생성해보자.

array([[ 0,  1,  2,  3,  4,  5],
       [10, 11, 12, 13, 14, 15],
       [20, 21, 22, 23, 24, 25],
       [30, 31, 32, 33, 34, 35],
       [40, 41, 42, 43, 44, 45],
       [50, 51, 52, 53, 54, 55]])

(1) 먼저 np.arange() 함수와 reshape() 어레이 메서드만 사용하여, 아래 모양의 어레이를 arr_e1 이름으로 생성한다.

array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

답:

2차원 어레이의 모양이 (6, 6)이지만 항목이 0에서 35까지의 정수로 구성된다. 따라서 np.arange(36)로 1차원 어레이를 만든 다음에 reshape() 메서드를 적용한다.

arr_e1 = np.arange(36)
arr_e1
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
       34, 35])

이제 (6, 6) 모양의 2차원 어레이로 변환한다.

arr_e1 = arr_e1.reshape((6,6))
arr_e1
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

또는

arr_e1 = arr_e1.reshape((6,-1))
arr_e1
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

한줄 코드로 작성하면 다음과 같다.

arr_e1 = np.arange(36).reshape((6,-1))
arr_e1
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

(2) np.arange() 함수와 reshape() 어레이 메서드만 사용하여 아래 모양의 어레이를 arr_e2 라는 이름으로 생성한다.

array([[ 0],
       [ 4],
       [ 8],
       [12],
       [16],
       [20]])

답:

2차원 어레이긴 하지만 0, 4, 8, 12, 16, 20으로 구성되었다. 따라서 먼저 해당 항목들로 구성된 1차원 어레이를 생성한다.

arr_e2 = np.arange(0, 21, 4)
arr_e2
array([ 0,  4,  8, 12, 16, 20])

이제 (6,1) 모양의 2차원 어레이로 변환한다.

arr_e2 = arr_e2.reshape((6,1))
arr_e2
array([[ 0],
       [ 4],
       [ 8],
       [12],
       [16],
       [20]])

한줄 코드로 작성하면 다음과 같다.

arr_e2 = np.arange(0, 21, 4).reshape((6,1))
arr_e2
array([[ 0],
       [ 4],
       [ 8],
       [12],
       [16],
       [20]])

(3) 이제 arr_e1과 arr_e2를 이용하여 아래 모양의 어레이를 생성한다.

array([[ 0,  1,  2,  3,  4,  5],
       [10, 11, 12, 13, 14, 15],
       [20, 21, 22, 23, 24, 25],
       [30, 31, 32, 33, 34, 35],
       [40, 41, 42, 43, 44, 45],
       [50, 51, 52, 53, 54, 55]])

답:

두 어레이를 더하면 arr_e2에 대해 브로드캐스팅이 작동하여 원하는 모양의 어레이가 생성된다.

arr = arr_e1 + arr_e2
arr
array([[ 0,  1,  2,  3,  4,  5],
       [10, 11, 12, 13, 14, 15],
       [20, 21, 22, 23, 24, 25],
       [30, 31, 32, 33, 34, 35],
       [40, 41, 42, 43, 44, 45],
       [50, 51, 52, 53, 54, 55]])

예제 2

아래 그림은 arr이 가리키는 2차원 어레이를 보여준다. 그림에 색깔별로 표시된 어레이를 슬라이싱을 이용하여 구해보자.

<그림 출처: Scipy Lecture Notes>

(1) 빨강색 상자로 표시된 1차원 어레이

답:

0번 행의 3번 열에서 4번 열까지로 구성된 1차원 어레이를 생성한다.

arr[0, 3:5]
array([3, 4])

(2) 파랑색 상자로 표시된 2차원 어레이

답:

2번 열에 위치한 항목들로 구성된 2차원 어레이를 생성하려면 모든 행에 대해 2번 열만 추출하되, 인덱싱이 아닌 슬라이싱을 이용해야 한다.

arr[:, 2:3]
array([[ 2],
       [12],
       [22],
       [32],
       [42],
       [52]])

2번 열에 대해 인덱싱을 적용하면 다음과 같은 1차원 어레이가 된다.

arr[:, 2]
array([ 2, 12, 22, 32, 42, 52])

(3) 보라색 상자로 감싸진 숫자들로 구성된 2차원 어레이

답:

행과 열에 대해 모두 스텝 2를 사용하는 슬라이싱을 적용한다.

arr[2:5:2, 0::2]
array([[20, 22, 24],
       [40, 42, 44]])

(4) 초록색 상자로 표시된 2차원 어레이

답:

4번부터 끝까지의 행과 열로 구성되었기에 콜론을 이용해 슬라이싱한다.

arr[4:, 4:]
array([[44, 45],
       [54, 55]])