이미지 로딩 중...

Pandas Series와 DataFrame 완벽 가이드 - 슬라이드 1/9
A

AI Generated

2025. 11. 21. · 9 Views

Pandas Series와 DataFrame 완벽 가이드

데이터 분석의 핵심 도구인 Pandas의 Series와 DataFrame을 처음부터 끝까지 배워봅니다. 실무에서 바로 활용 가능한 예제와 함께 데이터 처리의 기초를 탄탄히 다져보세요.


목차

  1. Series 생성하기 - 1차원 데이터의 시작
  2. Series 인덱싱과 슬라이싱 - 원하는 데이터만 쏙쏙 꺼내기
  3. DataFrame 생성하기 - 2차원 테이블 데이터의 시작
  4. DataFrame 기본 정보 확인하기 - 데이터 파악의 첫걸음
  5. DataFrame 열 선택과 추가 - 필요한 컬럼만 다루기
  6. DataFrame 행 필터링 - 조건에 맞는 데이터만 추출하기
  7. DataFrame 정렬하기 - 데이터를 원하는 순서로 배열하기
  8. DataFrame 그룹화와 집계 - 카테고리별 통계 내기

1. Series 생성하기 - 1차원 데이터의 시작

시작하며

여러분이 엑셀에서 한 열의 데이터를 다룰 때 이런 상황을 겪어본 적 있나요? 수백 개의 학생 점수를 정리하거나, 월별 매출 데이터를 분석해야 하는데 엑셀로는 복잡한 계산이 어렵고 시간이 오래 걸린다는 것을요.

이런 문제는 실제 데이터 분석 현장에서 자주 발생합니다. 단순히 데이터를 보는 것이 아니라 계산하고, 비교하고, 변형해야 하는데 엑셀의 한계에 부딪히게 됩니다.

바로 이럴 때 필요한 것이 Pandas의 Series입니다. Series는 여러분이 다루는 1차원 데이터를 프로그래밍으로 쉽고 빠르게 처리할 수 있게 해줍니다.

개요

간단히 말해서, Series는 같은 종류의 데이터를 한 줄로 쭉 늘어놓은 것입니다. 마치 여러분이 노트에 숫자를 세로로 적어 내려가는 것과 비슷하죠.

Series가 필요한 이유는 단순합니다. 우리가 분석하는 데이터는 대부분 여러 개의 값들로 이루어져 있기 때문입니다.

예를 들어, 일주일 동안의 기온을 기록한다면 7개의 숫자가 필요하고, 한 반 학생들의 시험 점수를 정리한다면 학생 수만큼의 점수가 필요합니다. 기존에는 Python의 리스트를 사용했다면, 이제는 Series를 사용하여 인덱스를 직접 지정하고, 데이터 타입을 명확히 하며, 훨씬 강력한 연산 기능을 활용할 수 있습니다.

Series의 핵심 특징은 세 가지입니다. 첫째, 각 값마다 라벨(인덱스)을 붙일 수 있어서 '월요일의 기온', '철수의 점수' 같은 의미 있는 접근이 가능합니다.

둘째, 모든 데이터가 같은 타입이라 계산이 빠르고 안전합니다. 셋째, 벡터화 연산으로 반복문 없이도 모든 값을 한 번에 처리할 수 있습니다.

이러한 특징들이 데이터 분석의 효율성과 코드의 가독성을 크게 향상시킵니다.

코드 예제

import pandas as pd
import numpy as np

# 리스트로 Series 만들기
temperatures = pd.Series([22, 25, 23, 27, 26, 24, 22])
print("기본 Series:\n", temperatures)

# 인덱스를 지정한 Series 만들기 - 의미 있는 라벨 사용
days = ['월', '화', '수', '목', '금', '토', '일']
temp_with_index = pd.Series([22, 25, 23, 27, 26, 24, 22], index=days)
print("\n요일별 기온:\n", temp_with_index)

# 딕셔너리로 Series 만들기 - 가장 직관적인 방법
student_scores = pd.Series({'철수': 85, '영희': 92, '민수': 78, '지영': 88})
print("\n학생별 점수:\n", student_scores)

설명

이 코드가 하는 일을 차근차근 살펴보겠습니다. Series는 여러분이 가진 데이터를 Pandas가 이해할 수 있는 형태로 바꿔주는 첫 번째 단계입니다.

첫 번째 예제에서는 가장 기본적인 방법으로 리스트를 Series로 변환합니다. [22, 25, 23, 27, 26, 24, 22]라는 7개의 숫자를 pd.Series()로 감싸면, Pandas가 자동으로 0부터 6까지의 인덱스를 붙여줍니다.

이렇게 하면 단순한 숫자 나열이 '0번째 값은 22도, 1번째 값은 25도'처럼 위치로 접근할 수 있는 구조화된 데이터가 됩니다. 두 번째 예제는 한 단계 더 나아갑니다.

숫자 인덱스(0, 1, 2...) 대신 의미 있는 라벨('월', '화', '수'...)을 직접 지정합니다. index=days 파라미터를 통해 각 온도 값이 어느 요일의 것인지 명확하게 표시할 수 있습니다.

이제 temp_with_index['수']처럼 요일로 직접 데이터를 찾을 수 있게 됩니다. 이는 마치 엑셀에서 A열은 요일, B열은 온도처럼 연결하는 것과 비슷한 개념입니다.

세 번째 예제는 실무에서 가장 많이 사용하는 방법입니다. 딕셔너리를 Series로 변환하면 키가 자동으로 인덱스가 되고 값이 데이터가 됩니다.

{'철수': 85, '영희': 92}처럼 작성하면 '철수'라는 키가 인덱스로, 85라는 값이 데이터로 자동 매핑됩니다. 이 방법은 코드를 읽는 사람이 데이터의 의미를 바로 이해할 수 있어 가독성이 뛰어납니다.

여러분이 이 코드를 사용하면 엑셀 없이도 데이터를 체계적으로 정리하고, 나중에 배울 강력한 분석 기능들을 적용할 준비가 완료됩니다. 인덱스를 활용한 데이터 접근은 실수를 줄이고, 코드의 의도를 명확하게 만들어줍니다.

실전 팁

💡 Series를 만들 때는 가능하면 의미 있는 인덱스를 지정하세요. series[3]보다 series['영희']가 훨씬 이해하기 쉽고 실수를 방지합니다.

💡 데이터 타입이 섞이지 않도록 주의하세요. 숫자와 문자열이 섞이면 모두 문자열로 변환되어 계산이 불가능합니다. dtype 파라미터로 타입을 명시할 수 있습니다.

💡 큰 데이터셋을 다룰 때는 head()tail() 메서드로 일부만 확인하세요. student_scores.head(3)처럼 사용하면 처음 3개만 출력되어 화면이 깔끔합니다.

💡 Series의 길이를 확인하려면 len() 대신 .shape 속성을 사용하는 습관을 들이세요. 나중에 DataFrame으로 확장할 때 일관성 있게 사용할 수 있습니다.

💡 NumPy 배열에서 Series를 만들 수도 있습니다. 이미 NumPy로 작업 중이라면 pd.Series(np.array([1, 2, 3]))처럼 변환하여 Pandas의 강력한 기능을 활용하세요.


2. Series 인덱싱과 슬라이싱 - 원하는 데이터만 쏙쏙 꺼내기

시작하며

여러분이 100명의 학생 점수 중에서 특정 학생의 점수만 확인하거나, 상위 10명의 점수만 추출해야 할 때 어떻게 하시나요? 전체 데이터를 일일이 확인하는 것은 비효율적이고 실수하기 쉽습니다.

