본문 바로가기
Study/Python

numpy 배열의 생성과 조작

by 개발새-발 2021. 6. 22.
반응형

dnumpy 사용을 위하여 import를 해준다.

import numpy as np

numpy 배열 생성

np.array

np.array 에 list를 넘겨주어 numpy 배열의 생성이 가능하다.

np.array([1,3,5,7,9])
array([1, 3, 5, 7, 9])

np.ndarray

ndarray에 원하는 shape로 생성이 가능하다.

np.ndarray((3,4))
array([[6.95174279e-310, 7.89754161e-312, 7.89754160e-312,
        4.94065646e-324],
       [8.32219288e-315, 0.00000000e+000, 0.00000000e+000,
        7.41098469e-323],
       [4.00752607e+175, 6.89912841e-310, 0.00000000e+000,
        7.41098469e-323]])

np.empty()

np.empty도 원하는 shape를 입력해주면 배열을 생성한다.

np.empty((3,4))
array([[6.95174279e-310, 7.89754161e-312, 7.89754160e-312,
        4.94065646e-324],
       [8.32219288e-315, 0.00000000e+000, 0.00000000e+000,
        7.41098469e-323],
       [4.00752607e+175, 6.89912841e-310, 0.00000000e+000,
        7.41098469e-323]])

np.zeros(), np.ones(), np.full()

원하는 shape의 배열을 생성한다. 이때 원하는 값으로 채울 수 있다. zeros, ones는 각각 0과 1로 차있는 배열을 생성하며, full은 입력한 값으로 배열을 채워준다.

np.zeros((2,3))
array([[0., 0., 0.],
       [0., 0., 0.]])
np.ones((3,4,2))
array([[[1., 1.],
        [1., 1.],
        [1., 1.],
        [1., 1.]],

       [[1., 1.],
        [1., 1.],
        [1., 1.],
        [1., 1.]],

       [[1., 1.],
        [1., 1.],
        [1., 1.],
        [1., 1.]]])
np.full((3,5),7)
array([[7, 7, 7, 7, 7],
       [7, 7, 7, 7, 7],
       [7, 7, 7, 7, 7]])

np.identity(), np.eyes()

대각성분이 1이고, 그 이외의 성분들은 0인 배열을 만든다. 단위행렬을 생성할 수 있다. np.eye()k값을 부여하면 다른곳에서부터 1이 시작되도록 할 수 있다.

np.identity(4)
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])
np.eye(3)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])
np.eye(4,6)
array([[1., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0.],
       [0., 0., 0., 1., 0., 0.]])
np.eye(4,6,k=2)
array([[0., 0., 1., 0., 0., 0.],
       [0., 0., 0., 1., 0., 0.],
       [0., 0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 0., 1.]])

np.random.random()

주어진 모양의 배열을 랜덤한 값을 주어 생성한다.

np.random.random((4,5))
array([[0.25172189, 0.93835137, 0.58273023, 0.56608304, 0.96701823],
       [0.70491474, 0.51763131, 0.62417371, 0.14752055, 0.77555513],
       [0.38663993, 0.43899763, 0.29439131, 0.44782526, 0.45064033],
       [0.00362388, 0.02453017, 0.60090339, 0.51641041, 0.57204693]])

np.arange()

python에서의 range와 비슷하다. 다만, np.arange()는 numpy 배열을 생성한다.

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

np.linspace()

np.linspace는 시작, 끝, 배열에 들아갈 값들의 개수를 입력해주면 구간을 일정하게 나눈 결과를 반환한다. endpoint를 부여하여 맨 끝 값을 포함할 지 포함하지 않을지 결정할 수 있다. endpoint의 기본값은 True이다.

np.linspace(2,6,4)
array([2.        , 3.33333333, 4.66666667, 6.        ])
np.linspace(2,6,4,endpoint=False)
array([2., 3., 4., 5.])

데이터 타입 조작 dtype, astype()

numpy 배열에 들어가는 값들의 데이터타입을 조작해보자. 배열 생성시에 dtype을 지정해 줌으로 원하는 데이터타입의 배열을 생성할 수 있다.

np.eye(3,dtype=bool)
array([[ True, False, False],
       [False,  True, False],
       [False, False,  True]])
np.array([3.3,4.4,5.5],dtype=np.int32)
array([3, 4, 5])

배열의 dtype을 알고싶은 경우, 호출하면 된다.

arr = np.array([4.4,3.3,2.2])
print(arr.dtype)
float64

astype은 기존에 존재하는 numpy 배열을 원하는 타입으로 변환한 복사된 배열을 반환한다.

np.eye(3).astype(bool)
array([[ True, False, False],
       [False,  True, False],
       [False, False,  True]])

numpy 배열의 차원과 크기

