콘텐츠로 이동

P2-11.4 보충학습: NumPy에서 모양(shape)과 원본 공유를 함께 읽는 법

Section ID: P2-11.4 Version: v2026.07.20

P2-11.2에서는 인덱싱(indexing), 슬라이싱(slicing), 축(axis)을 봤고, P2-11.3에서는 브로드캐스팅(broadcasting)과 벡터화(vectorization)를 봤습니다. 그런데 실제 NumPy 코드를 읽다 보면 그 다음에 자주 막히는 질문이 남습니다.

이 선택은 원본 배열을 그대로 보고 있는가? 아니면 새 배열을 하나 만든 것인가? shape를 왜 갑자기 (3,)에서 (3, 1)로 바꾸는가?

이 보충학습은 그 질문을 묶어서 다룹니다.

여기서는 불리언 마스크(boolean mask), 팬시 인덱싱(fancy indexing), np.newaxis, 원본 공유(shared view)를 묶어 보는 기본 설명을 제공합니다. 앞 절들의 인덱싱, 슬라이싱, 브로드캐스팅이 읽히더라도, 선택 방식과 shape 변화가 원본 공유와 어떻게 연결되는지는 여기 기준으로 다시 연결합니다.

처음 읽는 기준: NumPy에서 모양(shape)과 원본 공유를 함께 읽는 법

  • 기본 슬라이싱은 원본 배열의 일부를 보는 방식으로 자주 읽힌다고 설명할 수 있습니다.
  • 팬시 인덱싱과 불리언 마스크는 새 배열을 만드는 방식으로 자주 읽힌다고 설명할 수 있습니다.
  • shape가 같아 보여도 원본 공유 여부는 다를 수 있음을 설명할 수 있습니다.
  • np.newaxis를 길이 1인 축을 추가하는 표기로 설명할 수 있습니다.
  • broadcasting 코드를 읽을 때 , shape, 원본 공유를 함께 점검해야 함을 설명할 수 있습니다.

먼저 붙잡을 한 장면

이 보충학습에서 가장 먼저 붙잡아야 할 장면은 어떻게 골랐는가shape를 어떻게 바꿨는가가 원본 공유와 계산 방향을 함께 바꾼다는 점입니다.

코드 장면 먼저 읽는 질문 지금 붙잡을 핵심
x[1:4] 구간을 그대로 보고 있는가 원본 공유 가능성이 크다
x[[1, 3, 4]] 위치를 따로 모으는가 새 배열처럼 읽는 편이 안전하다
x[x >= 80] 조건에 맞는 값만 걸러내는가 새 배열처럼 읽는 편이 안전하다
x[:, np.newaxis] 축을 하나 더 추가하는가 shape가 바뀌고 broadcasting 읽기가 달라진다

즉 NumPy에서는 일부를 골랐다는 말만으로는 부족합니다. 구간 보기, 위치 모으기, 조건 걸러내기, 축 추가하기를 먼저 나눠 읽어야 view/copy, shape, broadcasting을 함께 이해할 수 있습니다.

배경

P2-8.7 보충학습에서는 Python 리스트와 복사에서 원본이 같이 바뀌는가를 먼저 봤습니다. NumPy에서도 같은 질문이 다시 나옵니다. 다만 NumPy에서는 복사했는가만 보는 것으로 부족하고, shape가 어떻게 바뀌었는가도 함께 봐야 합니다.

예를 들어 같은 x[1:4]x[[1, 3, 4]]도 둘 다 일부 값만 고른 것처럼 보이지만, 하나는 원본을 함께 볼 수 있고 다른 하나는 새 배열을 만들 수 있습니다. 또 x[:, np.newaxis]는 값을 바꾸지 않아도 shape를 바꿔 broadcasting 방식 전체를 바꿉니다.

이 절은 P2-11.2에서 슬라이싱과 축은 읽히지만 이 선택이 원본을 같이 바꾸는가가 헷갈릴 때, 또는 P2-11.3에서 broadcasting은 읽히는데 왜 갑자기 (3, 1)(1, 3)이 나오는지 멈출 때 함께 봅니다. 이 기준을 붙잡고 나면 슬라이싱, 축, broadcasting을 shape원본 공유까지 함께 읽을 수 있습니다.

세 가지 기준