이런 문제는 데이터가 많아질수록 더 심각해집니다. 천 개, 만 개의 데이터 중에서 필요한 것만 정확하게 찾아내는 능력은 데이터 분석의 핵심입니다.

바로 이럴 때 필요한 것이 인덱싱과 슬라이싱입니다. Series에서 원하는 데이터를 자유자재로 추출하는 방법을 마스터하면 데이터 분석의 효율이 10배는 높아집니다.

개요

간단히 말해서, 인덱싱은 하나의 값을 꺼내는 것이고, 슬라이싱은 여러 개의 값을 범위로 꺼내는 것입니다. 마치 과일 바구니에서 사과 한 개를 집는 것과 사과 여러 개를 한꺼번에 담는 것의 차이라고 생각하면 됩니다.

이 기능이 필요한 이유는 실제 분석에서는 전체 데이터보다 특정 조건에 맞는 일부 데이터만 필요한 경우가 대부분이기 때문입니다. 예를 들어, 월별 매출 데이터에서 여름철(6-8월) 매출만 분석하거나, 학생 성적에서 우수 학생(90점 이상)만 추출하는 경우가 그렇습니다.

기존에는 반복문으로 하나하나 확인하며 조건에 맞는 값을 찾았다면, 이제는 인덱스나 위치, 조건식으로 한 번에 필요한 데이터를 추출할 수 있습니다. Series 인덱싱의 핵심 특징은 세 가지입니다.

첫째, 라벨 기반 접근(.loc)과 위치 기반 접근(.iloc) 두 가지 방법을 모두 지원합니다. 둘째, 불린(Boolean) 마스킹으로 조건에 맞는 값만 필터링할 수 있습니다.

셋째, 슬라이싱으로 연속된 범위의 데이터를 한 번에 가져올 수 있습니다. 이러한 특징들이 코드를 간결하게 만들고 실수를 줄여줍니다.

코드 예제

import pandas as pd

# 학생 점수 Series 생성
scores = pd.Series({'철수': 85, '영희': 92, '민수': 78, '지영': 88, '현우': 95, '수진': 82})

# 1. 라벨로 접근 - 이름으로 찾기
print("영희의 점수:", scores['영희'])

# 2. 위치로 접근 - iloc 사용 (0부터 시작)
print("첫 번째 학생 점수:", scores.iloc[0])
print("처음 3명의 점수:\n", scores.iloc[0:3])

# 3. 조건으로 필터링 - 불린 인덱싱
high_scores = scores[scores >= 90]
print("\n90점 이상 학생:\n", high_scores)

# 4. 여러 라벨로 접근
selected = scores[['철수', '지영', '현우']]
print("\n선택한 학생들:\n", selected)

설명

이 코드가 하는 일은 Series에서 원하는 데이터를 다양한 방법으로 추출하는 것입니다. 각각의 방법은 상황에 따라 적절히 선택해서 사용해야 합니다.

첫 번째 예제는 가장 직관적인 방법입니다. scores['영희']처럼 대괄호 안에 라벨을 넣으면 해당 값을 바로 가져올 수 있습니다.

이는 딕셔너리에서 키로 값을 찾는 것과 똑같은 문법이라 Python 초보자도 쉽게 이해할 수 있습니다. 이름을 알고 있을 때 가장 명확하고 안전한 방법입니다.

두 번째 예제는 위치 기반 접근입니다. .iloc[0]은 '첫 번째 위치의 값'을 의미하고, .iloc[0:3]은 '0번부터 2번까지(3번 제외)'를 의미합니다.

이 방법은 라벨을 모르거나, 순서가 중요할 때 유용합니다. 예를 들어 "상위 5명"이나 "최근 10개 데이터"처럼 위치로 표현하는 것이 자연스러울 때 사용합니다.

Python의 리스트 슬라이싱과 동일한 규칙이라 익숙한 문법입니다. 세 번째 예제는 강력한 조건 필터링입니다.

scores >= 90은 각 점수가 90 이상인지 확인하여 True/False의 Series를 만들고, 이를 다시 대괄호에 넣으면 True인 항목만 추출됩니다. 이는 마치 체로 거르듯이 조건에 맞는 데이터만 걸러내는 것입니다.

SQL의 WHERE 절과 비슷한 역할을 하며, 복잡한 조건도 &(and), |(or)로 조합할 수 있습니다. 네 번째 예제는 리스트로 여러 라벨을 지정하는 방법입니다.

대괄호를 이중으로 사용(scores[['철수', '지영']])하면 원하는 항목들만 선택할 수 있습니다. 바깥 대괄호는 인덱싱을 의미하고, 안쪽 대괄호는 라벨 리스트를 의미합니다.

여러분이 이 기법들을 활용하면 대량의 데이터에서 필요한 정보만 빠르게 추출하여 분석 시간을 크게 단축할 수 있습니다. 특히 조건 필터링은 "우수 고객 찾기", "이상치 탐지" 같은 실무 작업에서 매일 사용하는 핵심 기능입니다.

실전 팁

💡 .loc.iloc의 차이를 명확히 하세요. .loc는 라벨 기반이라 끝 값을 포함하지만, .iloc는 위치 기반이라 끝 값을 포함하지 않습니다. scores.loc['철수':'민수']scores.iloc[0:2]의 결과가 다릅니다.

💡 불린 인덱싱에서 여러 조건을 결합할 때는 각 조건을 괄호로 감싸세요. scores[(scores >= 80) & (scores < 90)]처럼 작성해야 오류가 나지 않습니다.

💡 존재하지 않는 라벨에 접근하면 KeyError가 발생합니다. .get() 메서드를 사용하면 에러 대신 None을 반환받아 안전하게 처리할 수 있습니다: scores.get('없는이름', 0)

💡 조건 필터링 결과가 비어있을 수 있습니다. if len(high_scores) > 0: 같은 확인을 추가하면 빈 결과로 인한 에러를 방지할 수 있습니다.

💡 대용량 데이터에서는 .isin() 메서드가 유용합니다. scores[scores.isin([85, 92, 95])]처럼 특정 값들만 찾을 때 여러 조건을 or로 연결하는 것보다 훨씬 빠릅니다.


3. DataFrame 생성하기 - 2차원 테이블 데이터의 시작

시작하며

여러분이 학생들의 이름, 나이, 성적을 함께 관리해야 할 때 어떻게 하시나요? Series 하나로는 한 가지 정보만 담을 수 있어서 여러 개의 Series를 따로 만들어야 하고, 이들을 연결해서 보기도 어렵습니다.

이런 문제는 실제로 우리가 다루는 대부분의 데이터가 여러 속성을 가지고 있기 때문에 발생합니다. 고객 정보, 판매 기록, 실험 결과 등 거의 모든 데이터가 여러 열(컬럼)로 이루어진 테이블 형태입니다.

바로 이럴 때 필요한 것이 DataFrame입니다. DataFrame은 엑셀 시트처럼 행과 열로 이루어진 2차원 테이블을 Python에서 다룰 수 있게 해주는 Pandas의 핵심 도구입니다.

개요

간단히 말해서, DataFrame은 여러 개의 Series를 옆으로 나란히 붙여놓은 것입니다. 마치 엑셀의 스프레드시트나 데이터베이스의 테이블처럼 행(row)과 열(column)이 있는 2차원 구조입니다.

DataFrame이 필요한 이유는 현실의 데이터가 항상 여러 속성으로 이루어져 있기 때문입니다. 한 학생을 이름, 나이, 성적, 주소 등 여러 정보로 설명해야 하고, 이들을 한 곳에 모아서 관리하고 분석해야 합니다.