numpy 배열의 차원과 크기를 알아보자.

  • shape : 모양을 보여준다. row가 4이고 column이 5 인 배열의 shape는 (4,5) 이다.
  • ndim : 몇차원 배열인지 알 수 있다.
  • size : 배열에 몇개의 값이 들어가 있는지 알 수 있게 해준다.
arr = np.empty((4,5))
print(
    len(arr),
    arr.shape,
    arr.ndim,
    arr.size,
    sep='\n'
)
4
(4, 5)
2
20

numpy 배열의 복사와 view

단순 대입

아래코드의 경우 배열은 복사되지 않는다. 이름만 다른 동일한 객체가 있다고 생각하면 편하다.

arr1 = np.array([1,2,3,0],np.int8)
arr2 = arr1

print(arr1 is arr2)
print("Before : ",arr2)
arr1[0] = 100
print("After : ", arr2)
True
Before :  [1 2 3 0]
After :  [100   2   3   0]

view()

shallow copy가 이루어진다. 다른 객체로 만들어 지기는 하나 메모리상에 같은 배열에 접근하기 때문에 arr1의 값이 바뀌면 arr2의 값도 바뀌게 된다.

arr1 = np.array([1,2,3,0],np.int8)
arr2 = arr1.view()

print(arr1 is arr2)
print("Before : ",arr2)
arr1[0] = 100
print("After : ", arr2)
False
Before :  [1 2 3 0]
After :  [100   2   3   0]

view를 사용할 때 타입을 지정해 줄 수 있다. 타입을 지정해준 경우 메모리를 지정한 타입으로 읽어오게 된다. 이 경우에도 shallow copy가 이루어진다.

arr1 = np.array([1,2,3,0],np.int8)
arr2 = arr1.view(bool)

print("Before : ",arr2)
arr1[3] = 4
print("After : ", arr2)
Before :  [ True  True  True False]
After :  [ True  True  True  True]

copy()

진정한 복사가 이루어진다. arr1을 수정하여도 arr2의 값이 변하지 않는다.

arr1 = np.array([1,2,3,0],np.int8)
arr2 = arr1.copy()

print(arr1 is arr2)
print("Before : ",arr2)
arr1[3] = 4
print("After : ", arr2)
False
Before :  [1 2 3 0]
After :  [1 2 3 0]

하나의 numpy배열에 대해 수행할 수 있는 연산

하나의 numpy배열에 대해 수행할 수 있는 연산들 중 일부이다. 수치해석에 유용하게 쓰일 수 있다. 아래 shape가 (3, 3)인 배열에 대해 연산해보자.

arr = np.arange(9).reshape(3,3)
print(arr)
[[0 1 2]
 [3 4 5]
 [6 7 8]]

우리는 어떤 배열의 합,누적합, 최대값, 최소값, 쳥균, 분산 등을 간편하게 구할 수 있다.

  • sum() : 합을 구한다.
  • cumsum() : 누적합을 구한다.
  • max(), min() : 최대값, 최소값을 구한다.
  • mean(), var(), std() : 평균, 분산, 표준편차를 구한다.
  • median() : 중앙값을 구한다.
print(
    arr.sum(),
    arr.cumsum(),
    arr.max(),
    arr.min(),
    arr.mean(),
    arr.var(),
    arr.std(),
    np.median(arr),
    sep='\n')
36
[ 0  1  3  6 10 15 21 28 36]
8
0
4.0
6.666666666666667
2.581988897471611
4.0

위의 예제는 배열 전체에 대하여 연산이 수행되었다. 그러나 우리는 행 또는 열 별로 연산을 수행하고 싶을 때가 있다. 각 행들의 합을 구한다던가 각 열들의 최대값이 알고 싶은 경우가 존재한다. 이럴경우 axis를 지정해주면 된다. axis를 지정해주면, axis를 따라서 연산을 하게 된다. 2차원 배열에서 axis = 0 은 세로축, axis = 1은 가로축을 의미한다. 이때, sum에 axis=0을 지정해주면 세로방향으로 연산이 진행된다.

print(
    arr.sum(axis=0),
    arr.cumsum(axis=1),
    arr.max(axis=1),
    arr.min(axis=1),
    arr.mean(axis=0),
    arr.var(axis=0),
    arr.std(axis=0),
    np.median(arr,axis=1),
    sep='\n----\n'
)
[ 9 12 15]
----
[[ 0  1  3]
 [ 3  7 12]
 [ 6 13 21]]
----
[2 5 8]
----
[0 3 6]
----
[3. 4. 5.]
----
[6. 6. 6.]
----
[2.44948974 2.44948974 2.44948974]
----
[1. 4. 7.]

사칙연산, 내적, 제곱, 제곱근, 지수, 로그연산

사칙연산

numpy 배열끼리 더하고, 빼고, 곱하고 나누는 것이 가능하다. 아래 shape가 (2, 2)인 배열 두개로 연산을 수행해보자.

