국비과정에서 배운 넘파이(NumPy)를 정리했다. 왜 리스트 대신 넘파이를 쓰는지부터, 배열 생성/정보/변형 메소드, 통계·수학 연산까지 다룬다.
NumPy(Numerical Python)는 대규모 다차원 배열과 행렬 연산을 지원하는 라이브러리다.
파이썬 기본 자료형과 비교해보면, 리스트/세트/딕셔너리는 아래처럼 각자 다른 타입을 가진다.
lst = [1, 2, 3, 4, 5]
data_set = {1, 1, 1, 1, 1, 1, 2, 3, 3, 4, 4, 5}
data_dict = {"name": "김수한"}
print(type(lst))
print(type(data_set))
print(type(data_dict))
리스트와 넘파이 배열의 연산 방식 차이를 보면, 리스트는 for문으로 하나씩 더해야 하지만 넘파이는 배열 전체를 한 번에 연산(벡터화 연산)할 수 있다.
import numpy as np
lst1 = [1, 2, 3, 4]
lst2 = [5, 6, 7, 8]
print("=== list와 for문 이용 ===")
for i in range(len(lst1)):
print(lst1[i] + lst2[i], end=" ")
arr1 = np.array(lst1)
arr2 = np.array(lst2)
print()
print("=== 넘파이 이용 ===")
print("arr1 + arr2:", arr1 + arr2)
print("arr1 * 2", arr1 * 2)
100만 개 데이터로 리스트 연산과 넘파이 연산의 속도 차이를 직접 측정해볼 수 있다.
import numpy as np
import time
# 1. 속도 비교를 위해 대용량 데이터 준비 (100만 개)
DATA_SIZE = 1_000_000
lst1 = list(range(DATA_SIZE))
lst2 = list(range(DATA_SIZE))
arr1 = np.array(lst1)
arr2 = np.array(lst2)
# 2. 리스트와 for문(컴프리헨션)을 이용한 연산 측정
print("=== 파이썬 list와 for문 연산 시작 ===")
start_time = time.time()
list_result = [lst1[i] + lst2[i] for i in range(len(lst1))]
end_time = time.time()
list_duration = end_time - start_time
print(f"List 연산 소요 시간: {list_duration:.6f}초")
print("\n=== 넘파이(NumPy) 벡터화 연산 시작 ===")
# 3. 넘파이 배열을 이용한 연산 측정
start_time = time.time()
arr_result = arr1 + arr2
end_time = time.time()
arr_duration = end_time - start_time
print(f"NumPy 연산 소요 시간: {arr_duration:.6f}초")
# 4. 결과 및 배율 출력
print("\n=== 성능 비교 결과 ===")
if arr_duration > 0:
speed_ratio = list_duration / arr_duration
print(f"NumPy ndarray 연산이 파이썬 List보다 약 {speed_ratio:.1f}배 빠릅니다.")
else:
print("NumPy 연산이 너무 순식간에 끝나 측정 불가 수준입니다.")
넘파이는 전 세계적으로 np라는 별명(alias)으로 부르는 게 관례다.
import numpy as np
| 분류 | 명령어 | 용도 |
|---|---|---|
| 생성 | np.array() | 기존 리스트를 넘파이 배열로 바꿀 때 |
| 생성 | np.arange() | 0, 1, 2 ... 처럼 연속된 숫자 배열을 만들 때 |
| 생성 | np.zeros() / np.ones() | 0이나 1로 빈 배열을 미리 준비할 때 |
| 정보 | arr.shape | 몇 행 몇 열인지 확인할 때 |
| 정보 | arr.ndim | 1차원(선)인지 2차원(면)인지 확인할 때 |
| 정보 | arr.dtype | 담긴 값이 정수(int)인지 실수(float)인지 확인할 때 |
| 변형 | arr.reshape() | 1줄로 된 데이터를 원하는 행/열 모양으로 바꿀 때 |
import numpy as np
# 1. 리스트를 넘파이 배열로 변환
data = [1, 2, 3, 4, 5]
arr = np.array(data)
print("1. 생성된 배열:", arr)
# 2. 배열의 정체 확인하기
print(" - 모양(shape):", arr.shape) # (5,) -> 5개 요소의 1차원
print(" - 차원(ndim):", arr.ndim) # 1 -> 선 형태의 1차원 데이터
print(" - 타입(dtype):", arr.dtype) # int64 -> 정수가 담겨 있음
# 3. 연속된 숫자 만들기
# 0부터 시작해서 10 '전'까지 생성
seq_arr = np.arange(10)
print("\n2. 0부터 9까지:", seq_arr)
reshape()로 1차원 배열을 원하는 행렬 형태로 바꿀 수 있다. 단, 전체 요소 개수는 변형 전후가 같아야 한다.
import numpy as np
# 4. 12개의 숫자를 생성 (0~11)
base = np.arange(12)
print("5. 변신 전(1차원):", base)
# 5. 3행 4열로 모양 바꾸기 (3 x 4 = 12, 딱 맞아야 한다)
transformed = base.reshape(3, 4)
print("\n6. 변신 후(2차원):\n", transformed)
# 6. 바뀐 후의 정보 확인
print(" - 바뀐 모양:", transformed.shape) # (3, 4)
print(" - 바뀐 차원:", transformed.ndim) # 2차원(면)
| 분류 | 사용 예시 (코드) | 용도 |
|---|---|---|
| 통계 연산 | np.sum(arr, axis=0) | 배열 요소의 합계 |
| 통계 연산 | np.mean(arr) | 산술 평균 |
| 통계 연산 | np.max(arr) / np.min(arr) | 최댓값 / 최솟값 |
| 통계 연산 | np.argmax(arr) | 최댓값/최솟값이 위치한 인덱스 반환 |
| 형태 변경 | arr.reshape(2, 3) | 데이터 유지하며 차원(행, 열) 변경 |
| 형태 변경 | arr.T | 행과 열을 서로 바꾸기(전치) |
| 형태 변경 | arr.flatten() | 다차원 배열을 1차원으로 변경 |
| 조건/검색 | np.where(arr > 5, 1, 0) | 조건이 참이면 1, 거짓이면 0으로 변환 |
| 조건/검색 | np.sort(arr) | 오름차순 정렬 |
| 조건/검색 | np.unique(arr) | 중복 제거 후 고유값만 추출 |
| 배열 생성 | np.arange(0, 10, 2) | 시작~끝을 간격(step)에 맞춰 생성 |
| 배열 생성 | np.linspace(0, 1, 5) | 시작~끝 범위를 균등하게 N등분해서 생성 |
| 배열 생성 | np.random.rand(2, 2) | 0~1 사이의 랜덤 실수로 채운 배열 생성 |
성적 데이터를 예로 생성부터 변형까지 이어서 살펴보면:
import numpy as np
# 1. 생성: 60점부터 95점까지 5점 간격으로 점수 생성
scores = np.arange(60, 100, 5)
print("1. 생성된 점수들:", scores)
# 2. 변형: 8개 데이터를 4행 2열(4명 학생의 국어, 영어 점수)로 변환
score_table = scores.reshape(4, 2)
print("\n2. 성적 표(4행 2열):\n", score_table)
# 3. 변형: 행과 열 바꾸기
print("\n3. 과목별로 보기:\n", score_table.T)
# 4. 생성: 0~1 사이의 랜덤 가산점 생성
plus_score = np.random.rand(4, 2)
print("\n4. 랜덤 가산점:\n", plus_score)
통계 연산에서 눈여겨볼 점은 axis 파라미터다. axis=0은 세로(열) 방향, axis=1은 가로(행) 방향 연산을 뜻한다.
# 5. 통계: 전체 평균 점수
print(f"5. 전체 평균: {np.mean(score_table):.2f}")
# 6. 통계: 과목별 합계 (axis=0 → 세로 방향 합계)
print("6. 과목별 총점(국, 영):", np.sum(score_table, axis=0))
# 7. 통계: 최고점과 그 위치
print("7. 최고 점수:", np.max(score_table))
print(" 최고 점수가 있는 위치(인덱스):", np.argmax(score_table))
조건 검색과 정렬도 자주 쓰인다.
# 8. 조건: 80점 이상은 1(Pass), 아니면 0(Fail)
pass_fail = np.where(score_table >= 80, 1, 0)
print("8. 80점 이상 합격 표시(1:합격):\n", pass_fail)
# 9. 검색: 중복 제거
duplicate_data = np.array([1, 2, 2, 3, 3, 3])
print("\n9. 중복 제거 전:", duplicate_data)
print(" 중복 제거 후(고유값):", np.unique(duplicate_data))
# 10. 정렬: 1차원으로 펼쳐서(flatten) 오름차순 정렬
flat_scores = score_table.flatten()
print("\n10. 펼쳐진 점수:", flat_scores)
print(" 정렬된 점수:", np.sort(flat_scores))
for) 없이 배열 전체에 한 번에 연산을 수행한다.이번 주 우리 동네 일교차를 확인하는 코드를 작성하기.
temps에 월~일 최고 기온 [22, 25, 28, 23, 26, 29, 27]을 담기import numpy as np
temps = np.array([22, 25, 28, 23, 26, 29, 27])
print("이번 주 평균 기온:", np.mean(temps))
print("이번 주 최고 기온:", np.max(temps))
두 학생의 국어/영어/수학 점수로 2행 3열 배열 grades를 만들고, axis를 이용해 과목별 평균을 구하기.
grades.shape 출력하기import numpy as np
grades = np.array([
[80, 90, 70],
[85, 95, 80]
])
# axis=0 → 세로 방향(학생 간) 평균 → 과목별 평균
print("과목별 평균(국,영,수):", np.mean(grades, axis=0))
print("grades의 모양:", grades.shape) # (2, 3)
shape, ndim, dtype으로 배열의 "정체"를 파악하는 습관을 들이면 디버깅이 쉬워진다.axis=0(세로/열 방향)과 axis=1(가로/행 방향)의 차이를 헷갈리지 않는 게 중요하다.국비과정에서 배운 Pandas 기초를 정리했다. Series와 DataFrame이라는 두 가지 핵심 구조를 중심으로, 데이터 선택·필터링·통계까지 다룬다.
Pandas는 파이썬에서 데이터 분석을 쉽고 빠르게 할 수 있도록 도와주는 라이브러리다.
Pandas는 딱 두 가지만 기억하면 된다. Series와 DataFrame.
| 명칭 | 형태 | 설명 | 비유 |
|---|---|---|---|
| Series (시리즈) | 1차원 | 엑셀의 열(Column) 하나 | 세로 한 줄 |
| DataFrame (데이터프레임) | 2차원 | 엑셀의 시트(Table) 전체 | 행과 열이 있는 표 |
| 메소드 | 기능 |
|---|---|
s.sum() | 요소들의 합계 |
s.mean() | 산술 평균 |
s.median() | 중앙값 |
s.std() | 표준편차 |
s.min() / s.max() | 최솟값과 최댓값 |
s.idxmin() / s.idxmax() | 최솟값/최댓값을 가진 인덱스 반환 |
s.value_counts() | 고유값별 빈도수(개수)를 내림차순 정렬 |
s.unique() | 중복 제거한 고유값 배열 반환 |
s.nunique() | 고유값 개수 반환 |
| 속성 | 설명 | 예시 |
|---|---|---|
.loc | 라벨(Label) 기반 인덱싱. 인덱스 이름을 직접 지정 | s.loc['a'] |
.iloc | 정수 위치 기반 인덱싱. 0부터 시작하는 순서 지정 | s.iloc[0] |
.at | loc와 비슷하지만 단 하나의 값만 빠르게 가져올 때 | s.at['a'] |
.iat | iloc와 비슷하지만 단 하나의 값만 위치 기반으로 가져올 때 | s.iat[0] |
import pandas as pd
# 리스트를 활용한 시리즈 생성 (학생들의 성적 데이터 예시)
data = [85, 90, 78, 92, 88]
index = ['Student_A', 'Student_B', 'Student_C', 'Student_D', 'Student_E']
scores = pd.Series(data, index)
print(scores)
Series는 값(value)마다 정수 위치(iloc)와 인덱스 이름(loc)을 동시에 가진다.
| 정수 위치 (iloc) | 인덱스 이름 (loc) | 데이터 값 |
|---|---|---|
| 0 | 'Student_A' | 85 |
| 1 | 'Student_B' | 90 |
| 2 | 'Student_C' | 78 |
| 3 | 'Student_D' | 92 |
| 4 | 'Student_E' | 88 |
# 1. 주요 통계치 확인
print("평균 점수:", scores.mean())
print("최고 점수:", scores.max())
print("점수 표준편차:", scores.std())
# 2. 요약 통계량 (데이터 분포 파악 시 필수)
print("\n--- 전체 요약 ---")
print(scores.describe())
# 3. 조건부 연산: 90점 이상인 학생들의 점수 합계
high_scores_sum = scores[scores >= 90].sum()
print(f"\n90점 이상 학생 점수 합계: {high_scores_sum}")
# 1. loc (레이블 이름 기반 선택)
student_b_score = scores.loc['Student_B']
print(f"Student_B의 점수: {student_b_score}")
# 2. iloc (정수 위치 기반 슬라이싱) - 첫 번째부터 세 번째까지
top_three = scores.iloc[0:3]
print("\n--- 상위 3명 데이터 ---")
print(top_three)
# 3. 값 변경: Student_C의 점수를 78점에서 80점으로 수정
scores.loc['Student_C'] = 80
print("\n--- 점수 수정 후 Student_C ---")
print(scores.loc['Student_C'])
# 4. 불리언 인덱싱: 평균 점수보다 낮은 학생 필터링
under_mean = scores[scores < scores.mean()]
print("\n--- 평균 미달 학생 목록 ---")
print(under_mean)
실제 문제에서는 "특정 기준(평균 등)을 넘는 데이터만 추출하라"는 지시가 자주 나온다.
import pandas as pd
# 1. 데이터 생성 (요일별 배송 건수)
delivery = pd.Series([120, 150, 90, 200, 170],
index=['월', '화', '수', '목', '금'])
# 2. 평균 계산
avg_delivery = delivery.mean()
# 3. 평균보다 큰 데이터만 필터링
target_days = delivery[delivery > avg_delivery]
print(f"평균 배송 건수: {avg_delivery}")
print("--- 평균 이상 배송한 날 ---")
print(target_days)
인덱스 이름으로 접근할지, 순서 번호로 접근할지 구분하는 게 중요하다.
import pandas as pd
# 1. 데이터 생성 (환자 ID별 혈당)
sugar_levels = pd.Series([95, 110, 125, 105, 98],
index=['P01', 'P02', 'P03', 'P04', 'P05'])
# 2. 특정 이름(Label)으로 접근
p02_level = sugar_levels['P02']
# 3. 위치 번호(Position)로 슬라이싱 (앞에서 3명)
top_3 = sugar_levels[0:3]
print(f"환자 P02의 혈당: {p02_level}")
print("--- 상위 3명 데이터 ---")
print(top_3)
| 메소드 | 기능 |
|---|---|
df.head(n) | 상위 n개 행 보기 (기본 5개) |
df.tail(n) | 하위 n개 행 보기 |
df.info() | 컬럼명, 데이터 타입, 결측치 확인 |
df.describe() | 수치 데이터의 통계값(평균, 최솟값 등) |
df.shape | 행과 열의 개수 반환 |
| 메소드 | 기능 |
|---|---|
df.sort_values() | 특정 컬럼 기준으로 정렬 |
df.drop() | 특정 행이나 열 삭제 |
df.value_counts() | 특정 컬럼의 값별 개수 세기 |
df.astype() | 데이터 타입 변경 (예: 문자 → 숫자) |
import pandas as pd
# 1. 딕셔너리를 이용해 데이터프레임 만들기
data = {
'이름': ['Java', 'Bami', 'Choco', 'Cookie'],
'나이': [3, 2, 5, 1],
'종류': ['White', 'Black', 'Cheese', 'Grey'],
'점수': [90, 85, 70, 95]
}
df = pd.DataFrame(data)
# 2. 데이터 확인하기
print("--- 상위 2개 데이터 확인 ---")
print(df.head(2)) # head()는 위에서부터 데이터를 보여줌
print("\n--- 데이터 요약 정보 ---")
df.info() # 데이터 개수와 타입을 한눈에 보여줌
print("\n--- 통계 정보 ---")
print(df.describe()) # 숫자 데이터의 평균, 최솟값 등을 보여줌
결과는 아래와 같은 표 형태다.
| (Index) | 이름 | 나이 | 종류 | 점수 |
|---|---|---|---|---|
| 0 | Java | 3 | White | 90 |
| 1 | Bami | 2 | Black | 85 |
| 2 | Choco | 5 | Cheese | 70 |
| 3 | Cookie | 1 | Grey | 95 |
특정 컬럼만 뽑아서 통계를 낼 수도 있다.
import pandas as pd
data = {
'이름': ['Java', 'Bami', 'Choco', 'Cookie'],
'나이': [3, 2, 5, 1],
'종류': ['White', 'Black', 'Cheese', 'Grey'],
'점수': [90, 85, 70, 95]
}
df = pd.DataFrame(data)
print(df.describe())
print(df['점수'].mean())
print(df['나이'].max())
print("======== 데이터 프레임 출력 ===========")
print(df)
import pandas as pd
data = {
'이름': ['김철수', '이영희', '박민수', '최지우'],
'나이': [22, 25, 21, 28],
'성적': [85, 92, 78, 95],
'합격여부': [True, True, False, True]
}
df = pd.DataFrame(data)
# 열(Column) 선택 → 결과는 Series
names = df['이름']
print("--- [열 선택] 이름 컬럼만 출력 ---")
print(names)
# 행(Row) 선택 (.loc) → 인덱스 이름 기준
first_person = df.loc[0]
print("\n--- [행 선택] 첫 번째 행(김철수) 정보 ---")
print(first_person)
# 조건 필터링 ⭐⭐⭐ 성적 90점 이상만 추출
top_students = df[df['성적'] >= 90]
print("\n--- [필터링] 성적 90점 이상인 학생 ---")
print(top_students)
💡 조건 필터링(
df[df['컬럼'] >= 조건])은 실무와 시험 모두에서 가장 자주 쓰이는 핵심 패턴이다.
인덱스를 문자열로 바꿔서 loc/iloc 차이를 체감해보자.
import pandas as pd
# 1. 처음엔 기본 인덱스 (0, 1, 2)
df = pd.DataFrame({'값': [10, 20, 30]})
# 2. 인덱스 이름을 변경
df.index = ["첫 번째", "두 번째", "세 번째"]
print(df)
# 값
# 첫 번째 10
# 두 번째 20
# 세 번째 30
# [loc] 이름을 써야 함
df.loc["첫 번째"] = 100 # (O)
# [iloc] 이름이 바뀌었어도 번호를 써야 함
df.iloc[0] = 500 # (O)
좀 더 풍성한 성적표로 실습해보자. 이름을 인덱스로 지정하면 loc 연습이 훨씬 편하다.
import pandas as pd
data = {
'이름': ['Kim', 'Lee', 'Park', 'Choi', 'Jung'],
'국어': [80, 95, 78, 92, 85],
'영어': [88, 92, 85, 90, 82],
'수학': [85, 100, 80, 95, 88],
'코딩': [100, 85, 90, 80, 95]
}
df = pd.DataFrame(data)
df = df.set_index('이름') # 이름을 인덱스로 설정
print("--- 원본 데이터프레임 ---")
print(df)
단일 값 콕 집어내기
print(df.loc['Park', '수학']) # 이름과 과목으로 콕!
print(df.iloc[1, 0]) # 번호로 콕! (Lee는 1번 행, 국어는 0번 열)
범위(2x2) 가져오기
# loc 슬라이싱은 마지막 이름(Choi, 영어)을 포함한다!
print(df.loc['Lee':'Choi', '국어':'영어'])
# iloc 슬라이싱은 파이썬 리스트처럼 마지막 번호를 포함하지 않는다!
print(df.iloc[0:2, 0:2])
연속되지 않은 행/열 골라내기
# 리스트를 대괄호 [] 안에 한 번 더 넣어준다
print(df.loc[['Kim', 'Jung'], ['수학', '코딩']])
print(df.iloc[[0, 2], [1, 3]])
⚠️
loc의 슬라이싱은 끝 라벨을 포함하고,iloc의 슬라이싱은 파이썬 기본 규칙대로 끝 번호를 포함하지 않는다. 이 차이가 가장 헷갈리는 부분이니 꼭 기억해두자.
과일 가게의 5일간 사과 판매량 데이터로 Series를 만들고, 평균보다 많이 팔린 날만 추출하기.
import pandas as pd
sales_data = [15, 20, 12, 25, 18]
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
정답 코드 보기
import pandas as pd
sales_data = [15, 20, 12, 25, 18]
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
s = pd.Series(data=sales_data, index=days)
avg = s.mean() # 평균
result = s[s >= avg]
print("=== 평균 판매량보다 많이 판매된 날 ===")
print(result)
학생 정보 딕셔너리로 DataFrame을 만들고, 'Math' 컬럼만 출력한 뒤 90점 이상인 학생의 이름과 점수만 출력하기.
import pandas as pd
data = {
'Name': ['Kim', 'Lee', 'Park', 'Choi'],
'Age': [20, 21, 22, 20],
'Math': [85, 95, 78, 92]
}
정답 코드 보기
import pandas as pd
data = {
'Name': ['Kim', 'Lee', 'Park', 'Choi'],
'Age': [20, 21, 22, 20],
'Math': [85, 95, 78, 92]
}
df = pd.DataFrame(data)
print("=== 수학만 출력 ===")
print(df['Math'])
print("=== 수학 점수가 90점 이상인 학생의 'Name'과 'Math' 정보만 출력 ===")
print(df.loc[df['Math'] >= 90, ['Name', 'Math']])
# 아래처럼 loc 없이도 동일하게 표현 가능
print(df[df['Math'] >= 90][['Name', 'Math']])
loc(이름 기반)과 iloc(번호 기반)의 차이, 그리고 슬라이싱 시 loc은 끝을 포함하고 iloc은 포함하지 않는다는 규칙이 가장 자주 헷갈리는 포인트다.df[df['컬럼'] >= 값])은 AICE 실기에서도 자주 나오는 핵심 패턴이니 손에 익혀두자.