예를 들어, 온라인 쇼핑몰의 주문 데이터는 고객명, 상품명, 수량, 가격, 주문일시 등 최소 5개 이상의 열이 필요합니다. 기존에는 리스트의 리스트나 딕셔너리의 리스트로 복잡하게 관리했다면, 이제는 DataFrame 하나로 깔끔하게 정리하고, 엑셀보다 훨씬 강력한 분석 기능을 사용할 수 있습니다.

DataFrame의 핵심 특징은 네 가지입니다. 첫째, 각 열은 서로 다른 데이터 타입을 가질 수 있어서 이름(문자열), 나이(정수), 성적(실수)을 한 테이블에 담을 수 있습니다.

둘째, 행과 열 모두에 인덱스(라벨)를 지정할 수 있어 데이터 접근이 직관적입니다. 셋째, SQL처럼 필터링, 정렬, 그룹화 등 다양한 데이터 조작이 가능합니다.

넷째, CSV, 엑셀, 데이터베이스 등 다양한 포맷으로 쉽게 저장하고 불러올 수 있습니다. 이러한 특징들이 DataFrame을 데이터 분석의 표준 도구로 만들었습니다.

코드 예제

import pandas as pd
import numpy as np

# 1. 딕셔너리로 DataFrame 만들기 - 가장 많이 사용
data = {
    '이름': ['철수', '영희', '민수', '지영'],
    '나이': [20, 22, 21, 23],
    '성적': [85.5, 92.0, 78.5, 88.0],
    '학과': ['컴퓨터', '수학', '물리', '컴퓨터']
}
df = pd.DataFrame(data)
print("학생 정보:\n", df)

# 2. 리스트의 리스트로 만들기 - 열 이름 별도 지정
students = [
    ['철수', 20, 85.5, '컴퓨터'],
    ['영희', 22, 92.0, '수학'],
    ['민수', 21, 78.5, '물리']
]
df2 = pd.DataFrame(students, columns=['이름', '나이', '성적', '학과'])
print("\n리스트로 생성:\n", df2)

# 3. 인덱스 지정하기 - 학번을 인덱스로
df3 = pd.DataFrame(data, index=['2021001', '2021002', '2021003', '2021004'])
print("\n학번 인덱스:\n", df3)

설명

이 코드가 하는 일은 여러 방법으로 DataFrame을 생성하는 것입니다. 상황에 따라 가장 편한 방법을 선택하면 됩니다.

첫 번째 방법은 실무에서 가장 자주 사용하는 딕셔너리 방식입니다. {'이름': ['철수', '영희', ...], '나이': [20, 22, ...]}처럼 열 이름을 키로, 해당 열의 값들을 리스트로 만듭니다.

이렇게 하면 열 이름과 데이터가 명확히 연결되어 있어 코드를 읽는 사람이 데이터 구조를 바로 이해할 수 있습니다. Pandas는 각 리스트를 하나의 열(Series)로 만들고, 이들을 합쳐서 DataFrame을 생성합니다.

키가 자동으로 열 이름이 되고, 리스트의 길이가 모두 같아야 한다는 점을 기억하세요. 두 번째 방법은 데이터가 행 단위로 주어질 때 유용합니다.

[['철수', 20, 85.5, '컴퓨터'], ['영희', ...]]처럼 각 학생의 정보를 하나의 리스트로 묶은 후, 이를 다시 리스트로 감쌉니다. 이 방식은 CSV 파일이나 데이터베이스에서 행 단위로 데이터를 가져왔을 때 자연스럽습니다.

다만 열 이름 정보가 없으므로 columns 파라미터로 별도로 지정해야 합니다. 열 이름의 순서가 데이터의 순서와 정확히 일치해야 한다는 점이 중요합니다.

세 번째 방법은 행에 의미 있는 라벨을 붙이는 것입니다. index=['2021001', '2021002', ...]처럼 학번을 인덱스로 지정하면, 나중에 df3.loc['2021001']처럼 학번으로 학생을 찾을 수 있습니다.

인덱스를 지정하지 않으면 기본적으로 0, 1, 2, 3... 같은 숫자가 자동으로 부여되지만, 학번, 날짜, 제품코드처럼 고유한 식별자가 있다면 이를 인덱스로 사용하는 것이 데이터 관리에 훨씬 유리합니다.

여러분이 이 코드를 사용하면 엑셀 없이도 체계적인 테이블 데이터를 만들 수 있고, Pandas의 강력한 분석 기능을 바로 적용할 수 있습니다. 특히 딕셔너리 방식은 코드의 가독성이 뛰어나 협업할 때 다른 사람이 이해하기 쉽고, 나중에 열을 추가하거나 수정하기도 편리합니다.

실전 팁

💡 딕셔너리로 만들 때 모든 리스트의 길이가 같은지 확인하세요. 길이가 다르면 ValueError가 발생합니다. len() 함수로 미리 체크하는 습관을 들이세요.

💡 대용량 데이터는 딕셔너리보다 리스트의 리스트 방식이 메모리 효율이 좋습니다. 하지만 가독성과 유지보수를 고려하면 딕셔너리가 더 나은 선택인 경우가 많습니다.

💡 NumPy 배열로도 DataFrame을 만들 수 있습니다. pd.DataFrame(np.random.rand(4, 3), columns=['A', 'B', 'C'])처럼 수치 계산이 많은 경우 유용합니다.

💡 인덱스는 나중에도 변경 가능합니다. df.set_index('이름') 메서드로 기존 열을 인덱스로 바꿀 수 있고, df.reset_index()로 인덱스를 다시 일반 열로 만들 수 있습니다.

💡 빈 DataFrame을 먼저 만들고 나중에 데이터를 추가할 수도 있습니다. df = pd.DataFrame()df['새열'] = [1, 2, 3]처럼 사용하지만, 성능상 처음부터 데이터와 함께 생성하는 것이 좋습니다.


4. DataFrame 기본 정보 확인하기 - 데이터 파악의 첫걸음

시작하며

여러분이 처음 보는 데이터 파일을 받았을 때 어떻게 하시나요? 열이 몇 개인지, 각 열이 어떤 타입인지, 빈 값은 없는지 일일이 스크롤하며 확인하는 것은 비효율적이고, 큰 데이터에서는 불가능합니다.

이런 문제는 데이터 분석의 첫 단계에서 항상 발생합니다. 데이터를 제대로 이해하지 못한 채 분석을 시작하면 잘못된 결론을 내리거나 에러가 발생할 수 있습니다.

바로 이럴 때 필요한 것이 DataFrame의 기본 정보 확인 메서드들입니다. info(), describe(), head() 같은 메서드로 데이터의 전체적인 모습을 빠르게 파악할 수 있습니다.

개요

간단히 말해서, 기본 정보 확인은 데이터를 본격적으로 분석하기 전에 '이 데이터가 어떻게 생겼는지' 빠르게 스캔하는 과정입니다. 마치 책을 읽기 전에 목차와 서문을 먼저 보는 것과 같습니다.

이 과정이 필요한 이유는 데이터의 품질과 구조를 미리 확인해야 올바른 분석 전략을 세울 수 있기 때문입니다. 예를 들어, 빈 값이 많은 열은 제거하거나 채워야 하고, 숫자로 보이지만 문자열로 저장된 데이터는 타입을 변환해야 합니다.

데이터 크기를 알아야 처리 시간을 예측하고 적절한 방법을 선택할 수 있습니다. 기존에는 Excel에서 직접 눈으로 확인하거나 여러 명령을 조합해야 했다면, 이제는 몇 가지 메서드만으로 데이터의 모든 중요 정보를 한눈에 볼 수 있습니다.

