Fullstack 64

heo4·2026년 7월 15일

Fullstack

목록 보기
19/70

풀스택

NumPy(넘파이) 기초 정리

국비과정에서 배운 넘파이(NumPy)를 정리했다. 왜 리스트 대신 넘파이를 쓰는지부터, 배열 생성/정보/변형 메소드, 통계·수학 연산까지 다룬다.

1. NumPy란?

NumPy(Numerical Python)는 대규모 다차원 배열과 행렬 연산을 지원하는 라이브러리다.

  • 핵심은 ndarray라는 단일 데이터 타입을 쓴다는 점이다. 덕분에 계산 속도가 압도적으로 빠르다.

💡 왜 NumPy가 필요할까?

  1. 압도적 속도: 일반 리스트보다 수백 배 빠르다.
  2. 수학적 기능: 행렬 계산, 통계 데이터 처리가 매우 쉽다.
  3. AI의 기본: 머신러닝(Scikit-learn)이나 딥러닝(TensorFlow/PyTorch)은 내부적으로 모두 넘파이 배열로 데이터를 주고받는다.

파이썬 기본 자료형과 비교해보면, 리스트/세트/딕셔너리는 아래처럼 각자 다른 타입을 가진다.

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 연산이 너무 순식간에 끝나 측정 불가 수준입니다.")

2. NumPy 불러오기 (Import)

넘파이는 전 세계적으로 np라는 별명(alias)으로 부르는 게 관례다.

import numpy as np

3. 주요 메소드

3-1) 생성, 정보, 변형

분류명령어용도
생성np.array()기존 리스트를 넘파이 배열로 바꿀 때
생성np.arange()0, 1, 2 ... 처럼 연속된 숫자 배열을 만들 때
생성np.zeros() / np.ones()0이나 1로 빈 배열을 미리 준비할 때
정보arr.shape몇 행 몇 열인지 확인할 때
정보arr.ndim1차원(선)인지 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차원(면)

3-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))

4. 왜 리스트 대신 NumPy를 쓸까?

  1. 병렬 연산: 반복문(for) 없이 배열 전체에 한 번에 연산을 수행한다.
  2. 메모리 효율: 리스트보다 적은 메모리로 대용량 데이터를 처리한다.
  3. 행렬 연산: 인공지능 모델의 수식 계산에 필수적인 행렬 곱셈 등을 지원한다.

5. 연습문제

문제 1

이번 주 우리 동네 일교차를 확인하는 코드를 작성하기.

  • 넘파이 배열 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

두 학생의 국어/영어/수학 점수로 2행 3열 배열 grades를 만들고, axis를 이용해 과목별 평균을 구하기.

  • 학생 A: 80, 90, 70 (1행)
  • 학생 B: 85, 95, 80 (2행)
  • 과목별 평균(국어, 영어, 수학 순 3개) 구하기
  • 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 기초 정리

국비과정에서 배운 Pandas 기초를 정리했다. Series와 DataFrame이라는 두 가지 핵심 구조를 중심으로, 데이터 선택·필터링·통계까지 다룬다.

1. Pandas란?

Pandas는 파이썬에서 데이터 분석을 쉽고 빠르게 할 수 있도록 도와주는 라이브러리다.

  • 데이터를 Series(1차원)와 DataFrame(2차원/표 형태)이라는 구조에 담아 다룬다.

💡 왜 Pandas가 중요할까? (AICE 시험 필수 이유)

  1. 대용량 데이터: 엑셀이 멈추는 대용량 데이터도 순식간에 처리한다.
  2. 전처리 최적화: 결측치 처리, 정렬, 필터링을 코드 한 줄로 끝낸다.
  3. AICE 시험 비중: 대부분의 문제가 DataFrame 활용 능력에 달려 있다.

2. Pandas 핵심 구조

Pandas는 딱 두 가지만 기억하면 된다. Series와 DataFrame.

