멀티캠퍼스 데이터분석 수업 —
map(),str메서드, 파생변수 생성, 결측치 처리 정리
map() & 리스트 컴프리헨션
1. value_counts() — 빈도수 확인
2. 공백 제거 — for / while / map() 비교
3. Series.str — 문자열 메서드 접근
4. map() + 결측치 처리 (na_action)
5. map() 활용 — 요일 변환
6. map() vs replace()
7. 리스트 컴프리헨션
실전 데이터 전처리 — corona.csv
실전 데이터 전처리 — uriage.csv
특정 컬럼의 값(value)별 빈도수(count)를 확인하는 함수입니다.
import pandas as pd
df = pd.DataFrame({
'product': [' iphone ', ' iphone', 'iphone', 'galaxy ', 'galaxy']
})
df['product'].value_counts()
# 공백 차이 때문에 같은 값인데도 다르게 인식됨

Series에는 strip()이 존재하지 않아서 단일 데이터에만 바로 적용할 수 있습니다.
df['product'][0].strip() # ✅ 단일 데이터(str 타입)이므로 가능
df['product'].strip() # ❌ Series에는 strip() 없음 → 에러
df2 = df.copy() # 원본 백업
for i in range(len(df2)):
df2.iloc[i, 0] = df2.iloc[i, 0].strip()
df2['product'].value_counts()
df3 = df.copy()
i = 0
while i < len(df3):
print(df3.iloc[i, 0].strip())
i += 1
i = 0
while True:
try:
print(df3.iloc[i, 0].strip())
i += 1
except:
break # 인덱스 범위 초과 시 반복 종료
def data_strip(x):
return x.strip()
df3['product'].map(data_strip)
# lambda로 더 간결하게
df3['product'].map(lambda x: x.strip()).value_counts()
💡
map()이란?
1차원 데이터의 각 원소에 함수를 적용하여 데이터를 수정/변환합니다.
파이썬 기본 제공map()과Series내장map()두 가지가 있습니다.
Series에서 문자열 함수를 사용하려면 .str을 통해 접근합니다.
# 타입 변화 흐름
# DataFrame → Series → str 메서드 → Series
df['product'].str.strip()
| 접근 방식 | 타입 | 설명 |
|---|---|---|
df | DataFrame | 2차원 데이터 |
df['product'] | Series | 1차원 데이터 |
df['product'].str | StringMethods | 문자열 메서드 접근자 |
df['product'].str.strip() | Series | 결과 반환 |
import numpy as np
series_1 = pd.Series(['cat', 'dog', np.nan])
series_1.str.upper() # 결측치는 NaN 그대로 유지
map()에 lambda를 쓸 때 결측치가 있으면 에러가 발생합니다.
na_action='ignore'로 결측치를 건너뛸 수 있습니다.
series_1 = pd.Series(['cat', 'dog', np.nan])
# ❌ 결측치(float 타입)에 upper()가 없어서 에러 발생
series_1.map(lambda x: x.upper())
# ✅ na_action='ignore' → 결측치는 함수 적용 없이 그대로 유지
series_1.map(lambda x: x.upper(), na_action='ignore')
💡
str.upper()는na_action없이도 결측치를 알아서 처리합니다.
lambda를 써야 할 때만na_action='ignore'를 추가하세요.
요일 데이터는 숫자(0~6)로 저장하면 메모리를 훨씬 적게 사용합니다.
map()을 이용해 숫자를 한글 요일로 변환하는 방법 3가지입니다.
week_series = pd.Series([0, 1, 2, 3, 4, 5, 6])
def week_change(x):
if x == 0: return '일'
elif x == 1: return '월'
elif x == 2: return '화'
elif x == 3: return '수'
elif x == 4: return '목'
elif x == 5: return '금'
else: return '토'
week_series.map(week_change)
def week_change2(x):
week_list = ['일', '월', '화', '수', '목', '금', '토']
return week_list[x] # 숫자 → 인덱스로 활용
week_series.map(week_change2)
week_dict = {
0: '일', 1: '월', 2: '화',
3: '수', 4: '목', 5: '금', 6: '토'
}
week_series.map(week_dict)