기본 정보 확인의 핵심 메서드는 다섯 가지입니다. 첫째, head()tail()로 데이터의 첫/끝 부분을 샘플로 확인합니다.

둘째, info()로 각 열의 타입, 결측치 개수, 메모리 사용량을 파악합니다. 셋째, describe()로 숫자 열의 통계 요약(평균, 표준편차, 최소/최대값 등)을 확인합니다.

넷째, shape로 행과 열의 개수를 빠르게 확인합니다. 다섯째, dtypes로 각 열의 데이터 타입을 확인합니다.

이러한 도구들이 데이터 분석의 방향을 올바르게 설정하는 데 필수적입니다.

코드 예제

import pandas as pd
import numpy as np

# 샘플 데이터 생성
data = {
    '이름': ['철수', '영희', '민수', '지영', '현우', '수진'],
    '나이': [20, 22, 21, 23, 20, 24],
    '성적': [85.5, 92.0, 78.5, 88.0, 95.5, 82.0],
    '출석일수': [28, 30, 25, 29, 30, 27],
    '학과': ['컴퓨터', '수학', '물리', '컴퓨터', '수학', '물리']
}
df = pd.DataFrame(data)

# 1. 처음 3개 행 보기
print("처음 3개 행:\n", df.head(3))

# 2. 데이터 구조와 타입 확인
print("\n데이터 정보:")
print(df.info())

# 3. 숫자 열의 통계 요약
print("\n통계 요약:\n", df.describe())

# 4. 기본 속성들
print(f"\n행과 열 개수: {df.shape}")  # (행, 열)
print(f"전체 데이터 개수: {df.size}")  # 행 × 열
print(f"\n각 열의 타입:\n{df.dtypes}")

설명

이 코드가 하는 일은 DataFrame의 전체적인 모습을 빠르게 파악하는 것입니다. 각 메서드는 다른 관점에서 데이터를 보여줍니다.

첫 번째로 head(3)은 DataFrame의 처음 3개 행을 보여줍니다. 숫자를 생략하면 기본값인 5개를 보여주고, tail()을 사용하면 마지막 행들을 볼 수 있습니다.

이는 실제 데이터가 어떻게 생겼는지 감을 잡는 가장 빠른 방법입니다. 열 이름과 실제 값의 형태를 보면서 "아, 이름은 문자열이고, 나이는 숫자구나"처럼 직관적으로 이해할 수 있습니다.

특히 CSV 파일을 읽은 직후에는 꼭 head()로 제대로 읽혔는지 확인해야 합니다. 두 번째로 info()는 가장 중요한 메서드입니다.

이것은 각 열의 이름, 결측치가 아닌 값의 개수(Non-Null Count), 데이터 타입(Dtype), 전체 메모리 사용량을 한 번에 보여줍니다. 예를 들어 "6 entries"는 6개 행이 있다는 의미이고, "나이 6 non-null int64"는 나이 열에 6개의 값이 모두 있고(결측치 없음), 정수 타입이라는 뜻입니다.

만약 "5 non-null"이라고 나오면 1개의 결측치가 있다는 신호이므로 처리가 필요합니다. 세 번째로 describe()는 숫자 열에 대한 통계 요약을 제공합니다.

count(개수), mean(평균), std(표준편차), min(최소값), 25%/50%/75%(사분위수), max(최대값)를 자동 계산합니다. 예를 들어 성적의 평균이 86.9이고 최소값이 78.5라는 정보로 "점수 분포가 대체로 높구나"를 파악할 수 있습니다.

이상치(outlier)를 발견하는 데도 유용해서, min이나 max가 비정상적으로 크거나 작으면 데이터 오류를 의심할 수 있습니다. 네 번째로 기본 속성들은 간단하지만 자주 사용합니다.

shape는 (6, 5)처럼 튜플로 (행 수, 열 수)를 반환하고, size는 6×5=30처럼 전체 셀 개수를 반환합니다. dtypes는 각 열의 타입을 시리즈로 보여주는데, object는 문자열, int64는 정수, float64는 실수를 의미합니다.

이 정보로 "숫자로 보이는데 object 타입이네?"처럼 타입 변환이 필요한지 판단할 수 있습니다. 여러분이 이 메서드들을 사용하면 데이터를 만났을 때 당황하지 않고 체계적으로 파악할 수 있습니다.

특히 실무에서 받은 낯선 데이터는 항상 이 순서대로 확인하는 습관을 들이면 분석 실수를 크게 줄일 수 있습니다.

실전 팁

💡 head()를 실행한 후 모든 열이 보이지 않으면 pd.set_option('display.max_columns', None)을 실행하세요. Pandas는 기본적으로 너무 많은 열을 숨기므로 이 설정으로 모두 볼 수 있습니다.

💡 describe(include='all')을 사용하면 문자열 열의 통계(unique, top, freq)도 함께 볼 수 있습니다. 카테고리 데이터의 분포를 파악할 때 유용합니다.

💡 대용량 데이터는 info(memory_usage='deep')로 정확한 메모리 사용량을 확인하세요. 메모리 부족 문제를 미리 예방할 수 있습니다.

💡 df.isnull().sum()을 추가로 실행하면 각 열별 결측치 개수를 더 명확히 볼 수 있습니다. info()보다 결측치에 집중해서 확인할 때 좋습니다.

💡 Jupyter Notebook에서는 df만 입력하면 예쁘게 포맷된 테이블로 볼 수 있습니다. print()보다 훨씬 보기 좋으니 대화형 환경에서는 print를 생략하세요.


5. DataFrame 열 선택과 추가 - 필요한 컬럼만 다루기

시작하며

여러분이 20개 열이 있는 고객 데이터에서 이름과 구매금액 두 개만 필요할 때 어떻게 하시나요? 전체 DataFrame을 다 가져와서 사용하면 불필요한 데이터까지 메모리에 올라가고 코드도 복잡해집니다.

이런 문제는 실무에서 매우 흔합니다. 데이터베이스에서 가져온 테이블은 수십 개의 열이 있지만, 특정 분석에는 그 중 일부만 필요한 경우가 대부분입니다.

또한 새로운 계산 결과를 열로 추가해야 하는 경우도 많습니다. 바로 이럴 때 필요한 것이 열 선택과 추가 기능입니다.

필요한 열만 골라서 작업하고, 계산 결과를 새 열로 추가하는 방법을 익히면 데이터 조작이 훨씬 자유로워집니다.

개요

간단히 말해서, 열 선택은 큰 테이블에서 필요한 컬럼만 추출하는 것이고, 열 추가는 새로운 컬럼을 만들어 붙이는 것입니다. 마치 엑셀에서 특정 열만 복사하거나 새 열에 수식을 넣는 것과 같습니다.

이 기능이 필요한 이유는 데이터 분석이 항상 선택과 변형의 과정이기 때문입니다. 예를 들어, 100개 열 중 5개만 필요하면 나머지를 제거해서 메모리를 절약하고 코드 가독성을 높일 수 있습니다.

또한 기존 열들을 조합한 새로운 지표(예: 총점 = 국어 + 영어 + 수학)를 계산해서 추가하는 것이 분석의 핵심입니다. 기존에는 반복문으로 열을 하나씩 처리했다면, 이제는 열 이름으로 직접 접근하고, 벡터 연산으로 한 번에 계산한 결과를 새 열로 추가할 수 있습니다.

열 선택과 추가의 핵심 기능은 네 가지입니다. 첫째, 대괄호로 한 개 또는 여러 개의 열을 선택할 수 있습니다.

