NumPy 핵심 정리: 배열 생성, 조작, 연산 가이드

1. NumPy 배열(ndarray) 기본 구조

NumPy 배열은 ndarray라고 불리는 다차원 배열 객체입니다. 다음과 같은 두 부분으로 구성됩니다:

  • 실제 데이터
  • 데이터를 설명하는 메타데이터 (차원, 모양, 데이터 타입 등)

2. 배열의 주요 속성

import numpy as np

# 3차원 배열 생성
data = np.array([
    [[1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7]],
    [[1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7]]
])

print(data)          # 배열 출력 (리스트와 달리 쉼표 없이 출력)
print(data.ndim)     # 배열 차원의 개수 (축 개수, rank라고도 함)
print(data.shape)    # 배열의 모양 (n행 m열인 경우 (n, m))
print(data.size)     # 배열의 전체 요소 개수 (n * m)
print(data.dtype)    # 배열 요소의 데이터 타입 (type() 함수와 유사하지만 속성)
print(data.itemsize) # 배열 각 요소의 바이트 크기 (int32는 4, float64는 8)
print(data.data)     # 실제 배열 데이터가 저장된 버퍼 (일반적으로 인덱스로 접근)

3. 배열 생성 방법

3.1. array() 함수 사용

arr1 = np.array(range(10))          # 정수형 배열
arr2 = np.array([1, 2, 3.14, 4, 5]) # 실수형 배열 (정수와 실수 혼합 시 실수형으로 변환)
arr3 = np.array([[1, 2, 3], ('a', 'b', 'c')]) # 2차원 배열: 중첩된 시퀀스 (리스트, 튜플 모두 가능)

print(arr1, type(arr1), arr1.dtype)
print(arr2, type(arr2), arr2.dtype)
print(arr3, arr3.shape, arr3.ndim, arr3.size) # 2차원 배열, 총 6개 요소

3.2. arange() 함수 사용

print(np.arange(10))      # 0부터 9까지 정수형 배열
print(np.arange(10.0))    # 0.0부터 9.0까지 실수형 배열
print(np.arange(5, 12))   # 5부터 11까지 정수형 배열
print(np.arange(5.0, 12, 2)) # 5.0부터 12.0까지, 간격 2인 실수형 배열
print(np.arange(10000))   # 배열이 너무 크면 중간 부분을 생략하고 모서리만 출력

3.3. linspace() 함수 사용

# numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
res1 = np.linspace(2.0, 3.0, num=5)                # 2.0 ~ 3.0 사이를 5등분
res2 = np.linspace(2.0, 3.0, num=5, endpoint=False) # 마지막 값(3.0) 미포함
res3 = np.linspace(2.0, 3.0, num=5, retstep=True)   # (배열, 간격) 튜플 반환

print(res1, type(res1))
print(res2)
print(res3, type(res3))

3.4. zeros(), ones()*_like() 함수

# zeros(): 모든 요소를 0으로 채운 배열 생성
zero_arr1 = np.zeros(5)          # 1차원, 5개 요소
zero_arr2 = np.zeros((2, 2))      # 2x2 배열
print(zero_arr1, zero_arr1.dtype)
print(zero_arr2, zero_arr2.dtype)

# zeros_like(): 주어진 배열과 동일한 shape와 dtype을 가진 0으로 채워진 배열 생성
ref_arr = np.array([list(range(5)), list(range(5, 10))])
zeros_like_arr = np.zeros_like(ref_arr)
print(ref_arr)
print(zeros_like_arr)

# ones() / ones_like(): 모든 요소를 1로 채움 (zeros와 동일한 방식)
ones_arr1 = np.ones(9)
ones_arr2 = np.ones((2, 3, 4))
ones_like_arr = np.ones_like(ref_arr)
print(ones_arr1)
print(ones_arr2)
print(ones_like_arr)

3.5. eye() 함수 (단위 행렬)

# N x N 단위 행렬 생성 (대각선은 1, 나머지는 0)
identity_matrix = np.eye(5)
print(identity_matrix)

4. NumPy 일반 함수 (배열 조작)

4.1. 배열 모양 변경 (reshape, resize, T)

arr1 = np.arange(10)
arr2 = np.ones((5, 2))

# .T 속성: 전치(transpose)
# 1차원 배열은 전치해도 변하지 않음
print(arr1, '\n', arr1.T)
print(arr2, '\n', arr2.T)