map()은 딕셔너리와 함수를 모두 받을 수 있지만, 리스트는 받지 못합니다.
리스트 형태의 치환은 replace()를 사용하세요.
# map() — 딕셔너리 ✅, 함수 ✅, 리스트 ❌ (TypeError)
week_series.map(week_dict)
# replace() — 딕셔너리로 값 치환
week_series.replace(week_dict)
# 단순 두 값 치환 예시
gender_series = pd.Series(['f', 'm', 'f'])
gender_series.map(lambda x: 'female' if x == 'f' else 'male')
💡 한 줄 if문 (삼항 연산자)
참인 경우 if 조건식 else 거짓인 경우
리스트를 간결하게 생성하는 파이썬 문법입니다.
list_1 = [1, 2, 3, 4, 5, 6]
# for문
new_list = []
for i in list_1:
if i % 2 == 0:
new_list.append(i)
# 리스트 컴프리헨션 — 짝수만 추출
new_list = [x for x in list_1 if x % 2 == 0]
print(new_list) # [2, 4, 6]
# 홀수 → '홀수', 짝수 → '짝수'로 변환
new_list3 = ['홀수' if x % 2 == 1 else '짝수' for x in list_1]
print(new_list3) # ['홀수', '짝수', '홀수', '짝수', '홀수', '짝수']
# 3보다 큰 값만 추출 + 홀짝 변환
new_list4 = ['홀수' if x % 2 == 1 else '짝수' for x in list_1 if x > 3]
print(new_list4) # ['홀수', '짝수', '홀수']
| 형태 | 설명 |
|---|---|
[x for x in list] | 모든 원소 그대로 |
[x for x in list if 조건] | 조건을 만족하는 원소만 추출 |
[식 for x in list] | 모든 원소에 변환 적용 |
[참 if 조건 else 거짓 for x in list] | 조건에 따라 값 변환 |
[참 if 조건 else 거짓 for x in list if 조건2] | 필터링 + 값 변환 동시에 |
corona = pd.read_csv('../csv/corona.csv')
corona.head() # 상위 5개
corona.tail() # 하위 5개
corona.columns # 컬럼 목록 확인
corona.info() # 타입, 결측치 현황
corona.isna().sum() # 결측치 개수
corona.describe() # 수치형 통계 요약
# 문자형 컬럼 통계 요약
corona[['등록일시', '기준시간']].describe()
# 'Unnamed: 0' → 인덱스 역할이라 불필요
# 'seq' → 게시글 번호라 불필요
corona.drop(['Unnamed: 0', 'seq'], axis=1, inplace=True)
⚠️
inplace=True로 제거한 뒤 같은 셀을 다시 실행하면 에러 발생.
이미 제거된 컬럼을 또 제거하려 하기 때문입니다.
# 방법 1: rename() — 특정 컬럼만 변경할 때
corona.rename(columns={
'createDt': '등록일시',
'deathCnt': '총사망자',
'decideCnt': '총확진자',
'stateDt': '기준일',
'stateTime': '기준시간',
'updateDt': '수정일시',
'accExamCnt': '누적의심자',
'accDefRate': '누적확진율'
}, inplace=True)
# 방법 2: .columns 직접 대입 — 전체 컬럼을 한 번에 변경할 때
corona.columns = ['등록일시', '총사망자', '총확진자', '기준일',
'기준시간', '수정일시', '누적의심자', '누적확진율']
💡 방법 2는 컬럼 개수가 정확히 일치해야 하고, 변경 전 미리 확인이 어렵습니다.
일부만 바꿀 때는rename(), 전체 교체 시에는.columns대입이 편합니다.
# 등록일시 기준 오름차순 정렬
corona.sort_values('등록일시', ascending=True, inplace=True)
# 인덱스 초기화 — 기존 인덱스 제거
corona.reset_index(drop=True, inplace=True)
# 원본 백업
origin_data = corona.copy()


| 함수 | 설명 |
|---|---|
sort_values(컬럼, ascending=True) | 값 기준 오름차순 정렬 |
sort_values(컬럼, ascending=False) | 값 기준 내림차순 정렬 |
sort_index() | 인덱스 기준 정렬 |
reset_index(drop=True) | 인덱스 초기화 (기존 인덱스 제거) |
reset_index(drop=False) | 인덱스 초기화 (기존 인덱스를 컬럼으로 유지) |
데이터셋에 없는 컬럼이지만 기존 데이터로 계산해서 새 컬럼을 만드는 것을 파생변수라고 합니다.
일일확진자 = 오늘의 총확진자 - 어제의 총확진자
일일사망자 = 오늘의 총사망자 - 어제의 총사망자
new_list = []
for i in range(1, len(corona)):
result = corona.loc[i, '총확진자'] - corona.loc[i-1, '총확진자']
new_list.append(result)
# 첫날은 계산 불가 → 0으로 채우기
new_list.insert(0, 0)
# 또는: [0] + new_list
corona['일일확진자'] = new_list

