배열 생성, 자료형, 형태와 원소별 연산
이번 강의가 끝나면 다음을 할 수 있어야 한다.
shape, ndim, size, dtype을 확인할 수 있다.NumPy(Numerical Python)는 수치 계산에 사용하는 Python 라이브러리이다. NumPy 배열은 같은 종류의 수치 데이터를 효율적으로 저장하고, 여러 원소에 대한 계산을 간단하게 표현한다.
Python 리스트의 각 원소에 2를 곱하려면 반복문과 append()를 사용할 수 있다.
values = [1, 2, 3]
doubled = []
for value in values:
doubled.append(2 * value)
print(doubled)
NumPy 배열에서는 배열 전체에 2를 곱할 수 있다.
import numpy as np
values = np.array([1, 2, 3])
doubled = 2 * values
print(doubled)
이처럼 배열 전체에 적용되는 연산을 벡터화된 연산(vectorized operation)이라고 한다.
NumPy는 일반적으로 np라는 짧은 이름으로 불러온다.
import numpy as np
설치되지 않았다면 터미널에서 다음 명령을 사용할 수 있다.
python -m pip install numpy
현재 사용 중인 NumPy 버전은 다음과 같이 확인한다.
print(np.__version__)
np.array()는 입력 데이터로 numpy.ndarray 객체를 만든다.
vector = np.array([1, 2, 3])
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
])
print(vector)
print(matrix)
배열의 형태(shape)는 튜플로 전달한다.
zeros = np.zeros((2, 3))
ones = np.ones((2, 3))
sevens = np.full((2, 3), 7)
print(zeros)
print(ones)
print(sevens)
np.zeros((2, 3))의 괄호가 두 겹인 이유는 (2, 3)이라는 shape 튜플을 하나의 인자로 전달하기 때문이다.
np.arange()는 시작값 이상, 끝값 미만의 값을 일정한 간격으로 만든다.
even_numbers = np.arange(0, 10, 2)
print(even_numbers)
결과는 [0 2 4 6 8]이다.
np.linspace()는 시작값과 끝값을 포함하여 지정한 개수의 값을 만든다.
temperatures = np.linspace(300.0, 500.0, 5)
print(temperatures)
결과는 [300. 350. 400. 450. 500.]이다.
| 함수 | 지정하는 값 | 끝값 포함 |
|---|---|---|
np.arange(start, stop, step) | 간격 | 포함하지 않음 |
np.linspace(start, stop, num) | 원소 개수 | 기본적으로 포함 |
array = np.array([
[1, 2, 3],
[4, 5, 6],
])
print(array.shape)
print(array.ndim)
print(array.size)
print(array.dtype)
shape: 각 축의 원소 개수. 위 배열에서는 (2, 3) ndim: 배열 축의 개수. 위 배열에서는 2size: 전체 원소 개수. 위 배열에서는 6dtype: 배열 원소의 자료형NumPy의 배열 차원(ndim)과 물리학에서 사용하는 텐서의 rank는 같은 개념이 아니므로 구분한다.
NumPy 배열은 일반적으로 한 가지 자료형의 원소를 저장한다.
integer_array = np.array([1, 2, 3])
float_array = np.array([1.0, 2.0, 3.0])
print(integer_array.dtype)
print(float_array.dtype)
정수와 실수를 함께 넣으면 실수를 보존할 수 있는 자료형으로 변환된다.
mixed = np.array([1, 2.5, 3])
print(mixed)
print(mixed.dtype)
dtype을 직접 지정할 수도 있다.
measurements = np.array(
[1, 2, 3],
dtype=float,
)
print(measurements)
자료형마다 표현할 수 있는 범위와 정밀도가 다르다. 처음에는 측정값처럼 소수점이 필요한 데이터에는 실수형을 사용한다는 점을 기억하면 된다.
크기가 같은 두 배열에 사칙연산을 적용하면 같은 위치의 원소끼리 계산한다.
a = np.array([1.0, 2.0, 3.0])
b = np.array([4.0, 5.0, 6.0])
print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a**2)
여기서 a * b는 행렬곱이 아니라 원소별 곱이다. 행렬과 벡터의 곱은 뒤의 벡터·행렬 연산 강의에서 다룬다.
스칼라와 배열을 계산하면 모든 원소에 같은 연산을 적용한다.
stress_mpa = np.array([100.0, 150.0, 200.0])
print(stress_mpa / 1000.0)
print(stress_mpa + 10.0)
여러 원소를 하나의 값으로 요약하는 계산을 집계 연산(reduction)이라고 한다.
values = np.array([3.0, 5.0, 7.0, 9.0])
print(values.sum())
print(values.mean())
print(values.min())
print(values.max())
print(values.std())
위 결과는 각각 합계, 평균, 최솟값, 최댓값과 모집단 표준편차이다.
함수 형태로도 같은 계산을 할 수 있다.
print(np.sum(values))
print(np.mean(values))
reshape()는 원소 개수를 유지하면서 배열의 형태를 바꾼다.
values = np.arange(12)
matrix = values.reshape(3, 4)
print(values.shape)
print(matrix.shape)
print(matrix)
12개의 원소는 $3\times4$ 또는 $2\times6$으로 바꿀 수 있지만 $5\times3$으로는 바꿀 수 없다.
배열을 다시 1차원으로 펼칠 때는 ravel()을 사용할 수 있다.
flat = matrix.ravel()
print(flat)
print(flat.shape)
+ 연산의 의미가 서로 다르다.
list_a = [1, 2, 3]
list_b = [4, 5, 6]
array_a = np.array([1, 2, 3])
array_b = np.array([4, 5, 6])
print(list_a + list_b)
print(array_a + array_b)
+: 두 리스트를 이어 붙인다.+: 같은 위치의 원소끼리 더한다.배열은 수치 계산에 편리하지만 이름, 결정구조, 설명처럼 서로 다른 종류의 데이터를 함께 저장할 때는 리스트나 딕셔너리가 더 적합할 수 있다.
속도를 비교하려면 두 코드가 같은 데이터를 대상으로 같은 계산을 수행해야 한다.
python_values = list(range(1_000_000))
numpy_values = np.array(python_values)
Jupyter Notebook에서 다음 두 셀을 각각 실행할 수 있다.
%%timeit
sum(python_values)
%%timeit
numpy_values.sum()
결과는 컴퓨터와 NumPy 버전에 따라 달라진다. 특정 실행시간을 외우는 것이 아니라, 큰 수치 배열에서 NumPy 연산이 일반적인 Python 반복보다 효율적일 수 있음을 확인하는 실습이다.
세 인장 시편의 힘과 초기 단면적이 다음과 같이 측정되었다고 하자.
force_n = np.array([1000.0, 1500.0, 2200.0])
area_mm2 = np.array([10.0, 12.0, 20.0])
stress_mpa = force_n / area_mm2
print(stress_mpa)
print(f"평균 응력: {stress_mpa.mean():.2f} MPa")
$1\ \mathrm{N/mm^2}=1\ \mathrm{MPa}$이므로 결과의 단위는 MPa이다. 배열을 사용하면 여러 시편의 응력을 한 번에 계산할 수 있다.
np.array(), np.zeros(), np.arange(), np.linspace()로 배열을 만들 수 있다.shape, ndim, size, dtype은 배열의 구조를 설명한다.sum(), mean(), min(), max()로 데이터를 요약할 수 있다.reshape()는 원소 수를 유지하면서 배열의 형태를 바꾼다.NumPy를 np라는 이름으로 불러오는 문장을 쓰시오.
다음 배열의 shape, ndim, size를 구하라.
a = np.array([
[1, 2, 3],
[4, 5, 6],
])
2행 3열의 모든 원소가 0인 배열을 만드는 표현을 쓰시오.
np.arange(0, 7, 2)의 결과를 쓰시오.
다음 코드의 결과를 쓰시오.
a = np.array([1, 2, 3])
print(2 * a)
배열 a의 평균을 구하는 표현을 하나 쓰시오.