# reshape(): 데이터는 유지하면서 배열 모양만 변경 (요소 개수 일치 필요)
reshaped1 = arr1.reshape(2, 5)                     # 10개 요소를 2x5로 변경
reshaped2 = np.zeros((4, 6)).reshape(3, 8)         # 24개 요소를 3x8로 변경
reshaped3 = np.reshape(np.arange(12), (3, 4))       # 12개 요소를 3x4로 변경

print(arr1, '\n', reshaped1)
print(reshaped2)
print(reshaped3)

# resize(): 지정된 shape에 맞게 요소를 복사하여 채움 (부족하면 반복, 초과하면 잘라냄)
resized_arr = np.resize(np.arange(5), (3, 4)) # 5개 요소를 3x4(12개)로 복사하여 채움
print(resized_arr)

4.2. 배열 복사 (copy)

original = np.arange(10)
shallow_copy = original  # 참조 복사 (같은 메모리 공간을 가리킴)

print(shallow_copy is original)  # True
original[2] = 9
print(original, shallow_copy)    # 둘 다 변경됨

deep_copy = original.copy()      # 깊은 복사 (새로운 메모리 공간)
print(deep_copy is original)     # False
original[0] = 99
print(original, deep_copy)       # original만 변경됨

4.3. 데이터 타입 변환 (astype)

float_arr = np.arange(10, dtype=float)
print(float_arr, float_arr.dtype)

int_arr = float_arr.astype(np.int32) # 실수형 배열을 32비트 정수형으로 변환
print(int_arr, int_arr.dtype)
print(float_arr, float_arr.dtype)    # 원본은 변경되지 않음

4.4. 배열 결합 (hstack, vstack, stack)

# hstack(horizontal stack): 수평으로 배열 결합 (열 방향)
a = np.arange(5)       # 1차원, 5개
b = np.arange(5, 9)    # 1차원, 4개
h_combined = np.hstack((a, b)) # shape가 달라도 가능
print(a, a.shape)
print(b, b.shape)
print(h_combined, h_combined.shape)

# 2차원 배열의 hstack (행 수가 같아야 함)
a_2d = np.array([[1], [2], [3]])      # 3행 1열
b_2d = np.array([['a'], ['b'], ['c']]) # 3행 1열
h_combined_2d = np.hstack((a_2d, b_2d))
print(a_2d, a_2d.shape)
print(b_2d, b_2d.shape)
print(h_combined_2d, h_combined_2d.shape)

# vstack(vertical stack): 수직으로 배열 결합 (행 방향)
v_combined = np.vstack((a, b)) # a, b는 1차원 배열
print(a, a.shape)
print(b, b.shape)
print(v_combined, v_combined.shape)

# stack(): 새로운 축을 따라 배열 결합 (shape가 같아야 함)
stacked_axis0 = np.stack((a, b))          # axis=0, shape (2, 5)
stacked_axis1 = np.stack((a, b), axis=1)  # axis=1, shape (5, 2)
print(a, a.shape)
print(b, b.shape)
print(stacked_axis0, stacked_axis0.shape)
print(stacked_axis1, stacked_axis1.shape)

4.5. 배열 분할 (hsplit, vsplit)

arr = np.arange(16).reshape(4, 4)

# hsplit(): 수평으로 분할 (열 방향)
h_split = np.hsplit(arr, 2)  # 4열을 2개로 분할 (2열씩)
print(arr)
print(h_split, type(h_split)) # 리스트로 반환

# vsplit(): 수직으로 분할 (행 방향)
v_split = np.vsplit(arr, 4)  # 4행을 4개로 분할 (1행씩)
print(v_split, type(v_split))

4.6. 기본 연산 및 통계 함수

arr = np.arange(6).reshape(2, 3)

# 스칼라 연산
print(arr + 10)      # 덧셈
print(arr * 2)       # 곱셈
print(1 / (arr + 1)) # 나눗셈
print(arr ** 0.5)    # 제곱근

# 통계 함수
print(arr.mean())    # 평균
print(arr.max())     # 최대값
print(arr.min())     # 최소값
print(arr.std())     # 표준 편차
print(arr.var())     # 분산
print(arr.sum(), np.sum(arr, axis=0)) # 총합, 열 기준 합계
print(np.sort(np.array([1, 4, 3, 2, 5, 6]))) # 정렬

5. 인덱싱과 슬라이싱

