배열의 내용을 처음부터 알고 있다면 직접 만들 수 있지만, 대부분의 경우 크기만 알고 내용은 나중에 채우거나, 특정 패턴으로 초기화합니다.
# Python 리스트에서 변환
arr = np.array([1, 2, 3, 4, 5])
print(arr)
# 2D 배열 (리스트의 리스트)
matrix = np.array([[1, 2, 3], [4, 5, 6]])
print(matrix)
# dtype 지정
complex_arr = np.array([1, 2], dtype=np.complex128)
print(complex_arr)
배열에 들어갈 값과 크기를 알고 있다면 어려울건 없죠. 위의 코드처럼 미리 리스트에 자료들을 넣거나 자료들의 데이터 타입을 지정하여 넣는것도 가능합니다.
# zeros - 0으로 채워진 배열
zeros_arr = np.zeros((3, 4))
print("Zeros:\\n", zeros_arr)
# ones - 1로 채워진 배열
ones_arr = np.ones((2, 3))
print("\\nOnes:\\n", ones_arr)
# full - 특정 값으로 채우기
sevens = np.full((3, 3), 7)
print("\\nFull of 7s:\\n", sevens)
# empty - 초기화하지 않음 (메모리의 쓰레기 값)
empty_arr = np.empty((2, 2))
print("\\nEmpty (random garbage):\\n", empty_arr)
대부분 값은 미리 알 수 없지만 배열의 크기는 주어집니다.
그래서 NumPy는 초기값이 지정된 배열을 만드는 다양한 함수를 제공합니다.
이들은 배열을 확장하는 비효율적인 연산을 줄이며, 기본 데이터 타입은 float64로 생성됩니다.
zeros_int = np.zeros((2, 3), dtype=np.int32)
print(zeros_int.dtype) # int32)
데이터 타입을 변경하고 싶으면 dtype 키워드 인수로 타입을 지정할 수 있습니다.
import time
size = (500000, 500000)
start = time.time()
z = np.zeros(size)
print(f"zeros: {time.time() - start:.4f}초")
start = time.time()
e = np.empty(size)
print(f"empty: {time.time() - start:.4f}초")

기본적으로 초기화 과정이 없는 zeros나 ones보다 empty가 빠릅니다. 다만 값을 나중에 반드시 채워 넣어야 합니다.
# arange - Python range와 유사
arr = np.arange(10) # 0부터 9까지
print("0~9:", arr)
arr = np.arange(2, 10, 2) # 2부터 10 미만까지 2씩 증가
print("2,4,6,8:", arr)
# linspace - 구간을 균등하게 나누기
arr = np.linspace(0, 1, 5) # 0부터 1까지 5개의 점
print("0 to 1 (5 points):", arr) # [0. 0.25 0.5 0.75 1. ]
# 왜 arange 대신 linspace를 쓸까?
# arange는 부동소수점에서 예측 불가능한 개수가 나올 수 있음
arr1 = np.arange(0, 1, 0.1) # 10개? 11개?
arr2 = np.linspace(0, 1, 11) # 정확히 11개!
print(f"arange: {len(arr1)}개, linspace: {len(arr2)}개")