a = np.arange(4).reshape(2,2) + 1
b = np.arange(10,14).reshape(2,2) + 1
print(a)
print(b)
[[1 2]
 [3 4]]
[[11 12]
 [13 14]]

파이썬에서 숫자로 사칙연산을 하듯이 연산을 하면 된다. shape가 같은 numpy 배열끼리 사칙연산을 수행하게 되면 같은 위치에 있는 항목끼리 연산이 수행된다.

a+b
array([[12, 14],
       [16, 18]])
a-b
array([[-10, -10],
       [-10, -10]])
a*b
array([[11, 24],
       [39, 56]])
a/b
array([[0.09090909, 0.16666667],
       [0.23076923, 0.28571429]])

내적 연산

@ 기호를 사용하면 내적연산이 가능하다. dot() 을 사용하여도 된다.

a@b
array([[37, 40],
       [85, 92]])
a.dot(b)
array([[37, 40],
       [85, 92]])

제곱, 제곱근

numpy 배열의 각 요소에 대해 제곱연산이나 제곱근 연산이 필요할때 사용할 수 있는 함수들이 있다.

  • square() : 배열의 각 요소들을 제곱해준다.
  • sqrt() : 배열의 각 요소들에 대해 제곱근 연산을 수행해준다.

혹은 python에서 숫자에 제곱을 수행하는 방법을 사용할 수도 있다.

print(
    np.square(a),
    np.sqrt(a),
    a**2,
    a**0.5,
    sep='\n----\n'
)
[[ 1  4]
 [ 9 16]]
----
[[1.         1.41421356]
 [1.73205081 2.        ]]
----
[[ 1  4]
 [ 9 16]]
----
[[1.         1.41421356]
 [1.73205081 2.        ]]

지수, 로그연산

위에선 numpy 배열의 값들이 제곱연산의 밑으로 사용되었다. 이번엔 지수로 사용해 보자. 또, 로그연산을 수행하여 보자. 아래는 관련 함수들의 목록이다.

  • exp() : e의 x승을 연산한다.
  • exp2() : 2의 x승을 연산한다.
  • expm1() : e^x -1 을 연산한다.
  • log() : lnx 를 연산한다.
  • log2() : 밑이 2인 로그를 연산한다.
  • log1p() : expm1()의 역연산이다.
print(
    np.exp(a),
    np.exp2(a),
    np.expm1(a),
    np.log(a),
    np.log2(a),
    np.log1p(a),
    sep='\n----\n'
)
[[ 2.71828183  7.3890561 ]
 [20.08553692 54.59815003]]
----
[[ 2.  4.]
 [ 8. 16.]]
----
[[ 1.71828183  6.3890561 ]
 [19.08553692 53.59815003]]
----
[[0.         0.69314718]
 [1.09861229 1.38629436]]
----
[[0.        1.       ]
 [1.5849625 2.       ]]
----
[[0.69314718 1.09861229]
 [1.38629436 1.60943791]]

아래 처럼 **를 사용하여도 된다.

print(
    2**a,
    10**a,
    sep='\n----\n'
)
[[ 2  4]
 [ 8 16]]
----
[[   10   100]
 [ 1000 10000]]

Broadcasting 브로드캐스팅

연산하려는 두 배열의 모양이 같지 않으면 연산을 수행할 수 없을 것 같다. 그러나, 특정조건을 만족하면 Broadcasting을 통해 연산이 가능하다. 예를들어, 아래는 2x2 모양의 배열과 1x1 모양의 배열간의 연산이 이루어진다.

a = np.array([[1,2],[3,4]])
b = np.array([10])

print(a+b)
[[11 12]
 [13 14]]

numpy의 문서에서는 broadcasting이 이루어 질 수 있는 차원의 조건을 이렇게 말한다. "비교중인 축에서 배열 길이의 값이 같거나, 하나가 1인경우" 이 조건을 만족하는지 확인하기 위해 가장 오른쪽의 차원들부터 왼쪽으로 조건을 검사하게 된다. 몇가지 예를 들어 알아보자.

5x4x3 모양의 배열과 1x3 모양의 배열이 있다. 이 둘은 broadcasting을 적용할 수 있을 까? 가장 오른쪽부터 조건을 확인해보자. 가장 우측의 값이 둘다 3이다. 이제 다음 값을 비교해보자. 4와 1이다. 두 값은 다르지만 하나의 값이 1이다. 그럼으로 여전히 조건을 만족한다. 이제 더이상 검사할 값이 없고, 검사시에 조건을 만족하지 않는 값도 없었음으로 broadcasting 적용이 가능하다. 아래 코드는 방금 든 예시의 연산 결과의 shape를 보여준다.

a = np.arange(60).reshape(5,4,3)
b = np.arange(3).reshape(1,3)

print((a+b).shape)
(5, 4, 3)

4x4 모양의 배열과 4x1 모양의 배열도 broadcasting이 가능하다. 우측부터 검사해보면 조건을 만족하지 않는 값이 없기 때문이다. 아래는 연산 결과이다.

