금! 요! 일!
오늘 한 일은
- SQL 공부
- [QCC] 1회차 참여하기
- 문제 풀고 해설 강의까지 참여하기
- 제출한 쿼리 해설 내용 토대로 회고하기
- [코드카타] SQL 3문제 풀기 (61~63번)
- 이번 주 푼 문제들 복습하기 (52~60번)
- [QCC] 1회차 참여하기
- 파이썬 공부
- [라이브 세션] 파이썬 기초 문법 강의 수강하기
- [코드카타] 파이썬 2문제 풀기 (49~50번)
- 이번 주 푼 문제들 복습하기 (42~48번)
- [데이터 리터러시] 1주차 완강하기
[파이썬 종합반] 1주차 수강하기
데이터 리터러시 공부: [데이터 리터러시] 1주차 수강
결론 도출
결과와 결론의 차이
- 우리에게 필요한 것은 결론임에도 정작 결과만을 얘기하는 경우가 많음
- 결과 : 계산과 분석을 해서 나온 결과물 '무엇을'
- 데이터 처리, 분석, 모델링 후에 얻은 구체적인 데이터의 출력
- 숫자, 통계, 그래프, 차트 등
- 결론 : 분석한 데이터 결과를 바탕으로 이끌어낸 의미나 통찰 '그것이 왜 중요한지'
- 목적에 대해 어떤 의미가 있는지 설명하는 것
- 데이터에 기반한 해석, 추론 및 권고 사항을 포함함
결론을 도출하는 방법
- 결과에서 결론으로 이를 때는 스토리텔링이 필요하지만 그 과정에서 필요 이상으로 자신의 해석을 개입해선 안 됨
(데이터를 통해 알 수 있는 범위 내에서만 스토리텔링을 이끌어내야 함) - 앞선 문제 정의 및 지표 설정 단계에서 설정한 목적을 떠올리며 결론을 정리함
- 결론을 공유할 대상이 누구이며, 어떻게 변화하길 바라는지를 염두에 둠
- ✨단순하고 쉽게 전달하려고 노력함
- 핵심 지표 위주로 우선 공유하고, 지표를 해석하는 방법도 설명하고, 액션 아이템을 제안하는 것이 핵심
- 상대가 궁금해할 만한 내용 위주로 구성함 (추가적으로 상세한 설명을 원하는 사람들을 위해선 따로 문서를 공유)
- 결론을 전달 받는 사람의 입장에서 이해하기 쉽도록 정리함
- 시각화를 활용할 땐 화려한 그래프보다 대상이 직관적으로 이해할 수 있도록 구성함
- 보통은 꺾은선 그래프, 막대 그래프로 대부분의 보고서 작성이 가능
- 각 그래프의 범례와 단위를 함께 표기함
- 결론 보고서 작성의 흐름
- 전체 내용을 한 문장으로 정리
- 해당 보고서의 메인 주제
- 해당 보고서를 쓴 이유와 원하는 변화
- 문제 정의 단계 (문제 정의 어떻게 했는지 설명)
- 핵심 내용 전개 (핵심 지표 및 지표 해석하는 방법 등)
- 결론 도출 및 액션 아이템 제안
데이터 리터러시란
- 주어진 데이터에만 의존하지 않고 스스로 사고하여 목적과 문제를 정의하는 능력
- 해당 목적을 달성하는 데 필요한 데이터와 지표를 설정
- 어떻게 데이터를 봐야 문제를 해결하는 데 필요한 정보를 효과적으로 얻을 수 있는지를 분석
- 분석 방법론 및 통계 지식에 매몰되지 않고 항상 '왜?'를 고려하며 판단
-
더보기추천 도서
📕카시와기 요시키, 『빅데이터 시대, 성과를 이끌어 내는 데이터 문해력』, 프리렉, 2021.
📕데루야 하나코·오카다 게이코, 『로지컬 씽킹』, 비즈니스북스, 2019.
SQL 공부①: [코드카타] SQL 문제 풀기 (61~63번)
62. 자동차 대여 기록에서 장기/단기 대여 구분하기
CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블에서 대여 시작일이 2022년 9월에 속하는 대여 기록에 대해서 대여 기간이 30일 이상이면 '장기 대여' 그렇지 않으면 '단기 대여' 로 표시하는 컬럼(컬럼명: RENT_TYPE)을 추가하여 대여기록을 출력하는 SQL문을 작성해주세요. 결과는 대여 기록 ID를 기준으로 내림차순 정렬해주세요.
SELECT history_id,
car_id,
SUBSTR(start_date, 1, 10) start_date,
SUBSTR(end_date, 1, 10) end_date,
CASE WHEN DATEDIFF(end_date, start_date) >= 30 THEN '장기 대여'
ELSE '단기 대여' END rent_type
FROM car_rental_company_rental_history
WHERE start_date LIKE '2022-09%'
ORDER BY history_id DESC
처음에는 위와 같이 작성했다. 이렇게 제출하니 결과 테이블은 뜨는데 틀렸다고 결과가 떠서 DATEDIFF() 함수를 잘못 적었는지 확인하려고 검색해봤다.
[참고] [MS-SQL] 날짜, 시간차이 구하기 (DATEDIFF)
해당 게시글을 보다가 어디서 잘못했는지 알아챘다. 기본적인 부분인데, 허허.
그래서 다음과 같이 수정했다.
SELECT history_id,
car_id,
SUBSTR(start_date, 1, 10) start_date,
SUBSTR(end_date, 1, 10) end_date,
CASE WHEN DATEDIFF(end_date, start_date)+1 >= 30 THEN '장기 대여'
ELSE '단기 대여' END rent_type
FROM car_rental_company_rental_history
WHERE start_date LIKE '2022-09%'
ORDER BY history_id DESC
SQL 공부②: 제1회차 QCC 회고
오늘은 대망의 첫 번째 QCC가 있는 날이었다.
사실 어제 QCC를 앞두고 코드카타로 푼 SQL 문제들로 복습을 하려고 했는데 못했다...
어차피 모의고사는 현재 자기 수준을 파악하려고 푸는 거니까 별걱정 없이 시험을 보러 갔다.
첫 번째 시험이니까 쉬울 줄 알았는데 예상보다 어려웠다. 하나는 쉽고, 하나는 어렵고.
그래서 두 번째 문제를 풀다가 잦은 오류도 성가시기도 했고, 내 수준에선 튜닝이 중요한 게 아니라 우선 요구한 바를 수행할 수 있는 쿼리를 작성할 수 있는지가 중요하다고 생각해서 쿼리가 더러워도 우선 원하는 대로 돌아간다 싶으면 그냥 제출하기로 했다.
그래도 결과를 보니까 다행히도 둘 다 맞추기 했다.
문제 1. 이메일 프로모션 고객
당신은 마케팅 팀의 일원으로서, 최근에 진행한 이메일 프로모션 캠페인에 관심이 있습니다.
이메일 프로모션을 수락한 개인(소매) 고객의 수를 계산하여 출력하세요.
--내가 제출한 쿼리
SELECT COUNT(DISTINCT business_entity_id) customer_cnt --DISTINCT를 적어도, 안 적어도 조회되는 결과는 같긴 했음
FROM person
WHERE (person_type = 'IN') AND --개인(소매) 고객이자
(email_promotion != 0 ) --이메일 프로모션에 동의한 고객
- 문제에서 요구한 조건을 충족하기 위해 WHERE절에 작성한 부분에서,
- 이메일 프로모션에 동의한 고객만을 선택하기 위해서 email_promotion > 0으로도 작성할 수 있음
- 고객 수를 세기 위해 COUNT(DISTINCT business_entity_id)로 작성한 부분에서,
- 해당 컬럼이 PK이기에 DISTINCT를 굳이 적지 않아도 괜찮았음
- 그러나 데이터를 입력할 당시에 혹시 모를 실수가 있었을 수도 있다는 점에서 PK일지라도 DISTINCT를 붙여쓰는 것도 좋음
문제 2. VIP 고객 산출
당신의 회사는 2011년 10월 동안 자사 제품을 많이 주문한 고객들에게 특별 할인 쿠폰을 제공할 예정입니다.
이를 위해 2011년 10월 한 달 동안 구매한 회사 제품 수량이 총 70개 이상인 고객을 찾아주세요.
- 취소된 주문은 제외함
- 고객 정보를 포함하여 고객 ID, 이름, 성, 총 주문 수량을 출력
- 결과는 고객 ID 기준으로 오름차순 정렬
--내가 제출한 쿼리
SELECT DISTINCT sub.customer_id,
sub.first_name,
sub.last_name,
sub.total_order_qty
FROM (
SELECT soh.customer_id,
p.first_name,
p.last_name,
SUM(sod.order_qty) OVER(PARTITION BY soh.customer_id) total_order_qty
FROM (
SELECT sales_order_id,
customer_id
FROM sales_order_header
WHERE (order_date LIKE '2011-10%') AND
(status != 6)
) soh
LEFT JOIN
(
SELECT sales_order_id,
order_qty
FROM sales_order_detail
) sod
ON soh.sales_order_id = sod.sales_order_id
LEFT JOIN
(
SELECT customer_id,
person_id
FROM sales_customer
) sc
ON soh.customer_id = sc.customer_id
LEFT JOIN
(
SELECT business_entity_id,
first_name,
last_name
FROM person
) p
ON sc.person_id = p.business_entity_id
) sub
WHERE sub.total_order_qty >= 70
ORDER BY sub.customer_id
--튜터님의 정답 쿼리
SELECT c.customer_id as customer_id
, p.first_name as first_name
, p.last_name as last_name
, SUM(so.order_qty) AS total_quantity
FROM qcc.sales_customer c
INNER JOIN qcc.person p ON c.person_id = p.business_entity_id
INNER JOIN qcc.sales_order_header soh ON c.customer_id = soh.customer_id
INNER JOIN qcc.sales_order_detail so ON soh.sales_order_id = so.sales_order_id
WHERE DATE(order_date) BETWEEN '2011-10-01' AND '2011-10-31' -- 2011-10 주문
AND soh.status <> 6 -- 취소 비포함
GROUP BY c.customer_id, p.first_name, p.last_name
HAVING SUM(so.order_qty) >= 70 -- 총 주문 수량 70개 이상
ORDER BY c.customer_id -- 고객 ID 오름차순 정렬
...내 쿼리 진짜 길다. 무슨 정신으로 썼는지 모르겠다.
조금만 빨리 타이핑하면 커서가 계속 이동하는 바람에(진짜 최소 20번은 왔다갔다 함) 다시 보고 싶지도 않고 '그냥 돌아가기만 하면 제출해버릴거다'라는 생각으로 중간중간 실행해보며 에러는 안 뜨는지만 확인하면서 줄줄줄 썼다.
일단 쓰면서 전체 테이블들을 JOIN하기보다 필요한 컬럼들만 불러오는 게 좋겠다고 생각했다. (기초 분석 때 뼈저리게 느낀 점.. 전체 테이블 불러오는 거에 맛들려서는 안 된다. 그대로 디비버와 함께 시간의 방에 갇혀버리는 불상사가 발생한다....)
근데 SUM(sod.order_qty)를 윈도우 함수로 쓴 건 SELECT절에 customer_id뿐만 아니라 first_name, last_name도 있어서 순간적으로 '이걸 GROUP BY절에 1, 2, 3 잡으면 에러 뜨는 거 아냐?' 하는 생각에 GROUP BY절을 안 쓰겠다는 일념으로 선택한 방법이었다.
그러나 막상 그렇게 쓰고 돌리니까 중복값이 줄줄줄 뜨길래 '이게 뭐지..?' 싶어서 길게 생각할 거 없이 서브쿼리로 묶고 DISTINCT customer_id 해버렸다.
조금만 더 내 마음에 여유가 있었으면 시간도 여유가 있어서 이 방법, 저 방법 시도해보면서 해봤을 거 같은데.. 다음엔 그렇게 해봐야지, 뭐.
파이썬 공부①: [코드카타] 알고리즘 문제 풀기 (49~50번)
49. 두 개 뽑아서 더하기
ㅇ 정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요.
def solution(numbers):
answer = []
for i in numbers: #numbers 배열에서 순서대로
for j in range(i+1, len(numbers)): #numbers 배열에서 i를 제외하고서 순서대로
if numbers[i]+numbers[j] not in answer: #i와 j를 더한 값이 answer 안에 없다면
answer.append(numbers[i]+numbers[j]) #i와 j를 더한 값을 answer에 추가하라
answer.sort() #마지막으로 answer에 담긴 값들을 오름차순으로 정렬하라
return answer
이렇게 적었는데 기댓값과 다른 결과값이 나와서 중간에 print(answer)를 추가하여 answer 리스트에 어떻게 담기는지 확인했다.
…?
의도한 대로 계산됐다면 테스트 1에서 가장 처음 담긴 값은 2+1=3이어야 하는데 왜 7이 담겼지?
테스트 1의 numbers에서 두 수의 합이 7이 나오려면 3+4밖에 없는데 왜…음??
아 j의 범위를 range() 함수를 사용해서 주면 이상하게 지정된다.
그래서 해당 부분을 수정했다.
def solution(numbers):
answer = []
for i in numbers:
n = numbers.index(i) #i가 numbers에서 어떤 인덱스를 갖는지 찾기
for j in numbers[n+1:len(numbers)]: #j의 범위를 numbers에 슬라이싱을 이용해 지정하기
if i+j not in answer:
answer.append(i+j)
answer.sort()
return answer
사실 j의 범위뿐만 아니라 많은 부분들을 수정했다.
[참고] 파이썬 (Python) 기초_리스트(list) 다루기, index 사용, 유용한 함수 모음
이번 문제를 풀면서 새로 알게 된 메소드는 .index()다.
- 리스트.index(a) : 리스트에서 값 a가 몇 번 인덱스를 갖는지 찾아줌
50. 가장 가까운 같은 글자
문자열 s가 주어졌을 때, s의 각 위치마다 자신보다 앞에 나왔으면서, 자신과 가장 가까운 곳에 있는 같은 글자가 어디 있는지 알고 싶습니다.
우선 이 문제를 풀려고 처음 시도했던 것은 for문과 이전 문제에서 알게 된 .index() 메소드를 이용해서 코드를 써내려갔는데, for문이 한 4번인가 5번 정도 중첩될 때 '이건 아니다' 싶어서 위치에 따른 숫자들을 가져오는 함수든 메소드든 있지 않을까 싶어서 구글링하다가 .index()와 비슷한 .find()를 알게 됐다.
[참고] [python] 문자열에서 특정 문자열 찾기 (find, index, rfind, rindex)
- str.find(a, start, end): 문자열 str에서 문자 a를 str에서 str[start]부터 str[end]까지에서 찾아서 발견된 인덱스를 반환
- a는 한 글자가 아니라 더 길게도 줄 수 있음
- 해당 범위 내에서 a를 찾지 못하면 -1을 반환
- .rfind()을 하면 끝에서부터 찾기 시작함
- str.index(a, start, end): 앞서 언급한 내용과 동일한 기능
- 해당 범위 내에서 a를 찾지 못하면 ValueError가 발생
def solution(s):
answer = []
for i in range(len(s)):
if s[i] in s[0:i]: #문자열 슬라이싱의 끝숫자는 인덱스 i 전까지
answer.append(i - s[0:i].rfind(s[i])) #현재의 s[i]에서 가까운 문자열의 위치. i에서 rfind로 구한 값만큼 빼줌
else:
answer.append(-1)
return answer
'[내배캠] 데이터분석 6기 > 본캠프 기록' 카테고리의 다른 글
[본캠프 16일차] SQL 코드카타, 파이썬 코드카타, 파이썬 공부 (0) | 2025.03.11 |
---|---|
[본캠프 15일차] 아티클 스터디③, SQL 코드카타, 파이썬 코드카타, 파이썬 공부 (0) | 2025.03.10 |
[본캠프 13일차] 파이썬 코드카타, SQL 코드카타, 데이터 리터러시 공부, 아티클 스터디② (0) | 2025.03.06 |
[본캠프 12일차] ADsP 공부, 데이터 리터러시 공부, 파이썬 코드타카, SQL 코드카타 (0) | 2025.03.05 |
[본캠프 11일차] ADsP 공부, SQL 공부, SQL 코드카타, 파이썬 코드카타, 아티클 스터디 ① (0) | 2025.03.04 |