기준 왜 중요한가 이 절에서 필요한 이해 수준
무엇을 골랐는가 슬라이싱, 팬시 인덱싱, 마스크를 같은 선택으로 오해하지 않게 해 줍니다. 위치 범위를 자른 것인지, 특정 위치만 따로 모은 것인지 먼저 구분합니다.
shape가 어떻게 바뀌는가 같은 값이어도 계산 방식이 달라지는 이유를 드러냅니다. (3,), (3, 1), (1, 3) 차이가 계산 방향을 바꾼다는 점을 이해합니다.
원본을 함께 보는가 뒤에서 값 수정이 실험 결과를 바꾸는 상황을 막아 줍니다. 값을 바꿀 가능성이 있으면 복사 여부를 먼저 점검합니다.
용어 이 절에서 먼저 잡을 뜻
불리언 마스크(boolean mask) 조건이 참인 값만 골라 남기는 선택 방식입니다.
팬시 인덱싱(fancy indexing) 여러 위치를 목록처럼 지정해 값을 따로 모으는 선택 방식입니다.
np.newaxis 길이 1인 축을 추가해 배열 shape를 바꾸는 표기입니다.
원본 공유(shared view) 선택 결과가 원본 배열과 연결되어 함께 바뀔 수 있는 상태입니다.
복사(copy) 원본과 분리된 새 배열을 만드는 방식입니다.

기본 슬라이싱은 원본의 일부를 보는 방식으로 자주 읽힌다

P2-11.2에서 본 슬라이싱은 구간을 남기는 표기였습니다. NumPy에서는 이 기본 슬라이싱이 원본 배열의 일부를 함께 보는 방식으로 자주 동작합니다.

문제 상황: 슬라이싱으로 고른 배열 일부를 바꿨을 때 원본도 같이 바뀌는지 확인합니다. 입력(input): 1차원 배열 scores와 슬라이스 scores[1:4]입니다. 기대 출력(output): 슬라이스에서 바꾼 값이 원본 배열에도 반영됩니다. 확인할 개념: 기본 슬라이싱은 새 배열처럼 보여도 원본과 연결되어 읽힐 수 있다는 점을 봅니다.

# NumPy 배열의 shape 변경과 원본 공유 여부를 함께 확인하는 보충 예제입니다.
import numpy as np

scores = np.array([82, 75, 45, 90, 61])
middle = scores[1:4]

middle[0] = 999

print(scores)
print(middle)

출력은 다음과 같습니다.

[ 82 999  45  90  61]
[999  45  90]

여기서는 이렇게 읽습니다.

  • scores[1:4]는 값 세 개를 복사해 따로 모은 것처럼만 보면 위험합니다.
  • 기본 슬라이싱은 원본 배열의 일부를 보는 방식으로 자주 읽힙니다.
  • 그래서 뒤에서 값을 바꾸면 원본도 함께 바뀔 수 있습니다.

이 감각은 P2-8.7에서 본 원본과 복사본이 언제 함께 바뀌는가와 직접 연결됩니다.

팬시 인덱싱과 불리언 마스크는 고른 값을 새로 모은다

이번에는 같은 “일부 선택”처럼 보여도 방식이 다른 경우를 봅니다.

문제 상황: 특정 위치만 따로 고르는 팬시 인덱싱과 조건으로 고르는 불리언 마스크가 원본과 어떻게 다른지 비교합니다. 입력(input): 배열 scores, 위치 목록 [1, 3, 4], 조건 scores >= 80입니다. 기대 출력(output): 팬시 인덱싱과 불리언 마스크 결과를 바꿔도 원본 scores는 그대로 남습니다. 확인할 개념: 위치를 따로 모으거나 조건으로 걸러 낸 결과는 새 배열처럼 읽는다는 점을 봅니다.

# NumPy 배열의 shape 변경과 원본 공유 여부를 함께 확인하는 보충 예제입니다.
scores = np.array([82, 75, 45, 90, 61])

picked = scores[[1, 3, 4]]
high_scores = scores[scores >= 80]

picked[0] = 500
high_scores[0] = 700

print(scores)
print(picked)
print(high_scores)

출력은 다음과 같습니다.

1
2
3
[82 75 45 90 61]
[500  90  61]
[700  90]