5.1. 기본 인덱싱 및 슬라이싱

# 1차원 배열
one_d = np.arange(20)
print(one_d)
print(one_d[4])      # 5번째 요소 (인덱스 4)
print(one_d[3:6])    # 인덱스 3부터 5까지

# 2차원 배열
two_d = np.arange(16).reshape(4, 4)
print(two_d, '차원:', two_d.ndim)
print(two_d[2], '차원:', two_d[2].ndim)      # 3번째 행 (1차원 배열)
print(two_d[2][1])                          # 3행 2열 요소
print(two_d[1:3], '차원:', two_d[1:3].ndim) # 2~3행 (2차원 배열)
print(two_d[2, 2])                          # 3행 3열 요소
print(two_d[:2, 1:])                        # 1~2행, 2~4열

# 3차원 배열
three_d = np.arange(8).reshape(2, 2, 2)
print(three_d, '차원:', three_d.ndim)
print(three_d[0], '차원:', three_d[0].ndim)    # 첫 번째 2D 레이어
print(three_d[0][0], '차원:', three_d[0][0].ndim) # 첫 번째 레이어의 첫 번째 행
print(three_d[0][0][1])                         # 첫 번째 레이어의 첫 번째 행, 두 번째 열

5.2. 인덱싱/슬라이싱을 통한 값 변경 및 복사

arr = np.arange(10)
print(arr)
arr[5] = 100      # 특정 요소 값 변경
arr[7:9] = 200    # 슬라이스 범위 값 변경
print(arr)        # 원본 배열이 변경됨

# 깊은 복사를 통해 원본 보호
original = np.arange(10)
copied = original.copy()
copied[7:9] = 200
print(original)   # 원본은 변경되지 않음
print(copied)

6. 난수 생성

6.1. 정규 분포 (normal)

# 표준 정규 분포 (평균=0, 표준편차=1)에서 4x4 샘플 생성
samples = np.random.normal(size=(4, 4))
print(samples)

6.2. 균일 분포 (rand)

scalar = np.random.rand()        # 0~1 사이 난수 1개
vector = np.random.rand(4)       # 0~1 사이 난수 4개 (1차원 배열)
matrix = np.random.rand(2, 3)    # 0~1 사이 난수 6개 (2x3 배열)

print(scalar, type(scalar))
print(vector, type(vector))
print(matrix, type(matrix))

samples1 = np.random.rand(1000)  # 1000개의 난수
samples2 = np.random.rand(1000)

6.3. 표준 정규 분포 (randn)

samples1 = np.random.randn(1000) # 평균 0, 표준편차 1
samples2 = np.random.randn(1000)

6.4. 정수 난수 (randint)

# numpy.random.randint(low, high=None, size=None, dtype='l')
# high가 None이면 [0, low) 범위, 아니면 [low, high) 범위

print(np.random.randint(2))                 # [0, 2) 범위 정수 1개
print(np.random.randint(2, size=5))         # [0, 2) 범위 정수 5개
print(np.random.randint(2, 6, size=5))      # [2, 6) 범위 정수 5개
print(np.random.randint(2, size=(2, 3)))    # [0, 2) 범위 정수 2x3 배열
print(np.random.randint(2, 6, (2, 3)))      # [2, 6) 범위 정수 2x3 배열

7. 데이터 입출력

7.1. 배열 저장 (.npy 파일)

import os
os.chdir('./')  # 저장할 디렉토리로 변경 (예: 현재 디렉토리)

data = np.random.rand(5, 5)
np.save('array_data.npy', data)
# 또는 절대 경로 사용: np.save('/path/to/array_data.npy', data)

7.2. 배열 불러오기 (.npy 파일)

loaded_data = np.load('array_data.npy')
print(loaded_data)
# 또는 절대 경로 사용: np.load('/path/to/array_data.npy')

7.3. 텍스트 파일 저장/불러오기

data = np.random.rand(5, 5)

# 텍스트 파일로 저장 (쉼표 구분)
np.savetxt('array_data.txt', data, delimiter=',')

# 텍스트 파일 불러오기
loaded_txt = np.loadtxt('array_data.txt', delimiter=',')
print(loaded_txt)
# 또는 절대 경로 사용: np.loadtxt('/path/to/array_data.txt')

태그: NumPy ndarray python 데이터과학 수치연산

8월 1일 16:32에 게시됨