둘째, 새 열을 추가할 때는 딕셔너리처럼 df['새열'] = 값으로 간단히 할당합니다. 셋째, 기존 열들을 조합한 계산 결과를 바로 새 열로 만들 수 있습니다.

넷째, .drop()으로 불필요한 열을 제거할 수 있습니다. 이러한 기능들이 DataFrame을 동적으로 변형하며 분석하는 핵심 도구입니다.

코드 예제

import pandas as pd

# 샘플 데이터
data = {
    '이름': ['철수', '영희', '민수'],
    '국어': [85, 92, 78],
    '영어': [90, 88, 82],
    '수학': [88, 95, 79]
}
df = pd.DataFrame(data)

# 1. 한 개 열 선택 - Series 반환
names = df['이름']
print("이름 열:\n", names)
print(f"타입: {type(names)}\n")

# 2. 여러 열 선택 - DataFrame 반환
scores = df[['이름', '국어', '영어']]
print("선택한 열들:\n", scores)

# 3. 새 열 추가 - 총점 계산
df['총점'] = df['국어'] + df['영어'] + df['수학']
print("\n총점 추가:\n", df)

# 4. 조건부 열 추가 - 합격 여부
df['합격'] = df['총점'] >= 240  # 240점 이상이면 True
print("\n합격 여부 추가:\n", df)

# 5. 열 삭제
df_dropped = df.drop(['합격'], axis=1)
print("\n열 삭제 후:\n", df_dropped)

설명

이 코드가 하는 일은 DataFrame의 열을 자유자재로 선택하고 추가하고 삭제하는 것입니다. 각 방법의 차이를 정확히 이해해야 합니다.

첫 번째 예제는 한 개의 열을 선택합니다. df['이름']처럼 대괄호에 열 이름을 하나만 넣으면 Series가 반환됩니다.

이는 DataFrame의 한 열이 본질적으로 Series이기 때문입니다. type(names)를 출력하면 <class 'pandas.core.series.Series'>가 나옵니다.

Series로 반환되면 앞에서 배운 Series 메서드들(.mean(), .max() 등)을 바로 사용할 수 있습니다. 두 번째 예제는 여러 열을 선택합니다.

df[['이름', '국어', '영어']]처럼 대괄호를 이중으로 사용하면 DataFrame이 반환됩니다. 바깥 대괄호는 "선택한다"는 의미이고, 안쪽 대괄호는 "열 이름의 리스트"를 의미합니다.

이렇게 하면 원본 DataFrame의 부분집합인 새로운 DataFrame을 얻게 됩니다. 열 순서도 리스트의 순서대로 조정되므로 [['수학', '이름']]처럼 순서를 바꿔서 선택할 수도 있습니다.

세 번째 예제는 계산 결과를 새 열로 추가하는 핵심 기능입니다. df['총점'] = df['국어'] + df['영어'] + df['수학']는 세 열의 값을 더한 결과를 '총점'이라는 새 열에 저장합니다.

여기서 놀라운 점은 반복문 없이 벡터 연산으로 모든 행에 대해 한 번에 계산된다는 것입니다. Pandas는 내부적으로 각 행의 국어, 영어, 수학을 더해서 총점 Series를 만들고, 이를 DataFrame에 새 열로 붙입니다.

네 번째 예제는 조건부 열 추가입니다. df['총점'] >= 240은 각 행의 총점이 240 이상인지 비교하여 True/False의 Series를 만듭니다.

이를 '합격' 열에 할당하면 불린(Boolean) 값을 가진 열이 생성됩니다. 이런 불린 열은 나중에 필터링할 때 매우 유용합니다: df[df['합격']]처럼 합격한 학생만 골라낼 수 있습니다.

다섯 번째 예제는 열 삭제입니다. .drop(['합격'], axis=1)은 '합격' 열을 제거한 새 DataFrame을 반환합니다.

axis=1은 "열 방향"을 의미하고(axis=0은 행 방향), 원본 df는 변경되지 않습니다. 원본을 바로 수정하려면 inplace=True 파라미터를 추가하면 됩니다.

여러분이 이 기법들을 활용하면 복잡한 데이터 변형을 간결한 코드로 표현할 수 있습니다. 특히 새 열 추가는 분석의 90% 이상에서 사용되는 핵심 패턴이므로 반드시 익혀야 합니다.

실전 팁

💡 여러 열을 선택할 때 대괄호를 한 번만 쓰면 df['이름', '국어']처럼 에러가 납니다. 꼭 이중 대괄호 df[['이름', '국어']]를 사용하세요.

💡 새 열 추가 시 길이가 맞지 않으면 에러가 발생합니다. df['새열'] = [1, 2]처럼 3행짜리 DataFrame에 2개 값을 넣으면 안 됩니다. 단일 값은 자동으로 모든 행에 복사됩니다: df['상수'] = 100

💡 .drop()은 원본을 변경하지 않으므로 결과를 변수에 저장하거나 inplace=True를 사용하세요. df.drop(['열'], axis=1, inplace=True)

💡 열 이름에 공백이나 특수문자가 있으면 대괄호 방식만 사용 가능합니다. df['평균 점수']는 되지만 df.평균 점수는 안 됩니다.

💡 조건부 값 할당은 .apply().loc을 사용하면 더 복잡한 로직도 가능합니다: df['등급'] = df['총점'].apply(lambda x: 'A' if x >= 270 else 'B' if x >= 240 else 'C')


6. DataFrame 행 필터링 - 조건에 맞는 데이터만 추출하기

시작하며

여러분이 1000명의 고객 데이터에서 30대 여성 고객만 분석하고 싶을 때 어떻게 하시나요? 전체 데이터를 눈으로 확인하며 하나씩 찾는 것은 불가능하고, 엑셀의 필터 기능도 복잡한 조건에서는 한계가 있습니다.

이런 문제는 데이터 분석의 핵심입니다. 전체 데이터보다는 특정 조건을 만족하는 부분집합을 분석하는 경우가 훨씬 많기 때문입니다.

예를 들어, "지난 달 구매 금액이 10만원 이상인 VIP 고객"이나 "성적이 80점 이상이면서 출석률 90% 이상인 우수 학생" 같은 조건입니다. 바로 이럴 때 필요한 것이 행 필터링입니다.

불린 인덱싱과 .loc, .iloc을 활용하면 SQL의 WHERE 절처럼 강력한 조건 필터링을 한 줄로 처리할 수 있습니다.

개요

간단히 말해서, 행 필터링은 특정 조건을 만족하는 행들만 골라내는 것입니다. 마치 체로 거르듯이 원하는 데이터만 남기고 나머지는 제외하는 과정입니다.

행 필터링이 필요한 이유는 분석의 대부분이 조건부 분석이기 때문입니다. "전체 평균"보다는 "남성의 평균", "여성의 평균"을 따로 구하는 것이 더 의미 있고, "전체 매출"보다는 "성수기 매출", "비수기 매출"을 비교하는 것이 실질적인 인사이트를 제공합니다.

기존에는 반복문으로 조건을 체크하며 리스트를 만들었다면, 이제는 조건식 하나로 필터링된 DataFrame을 바로 얻을 수 있고, 여러 조건을 논리 연산자로 조합할 수 있습니다. 행 필터링의 핵심 기능은 네 가지입니다.

첫째, 불린 인덱싱으로 조건식의 결과(True/False)를 바탕으로 행을 선택합니다. 둘째, &(and), |(or), ~(not) 연산자로 복잡한 조건을 조합합니다.

셋째, .isin()으로 특정 값 목록에 포함되는지 확인합니다. 넷째, .query() 메서드로 SQL 스타일의 문자열 조건을 사용할 수 있습니다.