for문보다 훨씬 간결하게 같은 결과를 낼 수 있습니다.
# shift(n) : 데이터를 n칸 아래로 이동 → 이전 행 값과 비교 가능
# 첫 번째 행은 이전 값이 없으므로 NaN 발생 → fillna(0)으로 처리
corona['일일사망자'] = (corona['총사망자'] - corona['총사망자'].shift(1)).fillna(0)
# diff(n) : n칸 이동한 값과의 차이를 한 번에 계산
# shift()를 이용한 빼기 연산과 결과 동일
corona['일일확진자'] = corona['총확진자'].diff(1).fillna(0)
💡
Series - Series.shift(1)과Series.diff(1)의 결과는 동일합니다.
diff()가 더 간결하므로 파생변수 생성 시 권장합니다.
# 일일확진자나 일일사망자가 음수인 행 필터링
flag = (corona[['일일사망자', '일일확진자']] < 0).any(axis=1)
corona.loc[flag, ]
음수가 발생한 원인을 추적하니 445번 인덱스의 등록일시가 잘못 입력되어 있었습니다.
# 오류 데이터 주변 확인
corona.loc[444:448, ]
# 오류 수정 — 특정 위치 데이터를 직접 대입
corona.loc[445, '등록일시'] = '2021-05-31 00:00:00.000'
# 1. 등록일시 기준 오름차순 재정렬
corona.sort_values('등록일시', ascending=True, inplace=True)
# 2. 인덱스 초기화
corona.reset_index(drop=True, inplace=True)
# 3. 기존 파생변수 컬럼 제거
corona.drop(['일일사망자', '일일확진자'], axis=1, inplace=True)
# 4. 파생변수 재생성
corona['일일사망자'] = (corona['총사망자'] - corona['총사망자'].shift(1)).fillna(0)
corona['일일확진자'] = corona['총확진자'].diff(1).fillna(0)
# 5. 음수 여부 재확인
flag = (corona[['일일확진자', '일일사망자']] < 0).any(axis=1)
corona.loc[flag, ] # 빈 결과 → 음수 없음 ✅
uriage = pd.read_csv('../csv/uriage.csv')
uriage.info()
uriage.isna().sum()
item_name 컬럼에 두 가지 문제가 있습니다.
| 문제 | 예시 | 해결 방법 |
|---|---|---|
| 대소문자 혼재 | itemA, ITEMA | str.upper() 또는 str.lower() |
| 문자 사이 공백 | item A, itemA | str.replace(' ', '') |
⚠️
strip()은 문자열 앞뒤 공백만 제거합니다.
문자 사이의 공백은replace(' ', '')를 사용해야 합니다.
df = uriage.copy()
for i in range(len(df)):
df.iloc[i, 1] = df.iloc[i, 1].replace(' ', '')
df['item_name'] = df['item_name'].map(lambda x: x.replace(' ', ''))
# 공백 제거 + 대문자 통일을 한 번에
uriage['item_name'] = uriage['item_name'].str.upper().str.replace(' ', '')
💡
str메서드는 체이닝(.str.upper().str.replace())이 가능합니다.
단,.str접근자를 각 메서드마다 붙여야 합니다.
# 정형화 결과 확인
uriage['item_name'].unique() # 고유값 확인
uriage['item_name'].value_counts() # 빈도수 확인

item_price의 결측치를 같은 상품의 평균가격으로 채웁니다.
flag_A = uriage['item_name'] == '상품A'
flag_null = uriage['item_price'].isna()
# 상품A이고 가격이 있는 데이터의 평균
mean_price = uriage.loc[flag_A & ~flag_null, 'item_price'].mean()
# 상품A이고 가격이 결측치인 데이터에 평균 대입
uriage.loc[flag_A & flag_null, 'item_price'] = uriage.loc[
flag_A & flag_null, 'item_price'
].fillna(mean_price)
item_list = uriage['item_name'].unique() # 전체 상품 목록
flag_null = uriage['item_price'].isna() # 결측치 조건 (공통)
for item_name in item_list:
flag = uriage['item_name'] == item_name
mean_price = uriage.loc[flag & ~flag_null, 'item_price'].mean()
uriage.loc[flag & flag_null, 'item_price'] = mean_price
# 결측치 확인
uriage['item_price'].isna().sum() # 0 → 결측치 없음 ✅

for i in range(len(item_list)):
flag = uriage['item_name'] == item_list[i]
mean_price = uriage.loc[flag, 'item_price'].mean()
print(f'{item_list[i]} : {mean_price}')

| 함수/문법 | 설명 |
|---|---|
value_counts() | 값별 빈도수 확인 |
unique() | 고유값 목록 확인 |
Series.map(func) | 각 원소에 함수 적용 |
Series.map(dict) | 딕셔너리로 값 치환 |
Series.str.메서드 | Series에서 문자열 함수 접근 |
map(na_action='ignore') | 결측치는 건너뛰고 함수 적용 |
replace(dict) | 딕셔너리로 값 치환 |
sort_values() | 값 기준 정렬 |
reset_index(drop=True) | 인덱스 초기화 |
shift(n) | 데이터를 n칸 이동 |
diff(n) | n칸 이전 값과의 차이 계산 |
[... for ... if ...] | 리스트 컴프리헨션 |