a = np.arange(16).reshape(4,4)
b = np.arange(4).reshape(4,1)

print(
    a,
    b,
    a+b,
    sep='\n----\n'
)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]
----
[[0]
 [1]
 [2]
 [3]]
----
[[ 0  1  2  3]
 [ 5  6  7  8]
 [10 11 12 13]
 [15 16 17 18]]

numpy 배열과 비교연산자

numpy 배열에 비교연산자를 이용할 수 있다. 같은 shape의 배열끼리 연산을 수행하면 같은 위치의 요소끼리 연산이 수행되며 진리값들이 있는 배열들을 연산결과로 얻는다.

a = np.array([[1,3,5,7,9],[9,7,5,4,3]])
b = np.array([[10,8,5,4,2],[2,4,5,8,10]])
print(
    a>b,
    a<b,
    a>=b,
    a<=b,
    a==b,
    a!=b,
    sep='\n----\n'
)
[[False False False  True  True]
 [ True  True False False False]]
----
[[ True  True False False False]
 [False False False  True  True]]
----
[[False False  True  True  True]
 [ True  True  True False False]]
----
[[ True  True  True False False]
 [False False  True  True  True]]
----
[[False False  True False False]
 [False False  True False False]]
----
[[ True  True False  True  True]
 [ True  True False  True  True]]

numpy 배열과 숫자와도 비교가 가능하다.

print(a > 3)
[[False False  True  True  True]
 [ True  True  True  True False]]

numpy 배열 정렬하기 sort()

아래 코드는 1차원 배열에서 sort()를 수행한다.

arr = np.random.random(4)*10
print("Before sort")
print(arr)
arr.sort()
print("After sort")
print(arr)
Before sort
[5.74510398 2.69339236 1.32145774 9.43900013]
After sort
[1.32145774 2.69339236 5.74510398 9.43900013]

2차원 배열에서 sort()를 수행한다. axis의 기본값이 -1임으로 마지막 축을 기준으로 정렬이 수행된다. 2차원 배열에서는 마지막 축이 가로축임으로 각각의 행들이 정렬된다.

arr = np.random.random((4,5))*10
arr = arr.astype(np.int32)
print("Before sort")
print(arr)
arr.sort()
print("After sort")
print(arr)
Before sort
[[0 4 6 2 2]
 [0 3 2 0 8]
 [0 8 3 3 6]
 [0 3 4 8 1]]
After sort
[[0 2 2 4 6]
 [0 0 2 3 8]
 [0 3 3 6 8]
 [0 1 3 4 8]]

axis = 1로 지정하면 각각의 열들이 정렬된다.

arr = np.random.random((4,5))*10
arr = arr.astype(np.int32)
print("Before sort")
print(arr)
arr.sort(axis=0)
print("After sort")
print(arr)
Before sort
[[1 5 8 4 8]
 [6 2 6 5 7]
 [8 1 7 3 9]
 [7 5 0 3 3]]
After sort
[[1 1 0 3 3]
 [6 2 6 3 7]
 [7 5 7 4 8]
 [8 5 8 5 9]]

2차원 이상의 배열에서, 행이나 열별로 정렬하는 것이 아닌 모든 값들에 대해 정렬을 수행하고자 할 때에는 axis = None 으로 지정해주면 된다. 이때, axis = Nonenumpy.sort() 에서 지정가능하지만 numpy.ndarray.sort() 에서는 불가능하다. 또, numpy.sort() 는 정렬된 복사본을 반환하지만, numpy.ndarray.sort() 는 내부에서 정렬이 수행된다.

arr = np.random.random((4,5))*10
arr = arr.astype(np.int32)
print("Before sort")
print(arr)
print("After sort")
print(np.sort(arr,axis=None))
Before sort
[[1 6 7 2 5]
 [4 2 2 0 9]
 [6 2 0 5 2]
 [5 0 0 3 8]]
After sort
[0 0 0 0 1 2 2 2 2 2 3 4 5 5 5 6 6 7 8 9]

numpy 배열 요소 접근

아래 shape가 (3, 4)인 배열의 요소들에 접근해보자.

arr = np.arange(12).reshape(3,4)
print(arr)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

특정 값에만 접근하거나 특정 행, 열들에 접근이 가능하다. '...'은 숫자로 지정하지 않은 축에 대해 모두 ':' 를 적용하겠다는 의미이다.

print(
    arr[2],
    arr[2,3],
    arr[2,:],
    arr[:,1],
    arr[2,2:],
    arr[1,...],
    arr[...,2],
    sep='\n---\n'
)
[ 8  9 10 11]
---
11
---
[ 8  9 10 11]
---
[1 5 9]
---
[10 11]
---
[4 5 6 7]
---
[ 2  6 10]

bool 배열을 이용해서 원하는 값들만 불러올 수도 있다.

