부록 E07: numpy와 수치계산
이 장에서 배울 것
이번 장에서는 넘파이(numpy)를 배웁니다. 넘파이는 파이썬에서 숫자 계산을 빠르고 편하게 하기 위한 대표적인 라이브러리입니다. 앞 장의 판다스가 “표를 다루는 도구”라면, 넘파이는 그 표 안에 들어 있는 숫자들을 빠르게 계산하는 바닥 도구에 가깝습니다.
핵심 용어를 먼저 정리하겠습니다.
- 넘파이(numpy): 숫자 배열을 빠르게 계산하는 파이썬 라이브러리입니다. 보통
np라는 짧은 이름으로 불러옵니다. - 배열(array): 같은 종류의 숫자들을 줄 세워 저장한 구조입니다.
- 벡터(vector): 숫자들이 한 줄로 모인 배열입니다. 예를 들어
[1, 2, 3]같은 것입니다. - 행렬(matrix): 숫자가 행과 열로 놓인 2차원 배열입니다.
- 모양(shape): 배열이 몇 행 몇 열인지 알려주는 정보입니다.
- 벡터화 계산(vectorized operation): 반복문을 직접 쓰지 않고 배열 전체에 한 번에 계산을 적용하는 방식입니다.
- 축(axis): 2차원 배열에서 행 방향 또는 열 방향을 가리키는 기준입니다.
가장 쉬운 비유: 계산기를 여러 개 붙여 놓은 도구
파이썬 리스트는 숫자들을 담을 수 있습니다.
values = [1, 2, 3, 4]
하지만 리스트는 기본적으로 “자료를 담는 상자”에 가깝습니다. 숫자 계산을 대량으로 하려면 넘파이 배열이 더 편합니다.
import numpy as np
values = np.array([1, 2, 3, 4])
print(values * 2)
결과는 다음과 같습니다.
[2 4 6 8]
리스트라면 원소를 하나씩 꺼내 반복문으로 계산해야 하지만, 넘파이 배열은 배열 전체에 한 번에 곱셈을 적용할 수 있습니다.
넘파이 불러오기
넘파이는 보통 이렇게 불러옵니다.
import numpy as np
여기서 np는 넘파이를 짧게 부르는 별명입니다. 생물정보학과 데이터 과학 코드에서 거의 관습처럼 쓰입니다.
배열 만들기
가장 기본적인 배열은 이렇게 만듭니다.
values = np.array([10, 20, 30])
이 배열은 숫자 3개를 담은 1차원 배열입니다. 생물정보학에서는 한 샘플의 여러 유전자 발현량, 한 유전자의 여러 샘플 값, 여러 품질 점수 등을 이런 숫자 배열로 다룰 수 있습니다.
평균과 표준편차
숫자 묶음의 대표값을 구할 수 있습니다.
values = np.array([2, 4, 6, 8])
print(np.mean(values))
print(np.std(values))
np.mean은 평균, np.std는 표준편차를 구합니다. 표준편차는 값들이 평균 주변에 얼마나 퍼져 있는지 보여주는 숫자입니다.
간단한 손계산 감각도 가져 봅시다. [2, 4, 6, 8]의 평균은 다음과 같습니다.
(2 + 4 + 6 + 8) / 4 = 5
그래서 np.mean(values)의 결과는 5.0입니다.
배열 전체에 한 번에 계산하기
배열에는 덧셈, 뺄셈, 곱셈, 나눗셈을 한 번에 적용할 수 있습니다.
values = np.array([1, 2, 3])
print(values + 10)
print(values * 2)
결과는 다음과 같습니다.
[11 12 13]
[2 4 6]
이 방식은 유전자 발현량 전체에 같은 변환을 적용하거나, 품질 점수 전체를 한 번에 계산할 때 유용합니다.
2차원 배열과 모양
행렬처럼 생긴 2차원 배열도 만들 수 있습니다.
matrix = np.array([
[1, 2, 3],
[4, 5, 6]
])
print(matrix.shape)
결과는 다음과 같습니다.
(2, 3)
이것은 2행 3열이라는 뜻입니다. 생물정보학에서는 “샘플 × 유전자” 또는 “세포 × 유전자” 형태의 숫자 표가 자주 나오므로 2차원 배열 감각이 중요합니다.
축(axis) 이해하기
2차원 배열에서 평균을 낼 때는 방향을 정할 수 있습니다.
matrix = np.array([
[1, 2, 3],
[4, 5, 6]
])
print(np.mean(matrix, axis=0))
print(np.mean(matrix, axis=1))
axis=0은 열별 계산입니다. 위아래 방향으로 계산해서 각 열의 평균을 냅니다.
[2.5 3.5 4.5]
axis=1은 행별 계산입니다. 왼쪽 오른쪽 방향으로 계산해서 각 행의 평균을 냅니다.
[2. 5.]
처음에는 헷갈릴 수 있습니다. 간단히 외우면 됩니다. axis=0은 열마다 계산, axis=1은 행마다 계산입니다.
조건으로 고르기
넘파이 배열에서도 조건을 이용해 값을 고를 수 있습니다.
values = np.array([5, 12, 3, 20])
high = values[values >= 10]
print(high)
결과는 다음과 같습니다.
[12 20]
발현량, 품질 점수, read 수처럼 숫자 조건으로 데이터를 걸러낼 때 이런 방식을 자주 씁니다.
생물정보학에서 왜 중요한가
RNA-seq count matrix, single-cell expression matrix, 품질 점수, 거리 행렬, 모델 입력값은 모두 숫자 배열로 다룰 수 있습니다. 넘파이는 판다스, scikit-learn, scanpy 같은 많은 라이브러리의 바닥에서 작동합니다. 겉으로는 표나 모델을 다루는 것처럼 보여도, 내부에서는 숫자 배열 계산이 계속 일어납니다.
계산 감각: 행렬에서 축을 읽는 법
넘파이에서 axis는 초보자가 가장 자주 헷갈리는 부분입니다. 다음 행렬을 봅시다.
matrix = np.array([
[1, 2, 3],
[4, 5, 6]
])
이 행렬은 2행 3열입니다. axis=0 평균은 위아래로 계산해서 열마다 평균을 냅니다.
[(1+4)/2, (2+5)/2, (3+6)/2] = [2.5, 3.5, 4.5]
axis=1 평균은 왼쪽에서 오른쪽으로 계산해서 행마다 평균을 냅니다.
[(1+2+3)/3, (4+5+6)/3] = [2.0, 5.0]
생물정보학에서는 행이 샘플이고 열이 유전자인 경우가 많습니다. 이때 axis=0 평균은 유전자별 평균, axis=1 평균은 샘플별 평균이 됩니다. 반대로 행과 열의 의미가 바뀐 데이터라면 해석도 바뀝니다.
shape mismatch도 자주 생깁니다. 길이가 다른 배열끼리 무리하게 더하려 하면 넘파이가 어떤 값을 어떤 값과 계산해야 할지 알 수 없습니다. 계산 전에 shape을 확인하는 습관이 중요합니다.
어려운 개념 보강: 평균, 표준편차, axis를 손으로 읽는 법
넘파이를 쓰면 np.mean(values)처럼 한 줄로 평균을 구할 수 있습니다. 하지만 함수 이름만 외우면 나중에 결과가 이상할 때 원인을 찾기 어렵습니다. 그래서 평균과 표준편차는 “공식이 무엇을 세는가”까지 알아야 합니다.
평균은 여러 값을 하나의 대표값으로 줄이는 계산입니다.
평균 = 모든 값을 더한 값 / 값의 개수
예를 들어 발현량 배열이 [2, 4, 6, 8]이라면 평균은 다음과 같습니다.
(2 + 4 + 6 + 8) / 4 = 5
여기서 4는 숫자 4가 특별해서가 아니라 값이 4개 있기 때문입니다. 생물정보학에서는 한 유전자의 여러 샘플 발현량 평균, 한 샘플의 여러 품질 점수 평균처럼 “많은 숫자를 한눈에 보기 위한 요약”에 평균을 씁니다.
표준편차는 값들이 평균에서 얼마나 떨어져 있는지 보는 숫자입니다. 입문 단계에서는 다음 순서로 이해하면 충분합니다.
1. 평균을 구한다.
2. 각 값이 평균에서 얼마나 떨어졌는지 구한다.
3. 그 떨어짐의 크기를 모아 하나의 숫자로 요약한다.
예를 들어 [2, 4, 6, 8]의 평균은 5입니다. 각 값은 평균에서 -3, -1, 1, 3만큼 떨어져 있습니다. 값들이 평균 근처에 몰려 있으면 표준편차가 작고, 멀리 퍼져 있으면 표준편차가 큽니다. 그래서 표준편차는 “대표값 하나로 설명하기 어려운 정도”를 알려 줍니다.
import numpy as np
values = np.array([2, 4, 6, 8])
print(np.mean(values)) # 5.0
print(np.std(values)) # 값들이 평균 주변에 얼마나 퍼졌는지
주의할 점은 np.std(values)의 기본값이 모집단 표준편차 방식이라는 점입니다. 통계학에서 표본 표준편차를 구할 때는 np.std(values, ddof=1)처럼 쓰기도 합니다. 지금 단계에서는 두 방식이 있다는 사실만 기억하면 됩니다. 중요한 것은 “표준편차가 크다 = 값들이 더 들쭉날쭉하다”는 해석입니다.
2차원 배열에서는 axis가 자주 헷갈립니다. axis=0은 행들을 세로 방향으로 훑어서 열마다 계산하고, axis=1은 열들을 가로 방향으로 훑어서 행마다 계산합니다.
import numpy as np
matrix = np.array([
[10, 20, 30],
[1, 2, 3]
])
print(np.mean(matrix, axis=0)) # 열마다 평균: [5.5, 11.0, 16.5]
print(np.mean(matrix, axis=1)) # 행마다 평균: [20.0, 2.0]
생물정보학 행렬에서 행이 유전자이고 열이 샘플이라면, axis=0과 axis=1을 반대로 쓰는 순간 “샘플별 평균”과 “유전자별 평균”이 뒤바뀝니다. 공식보다 더 위험한 실수는 축의 의미를 데이터 구조와 연결하지 않고 기계적으로 쓰는 것입니다.
미니 실습 블록: count matrix의 모양과 axis 이해하기
이 실습은 count matrix의 모양과 axis 이해하기를 직접 손으로 확인하는 연습입니다. 왜 필요한가 하면, 행이 유전자이고 열이 샘플인지, 그 반대인지에 따라 평균 계산의 의미가 달라지기 때문입니다.
import numpy as np
counts = np.array([
[10, 20, 30],
[5, 0, 15]
])
print(counts.shape)
print(counts.mean(axis=0))
print(counts.mean(axis=1))
각 코드 요소의 의미를 풀어보면 다음과 같습니다. shape가 (2, 3)이면 행 2개, 열 3개입니다. axis=0은 열별 요약, axis=1은 행별 요약으로 이해할 수 있습니다.
생물정보학/계산생물학에서 쓰이는 장면은 분명합니다. count matrix, expression matrix, distance matrix를 다룰 때 행과 열의 의미를 잃지 않는 데 필요합니다.
흔한 오해 또는 주의점도 있습니다. numpy 배열은 빠르지만 행 이름과 열 이름이 사라지기 쉬워 sample_id와 gene_id를 따로 보존해야 합니다.
핵심 정리
넘파이는 숫자 배열을 빠르게 계산하는 도구입니다. np.array로 배열을 만들고, np.mean, np.std로 평균과 표준편차를 구하며, 배열 전체에 한 번에 계산을 적용할 수 있습니다. 2차원 배열에서는 shape으로 모양을 확인하고, axis=0, axis=1로 계산 방향을 정할 수 있습니다.
문제 풀이
numpy와 수치계산
주관식 답안은 Gemini API로 채점합니다. API 키는 이 브라우저에만 저장됩니다.
-
1. [쉬움] 객관식
넘파이(numpy)의 역할로 가장 적절한 것은?
-
2. [코드] 객관식
보통 넘파이를 불러오는 코드로 적절한 것은?
-
3. [코드] 객관식
np.array([1, 2, 3])의 의미는? -
4. [계산] 객관식
values = np.array([1,2,3])일 때values * 2의 결과는? -
8. [계산] 객관식
np.mean([2,4,6,8])의 값은? -
9. [보통] 객관식
np.std(values)는 무엇을 계산하는가? -
10. [보통] 객관식
matrix.shape이(2, 3)이면 무슨 뜻인가? -
11. [계산] 객관식
matrix = np.array([[1,2,3],[4,5,6]])에서axis=0평균은? -
16. [계산] 객관식
같은 matrix에서
axis=1평균은? -
21. [계산] 객관식
values = np.array([5,12,3,20])에서values[values >= 10]결과는? -
26. [쉬움] 객관식
벡터(vector)의 설명으로 적절한 것은?
-
27. [쉬움] 객관식
행렬(matrix)의 설명으로 적절한 것은?
-
28. [보통] 객관식
벡터화 계산의 장점으로 적절한 것은?
-
29. [계산] 객관식
np.array([10,20,30]) + 5의 결과는? -
34. [계산] 객관식
np.array([10,20,30]) / 10의 결과는? -
39. [보통] 객관식
생물정보학에서 넘파이가 중요한 이유는?
-
40. [계산] 객관식
np.sum(np.array([1,2,3]))의 결과는? -
42. [계산] 객관식
np.max(np.array([5,12,3]))의 결과는? -
43. [계산] 객관식
np.min(np.array([5,12,3]))의 결과는? -
44. [코드] 객관식
2차원 배열에서 행별 평균을 구할 때 본문에서 사용한 축은?
-
45. [중간] 객관식
np.array([1,2,3]) + 10의 결과로 맞는 것은? -
46. [중간] 객관식
행렬
[[1,2,3],[4,5,6]]의 shape은? -
47. [중간] 객관식
행렬
[[1,2,3],[4,5,6]]에서axis=0평균은? -
48. [중간] 객관식
같은 행렬에서
axis=1평균은? -
49. [중간] 객관식
values=np.array([5,12,3,20])일 때values[values >= 10]의 결과는? -
50. [중간] 객관식
생물정보학에서 count matrix를 넘파이 배열로 이해할 때 자주 필요한 확인은?
-
51. [중간] 객관식
행이 샘플, 열이 유전자일 때
axis=0평균은 무엇으로 해석하기 좋은가? -
52. [중간] 객관식
길이가 맞지 않는 배열끼리 무리하게 계산할 때 생길 수 있는 문제는?
-
53. [실전] 객관식
counts.shape가(2, 3)이면 무슨 뜻인가? -
54. [실전] 객관식
행이 유전자, 열이 샘플인 matrix에서
mean(axis=1)은 무엇을 계산하는가? -
주관식 55. [실습] 주관식 · Gemini 채점
[3, 6, 9]를 넘파이 배열로 만들고 평균을 출력하는 코드를 작성하라. -
주관식 56. [실습] 주관식 · Gemini 채점
values = np.array([5, 10, 15])에서 모든 값에 2를 곱해 출력하는 코드를 작성하라. -
주관식 57. [실습] 주관식 · Gemini 채점
2행 3열 배열을 만들고
shape을 출력하는 예시 코드를 작성하라. -
주관식 58. [실습] 주관식 · Gemini 채점
axis=0과axis=1의 차이를 2차원 배열 평균 계산 관점에서 설명하라. -
주관식 59. [실습] 주관식 · Gemini 채점
values = np.array([1, 12, 7, 20])에서 10 이상인 값만 고르는 코드를 작성하라. -
주관식 60. [실습] 주관식 · Gemini 채점
넘파이가 판다스나 머신러닝 라이브러리의 바닥 도구라고 할 수 있는 이유를 설명하라.
-
주관식 61. [실습] 주관식 · Gemini 채점
행렬
[[1,2,3],[4,5,6]]에서axis=0평균과axis=1평균을 계산하고 생물정보학적으로 어떻게 해석할 수 있는지 설명하라. -
주관식 62. [실습] 주관식 · Gemini 채점
넘파이 배열에서 조건 필터링을 이용해 10 이상인 값만 고르는 코드 예시를 작성하라.
-
주관식 63. [실습] 주관식 · Gemini 채점
np.array([[1, 3], [5, 7]])의 전체 평균, axis=0 평균, axis=1 평균을 계산하라. -
주관식 64. [실습] 주관식 · Gemini 채점
numpy 배열에서 10 이상인 값만 고르는 코드를 작성하라.