오늘은 2주 차 마지막 날이다. 오늘까지 데이터 전처리를 마무리하고 다음 주 월요일에 시각화에 대해 배우면 이번 파트도 끝이 나는 것 같다.
’yyyy-mm-dd’apt['계약일자'] = apt['계약년도'] + '-' + apt['계약월'] + '-' + apt['계약일']
‘datetime64[ns]’apt['계약일자'] = apt['계약일자'].astype('datetime64[ns]')
| 구분 | 상세 내용 | 구분 | 상세내용 |
|---|---|---|---|
sr.dt.year | sr의 원소에서 년(year)을 정수형 시리즈로 반환 | sr.dt.month | sr의 원소에서 월(month)을 정수형 시리즈로 반환 |
sr.dt.day | sr의 원소에서 일(day)을 정수형 시리즈로 반환 | sr.dt.hour | sr의 원소에서 시(hour)을 정수형 시리즈로 반환 |
sr.dt.minute | sr의 원소에서 분(minute)을 정수형 시리즈로 반환 | sr.dt.second | sr의 원소에서 초(second)을 정수형 시리즈로 반환 |
sr.dt.dayofweek | sr의 원소에서 요일을 정수형 시리즈로 반환(월: 0, 일: 6) | sr.dt.day_name() | sr의 원소에서 요일을 문자열 시리즈로 반환(로케일에 따라 다름) |
sr.dt.strftime(date_format) | sr의 원소에서 지정한 날짜시간 포맷의 문자열 시리즈로 반환 |
| 구분 | 상세 내용 | 예시 | 구분 | 상세 내용 | 예시 |
|---|---|---|---|---|---|
%Y | 연도(4자리) | ‘2025’ | %p | AM/PM 표기 | ‘AM’ |
%y | 연도(2자리) | ‘25’ | %M | 분(정수, 00!59) | ‘02’ |
%m | 월(정수, 01~12) | ‘12’ | %S | 초(정수, 00~59) | ‘’03 |
%B | 월(Locale 전체) | ‘December’ | %f | 백만 분의 1초 | ‘456789’ |
%b | 월(Locale 단축) | ‘Dec’ | %s | 유닉스 시간 | ‘1703952123’ |
%d | 일(정수, 01~31) | ‘31’ | %W | 연중 주의 순번(00~52) | ‘52’ |
%j | 연중 일의 순번(001~366) | ‘265’ | %w | 주중 요일의 순번(0~6) | ‘0’ |
%H | 시(24시간, 00~23) | ‘22’ | %A | 요일(Locale 전체) | ‘Sunday’ |
%I | 시(12시간, 01~12) | ‘10’ | %a | 요일(Locale 단축) | ‘Sun’ |
dt.day_of_weekapt['계약일자'].dt.day_of_week.head()
# 0 4
# 1 0
# 2 1
# 3 5
# 4 6
# Name: 계약일자, dtype: int32
dt.day_nameapt['계약일자'].dt.day_name().head()
# 0 Friday
# 1 Monday
# 2 Tuesday
# 3 Saturday
# 4 Sunday
# Name: 계약일자, dtype: object
locale 지정apt['계약일자'].dt.day_name(locale='ko_kr').head()
# 0 금요일
# 1 월요일
# 2 화요일
# 3 토요일
# 4 일요일
# Name: 계약일자, dtype: object
dt.strftimeapt['계약일자'].dt.strftime('%Y년 %m월 %d일 %a').head()
# 0 2020년 01월 31일 금
# 1 2020년 01월 06일 월
# 2 2020년 01월 14일 화
# 3 2020년 01월 04일 토
# 4 2020년 01월 12일 일
# Name: 계약일자, dtype: object
value_countsapt['계약일자'].dt.day_name(locale='ko_kr').value_counts()
# 계약일자
# 토요일 57664
# 월요일 33447
# 금요일 33338
# 화요일 32698
# 수요일 32663
# 목요일 31074
# 일요일 11020
# Name: count, dtype: int64
normalize 설정(%로 확인 가능)apt['계약일자'].dt.day_name(locale='ko_kr').value_counts(normalize=True) * 100
# 계약일자
# 토요일 24.865462
# 월요일 14.422778
# 금요일 14.375776
# 화요일 14.099800
# 수요일 14.084707
# 목요일 13.399510
# 일요일 4.751966
# Name: proportion, dtype: float64
dt1 = apt['계약일자'].dt.day_of_week.astype(str)
dt2 = apt['계약일자'].dt.day_name(locale='ko_kr')
(dt1 + '-' + dt2).value_counts().sort_index()
# 계약일자
# 0-월요일 33447
# 1-화요일 32698
# 2-수요일 32663
# 3-목요일 31074
# 4-금요일 33338
# 5-토요일 57664
# 6-일요일 11020
# Name: count, dtype: int64
| 구분 | 로케일 이름 | 인코딩 방식 |
|---|---|---|
| 대한민국 | ko_KR | UTF-8 |
| 미국 | en_US | UTF-8 |
| 중국 | zh_CN | UTF-8 |
| 일본 | ja_JP | UTF-8 |
| 기본값 | C | ASCII |
import locale
locale.getlocale(category=locale.LC_TIME)
locale.setlocale(category=locale.LC_TIME, locale='ko_KR')
apt['계약일자'].dt.strftime('%Y년 %m월 %d일 %A').head()
# 0 2020년 01월 31일 금요일
# 1 2020년 01월 06일 월요일
# 2 2020년 01월 14일 화요일
# 3 2020년 01월 04일 토요일
# 4 2020년 01월 12일 일요일
# Name: 계약일자, dtype: object
apt['처리일수'] = apt['등기일자'] - apt['계약일자']
apt['처리일수'].tail()
# 231899 22 days
# 231900 35 days
# 231901 2 days
# 231902 NaT
# 231903 NaT
# Name: 처리일수, dtype: timedelta64[ns]
apt['처리일수'].dt.days.tail()
# 231899 22.0
# 231900 35.0
# 231901 2.0
# 231902 NaN
# 231903 NaN
# Name: 처리일수, dtype: float64
apt['처리일수'].dt.days.mean()
# np.float64(78.1928333438109)
apt['계약일자'].iloc[0].timestamp()
# 1580428800.0
birth = pd.to_datetime(arg='1999년 8월 17일', format='%Y년 %m월 %d일')
birth
# Timestamp('1999-08-17 00:00:00')
today = pd.Timestamp.today().normalize()
today
# Timestamp('2026-01-09 00:00:00')
freq 매개변수에 간격을 문자열로 지정('D', 'ME', 'MS')pd.date_range(start='2026-01-01', periods=10, freq='MS')
# DatetimeIndex(['2026-01-01', '2026-02-01', '2026-03-01', '2026-04-01',
# '2026-05-01', '2026-06-01', '2026-07-01', '2026-08-01',
# '2026-09-01', '2026-10-01'],
# dtype='datetime64[ns]', freq='MS')
pd.date_range(start='2026-01-01', periods=10, freq='MS')
# DatetimeIndex(['2026-01-01', '2026-02-01', '2026-03-01', '2026-04-01',
# '2026-05-01', '2026-06-01', '2026-07-01', '2026-08-01',
# '2026-09-01', '2026-10-01'],
# dtype='datetime64[ns]', freq='MS')
shift , diff , pct_change , rollingsr = pd.Series(data=values, index=dates, name='price')
sr
# 2026-01-01 87
# 2026-01-02 93
# 2026-01-03 62
# 2026-01-04 58
# 2026-01-05 59
# 2026-01-06 61
# 2026-01-07 55
# 2026-01-08 65
# 2026-01-09 50
# 2026-01-10 66
# Freq: D, Name: price, dtype: int64
sr.shift(peridos=1) : 이전 시점이로 1칸 이동-1 입력 시 미래로 1칸sr.shift()
# 2026-01-01 NaN
# 2026-01-02 87.0
# 2026-01-03 93.0
# 2026-01-04 62.0
# 2026-01-05 58.0
# 2026-01-06 59.0
# 2026-01-07 61.0
# 2026-01-08 55.0
# 2026-01-09 65.0
# 2026-01-10 50.0
# Freq: D, Name: price, dtype: float64
sr.shift(-1)
# 2026-01-01 93.0
# 2026-01-02 62.0
# 2026-01-03 58.0
# 2026-01-04 59.0
# 2026-01-05 61.0
# 2026-01-06 55.0
# 2026-01-07 65.0
# 2026-01-08 50.0
# 2026-01-09 66.0
# 2026-01-10 NaN
# Freq: D, Name: price, dtype: float64
sr.diff(periods=1) : 원소간 차이를 계산sr.diff()
# 2026-01-01 NaN
# 2026-01-02 6.0
# 2026-01-03 -31.0
# 2026-01-04 -4.0
# 2026-01-05 1.0
# 2026-01-06 2.0
# 2026-01-07 -6.0
# 2026-01-08 10.0
# 2026-01-09 -15.0
# 2026-01-10 16.0
# Freq: D, Name: price, dtype: float64
sr.pct_change(periods=1) : 전일 대비 증감률 계산sr.pct_change() * 100
# 2026-01-01 NaN
# 2026-01-02 6.896552
# 2026-01-03 -33.333333
# 2026-01-04 -6.451613
# 2026-01-05 1.724138
# 2026-01-06 3.389831
# 2026-01-07 -9.836066
# 2026-01-08 18.181818
# 2026-01-09 -23.076923
# 2026-01-10 32.000000
# Freq: D, Name: price, dtype: float64
sr.rolling(window=5).mean() : 5일 이동평균 계산min_periods=1 속성으로 최소 기간 지정 가능(기본값=None)2026-01-01 87.000000
2026-01-02 90.000000
2026-01-03 80.666667
2026-01-04 75.000000
2026-01-05 71.800000
2026-01-06 66.600000
2026-01-07 59.000000
2026-01-08 59.600000
2026-01-09 58.000000
2026-01-10 59.400000
Freq: D, Name: price, dtype: float64
errors 매개변수에 ‘coerce’ 지정 시 수치형으로 변환할 수 없는 문자열을 결측값으로 변환pd.to_numeric(arg=ages, errors='coerce')
# 0 24.0
# 1 31.0
# 2 29.0
# 3 43.0
# 4 NaN
# dtype: float64
# ['구축', '준신축', '신축']
ctgry = pd.Series(data=gbn).astype('category')
ctgry
# 0 구축
# 1 준신축
# 2 신축
# dtype: category
# Categories (3, object): ['구축', '신축', '준신축']
ctgry.cat.codes
# 0 0
# 1 2
# 2 1
# dtype: int8
ordered=True : 서열형ctgry.cat.set_categories(new_categories=['구축', '준신축', '신축'], ordered=True)
# 0 구축
# 1 준신축
# 2 신축
# dtype: category
# Categories (3, object): ['구축' < '준신축' < '신축']
applyaxis 생략 시 열 시리즈 선택axis 1 지정 시 행 시리즈 선택func 에 실행할 함수 지정apt['단지명'].head()
# 0 삼익대청아파트
# 1 개포자이
# 2 개포주공6단지
# 3 디에이치아너힐즈
# 4 개포주공7단지
# Name: 단지명, dtype: object
apt['단지명'].apply(func=len).head()
# 0 7
# 1 4
# 2 7
# 3 8
# 4 7
# Name: 단지명, dtype: int64
apply 또는 map 사용cols = ['시도명', '시군구', '법정동', '지번']
apt[cols].shape
# (231904, 4)
apt[cols].apply(func=len)
# 시도명 231904
# 시군구 231904
# 법정동 231904
# 지번 231904
# dtype: int64
apt[cols].apply(func=len, axis=1).tail()
# 231899 4
# 231900 4
# 231901 4
# 231902 4
# 231903 4
# dtype: int64
map 사용apt[cols].map(func=len).tail()
# 시도명 시군구 법정동 지번
# 231899 5 3 3 3
# 231900 5 3 3 3
# 231901 5 3 3 3
# 231902 5 3 3 3
# 231903 5 3 3 5
statistic : 상관계수coef = lambda x: pearsonr(x=x, y=iris['petal_width']).statistic
iris.apply(func=coef, axis=0)
# sepal_length 0.817941
# sepal_width -0.366126
# petal_length 0.962865
# petal_width 1.000000
# dtype: float64
pvalue : 유희확률(귀무가설을 지지하는 정도(유의수준 0.05 기준으로 판단))corr = lambda x: pearsonr(x=x, y=iris['petal_width']).pvalue
iris.apply(func=corr, axis=0).lt(0.05)
# sepal_length True
# sepal_width True
# petal_length True
# petal_width True
# dtype: bool
join_str = lambda x: ' '.join(x)
apt[cols].apply(func=join_str, axis=1).head()
# 0 서울특별시 강남구 개포동 12
# 1 서울특별시 강남구 개포동 12-2
# 2 서울특별시 강남구 개포동 185
# 3 서울특별시 강남구 개포동 1281
# 4 서울특별시 강남구 개포동 185
# dtype: object
isna() : 시리즈의 원소별 결측값 여부 반환apt.isna().sum()
# 거래금액 0
# 단지명 0
# 시도명 0
# 시군구 0
# 법정동 0
# 지번 0
# 입주년도 0
# 계약년도 0
# 계약월 0
# 계약일 0
# 등기일자 152369
# 전용면적 0
# 층 0
# 평당금액 0
# 경과년수 0
# 재건축 0
# 계약일자 0
# 처리일수 152369
# 주소 0
# dtype: int64
apt.isna().mean() * 100
# 거래금액 0.000000
# 단지명 0.000000
# 시도명 0.000000
# 시군구 0.000000
# 법정동 0.000000
# 지번 0.000000
# 입주년도 0.000000
# 계약년도 0.000000
# 계약월 0.000000
# 계약일 0.000000
# 등기일자 65.703481
# 전용면적 0.000000
# 층 0.000000
# 평당금액 0.000000
# 경과년수 0.000000
# 재건축 0.000000
# 계약일자 0.000000
# 처리일수 65.703481
# 주소 0.000000
# dtype: float64
단순대체는 분산을 인위적으로 감소시키고 변수 간 상관관계를 왜곡할 수 있어 추론과 예측 성능에 영향을 미칠 수 있음
fillna()apt['등기일자'].fillna('1900-01-01').head()
days_mean = apt['처리일수'].dt.days.mean()
apt['등기일자'].fillna(apt['계약일자'] + pd.Timedelta(days_mean, unit='D')).head()
# 0 2020-04-18 04:37:40.800905261
# 1 2020-03-24 04:37:40.800905261
# 2 2020-04-01 04:37:40.800905261
# 3 2020-03-22 04:37:40.800905261
# 4 2020-03-30 04:37:40.800905261
# Name: 등기일자, dtype: datetime64[ns]
dropna()axis=1 지정 시 결측이 있는 열을 모두 삭제ffill() , interpolate()ffill() : 결측값을 이전 셀 값으로 채움na = pd.read_excel('NA_Sample.xlsx')
na
# 구분 항목 값
# 0 A a 1.2
# 1 NaN b NaN
# 2 NaN c 3.5
# 3 NaN d 4.2
# 4 B a 1.7
# 5 NaN b 2.6
# 6 NaN c NaN
# 7 NaN d 3.6
interpolate() : 결측값을 선형 보간법으로 채움na['구분'] = na['구분'].ffill()
na
# 구분 항목 값
# 0 A a 1.2
# 1 A b NaN
# 2 A c 3.5
# 3 A d 4.2
# 4 B a 1.7
# 5 B b 2.6
# 6 B c NaN
# 7 B d 3.6
transform() : 원본과 같은 길이 반환na['값'].fillna(na.groupby(by='구분')['값'].transform(func='mean'))
# 0 1.200000
# 1 2.966667
# 2 3.500000
# 3 4.200000
# 4 1.700000
# 5 2.600000
# 6 2.633333
# 7 3.600000
# Name: 값, dtype: float64
df1.columns.equals(df2.columns)
# True
ignore_index=True : 인덱스 번호 초기화pd.concat([df1, df2], ignore_index=True)
# 거래금액 단지명 시도명 시군구 법정동 지번
# 0 10.05 삼익대청아파트 서울특별시 강남구 개포동 12
# 1 20.30 개포자이 서울특별시 강남구 개포동 12-2
# 2 18.00 개포주공6단지 서울특별시 강남구 개포동 185
# 3 28.50 디에이치아너힐즈 서울특별시 강남구 개포동 1281
# 4 17.10 개포주공7단지 서울특별시 강남구 개포동 185
# 5 7.40 한신아파트상가동유치원동(102~102) 서울특별시 중랑구 중화동 450
# 6 5.05 삼익아파트.상가동 서울특별시 중랑구 중화동 438
# 7 6.00 한신아파트상가동유치원동(103~109) 서울특별시 중랑구 중화동 450
# 8 6.70 한신아파트상가동유치원동(102~102) 서울특별시 중랑구 중화동 450
# 9 1.10 범양프레체 서울특별시 중랑구 중화동 208-4
pd.concat([df1, df2], ignore_index=True)
# 거래금액 단지명 시도명 시군구 법정동 지번 아파트단지명
# 0 10.05 삼익대청아파트 서울특별시 강남구 개포동 12 NaN
# 1 20.30 개포자이 서울특별시 강남구 개포동 12-2 NaN
# 2 18.00 개포주공6단지 서울특별시 강남구 개포동 185 NaN
# 3 28.50 디에이치아너힐즈 서울특별시 강남구 개포동 1281 NaN
# 4 17.10 개포주공7단지 서울특별시 강남구 개포동 185 NaN
# 5 7.40 NaN 서울특별시 중랑구 중화동 450 한신아파트상가동유치원동(102~102)
# 6 5.05 NaN 서울특별시 중랑구 중화동 438 삼익아파트.상가동
# 7 6.00 NaN 서울특별시 중랑구 중화동 450 한신아파트상가동유치원동(103~109)
# 8 6.70 NaN 서울특별시 중랑구 중화동 450 한신아파트상가동유치원동(102~102)
# 9 1.10 NaN 서울특별시 중랑구 중화동 208-4 범양프레체
reset_index(drop=True) : 인덱스 초기화로 행이름 통일ignore_index=True 지정하면 컬럼명 초기화pd.concat([df1, df2], axis=1)
# 거래금액 단지명 시도명 시군구 법정동 지번 거래금액 아파트단지명 시도명 시군구 법정동 지번
# 0 10.05 삼익대청아파트 서울특별시 강남구 개포동 12 NaN NaN NaN NaN NaN NaN
# 1 20.30 개포자이 서울특별시 강남구 개포동 12-2 NaN NaN NaN NaN NaN NaN
# 2 18.00 개포주공6단지 서울특별시 강남구 개포동 185 NaN NaN NaN NaN NaN NaN
# 3 28.50 디에이치아너힐즈 서울특별시 강남구 개포동 1281 NaN NaN NaN NaN NaN NaN
# 4 17.10 개포주공7단지 서울특별시 강남구 개포동 185 NaN NaN NaN NaN NaN NaN
# 220885 NaN NaN NaN NaN NaN NaN 7.40 한신아파트상가동유치원동(102~102) 서울특별시 중랑구 중화동 450
# 220886 NaN NaN NaN NaN NaN NaN 5.05 삼익아파트.상가동 서울특별시 중랑구 중화동 438
# 220887 NaN NaN NaN NaN NaN NaN 6.00 한신아파트상가동유치원동(103~109) 서울특별시 중랑구 중화동 450
# 220888 NaN NaN NaN NaN NaN NaN 6.70 한신아파트상가동유치원동(102~102) 서울특별시 중랑구 중화동 450
# 220889 NaN NaN NaN NaN NaN NaN 1.10 범양프레체 서울특별시 중랑구 중화동 208-4
pd.concat([df1, df2], axis=1)
# 거래금액 단지명 시도명 시군구 법정동 지번 거래금액 아파트단지명 시도명 시군구 법정동 지번
# 0 10.05 삼익대청아파트 서울특별시 강남구 개포동 12 7.40 한신아파트상가동유치원동(102~102) 서울특별시 중랑구 중화동 450
# 1 20.30 개포자이 서울특별시 강남구 개포동 12-2 5.05 삼익아파트.상가동 서울특별시 중랑구 중화동 438
# 2 18.00 개포주공6단지 서울특별시 강남구 개포동 185 6.00 한신아파트상가동유치원동(103~109) 서울특별시 중랑구 중화동 450
# 3 28.50 디에이치아너힐즈 서울특별시 강남구 개포동 1281 6.70 한신아파트상가동유치원동(102~102) 서울특별시 중랑구 중화동 450
# 4 17.10 개포주공7단지 서울특별시 강남구 개포동 185 1.10 범양프레체 서울특별시 중랑구 중화동 208-4
두 외래키에서 서로 일치하는 원소가 있는가?
오른쪽 외래키에 중복된 원소가 있는가?
duplicated() , drop_duplicates()duplicated() : 중복 여부를 True False로 반환keep='first' : 중복 건에 대해 처음 나오는 원소는 False 나머지는 Truekeep='last' : 중복 건에 대해 마지막에 나오는 원소는 False 나머지는 Truekeep=False를 지정하면 모든 중복 건에 대해 True 지정subset 매개변수에 열이름 리스트를 지정하면 해당 컬럼을 기준으로 중복 여부 확인apt['주소'].duplicated(keep=False).sum()
apt.duplicated(subset='주소', keep=False).sum()
# np.int64(220141)
drop_duplicates() : 중복인 값 제거keep='first' : 중복이 아닌 값은 남기고, 중복이면 처음 나온 값만 남김keep='last' : 중복이 아닌 값은 남기고, 중복이면 마지막에 나온 값만 남김keep=False : 중복이 아닌 값은 남기고, 중복이면 모두 제거apt.drop_duplicates(subset='주소', keep='first')
두 데이터프레임의 외래키에 중복 건이 있으면 병합 결과 행 개수가 늘어남
apt.shape # (231904, 17)
pd.merge(left=apt, right=dtl, how='left', on='주소').shape # (235991, 28)
dups = dtl['주소'].duplicated(keep=False)
dtl.loc[dups, :]
dtl = dtl.drop_duplicates(subset='주소')
how : 병합 방법 (inner, outer, left)on : 외래키의 열이름 지정, 두 개 이상일 때는 리스트로 지정cols = ['주소', '세대수', '주차대수']
apt = pd.merge(left=apt, right=dtl[cols], how='inner', on='주소')
melt()id_vars : 기준 변수명 리스트 지정value_vars : 행 방향으로 변환할 변수명 리스트 지정ignore_index=False : 인덱스 초기화 Falseelong = widen.melt(
id_vars=id_vars,
value_vars=value_vars,
ignore_index=False
)
elong.sort_index()
# 단지명 variable value
# 0 목동휘버스 세대수 24
# 0 목동휘버스 주차대수 28
# 1 북가좌삼호 세대수 616
# 1 북가좌삼호 주차대수 749
# 2 하이츠 세대수 24
# 2 하이츠 주차대수 50
pivot()index : 기준 변수명 리스트 지정columns : 열 방향으로 펼칠 변수명 지정values : 값으로 채울 변수명 지정widen = elong.pivot(
index=id_vars,
columns='variable',
values='value'
)
widen
# variable 세대수 주차대수
# 단지명
# 목동휘버스 24 28
# 북가좌삼호 616 749
# 하이츠 24 50
widen.index
# Index(['목동휘버스', '북가좌삼호', '하이츠'], dtype='object', name='단지명')
widen.columns
# Index(['세대수', '주차대수'], dtype='object', name='variable')
reset_index 로 index.name 초기화widen = widen.reset_index()
# variable 단지명 세대수 주차대수
# 0 목동휘버스 24 28
# 1 북가좌삼호 616 749
# 2 하이츠 24 50
widen.columns.name # 'variable'
widen.columns.name = ''
widen.columns # Index(['단지명', '세대수', '주차대수'], dtype='object', name='')
widen.columns.name = None
widen.columns # Index(['단지명', '세대수', '주차대수'], dtype='object')
매일 느끼는 거지만 진도가 상당히 빠른 것 같다. 하지만 전부 중요한 내용들이고 많은 도움이 되는 내용들이라 하나도 놓칠 수 없을 것 같다. 주말간 복습도 꾸준히 하면서 더 익숙해져야겠다.