데이터 분석을 위한 SQL 레시피 - 3장 데이터 가공을 위한 SQL
Contents
🗄️ 데이터분석을 위한 SQL 레시피 책을 읽고 정리 / 요약 한 내용입니다.
데이터를 가공해야 하는 이유
- 다룰 데이터가 데이터 분석 용도로 상정되지 않은 경우
- 연산할 때 비교 가능한 상태를 만들고 오류를 회피하기 위한 경우
5. 하나의 값 조작하기
5.1 코드 값을 레이블로 변경하기
디폴트 값을 지정할 경우 ELSE 구문을 사용한다.
|
|
5.2 URL 요소 추출하기
접근 로그 테이블
stamp | referrer | url |
---|---|---|
2016-08-26 12:02:00 | http://www.other.com/path1/index.php?k1=v1&k2=v2#Ref1 | http://www.example.com/video/detail?id=001 |
2016-08-26 12:02:01 | http://www.other.net/path1/index.php?k1=v1&k2=v2#Ref1 | http://www.example.com/video#ref |
2016-08-26 12:02:01 | https://www.other.com/ | http://www.example.com/book/detail?id=002 |
레퍼러로 어떤 웹 페이지를 거쳐 넘어왔는지 판별하기
|
|
실행결과
stamp | referrer_host |
---|---|
2016-08-26 12:02:00 | www.other.com |
2016-08-26 12:02:01 | www.other.net |
2016-08-26 12:02:01 | www.other.com |
URL에서 경로와 요청 매개변수 값 추출하기
|
|
실행결과
stamp | url | path | id |
---|---|---|---|
2016-08-26 12:02:00 | http://www.example.com/video/detail?id=001 | /video/detail | 001 |
2016-08-26 12:02:01 | http://www.example.com/video#ref | /video | null |
2016-08-26 12:02:01 | http://www.example.com/book/detail?id=002 | /book/detail | 002 |
5.3 문자열을 배열로 분해하기
- 빅데이터 분석시 가장 많이 사용되는 자료형은 문자열
- 문자열 자료형은 범용적이므로 세부적으로 분해해서 사용하는 경우가 많음
|
|
실행결과
stamp | url | path1 | path2 |
---|---|---|---|
2016-08-26 12:02:00 | http://www.example.com/video/detail?id=001 | video | detail |
2016-08-26 12:02:01 | http://www.example.com/video#ref | video | |
2016-08-26 12:02:01 | http://www.example.com/book/detail?id=002 | book | detail |
Redshift
는 배열자료형을 지원하지 않음BigQuery
는 배열의 인덱스를 0부터 시작하려면OFFSET
, 1부터 시작하려면ORDINAL
을 지정- 배열 길이 이상의 인덱스에 접근하면 오류를 리턴하며 NULL을 리턴하고 싶은 경우
SAFE_OFFSET
또는SAFE_ORDINAL
을 지정
- 배열 길이 이상의 인덱스에 접근하면 오류를 리턴하며 NULL을 리턴하고 싶은 경우
5.4 날짜와 타임스탬프 다루기
- 날짜와 시간 정보는 로그 데이터에서 빠지지 않는 정보
- 타임존을 고려해야 하고 미들웨어 차이를 주의
현재 날짜와 타임스탬프 추출하기
PostgreSQL
을 제외한 미들웨어는 타임존 없는 타임스템프 리턴BigQuery
는 UTC로 리턴됨
|
|
실행결과
dt | stamp |
---|---|
2023-11-21 | 2023-11-21 11:27:01.452351 +00:00 |
지정한 값의 날짜/시각 데이터 추출
- 문자열로 지정한 날짜와 시각은 CAST 함수를 사용하는 방법이 범용적
|
|
실행결과
dt | stamp |
---|---|
2016-01-30 | 2016-01-30 12:00:00.000000 |
날짜/시각에서 특정 필드 추가하기
|
|
실행결과
stamp | year | month | day | hour |
---|---|---|---|---|
2016-01-30 12:00:00.000000 | 2016 | 1 | 30 | 12 |
|
|
실행결과
stamp | year | month | day | hour | year_month |
---|---|---|---|---|---|
2016-01-30 12:00:00 | 2016 | 01 | 30 | 12 | 2016-01 |
5.5 결손 값을 디폴트 값으로 대치하기
- 문자열과 숫자를 다룰때는 NULL 값에 주의
- 처리 대상 데이터가 원하는 형태가 아닐 경우 반드시 데이터를 가공
쿠폰 사용 여부가 함께 있는 구매로그 테이블
purchase_id | amount | coupon |
---|---|---|
10001 | 3280 | null |
10002 | 4650 | 500 |
10003 | 3870 | null |
|
|
실행결과
purchase_id | amount | coupon | discount_amount | discount_amount2 |
---|---|---|---|---|
10001 | 3280 | null | null | 3280 |
10002 | 4650 | 500 | 4150 | 4150 |
10003 | 3870 | null | null | 3870 |
6. 여러 개의 값에 대한 조작
새로운 지표 정의하기
- 여러 지표를 기반으로 새로운 지표를 정의 할 수 있음
페이지 뷰
/방문자 수
=사용자 한 명이 페이지를 몇 번이나 방문 했는가?
- 방문한 사용자 중 특정 행동(클릭 또는 구매 등)을 한 사용자의 비율
- CTR(Click Through Rate) : 클릭율
- CVR(Conversion Rate) : 전환율
6.1 문자열 연결하기
사용자 주소 정보 테이블
user_id | pref_name | city_name |
---|---|---|
U001 | 서울특별시 | 강서구 |
U002 | 경기도수원시 | 장안구 |
U003 | 제주특별자치도 | 서귀포시 |
문자열을 연결하는 쿼리
|
|
실행결과
user_id | perf_city | pref_city2 |
---|---|---|
U001 | 서울특별시강서구 | 서울특별시강서구 |
U002 | 경기도수원시장안구 | 경기도수원시장안구 |
U003 | 제주특별자치도서귀포시 | 제주특별자치도서귀포시 |
6.2 여러 개의 값 비교하기
4분기 매출 테이블
year | q1 | q2 | q3 | q4 |
---|---|---|---|---|
2015 | 82000 | 83000 | 78000 | 83000 |
2016 | 85000 | 85000 | 80000 | 81000 |
2017 | 92000 | 81000 | null | null |
분기별 매출 증감 판정하기
SIGN
: 매개변수 양수이면 1, 0이면 0, 음수 -1을 리턴하는 함수
|
|
실행결과
year | q1 | q2 | judge_q1_q2 | diff_q2_q1 | sign_q1_q2 |
---|---|---|---|---|---|
2015 | 82000 | 83000 | + | 1000 | 1 |
2016 | 85000 | 85000 | 0 | 0 | |
2017 | 92000 | 81000 | - | -11000 | -1 |
연간 최대/최소 4분기 매출 찾기
- 여러 컬럼에서 최댓값, 최솟값 찾기 : greatest, least
- greatest, least 함수 둘다 SQL 표준에는 포함되지 않지만 대부분의 SQL 쿼리 엔진에서 구현
|
|
실행결과
year | greatest_sales | least_sales |
---|---|---|
2015 | 83000 | 78000 |
2016 | 85000 | 80000 |
2017 | 92000 | 81000 |
연간 평균 4분기 매출 계산하기
단순한 연산으로 평균 4분기 매출을 구하는 쿼리
- NULL 값에 대해
COALESCE
함수를 사용해 적절한 값으로 변환
|
|
실행결과
year | average |
---|---|
2015 | 81500 |
2016 | 82750 |
2017 | null |
COALESCE를 사용해 NULL을 0으로 변환하고 평균값을 구하는 쿼리
- q3, q4를 매출을 0으로 변환하면 q1, q2 의 매출 합계를 4로 나누게 되어 평균값이 낮아짐
|
|
실행결과
year | average |
---|---|
2015 | 81500 |
2016 | 82750 |
2017 | 43250 |
NULL이 아닌 컬럼만 사용해서 평균값을 구한는 쿼리
- 2017년의 q1, q2의 매출로만 평균을 구하려면 NULL 이 아닌 컬럼의 수를 세서 나눠야함
- COALESCE, SIGN 함수를 사용하여 NULL이 아닌 컬럼의 수를 셀 수 있다.
|
|
실행결과
year | average3 |
---|---|
2015 | 81500 |
2016 | 82750 |
2017 | 86500 |
6.3 2개의 값 비율 계산하기
광고 통계 정보 테이블
dt | ad_id | impressions | clicks |
---|---|---|---|
2017-04-01 | 001 | 100000 | 3000 |
2017-04-01 | 002 | 120000 | 1200 |
2017-04-01 | 003 | 500000 | 10000 |
2017-04-02 | 001 | 0 | 0 |
2017-04-02 | 002 | 130000 | 1400 |
2017-04-02 | 003 | 620000 | 15000 |
정수 자료형의 데이터 나누기
- 데이터에서 각 광고의 CTR (클릭수 / 노출 수)을 계산
- 실수를 상수로 앞에 두고 계산하면, 암묵적으로 자료형 변환
- 나는 해당 결과가 실수가 안나옴
|
|
실행결과
year | average3 |
---|---|
2015 | 81500 |
2016 | 82750 |
2017 | 86500 |
0 으로 나누는 것 피하기
- NULL 전파를 사용하면 0으로 나누는 것을 피할 수 있다.
- NULL 전파란 연산 결과 모두가 NULL이 되는 SQL 성질
NULLIF(impressions, 0)
: impressions 값이 0이면 NULL로 처리
|
|
6.4 두 값의 거리 계산하기
- 데이터 분석 분야에서는 물리적 공간 길이가 아닌 거리라는 개념이 많이 등장
- 시험을 보았을 때 평균에서 어느정도 떨어져 있는가?
- 작년 매출과 올해 매출에 어느 정도의 차이가 있는가
- 어떤 사용자가 있을 때, 해당 사용자와 구매 경향이 비슷한 사용자를 뽑을 때도 사용
숫자 데이터의 절대값, 제곱 평균 제곱근(RMS) 계산하기
일차원 위치 정보 테이블
x1 | x2 |
---|---|
5 | 10 |
10 | 5 |
-2 | 4 |
3 | 3 |
0 | 1 |
- x1, x2의 거리를 절댓값을 사용
- 평균 제곱근 : 두 값의 차이를 제곱한 뒤 제곱근을 적용
- ABS : 절대값 계산
- POWER : 제곱 계산
- SQRT : 제곱근 계산
|
|
실행결과
abs | rms |
---|---|
5 | 5 |
5 | 5 |
6 | 6 |
0 | 0 |
1 | 1 |
xy 평면 위에 있는 두 점의 유클리드 거리 계산하기
이차원 위치 정보 테이블
x1 | y1 | x2 | y2 |
---|---|---|---|
0 | 0 | 2 | 2 |
3 | 5 | 1 | 2 |
5 | 3 | 2 | 1 |
- xy 평면위에 존재하는 두 점 (x1, y1), (x2, y2) 사이의 유클리드 거리 계산
- PostgreSQL에는 POINT 자료형으로 좌표를 다루는 자료구조가 존재
- POINT 자료형으로 변환한뒤 거리 연산자
<->
을 이용
- POINT 자료형으로 변환한뒤 거리 연산자
|
|
실행결과
dist |
---|
2.8284271247461903 |
3.605551275463989 |
3.605551275463989 |
6.5 날짜/시간 계산하기
등록 시간과 생일을 포함하는 사용자 마스터
user_id | register_stamp | birth_date |
---|---|---|
U001 | 2016-02-28 10:00:00 | 2000-02-29 |
U002 | 2016-02-29 10:00:00 | 2000-02-29 |
U003 | 2016-03-01 10:00:00 | 2000-02-29 |
미래 또는 과거의 날짜/시간을 계산하는 쿼리
|
|
날짜 데이터의 차이 계산하기
- 날짜/시간 데이터의 계산은 미들웨어에 따라 표현에 차이가 큼
- 실무에서는 날짜/시간 데이터는 수치 또는 문자열 등으로 변환해 다루는 것이 편한 경우가 많음
|
|
사용자의 생년월일로 나이 계산하기
- 나이는 윤년 등으로 단순히 365로 나누어 계산 할 수 없음
- PostgreSQL의 경우
age
함수로 나이 계산 가능 (Redshift도 age 함수가 존재하나 공식지원은 아님)
age 함수를 사용해 나이를 계산하는 쿼리
|
|
실행결과
user_id | today | register_date | birth_date | current_age | register_age |
---|---|---|---|---|---|
U001 | 2023-11-21 | 2016-02-28 | 2000-02-29 | 23 | 15 |
U002 | 2023-11-21 | 2016-02-29 | 2000-02-29 | 23 | 16 |
U003 | 2023-11-21 | 2016-03-01 | 2000-02-29 | 23 | 16 |
연 부분 차이를 계산하는 쿼리
|
|
등록 시점과 현재 시점의 나이를 문자열로 계산하는 쿼리
|
|
실행결과
user_id | register_date | birth_date | register_age | register_age | t |
---|---|---|---|---|---|
U001 | 2016-02-28 | 2000-02-29 | 15 | 23 | 20231121 |
U002 | 2016-02-29 | 2000-02-29 | 16 | 23 | 20231121 |
U003 | 2016-03-01 | 2000-02-29 | 16 | 23 | 20231121 |
6.6 IP 주소 다루기
IP 주소 자료형 활용하기
- PostgreSQL에는 IP 주소를 다루기 위한 inet 자료형이 존재
- inet 자료형을 사용하여 IP 주소를 쉽게 비교 가능
|
|
실행결과
lt | gt |
---|---|
true | false |
inet 자료형을 사용해 IP 주소 범위를 다루는 쿼리
|
|
실행결과
is_contained |
---|
true |
정수 또는 문자열로 IP 주소 다루기
- IP 주소 전용 자료형이 제공되지 않는 미들웨어에 사용
IP 주소를 정수 자료형으로 변환하기
- IP 주소를 정수 자료형으로 변경하면 대소 비교가 가능
- 텍스트 자료형으로 정의된 IP 주소에 4개의 10진수 부분을 정수 자료형으로 추출
|
|
실행결과
ip | ip_part_1 | ip_part_2 | ip_part_3 | ip_part_4 |
---|---|---|---|---|
192.168.0.1 | 192 | 168 | 0 | 1 |
IP 주소를 정수 자료형 표기로 변환하는 쿼리
- 4개의 10진수 부분을 2^24, 2^16, 2^8, 2^0 만큼 곱하고 더하여 정수 자료형으로 표기
- IP 주소가 정수 자료형으로 변환되므로 대소 비교 또는 범위 판정 가능
|
|
실행결과
ip | ip_integer |
---|---|
192.168.0.1 | 3232235521 |
IP 주소를 0으로 메우기
- 각 10진수 부분을 3자리 숫자가 되게 앞 부분을 0으로 채워 문자열로 변환
|
|
실행결과
ip | ip_padding |
---|---|
192.168.0.1 | 192168000001 |
- lpad : 지정한 문자 수가 되게 문자열 왼쪽을 메우기