이러한 기능들이 데이터의 부분집합을 자유롭게 추출하게 해줍니다.

코드 예제

import pandas as pd

# 샘플 데이터
data = {
    '이름': ['철수', '영희', '민수', '지영', '현우', '수진'],
    '나이': [20, 22, 21, 23, 20, 24],
    '성적': [85, 92, 78, 88, 95, 82],
    '성별': ['남', '여', '남', '여', '남', '여']
}
df = pd.DataFrame(data)

# 1. 단일 조건 - 성적 90점 이상
high_scores = df[df['성적'] >= 90]
print("90점 이상:\n", high_scores)

# 2. 여러 조건 결합 - 20대 초반 여성
young_female = df[(df['나이'] <= 22) & (df['성별'] == '여')]
print("\n20대 초반 여성:\n", young_female)

# 3. OR 조건 - 성적 90점 이상 또는 나이 24세
condition = df[(df['성적'] >= 90) | (df['나이'] == 24)]
print("\n성적 90점 이상 OR 24세:\n", condition)

# 4. isin() 사용 - 특정 이름들만
selected_names = df[df['이름'].isin(['철수', '영희', '현우'])]
print("\n특정 학생들:\n", selected_names)

# 5. query() 메서드 - 더 읽기 쉬운 문법
result = df.query('나이 >= 22 and 성적 >= 85')
print("\nquery 사용:\n", result)

설명

이 코드가 하는 일은 다양한 조건으로 원하는 행만 선택하는 것입니다. 각 방법은 상황에 따라 가장 편한 것을 선택하면 됩니다.

첫 번째 예제는 가장 기본적인 불린 인덱싱입니다. df['성적'] >= 90은 각 행의 성적을 90과 비교하여 True/False의 Series를 만듭니다.

예를 들어 [False, True, False, False, True, False] 같은 형태입니다. 이를 df[]의 대괄호 안에 넣으면 True인 위치의 행만 선택됩니다.

결과는 영희(92점)와 현우(95점) 두 행만 포함한 새 DataFrame입니다. 이는 마치 엑셀의 자동 필터와 같지만 코드로 자동화되고 재사용 가능합니다.

두 번째 예제는 여러 조건을 결합합니다. (df['나이'] <= 22) & (df['성별'] == '여')는 두 조건을 모두 만족하는 행을 찾습니다.

여기서 중요한 점은 각 조건을 괄호로 감싸야 한다는 것입니다. &의 연산 우선순위가 비교 연산자보다 높아서 괄호 없이 쓰면 에러가 발생합니다.

&는 element-wise AND로, 두 불린 Series의 같은 위치끼리 AND 연산을 수행합니다. 결과는 20대 초반(나이 ≤ 22)이면서 여성인 영희 한 명입니다.

세 번째 예제는 OR 조건입니다. | 연산자를 사용하면 둘 중 하나라도 True면 선택됩니다.

"성적이 90점 이상" 또는 "나이가 24세"인 사람을 모두 포함하므로, 영희(92점), 현우(95점), 수진(24세) 세 명이 선택됩니다. ~ 연산자는 NOT을 의미해서 ~(df['성별'] == '남')처럼 조건을 반대로 뒤집을 수 있습니다.

네 번째 예제는 .isin() 메서드입니다. 특정 값들의 리스트에 포함되는지 확인할 때 유용합니다.

df['이름'].isin(['철수', '영희', '현우'])는 이름 열의 각 값이 리스트에 있는지 체크합니다. 이는 (df['이름'] == '철수') | (df['이름'] == '영희') | (df['이름'] == '현우')를 간결하게 표현한 것입니다.

특히 값이 많을 때 코드가 훨씬 깔끔해집니다. 다섯 번째 예제는 .query() 메서드로, SQL에 익숙한 사람에게 직관적입니다.

문자열 형태로 조건을 작성하면 되고, and, or 같은 자연스러운 키워드를 사용할 수 있습니다. 괄호도 덜 필요해서 가독성이 좋지만, 동적으로 조건을 만들기는 불린 인덱싱이 더 유리합니다.

여러분이 이 기법들을 활용하면 대용량 데이터에서 원하는 부분만 빠르게 추출하여 분석의 정확도와 속도를 동시에 높일 수 있습니다. 특히 실무에서는 "이번 분기 매출 상위 10% 고객"처럼 복잡한 조건을 자주 사용하므로 조건 결합에 익숙해져야 합니다.

실전 팁

💡 조건 결합 시 반드시 각 조건을 괄호로 감싸세요. df[df['나이'] <= 22 & df['성별'] == '여']는 에러가 나고, df[(df['나이'] <= 22) & (df['성별'] == '여')]가 올바릅니다.

💡 and, or 대신 &, |를 사용해야 합니다. Python의 and/or는 불린 Series에 사용할 수 없으므로 비트 연산자를 사용합니다.

💡 문자열 비교는 대소문자를 구분합니다. 대소문자 무시하려면 .str.lower()로 소문자 변환 후 비교하세요: df[df['이름'].str.lower() == 'chulsu']

💡 결측치(NaN) 체크는 == None이 아닌 .isnull()이나 .notna()를 사용하세요: df[df['성적'].notna()]로 성적이 있는 행만 선택합니다.

💡 필터링 결과가 비어있을 수 있습니다. if not filtered_df.empty: 같은 체크를 추가하면 빈 DataFrame으로 인한 에러를 방지할 수 있습니다.


7. DataFrame 정렬하기 - 데이터를 원하는 순서로 배열하기

시작하며

여러분이 학생 성적 데이터를 높은 순서대로 보고 싶을 때, 또는 날짜별로 정렬된 매출 데이터를 확인하고 싶을 때 어떻게 하시나요? 순서가 뒤죽박죽인 데이터는 패턴을 파악하기 어렵고, 상위/하위 값을 찾기도 불편합니다.

이런 문제는 데이터 탐색 단계에서 항상 발생합니다. 데이터를 의미 있는 순서로 정렬해야 트렌드를 파악하고, 이상치를 발견하고, 순위를 매길 수 있습니다.

바로 이럴 때 필요한 것이 정렬 기능입니다. .sort_values().sort_index()로 데이터를 원하는 기준으로 오름차순 또는 내림차순 정렬할 수 있습니다.

개요

간단히 말해서, 정렬은 DataFrame의 행을 특정 기준에 따라 순서대로 재배열하는 것입니다. 마치 책을 가나다순으로 정리하거나 카드를 숫자 순서대로 늘어놓는 것과 같습니다.

정렬이 필요한 이유는 순서가 의미를 가지는 경우가 많기 때문입니다. 예를 들어, 매출 데이터를 금액 순으로 정렬하면 최고/최저 매출 상품을 즉시 파악할 수 있고, 날짜순으로 정렬하면 시간에 따른 변화를 추적할 수 있습니다.

성적순 정렬은 등수를 매기는 기본 작업입니다. 기존에는 정렬 알고리즘을 직접 구현하거나 Python의 sorted()를 사용했다면, 이제는 Pandas의 메서드로 한 줄에 처리하고, 여러 열을 기준으로 하는 복잡한 정렬도 쉽게 할 수 있습니다.

정렬의 핵심 기능은 네 가지입니다. 첫째, .sort_values()로 특정 열의 값을 기준으로 정렬합니다.

둘째, ascending 파라미터로 오름차순/내림차순을 선택합니다. 셋째, 여러 열을 리스트로 전달하면 우선순위에 따라 정렬됩니다.

넷째, .sort_index()로 인덱스를 기준으로 정렬할 수 있습니다. 이러한 기능들이 데이터를 의미 있는 순서로 배열하여 분석을 용이하게 만듭니다.