barr = np.array(([1,0]*6),dtype=bool).reshape(3,4)
print(barr)
print('----')
print(arr[barr])
[[ True False  True False]
 [ True False  True False]
 [ True False  True False]]
----
[ 0  2  4  6  8 10]

숫자값이 들어간 배열을 이용하여 특정위치에 있는 값을 불러올 수 있다. 아래처럼 코드를 작성하면, 2,2 에 있는 값과 0,1에 있는 값을 불러온다.

arr[[2,0],[2,1]]
array([10,  1])

아래처럼 작성한 경우 2번째, 0번째 행이면서 2번째, 1번째 열에 위치한 값들을 불러온다.

arr[[2,0]][:,[2,1]]
array([[10,  9],
       [ 2,  1]])

shape, axis 조작

reshape()

numpy 배열의 shape를 바꾼다. 이때 바꾸려는 shape는 기존 shape에서 바꿀 수 있는 형태여야 한다. shape를 바꿀 수 있기 위해선, 바뀌기 전과 바뀐 후의 배열의 size가 동일하여야 한다. 예를 들어 3x3 형태의 배열을 4x3 형태의 배열로 바꿀 수 없다. 그러나 3x4의 배열을 2x2x3의 형태의 배열로 바꾸는 것은 가능하다.

reshape에 -1이 사용되는 경우를 볼 수 있다. 이때, reshape는 -1이 들어간 자리에 적절한 값을 넣어서 처리한다. 적절한 값은 배열의 size와 다른 축 방향으로 설정된 값들을 통해 결정된다.

arr = np.arange(27).reshape(3,3,3)
print(
    arr.reshape(9,3),
    arr.reshape(3,-1),
    sep='\n----\n'
)
[[ 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]]
----
[[ 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]]

1차원 배열로 만들기 ravel(), flatten()

다차원 배열을 1차원 배열로 만든다. raven(), flatten() 의 차이는 raven()view를 반환하고, flatten()복사된 값들을 반환한다.

arr = np.arange(27).reshape(3,3,3)

print(
    arr.ravel(),    #flatten view
    arr.flatten(),  #flatten copy
    sep='\n----\n'
)
[ 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]
----
[ 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]

numpy 배열의 전치행렬, 축 변환 transpose()

transpose() 를 이용하면 전치행렬을 구할 수 있다. .T 를 사용해도 된다.

arr = np.arange(16).reshape(4,4)
print(
    arr,
    arr.transpose(),
    arr.T,
    sep='\n----\n'
)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]
----
[[ 0  4  8 12]
 [ 1  5  9 13]
 [ 2  6 10 14]
 [ 3  7 11 15]]
----
[[ 0  4  8 12]
 [ 1  5  9 13]
 [ 2  6 10 14]
 [ 3  7 11 15]]

3차원 이상의 배열에서 transpose는 축을 변환한다. 만약 기존의 배열이 3차원의 배열이라고 하자. 이때 우리는 transpose에 3개의 값을 넣게된다. 이때, 0번째로 넣은 값이 변환후의 0번째 축, 1번째에 넣은 값이 변환후 1번째 축, 2번째에 넣은 값이 변환후의 2번째 축이 된다. 즉, 입력한 순서대로 새로운 축의 방향들이 설정된다.

arr = np.arange(27).reshape(3,3,3)
print(
    arr,
    arr.transpose(),
    arr.transpose(0,2,1),
    sep='\n----\n'
)
[[[ 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]]]
----
[[[ 0  9 18]
  [ 3 12 21]
  [ 6 15 24]]

 [[ 1 10 19]
  [ 4 13 22]
  [ 7 16 25]]

 [[ 2 11 20]
  [ 5 14 23]
  [ 8 17 26]]]
----
[[[ 0  3  6]
  [ 1  4  7]
  [ 2  5  8]]

 [[ 9 12 15]
  [10 13 16]
  [11 14 17]]

 [[18 21 24]
  [19 22 25]
  [20 23 26]]]

numpy 배열과 bool 연산, bit연산

numpy 배열의 not, and, or, xor 연산

python에서 bool 값끼리 연산을 하듯 numpy배열에서도 bool배열 끼리 연산을 할 수 있다. 아래와 같은 bool 배열 a,b가 있다고 하자.

a = np.array([0,0,1,1],dtype=bool)
b = np.array([0,1,0,1],dtype=bool)

print(a)
print(b)
[False False  True  True]
[False  True False  True]

아래는 not연산의 결과이다.

print(~a)
[ True  True False False]

아래는 차례대로 and, or, xor을 연산한 결과이다.

print(
    a&b,
    a|b,
    a^b,
    sep='\n'
)
[False False False  True]
[False  True  True  True]
[False  True  True False]

숫자 배열에 대해서도 숫자끼리 bit연산을 하듯 연산이 가능하다.