여기서 scores[[1, 3, 4]]는 1번, 3번, 4번 위치 값을 따로 모읍니다. 이런 방식을 팬시 인덱싱이라고 부릅니다.

scores[scores >= 80]는 조건이 참인 값만 고릅니다. 이런 방식은 불리언 마스크라고 부릅니다.

둘 다 여기서는 이렇게 이해합니다.

  • 기본 슬라이싱은 구간을 그대로 본다에 가깝습니다.
  • 팬시 인덱싱과 불리언 마스크는 고른 값을 따로 모은다에 가깝습니다.

그래서 값을 바꿨을 때 원본 공유 여부도 달라집니다.

같은 일부 선택이어도 질문이 다르다

슬라이싱, 팬시 인덱싱, 불리언 마스크는 모두 일부 값만 고른다는 점에서는 비슷해 보입니다. 하지만 실제로는 서로 다른 질문에 답합니다.

표현 먼저 읽을 질문 입문 단계의 안전한 해석
x[1:4] 어느 구간을 남길까? 구간 보기
x[[1, 3, 4]] 어느 위치를 따로 뽑을까? 위치 모으기
x[x >= 80] 어느 조건을 만족하는 값만 남길까? 조건 걸러내기

이 차이를 먼저 구분하면 NumPy 코드가 훨씬 덜 헷갈립니다.

np.newaxis는 길이 1인 축을 추가한다

이번에는 값을 고르는 것이 아니라 shape를 바꾸는 표기를 봅니다.

문제 상황: 같은 1차원 배열을 행처럼 볼지, 열처럼 볼지 shape만 바꿔 구분합니다. 입력(input): 1차원 배열 scoresscores[:, np.newaxis], scores[np.newaxis, :]입니다. 기대 출력(output): (3,), (3, 1), (1, 3) 세 가지 shape가 출력됩니다. 확인할 개념: np.newaxis는 값 자체를 바꾸지 않고 길이 1인 축을 추가해 계산 방향을 바꾼다는 점을 봅니다.

1
2
3
4
5
6
# NumPy 배열의 shape 변경과 원본 공유 여부를 함께 확인하는 보충 예제입니다.
scores = np.array([82, 75, 45])

print(scores.shape)
print(scores[:, np.newaxis].shape)
print(scores[np.newaxis, :].shape)

출력은 다음과 같습니다.

1
2
3
(3,)
(3, 1)
(1, 3)

세 배열은 담고 있는 숫자는 비슷해 보이지만, 읽는 방식은 다릅니다.

표현 shape 읽는 법
scores (3,) 길이 3인 1차원 배열
scores[:, np.newaxis] (3, 1) 3행 1열 열 벡터처럼 보기
scores[np.newaxis, :] (1, 3) 1행 3열 행 벡터처럼 보기

np.newaxis는 숫자를 새로 만드는 것이 아니라, 배열을 어느 방향으로 계산에 맞출지 정리하는 표기입니다.

np.newaxis는 broadcasting을 의도적으로 만드는 데 자주 쓰인다

P2-11.3에서는 (4, 3)(3,)처럼 이미 맞는 shape를 봤습니다. 그런데 어떤 계산은 일부러 축을 하나 더 만들어야 읽기 쉬워집니다.

문제 상황: 열 방향과 행 방향을 분리해 두 집합의 차이를 한 번에 계산합니다. 입력(input): 길이 3 배열 a와 길이 2 배열 b입니다. 기대 출력(output): (3, 1)(1, 2)가 만나 (3, 2) 결과가 만들어집니다. 확인할 개념: np.newaxis는 broadcasting을 위한 shape 정렬 도구라는 점을 봅니다.

1
2
3
4
5
6
7
8
9
# NumPy 배열의 shape 변경과 원본 공유 여부를 함께 확인하는 보충 예제입니다.
a = np.array([10, 20, 30])
b = np.array([1, 2])

diff = a[:, np.newaxis] - b[np.newaxis, :]

print(a[:, np.newaxis].shape)
print(b[np.newaxis, :].shape)
print(diff)

출력은 다음과 같습니다.

1
2
3
4
5
(3, 1)
(1, 2)
[[ 9  8]
 [19 18]
 [29 28]]