코드 예제

import pandas as pd

# 샘플 데이터
data = {
    '이름': ['철수', '영희', '민수', '지영', '현우'],
    '나이': [20, 22, 20, 23, 22],
    '성적': [85, 92, 78, 88, 95]
}
df = pd.DataFrame(data)
print("원본 데이터:\n", df)

# 1. 단일 열로 정렬 - 성적 오름차순
sorted_asc = df.sort_values('성적')
print("\n성적 오름차순:\n", sorted_asc)

# 2. 내림차순 정렬 - 성적 높은 순
sorted_desc = df.sort_values('성적', ascending=False)
print("\n성적 내림차순:\n", sorted_desc)

# 3. 여러 열로 정렬 - 나이 먼저, 같으면 성적 순
multi_sort = df.sort_values(['나이', '성적'], ascending=[True, False])
print("\n나이↑, 성적↓:\n", multi_sort)

# 4. 인덱스로 정렬 - 정렬 후 원래 순서로 복구
reset = sorted_desc.sort_index()
print("\n인덱스 순서로 복구:\n", reset)

# 5. 원본 수정 - inplace 사용
df.sort_values('이름', inplace=True)
print("\n이름순(원본 수정):\n", df)

설명

이 코드가 하는 일은 DataFrame을 다양한 기준으로 정렬하는 것입니다. 각 방법은 분석 목적에 맞게 선택하면 됩니다.

첫 번째 예제는 가장 기본적인 단일 열 정렬입니다. df.sort_values('성적')은 성적 열의 값을 기준으로 작은 것부터 큰 순서로(오름차순) 행을 재배열합니다.

원본 df는 변경되지 않고 새로운 정렬된 DataFrame이 반환됩니다. 결과는 민수(78점) → 철수(85점) → 지영(88점) → 영희(92점) → 현우(95점) 순서가 됩니다.

이렇게 정렬하면 최저점과 최고점이 양 끝에 위치해 한눈에 파악할 수 있습니다. 두 번째 예제는 내림차순 정렬입니다.

ascending=False를 추가하면 큰 것부터 작은 순서로 정렬됩니다. 실무에서는 "매출 상위 10개 상품", "성적 우수 학생" 같은 케이스에서 내림차순을 더 자주 사용합니다.

결과는 현우(95점) → 영희(92점) → ... 순서로, Top N을 찾을 때 매우 유용합니다.

.head(3)과 결합하면 "상위 3명"을 즉시 추출할 수 있습니다. 세 번째 예제는 여러 열을 기준으로 하는 복합 정렬입니다.

['나이', '성적']을 전달하면 먼저 나이로 정렬하고, 나이가 같은 경우 성적으로 정렬합니다. ascending=[True, False]는 나이는 오름차순, 성적은 내림차순을 의미합니다.

예를 들어 나이 20세 그룹 내에서는 성적이 높은 순서로 정렬됩니다. 이는 마치 엑셀의 "기본 정렬 > 추가 정렬" 기능과 같으며, 리스트의 순서가 정렬 우선순위입니다.

네 번째 예제는 인덱스 기준 정렬입니다. .sort_index()는 행의 인덱스(기본값은 0, 1, 2, ...)를 기준으로 정렬합니다.

값으로 정렬한 후 원래 순서로 되돌리고 싶을 때 유용합니다. 인덱스가 날짜나 시간인 시계열 데이터에서는 .sort_index()로 시간순 정렬을 자주 사용합니다.

또한 .reset_index(drop=True)와 결합하면 정렬 후 인덱스를 0부터 다시 매길 수 있습니다. 다섯 번째 예제는 원본을 직접 수정하는 방법입니다.

inplace=True를 추가하면 새 DataFrame을 반환하지 않고 원본 df 자체를 정렬합니다. 메모리를 절약할 수 있지만, 원본을 유지하고 싶다면 사용하지 말아야 합니다.

실무에서는 명확성을 위해 df = df.sort_values(...) 형태로 재할당하는 것을 선호하는 경우도 많습니다. 여러분이 이 정렬 기법들을 활용하면 데이터의 패턴을 빠르게 파악하고, 극값을 찾고, 순위를 매기는 작업을 자동화할 수 있습니다.

특히 복합 정렬은 "지역별 → 나이대별 → 구매금액별" 같은 계층적 분석에서 필수적입니다.

실전 팁

💡 정렬 후 인덱스가 뒤죽박죽이면 .reset_index(drop=True)로 0부터 다시 매기세요. df.sort_values('성적').reset_index(drop=True)처럼 체이닝할 수 있습니다.

💡 결측치(NaN)는 기본적으로 끝으로 정렬됩니다. na_position='first'로 앞으로 보낼 수 있습니다: df.sort_values('성적', na_position='first')

💡 문자열 정렬은 유니코드 순서를 따릅니다. 한글은 가나다순으로 정렬되고, 영어는 알파벳순입니다. 대소문자 구분이 필요하면 .str.lower()로 소문자 변환 후 정렬하세요.

💡 대용량 데이터 정렬은 시간이 걸립니다. 필요한 행만 먼저 필터링한 후 정렬하면 속도가 크게 향상됩니다: df[df['성적'] >= 80].sort_values('성적')

💡 정렬된 상태에서 Top N만 필요하면 .nlargest(n, '열명')이나 .nsmallest(n, '열명')를 사용하세요. 전체 정렬보다 훨씬 빠릅니다: df.nlargest(3, '성적')


8. DataFrame 그룹화와 집계 - 카테고리별 통계 내기

시작하며

여러분이 전체 학생의 평균 성적이 아니라 학과별 평균 성적을 알고 싶을 때 어떻게 하시나요? 각 학과를 일일이 필터링해서 평균을 구하는 것은 번거롭고, 학과가 수십 개라면 사실상 불가능합니다.

이런 문제는 실무에서 가장 흔한 분석 패턴입니다. "전체" 통계보다는 "그룹별" 통계가 훨씬 의미 있고 실용적입니다.

예를 들어, "월별 매출", "지역별 고객 수", "제품 카테고리별 평균 가격" 같은 분석이 실제로 필요한 인사이트를 제공합니다. 바로 이럴 때 필요한 것이 그룹화(GroupBy)와 집계(Aggregation)입니다.

SQL의 GROUP BY처럼 데이터를 카테고리별로 나누고, 각 그룹에 대해 합계, 평균, 개수 등을 자동으로 계산할 수 있습니다.

개요

간단히 말해서, 그룹화는 데이터를 특정 기준으로 묶는 것이고, 집계는 각 묶음에 대해 통계를 계산하는 것입니다. 마치 과일을 종류별로 분류한 후 각 종류의 개수를 세는 것과 같습니다.

그룹화와 집계가 필요한 이유는 비교 분석이 데이터 분석의 핵심이기 때문입니다. "컴퓨터학과의 평균 성적이 수학과보다 높다"처럼 그룹 간 차이를 파악해야 의미 있는 결론을 도출할 수 있습니다.

예를 들어, 온라인 쇼핑몰에서 "연령대별 평균 구매 금액"을 분석하면 타겟 마케팅 전략을 수립할 수 있습니다. 기존에는 각 그룹을 필터링하고 통계를 계산하는 반복문을 작성했다면, 이제는 .groupby()와 집계 함수로 한 줄에 모든 그룹의 통계를 동시에 구할 수 있습니다.

그룹화와 집계의 핵심 기능은 다섯 가지입니다. 첫째, .groupby('열명')으로 해당 열의 고유값별로 데이터를 그룹화합니다.