c = np.array([1,3,5])
d = np.array([8,6,3])

print(
    c&d,
    c|d,
    c^d,
    sep='\n'
)
[0 2 1]
[9 7 7]
[9 5 6]

특정 조건을 만족하는 경우 판별

비교연산자와 bool 배열 사용

bool 배열을 사용해서 인덱싱을 하는 방법을 안다면 특정조건을 만족하는 값들을 쉽게 구할 수 있다. 예를들어 배열 arr에서 3을 초과하면서 6 미만인 값들을 구한다고 해보자. arr > 3 을 적으면 연산결과로 bool 배열이 나올것이고 이는 arr < 6 도 마찬가지이다. 이 두 bool 배열에 and 연산을 취해주면 자연스럽게 arr > 3 이면서 arr < 6인 값들의 위치에만 True인 bool 배열을 구할 수 있다. 기존 배열에 이 bool배열로 인덱싱을 해주면 조건을 만족하는 값들을 불러올 수 있다. 구한 bool 배열에 대해 sum()연산을 취한다면 조건을 만족하는 값들의 개수도 알 수 있다.

arr = np.array([1,2,3,4,5,6,7,8,9,0])
boolarr = (arr > 3) & (arr < 6)
print(arr[boolarr], boolarr.sum())
[4 5] 2

any(), all()

any() 를 사용해서 배열의 값 중 단 하나라도 True 라면 True를 반환한다. 각 행, 열 별로도 연산 가능하다.

arr = np.array([[True,False],[False,False]])

print(
    arr.any(),
    arr.any(axis=1),
    sep='\n'
)
True
[ True False]

all() 를 사용해서 배열의 모든 값이 True인지 확인한다. 각 행, 열 별로도 연산 가능하다.

arr = np.array([[True,True],[False,True]])

print(
    arr.all(),
    arr.all(axis=1),
    sep = '\n'
)
False
[ True False]

bool 값이 아닌 숫자가 담긴 배열에서도 사용가능하다. 이때는 0을 False처럼 취급한다.

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

print(arr.any(), arr.all(),sep=',')
True,False

단, np.nan은 False로 취급되지 않는다.

arr = np.array([np.nan])
print(arr.any())
True

배열의 값들이 숫자가 아닌 object인 경우에 반환값은 True나 False가 아닐 수 있다.

arr = np.array([{'a':1},0,None])
print(arr.all())
arr = np.array([{'a':1},None,None])
print(arr.all())
arr = np.array([{'a':1},0,None])
print(arr.any())
0
None
{'a': 1}

isnan()

위의 any()나 all()은 nan을 세어주지 않는다. isnan()은 배열의 어떤 위치의 값이 nan값인지 알 수 있도록 bool 배열을 반환한다.

arr = np.array([0,1,2,np.nan])
print(np.isnan(arr))
[False False False  True]

isfinite()

np.nan, np.inf 등등, 셀 수 없는 값들의 위치에 False가 들어있는 bool배열을 반환해준다.

arr = np.array([np.inf,np.nan,1,2,3,4,0])
print(np.isfinite(arr))
[False False  True  True  True  True  True]

특정 조건을 만족시 값 변경

비교연산자와 bool 연산 활용

위에서 조건을 만족하는지 파악만 하였다면, 이번에는 그 값을 원하는 값으로 변경할 것이다. arr > 3 이면서 arr < 6인 값을 -1로 바꾸어 보자.

arr = np.array([1,2,3,4,5,6,7,8,9,0])
arr[(arr > 3) & (arr < 6)] = -1
print(arr)
[ 1  2  3 -1 -1  6  7  8  9  0]

where()

np.where()을 사용하면 조건과 만족하는 경우와 그렇지 않은 경우에 들어갈 값을 지정할 수 있다. where(조건문,조건문을 만족할시 들어갈 값,조건문을 만족하지 않을 시에 들어갈 값) 의 형태로 사용한다. 들어갈 값으로는 배열이 들어갈 수도 있는데, 이 경우에는 그 배열에서 같은 위치에 있는 값이 들어가게 된다.

arr = np.array([1,2,3,4,5,6,7,8,9,10])
print(arr)
print(np.where(arr > 8, 8, arr))
[ 1  2  3  4  5  6  7  8  9 10]
[1 2 3 4 5 6 7 8 8 8]

비교연산자와 bool연산으로도 구현가능하지만, 조건을 만족하는 경우와 그렇지 않은 경우 둘다 값을 바꾸려고 하는 경우에는 where을 쓰는것이 더 간결한 경우가 많다. 또, bool 배열을 이용한 인덱싱은 원본 배열의 값을 바꾸지만, where은 원본 배열의 값이 변하지 않는다.

numpy 배열값의 상한, 하한 값 정하기 clip(), minimum(), maximum()