명칭형태설명비유
Series (시리즈)1차원엑셀의 열(Column) 하나세로 한 줄
DataFrame (데이터프레임)2차원엑셀의 시트(Table) 전체행과 열이 있는 표

3. Series 메소드 & 속성

3-1) 수치 연산 및 통계

메소드기능
s.sum()요소들의 합계
s.mean()산술 평균
s.median()중앙값
s.std()표준편차
s.min() / s.max()최솟값과 최댓값
s.idxmin() / s.idxmax()최솟값/최댓값을 가진 인덱스 반환
s.value_counts()고유값별 빈도수(개수)를 내림차순 정렬
s.unique()중복 제거한 고유값 배열 반환
s.nunique()고유값 개수 반환

3-2) 데이터 선택 및 슬라이싱

속성설명예시
.loc라벨(Label) 기반 인덱싱. 인덱스 이름을 직접 지정s.loc['a']
.iloc정수 위치 기반 인덱싱. 0부터 시작하는 순서 지정s.iloc[0]
.atloc와 비슷하지만 단 하나의 값만 빠르게 가져올 때s.at['a']
.iatiloc와 비슷하지만 단 하나의 값만 위치 기반으로 가져올 때s.iat[0]

4. Series 예제

예제 1) Pandas 불러오기 및 Series 생성

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

예제 2) 수치 연산 및 통계

# 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}")

예제 3) 데이터 선택 및 슬라이싱

# 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)

예제 4) 조건에 맞는 데이터 필터링

실제 문제에서는 "특정 기준(평균 등)을 넘는 데이터만 추출하라"는 지시가 자주 나온다.

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)

예제 5) 이름(Label)과 번호(Position)로 데이터 추출

인덱스 이름으로 접근할지, 순서 번호로 접근할지 구분하는 게 중요하다.

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)

5. DataFrame 메소드 & 속성

5-1) 데이터 확인 및 탐색 (Inspection)

메소드기능
df.head(n)상위 n개 행 보기 (기본 5개)
df.tail(n)하위 n개 행 보기
df.info()컬럼명, 데이터 타입, 결측치 확인
df.describe()수치 데이터의 통계값(평균, 최솟값 등)
df.shape행과 열의 개수 반환

5-2) 데이터 가공 및 정렬 (Manipulation)

메소드기능
df.sort_values()특정 컬럼 기준으로 정렬
df.drop()특정 행이나 열 삭제
df.value_counts()특정 컬럼의 값별 개수 세기
df.astype()데이터 타입 변경 (예: 문자 → 숫자)

6. DataFrame 예제

예제 1) 딕셔너리를 표로 변환

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)이름나이종류점수
0Java3White90
1Bami2Black85
2Choco5Cheese70
3Cookie1Grey95

특정 컬럼만 뽑아서 통계를 낼 수도 있다.

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)

예제 2) 데이터 선택(Selection) 실습

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['컬럼'] >= 조건])은 실무와 시험 모두에서 가장 자주 쓰이는 핵심 패턴이다.

예제 3) loc와 iloc 행렬 실습

인덱스를 문자열로 바꿔서 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의 슬라이싱은 파이썬 기본 규칙대로 끝 번호를 포함하지 않는다. 이 차이가 가장 헷갈리는 부분이니 꼭 기억해두자.


7. 연습문제

문제 1

과일 가게의 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)

문제 2

학생 정보 딕셔너리로 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']])

마무리

  • Series는 "이름표 붙은 세로 한 줄", DataFrame은 "행과 열이 있는 표"라고 구분해서 기억하자.
  • loc(이름 기반)과 iloc(번호 기반)의 차이, 그리고 슬라이싱 시 loc은 끝을 포함하고 iloc은 포함하지 않는다는 규칙이 가장 자주 헷갈리는 포인트다.
  • 조건 필터링(df[df['컬럼'] >= 값])은 AICE 실기에서도 자주 나오는 핵심 패턴이니 손에 익혀두자.

0개의 댓글