이 예제의 핵심은 값보다 shape입니다.

  • a[:, np.newaxis](3, 1)입니다.
  • b[np.newaxis, :](1, 2)입니다.
  • 두 배열은 broadcasting으로 (3, 2) 결과를 만듭니다.

여기서는 np.newaxis를 broadcasting을 위해 행과 열 역할을 더 분명하게 만드는 표기로 이해합니다.

실무형 코드에서는 shape와 원본 공유를 같이 본다

NumPy 코드가 헷갈리는 이유는 무엇을 골랐는가shape가 어떻게 바뀌는가, 원본이 같이 바뀌는가가 한 줄에서 함께 걸리기 때문입니다.

예를 들어 다음처럼 읽는 습관이 안전합니다.

  1. 이 코드는 구간을 잘랐는가, 위치를 모았는가, 조건으로 걸렀는가?
  2. 결과 shape는 무엇인가?
  3. 뒤에서 값을 바꿀 일이 있는가?
  4. 원본을 보존해야 하면 .copy()가 필요한가?

특히 데이터 전처리에서는 이 판단이 중요합니다. 일부 샘플만 골라 새 데이터셋을 만드는지, 기존 배열 일부를 직접 수정하는지에 따라 실험 결과 해석이 달라질 수 있기 때문입니다.

이 흐름을 한 번에 묶으면 다음과 같습니다.

flowchart TD
    select["값 선택 방식<br/>슬라이스 / 팬시 / 마스크"]
    share["원본을 함께 보는가<br/>view인지 copy인지"]
    reshape["newaxis로 shape 변경<br/>축이 어떻게 바뀌는가"]
    broadcast["broadcasting 규칙<br/>어떻게 맞춰 계산되는가"]
    result["계산 결과와 부작용<br/>값 변화와 원본 영향"]

    select --> share
    select --> reshape
    reshape --> broadcast
    share --> result
    broadcast --> result

독자가 여기서 남겨야 할 최소 문장은 다음입니다.

  • 어떻게 골랐는가가 원본 공유를 바꾸고, shape를 어떻게 바꿨는가가 broadcasting 방향을 바꾼다.

어디로 돌아가야 하나

이 절을 읽은 뒤에는 다음 본문으로 다시 연결해 읽습니다.

지금 다시 읽을 질문 먼저 돌아갈 본문
어느 값이나 구간을 고른 것인가 P2-11.2 인덱싱, 슬라이싱, 축
왜 배열 전체 계산이 shape에 따라 달라지는가 P2-11.3 브로드캐스팅과 벡터화
원본 공유라는 감각 자체가 아직 낯설다 P2-8.7 참조, 얕은 복사, 깊은 복사

짧은 복귀 표

막히는 장면 먼저 돌아갈 곳
인덱싱, 슬라이싱, 축 자체가 아직 헷갈린다 P2-11.2
broadcasting 방향이 왜 바뀌는지 흐릿하다 P2-11.3
원본과 복사본이 왜 함께 바뀌는지 낯설다 P2-8.7

체크리스트

  • x[1:4]x[[1, 3, 4]]의 차이를 설명할 수 있는가?
  • 불리언 마스크가 무엇을 고르는지 설명할 수 있는가?
  • (3,), (3, 1), (1, 3)의 차이를 설명할 수 있는가?
  • np.newaxis가 broadcasting과 왜 연결되는지 설명할 수 있는가?
  • 원본 보존이 중요할 때 .copy() 여부를 먼저 점검해야 한다는 점을 기억하는가?
  • NumPy 코드를 읽을 때 값만이 아니라 shape와 원본 공유 여부를 함께 봐야 한다는 점을 설명할 수 있는가?

출처와 참고 자료

  • NumPy Developers, Copies and views, NumPy v2.5 Manual, 확인 날짜: 2026-07-20. view와 copy의 차이, 기본 인덱싱 view, advanced indexing copy, .base 확인 설명의 근거로 사용했다.
  • NumPy Developers, Indexing on ndarrays, NumPy v2.5 Manual, 확인 날짜: 2026-07-20. 선택 방식에 따라 shape와 원본 공유 여부가 달라지는 설명 확인에 사용했다.
  • NumPy Developers, Broadcasting, NumPy v2.5 Manual, 확인 날짜: 2026-07-20. np.newaxis와 shape 조정이 broadcasting 이해와 연결되는 설명의 근거로 사용했다.