어떤 배열에서 상한값, 하한값을 정하고 싶을 경우가 있다. 최대값, 최솟값을 제한하고 싶다면 clip()을 써주면 된다. clip()에서 지정한 최소값보다 작은 값은 지정한 값으로 바뀌며, clip()에서 지정한 최대값보다 큰 값도 지정한 값으로 바뀐다. clip은 원본 배열의 값을 바꾸지 않는다.

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

print(
    arr.clip(3,7),
    arr.clip(3),
    arr.clip([5,5,5,7,7,7,8,8,8,0],8),
    arr,
    sep = '\n'
)
[3 3 3 4 5 6 7 7 7 7]
[ 3  3  3  4  5  6  7  8  9 10]
[5 5 5 7 7 7 8 8 8 8]
[ 1  2  3  4  5  6  7  8  9 10]

minimum(), maximum()을 사용할 수도 있다. minmum()은 둘중 작은값, maximum()은 둘중 큰값을 반환한다.

arr = np.array([1,2,3,4,5,6,7,8,9,10])
print(
    np.minimum(arr,3),
    np.maximum(arr,7),
    sep = '\n'
)
[1 2 3 3 3 3 3 3 3 3]
[ 7  7  7  7  7  7  7  8  9 10]

numpy배열에서 중복이 제거된 값 불러오기 unique()

배열내에 어떤 값들이 존재하지 알고싶은 경우가 있다. 이때, 중복되는 값들이 많다면 어떤 값들이 존재하는지 파악하기 힘들 것이다. unique()를 이용하면 배열에서 중복이 제거된 값들을 볼 수 있다.

arr = np.array([1,2,3,4,3,3,2])
print(np.unique(arr))
[1 2 3 4]

return_counts 옵션을 True로 주는 경우, 어떤 값이 몇개가 들어있어쓴지를 알 수 있게 된다.

unique, counts = np.unique(arr,return_counts=True)
print(unique,counts)
[1 2 3 4] [1 2 3 1]

return_reverse를 True로 주는경우 원본 배열을 만들 수 있는 배열을 같이 반환한다. 기존에 반환하는 unique한 배열을 같이 반환된 배열로 접근을 하면 원본 배열을 다시 만들 수 있다.

unique, indices = np.unique(arr,return_inverse=True)
print(unique,indices)
print(unique[indices])
[1 2 3 4] [0 1 2 3 2 2 1]
[1 2 3 4 3 3 2]

연산이 진행될 축을 지정할 수 있다. 이 경우에 행이나 열 내부에서 연산이 이루어지는 것이 아니다. 연산이 이루어지는 방향으로 행이나 열 자체에 대해 연산이 이루어지게 된다. 즉, 행이나 열이 동일한지 아닌지를 판별하게 된다.

arr = np.array([[1,3,3],[2,2,2],[3,3,3],[2,2,2]])
print(
    arr,
    np.unique(arr),
    np.unique(arr,axis=0),
    np.unique(arr,axis=1),
    sep='\n----\n'
)
[[1 3 3]
 [2 2 2]
 [3 3 3]
 [2 2 2]]
----
[1 2 3]
----
[[1 3 3]
 [2 2 2]
 [3 3 3]]
----
[[1 3]
 [2 2]
 [3 3]
 [2 2]]

numpy 배열들 이어붙이기, 쌓기, 합치기

두개 이상의 배열을 연결하고, 쌓을 수 있다.

concatenate()

존재하는 축 방향으로 배열을 이어붙인다. axis값을 부여해서 원하는 방향을 지정할 수 있으며, None으로 주어진 경우 1차원 배열로 flat된 배열을 이어붙이게 된다.

a = np.full((2,3),1)
b = np.full((2,3),2)

print(
    np.concatenate([a,b]),
    np.concatenate([a,b],axis=1),
    np.concatenate([a,b],axis=None),
    sep='\n----\n'
)
[[1 1 1]
 [1 1 1]
 [2 2 2]
 [2 2 2]]
----
[[1 1 1 2 2 2]
 [1 1 1 2 2 2]]
----
[1 1 1 1 1 1 2 2 2 2 2 2]

stack()

축을 하나 더 생성하여 배열을 이어붙인다. axis값을 부여해서 배열들이 쌓일 축을 지정할 수 있다.

a = np.full((2,3),1)
b = np.full((2,3),2)

print(
    np.stack([a,b]),
    np.stack([a,b],axis=1),
    sep='\n----\n'
)
[[[1 1 1]
  [1 1 1]]

 [[2 2 2]
  [2 2 2]]]
----
[[[1 1 1]
  [2 2 2]]

 [[1 1 1]
  [2 2 2]]]

concatenate vs stack

아래 shape를 보는 코드에서 둘의 차이를 확인할 수 있다. concatenate는 존재하는 축 방향으로 배열을 이어붙히고, stack은 축을 하나 새로 만들어 그 방향으로 배열을 쌓는다.

a = np.full((2,3),1)
b = np.full((2,3),2)