둘째, .mean(), .sum(), .count() 같은 집계 함수로 각 그룹의 통계를 계산합니다. 셋째, .agg()로 여러 집계 함수를 동시에 적용할 수 있습니다.

넷째, 여러 열로 그룹화하여 계층적 집계가 가능합니다. 다섯째, .size()로 각 그룹의 크기(행 수)를 확인할 수 있습니다.

이러한 기능들이 복잡한 비교 분석을 간단한 코드로 만들어줍니다.

코드 예제

import pandas as pd

# 샘플 데이터
data = {
    '이름': ['철수', '영희', '민수', '지영', '현우', '수진'],
    '학과': ['컴퓨터', '수학', '물리', '컴퓨터', '수학', '물리'],
    '성적': [85, 92, 78, 88, 95, 82],
    '학년': [2, 3, 2, 3, 2, 3]
}
df = pd.DataFrame(data)

# 1. 기본 그룹화 - 학과별 평균 성적
major_avg = df.groupby('학과')['성적'].mean()
print("학과별 평균:\n", major_avg)

# 2. 여러 통계 한번에 - agg 사용
major_stats = df.groupby('학과')['성적'].agg(['mean', 'min', 'max', 'count'])
print("\n학과별 상세 통계:\n", major_stats)

# 3. 여러 열 집계
multi_col = df.groupby('학과').agg({'성적': 'mean', '학년': 'max'})
print("\n여러 열 집계:\n", multi_col)

# 4. 다중 그룹화 - 학과와 학년별
multi_group = df.groupby(['학과', '학년'])['성적'].mean()
print("\n학과+학년별 평균:\n", multi_group)

# 5. 그룹 크기 확인
group_size = df.groupby('학과').size()
print("\n학과별 학생 수:\n", group_size)

설명

이 코드가 하는 일은 데이터를 카테고리별로 나누고 각 그룹의 통계를 계산하는 것입니다. GroupBy는 "분할-적용-결합" 패턴으로 작동합니다.

첫 번째 예제는 가장 기본적인 그룹화와 집계입니다. df.groupby('학과')는 학과 열의 고유값(컴퓨터, 수학, 물리)별로 데이터를 그룹으로 나눕니다.

그 다음 ['성적']으로 성적 열만 선택하고, .mean()으로 각 그룹의 평균을 계산합니다. 내부적으로는 (1) 학과별로 데이터를 분할, (2) 각 그룹에 mean() 적용, (3) 결과를 하나의 Series로 결합하는 과정이 일어납니다.

결과는 학과를 인덱스로 하고 평균 성적을 값으로 하는 Series입니다. 두 번째 예제는 여러 통계를 한 번에 계산합니다.

.agg(['mean', 'min', 'max', 'count'])는 평균, 최소값, 최대값, 개수를 동시에 구합니다. 결과는 학과를 행으로, 통계 종류를 열로 하는 DataFrame이 됩니다.

이렇게 하면 각 학과의 성적 분포를 종합적으로 파악할 수 있습니다. 예를 들어 컴퓨터학과는 평균 86.5, 최소 85, 최대 88, 학생 수 2명 같은 정보가 한눈에 보입니다.

세 번째 예제는 여러 열에 대해 서로 다른 집계를 적용합니다. agg({'성적': 'mean', '학년': 'max'})는 딕셔너리로 열마다 다른 함수를 지정합니다.

성적은 평균을, 학년은 최대값을 계산해서 "각 학과의 평균 성적"과 "최고 학년"을 동시에 볼 수 있습니다. 실무에서는 "카테고리별 평균 가격, 총 판매량, 최신 날짜" 같은 복합 집계에 유용합니다.

네 번째 예제는 다중 컬럼 그룹화입니다. groupby(['학과', '학년'])은 학과와 학년의 조합별로 그룹을 만듭니다.

예를 들어 (컴퓨터, 2학년), (컴퓨터, 3학년), (수학, 2학년) 같은 세부 그룹으로 나뉩니다. 결과는 MultiIndex Series로, 계층적 구조를 가집니다.

.unstack()을 추가하면 피벗 테이블 형태로 볼 수 있습니다: multi_group.unstack() 다섯 번째 예제는 그룹 크기를 확인합니다. .size()는 각 그룹에 몇 개의 행이 있는지 세줍니다.

이는 .count()와 비슷하지만, .count()는 결측치를 제외한 개수를 세고, .size()는 모든 행을 셉니다. "학과별 학생 수" 같은 빈도 분석에 사용됩니다.

여러분이 이 기법들을 활용하면 복잡한 비교 분석을 자동화하고, 카테고리별 패턴을 빠르게 발견할 수 있습니다. 특히 실무에서는 "월별", "지역별", "제품별" 집계가 리포트의 기본이므로 GroupBy는 가장 많이 사용하는 기능 중 하나입니다.

실전 팁

💡 그룹화 후 모든 열이 집계되는 것을 방지하려면 집계 전에 열을 선택하세요. df.groupby('학과')['성적'].mean()df.groupby('학과').mean()보다 명확하고 빠릅니다.

💡 집계 결과의 인덱스를 일반 열로 만들려면 .reset_index()를 사용하세요. major_avg.reset_index()는 학과를 인덱스가 아닌 일반 열로 만듭니다.

💡 사용자 정의 함수도 집계에 사용할 수 있습니다. df.groupby('학과')['성적'].agg(lambda x: x.max() - x.min())처럼 범위를 계산할 수 있습니다.

💡 여러 집계 함수에 이름을 붙일 수 있습니다. agg(평균='mean', 최대='max')처럼 하면 열 이름이 '평균', '최대'로 바뀝니다.

💡 그룹별로 상위 N개를 추출하려면 .nlargest()와 결합하세요. df.groupby('학과', group_keys=False).apply(lambda x: x.nlargest(2, '성적'))로 학과별 상위 2명을 뽑을 수 있습니다.


#Python#Pandas#Series#DataFrame#DataAnalysis#Data Science

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스

데이터 증강과 정규화 완벽 가이드

머신러닝 모델의 성능을 극대화하는 핵심 기법인 데이터 증강과 정규화에 대해 알아봅니다. 실무에서 바로 활용할 수 있는 다양한 기법과 실전 예제를 통해 과적합을 방지하고 모델 성능을 향상시키는 방법을 배웁니다.

ResNet과 Skip Connection 완벽 가이드

딥러닝 모델이 깊어질수록 성능이 떨어지는 문제를 해결한 혁신적인 기법, ResNet과 Skip Connection을 초급자도 이해할 수 있도록 쉽게 설명합니다. 실제 구현 코드와 함께 배워보세요.

CNN 아키텍처 완벽 가이드 LeNet AlexNet VGGNet

컴퓨터 비전의 기초가 되는 세 가지 핵심 CNN 아키텍처를 배웁니다. 손글씨 인식부터 이미지 분류까지, 딥러닝의 발전 과정을 따라가며 각 모델의 구조와 특징을 실습 코드와 함께 이해합니다.

CNN 기초 Convolution과 Pooling 완벽 가이드

CNN의 핵심인 Convolution과 Pooling을 초급자도 쉽게 이해할 수 있도록 설명합니다. 이미지 인식의 원리부터 실제 코드 구현까지, 실무에서 바로 활용 가능한 내용을 담았습니다.

TensorFlow와 Keras 완벽 입문 가이드

머신러닝과 딥러닝의 세계로 들어가는 첫걸음! TensorFlow와 Keras 프레임워크를 처음 접하는 분들을 위한 친절한 가이드입니다. 실무에서 바로 활용할 수 있는 핵심 개념과 예제를 통해 AI 모델 개발의 기초를 탄탄히 다져보세요.