print(
    np.concatenate([a,b],axis=0).shape,
    np.stack([a,b],axis=0).shape
)
(4, 3) (2, 2, 3)

vstack, hstack, dstack

vstack은 세로, hstack은 가로방향으로 배열을 이어붙인다. dstack은 축을 하나 추가하여 그 방향으로 배열을 쌓는다.

a = np.full((2,3),1)
b = np.full((2,3),2)

print(
    np.vstack([a,b]),
    np.hstack([a,b]),
    np.dstack([a,b]),
    sep='\n----\n'
)
[[1 1 1]
 [1 1 1]
 [2 2 2]
 [2 2 2]]
----
[[1 1 1 2 2 2]
 [1 1 1 2 2 2]]
----
[[[1 2]
  [1 2]
  [1 2]]

 [[1 2]
  [1 2]
  [1 2]]]
print(
    np.vstack([a,b]).shape,
    np.hstack([a,b]).shape,
    np.dstack([a,b]).shape,
    sep='\n----\n'
)
(4, 3)
----
(2, 6)
----
(2, 3, 2)

numpy 배열 여러개로 쪼개기, 분활하기

concatenate()와 stack()으로 여러개의 배열을 합쳤다면, 이번에는 하나의 배열을 나눌것이다.

split()

split()에 두번째 인자로 정수를 입력하면, 배열을 입력한 정수만큼 나눈다. 아래 코드에서는 4를 입력했음으로 4개의 배열로 나누어질 것이다.
두번째 인자에 배열을 입력한 경우 입력한 배열에 적힌 숫자를 나눌 위치로 간주하고 배열을 나눈다. 아래 코드에선 [2,5]가 입력되었음으로 0:2, 2:5, 5: 구간으로 배열이 나누어 진다.

arr = np.arange(12)
print(
    np.split(arr,4),
    np.split(arr,[2,5]),
    sep='\n----\n'
)
[array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8]), array([ 9, 10, 11])]
----
[array([0, 1]), array([2, 3, 4]), array([ 5,  6,  7,  8,  9, 10, 11])]

축을 지정하여 원하는 방향으로 연산을 수행할 수 있다.

arr = np.arange(12).reshape(3,4)
print(
    np.split(arr,2,axis=1),
    np.split(arr,[3],axis=1),
    sep='\n----\n'
)
[array([[0, 1],
       [4, 5],
       [8, 9]]), array([[ 2,  3],
       [ 6,  7],
       [10, 11]])]
----
[array([[ 0,  1,  2],
       [ 4,  5,  6],
       [ 8,  9, 10]]), array([[ 3],
       [ 7],
       [11]])]

vsplit(), hsplit(), dsplit()

stack에서처럼 세로, 가로, 깊이 방향으로 수행할 수 있는 함수가 있다.

arr = np.arange(24).reshape(2,3,4)
print(
    np.vsplit(arr,2),
    np.hsplit(arr,3),
    np.dsplit(arr,4),
    sep='\n----\n'
)
[array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]]]), 
        
array([[[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])]
----
[array([[[ 0,  1,  2,  3]],
        [[12, 13, 14, 15]]]), 
       
 array([[[ 4,  5,  6,  7]],
       [[16, 17, 18, 19]]]), 
       
 array([[[ 8,  9, 10, 11]],
        [[20, 21, 22, 23]]])]
----
[array([[[ 0],
        [ 4],
        [ 8]],

       [[12],
        [16],
        [20]]]),
        
 array([[[ 1],
        [ 5],
        [ 9]],

       [[13],
        [17],
        [21]]]),
        
 array([[[ 2],
        [ 6],
        [10]],

       [[14],
        [18],
        [22]]]),
        
 array([[[ 3],
        [ 7],
        [11]],

       [[15],
        [19],
        [23]]])]

numpy 배열과 값의 부호

값의 부호와 관련된 연산들이다.

  • abs() : abs()를 사용하면 각각의 값들의 절대값이 적용된 배열을 받을 수 있다.
  • sign() : sign()을 사용하면 양수인 경우 1, 음수인경우 -1, 0 인경우 0의 값을 적용하는 배열을 받을 수 있다.
arr = np.array([-2,-3,4,5,6,-7,-8,0])
print(np.abs(arr),np.sign(arr))
[2 3 4 5 6 7 8 0] [-1 -1  1  1  1 -1 -1  0]

numpy 배열에서 반올림, 올림, 내림 수행하기

numpy 배열의 값들에 대해 반올림, 올림, 내림의 연산이 가능하다.

  • round() : 반올림
  • ceil() : 올림
  • floor() : 내림
arr = np.array([1.2,2.5,3.8])
print(
    np.round(arr),
    np.ceil(arr),
    np.floor(arr),
    sep='\n'
)
[1. 2. 4.]
[2. 3. 4.]
[1. 2. 3.]
반응형

댓글