티스토리 뷰

SQL 코드카타

Q127. Revising the Select Query I

1. 문제 링크: https://www.hackerrank.com/challenges/revising-the-select-query/problem

2. 정답 코드:

SELECT *
FROM CITY
WHERE population > 100000
  AND CountryCode = "USA";

 

Q128. Revising the Select Query II

1. 문제 링크: https://www.hackerrank.com/challenges/revising-the-select-query-2/problem

2. 정답 코드:

SELECT NAME
FROM CITY
WHERE population > 120000
  AND CountryCode = "USA";

 

Q129. Select All

1. 문제 링크: https://www.hackerrank.com/challenges/select-all-sql/problem

2. 정답 코드:

SELECT *
FROM CITY;

 

Q107. 1789. Primary Department for Each Employee

1. 문제 링크: https://leetcode.com/problems/primary-department-for-each-employee/description/

2. 정답 코드:

SELECT
    employee_id,
    department_id
FROM (
    SELECT
        *,
        COUNT(department_id) OVER (PARTITION BY employee_id) as cnt_department
    FROM Employee 
    ) as p
WHERE cnt_department = 1
  OR primary_flag = "Y";

3. 오류 상황: r값들이 섞여서 일부는 원하는 결과로, 일부는 조건에 맞지 않는 결과로 나옴

4. 시도 방법: 어떻게 원하는 값들만 뽑아낼 수 있을지 생성형 AI에게 힌트를 물어봄

5. 최종 문제 해결 방법: LEFT JOIN이 아니라 메인 쿼리의 FROM 절에 서브쿼리와 윈도우 함수를 사용하여 employee_id 별로 department_id의 COUNT가 1이거나 primary_flag가 "Y"인 행만 볼 수 있도록 정리한다!


문제 해결 흐름 정리)

 

1. 서브쿼리 (안쪽 괄호) : 이름표 미리 붙이기

먼저 FROM 안에 있는 SELECT 문이 하는 일은 아래와 같다.

  • PARTITION BY employee_id : 사원 번호별로 사람들을 따로 모음 (예: 1번 사원 팀, 2번 사원 팀...)
  • COUNT(department_id) OVER(...) : 각 팀의 인원수(소속 부서 개수)를 세어서, 모든 행 옆에 cnt_department라는 이름표로 붙임

이 작업이 끝나면 데이터는 이런 모습이 된다 :

  • 부서가 1개인 A 사원 : 옆에 '1'이라는 이름표가 붙음
  • 부서가 3개인 B 사원 : 3개 행 모두 옆에 '3'이라는 이름표가 붙음

2. 메인 쿼리 (바깥쪽) : 조건에 맞는 행만 골라내기

이제 이름표가 붙은 전체 목록(p)에서 우리가 원하는 조건만 쏙쏙 뽑아낸다

  • WHERE cnt_department = 1 : "이름표에 1이라고 적힌 사람은 다 나와!" (소속 부서가 하나뿐인 사람은 고민할 필요 없이 결과에 포함시킴)
  • OR primary_flag = 'Y' : "이름표 숫자가 무엇이든, 'Y'라고 표시된 행은 다 나와!" (부서가 여러 개인 사람이라도, 회사에서 지정한 주 부서(Y)는 포함시킴)

3. 최종 선택 : 필요한 정보만 보여주기

마지막으로 가장 바깥의 SELECT 문이 복잡한 이름표(cnt_department)나 다른 정보들은 숨기고, 문제에서 요구한 employee_iddepartment_id만 깔끔하게 출력!


SELECT
    e.employee_id,
    e.department_id
FROM Employee as e
LEFT JOIN (SELECT employee_id, IF((primary_flag = "Y") OR (primary_flag ="N" AND COUNT(employee_id) = 1), 1, 0) as result FROM Employee GROUP BY employee_id) as p
  ON e.employee_id = p.employee_id
WHERE p.result = 1

# 이전에 작성했던 쿼리

 

Q112. 1907. Count Salary Categories

1. 문제 링크: https://leetcode.com/problems/count-salary-categories/description/

2. 정답 코드:

SELECT
    'Low Salary' as category,
    COUNT(*) as accounts_count 
FROM Accounts
WHERE income < 20000
UNION
SELECT
    'Average Salary' as category,
    COUNT(*) as accounts_count 
FROM Accounts
WHERE income BETWEEN 20000 AND 50000
UNION
SELECT
    'High Salary' as category,
    COUNT(*) AS accounts_count 
FROM Accounts
WHERE income > 50000
ORDER BY accounts_count DESC;

3. 오류 상황: "High Salary"에 해당하는 account_id가 없어서 카테고리가 2개만 나옴

4. 시도 방법: 해당하는 값이 없더라도 3가지 카테고리가 모두 나올 수 있는 방법을 알고자 생성형 AI에게 힌트를 물어봄

5. 최종 문제 해결 방법: UNION을 사용해서 각각의 카테고리별 COUNT 쿼리를 만들어 위아래로 붙여준다!


헷갈리는 개념 이해하기)

1. JOIN (옆으로 붙이기 = 가로 확장)

  • 방향 : 기존 행 옆에 새로운 정보(컬럼)를 추가
  • 특징 : 두 테이블을 연결할 고리(공통 컬럼)가 반드시 필요 (예: 사원번호, 계좌ID)
  • 비유 : 사람 이름만 적힌 명찰 옆에 그 사람의 '전화번호'와 '주소' 정보를 옆으로 덧붙여서 종이를 넓게 만드는 것과 같음

2. UNION (아래로 붙이기 = 세로 확장)

  • 방향 : 기존 데이터 아래에 새로운 데이터(행)를 쌓음
  • 특징 : 합치려는 두 테이블의 컬럼 개수와 데이터 타입이 같아야 함
  • 비유 : A반 학생 명단 아래에 B반 학생 명단을 이어서 붙여서 명단 길이를 길게 만드는 것과 같음

이번 문제에 UNION이 더 유리했던 이유?

  1. Low 그룹을 계산해서 한 줄 만들고,
  2. Average 그룹을 계산해서 그 아래에 붙이고,
  3. High 그룹을 계산해서 그 아래에 붙여야 하기 때문에, 옆으로 정보를 붙이는 JOIN보다 아래로 결과를 쌓는 UNION이 훨씬 직관적이고 오류가 적은 것!

SELECT
    cnt.category,
    COUNT(a.account_id) as accounts_count
FROM Accounts as a
RIGHT JOIN (
    SELECT
        *,
        CASE
            WHEN income < 20000 THEN "Low Salary"
            WHEN income >= 20000 AND income <= 50000 THEN "Average Salary"
            WHEN income > 50000 THEN "High Salary"
        END AS category
    FROM Accounts
    ) as cnt
  ON a.account_id = cnt.account_id
GROUP BY cnt.category;

Python 코드카타 (https://github.com/heeso0908/codekata.git)

Q36. 문자열 다루기 기본

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/12918

2. 정답 코드:

def solution(s):
    if (len(s) == 4 or len(s) == 6) and s.isdigit() == True :
        answer = True
    else :
        answer = False
    return answer
숫자만으로 이루어져 있는지 확인하는 방법
"".isdigit() == True

 

Q37. 행렬의 덧셈 ★헷갈림★

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/12950

2. 정답 코드:

def solution(arr1, arr2):
    answer = []
    for i in range(len(arr1)) :
        row = []
        for j in range(len(arr1[i])) :
            row.append(arr1[i][j] + arr2[i][j])
        answer.append(row)
    return answer
for 문을 2개 사용해서 행별 계산 후 각 결과를 하나의 리스트로 합쳐주는 과정이 필요!
제일 큰 리스트는 answer로 그 안에 들어갈 행별 계산은 row로 저장해 줌
range 설정은 큰 리스트에 대해서는 range(len(arr1)), 내부는 range(len(arr1[i]))로 설정해줘야 함

 

Q38. 직사각형 별찍기

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/12969

2. 정답 코드:

a, b = map(int, input().strip().split(' '))
for i in range(b) :
    print(a*"*")

 

Q39. 최대공약수와 최소공배수 ★어려움★

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/12940

2. 정답 코드:

def solution(n, m):
    answer = []
    a, b = n, m
    while b > 0 :
        a, b = b, a % b
    answer.append(a)
    answer.append(int(n*m/a))
    return answer

3. 오류 상황: 원하는 결과가 나오지 않는 케이스가 있음

4. 시도 방법: 해결할 방법을 모르겠어서 생성형 AI에게 물어봄

5. 최종 문제 해결 방법: 최대공약수와 최소공배수를 구하려면 유클리드 호제법 이라는 개념을 아는게 훨씬 쉽다!


🧮 유클리드 호제법 (Euclidean Algorithm) — 개념 정리 & 파이썬 코드

1. 최대공약수(GCD)란?

두 수를 모두 나눌 수 있는 가장 큰 자연수를 최대공약수(GCD)라고 함
예를 들어 48과 18의 공약수는 1, 2, 3, 6이고, 이 중 가장 큰 수는 6

 

2. 유클리드 호제법이란?

유클리드 호제법은 두 수의 최대공약수를 빠르게 구하는 알고리즘
핵심 아이디어는 다음과 같다

큰 수를 작은 수로 나눈 나머지와 작은 수의 최대공약수는 같다

 

이를 식으로 쓰면 다음과 같다

이 성질을 이용해 숫자를 점점 줄여가며 계산

 

3. 예제로 이해하기 (48, 18)

  1. 48 ÷ 18 = 2 … 12
  2. 18 ÷ 12 = 1 … 6
  3. 12 ÷ 6 = 2 … 0

👉 나머지가 0이 되었을 때의 나누는 수(6)가 최대공약수

 

4. 파이썬 코드로 구현하기

def gcd(a, b): while b != 0: a, b = b, a % b return a

 

실행 예시

print(gcd(48, 18)) # 6

 

5. 코드로 보면 더 확실한 이유

이 코드에는 중요한 특징이 두 가지가 있다!

 

① 최대공약수는 변하지 않는다 (불변식)

반복문에서 (a, b)를 (b, a % b)로 바꾸지만,
두 경우의 최대공약수는 항상 같음

즉, 정답은 유지한 채 숫자만 작아짐

 

② 반드시 끝난다

a % b는 항상 b보다 작은 값이므로
b는 반복할수록 계속 작아짐

결국 b = 0이 되고 반복문 종료

 

6. 왜 b가 0이 되면 a가 답일까?

어떤 수와 0의 최대공약수는 그 수 자신이다

따라서 반복이 끝났을 때 남아 있는 a가 두 수의 최대공약수가 된다

 

7. 최대공약수와 최소공배수의 관계 ⭐

두 자연수 a, b에 대해 항상 다음 관계가 성립

즉,

📌 유클리드 호제법으로 GCD(최대공약수)만 구할 수 있으면, LCM(최소공배수)은 자동으로 구할 수 있다!

 

💡 한 줄 요약

유클리드 호제법은 최대공약수는 유지하면서 숫자를 점점 줄여 나가고, 나머지가 0이 되는 순간 정답을 얻는 알고리즘!
유클리드 호제법으로 최대공약수를 구한 뒤, 두 수의 곱을 최대공약수로 나누면 최소공배수도 바로 구할 수 있다!


def solution(n, m):
    answer = []
    if n <= m and m % n == 0 :
        answer.append(n)
        answer.append(m)
    elif n > m and n % m == 0 :
        answer.append(m)
        answer.append(n)
    elif n % m != 0 :
        answer.append(1)
        answer.append(m*n)
    return answer

# 이전에 작성했던 코드

 

Q40. 3진법 뒤집기 ★어려움★

1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/68935

2. 정답 코드:

def solution(n):
    num = ""
    answer = 0
    while n > 0 :
        num += str(n % 3)
        n = n // 3
    
    num2 = num[::-1]
    
    for i in range(len(num2)) :
        answer += int(num2[i])*(3**i)  
    return answer

🚀 [Python] 3진법 뒤집기: 로직의 흐름을 파악하라

이 문제는 단순히 진법을 변환하는 것뿐만 아니라, 앞뒤를 뒤집는다는 조건이 붙어 있어 혼동하기 쉽다!

 

1. 문제 분석

  • Input: 10진법 숫자 n
  • Process: 10진법 → 3진법  앞뒤 반전 10진법
  • Output: 최종 10진법 숫자

2. 코드 복기

def solution(n):
    num = ""
    # 단계 1: 3진법으로 변환 (나머지를 뒤로 붙여서 자동으로 뒤집힘)
    while n > 0 :
        num += str(n % 3)
        n = n // 3
    
    # 단계 2: 뒤집힌 문자열을 다시 원래 순서로 복구
    num2 = num[::-1]
    
    # 단계 3: 자릿수 가중치를 거꾸로 곱해서 10진법 변환
    answer = 0
    for i in range(len(num2)) :
        answer += int(num2[i]) * (3**i)  
    return answer

 

3. 왜 이 코드가 정답일까? (핵심 로직)

"왜 num2를 만들고 다시 3**i를 곱하는지" 궁금해할 수 있는데, 여기에는 수학적 반전이 숨어 있다!

 

① while 문을 지나면 이미 뒤집혀 있다!

보통 10진수를 3진수로 바꾸면 나머지를 역순으로 읽어야 한다

하지만 코드에서 num += str(n % 3)을 하면 나머지가 들어온 순서대로 쌓이기 때문에, num은 이미 앞뒤가 뒤집힌 3진법이 된다!

  • 예: 45 $ 원래 3진법 1200 num에는 "0021" 저장

② 다시 뒤집어서 정방향으로 만들기

num2 = num[::-1]을 통해 "0021"을 다시 "1200"으로 돌려놓기

 

③ 인덱스(i)를 이용한 '역순 가중치' 계산 (가장 중요!)

보통 10진법 변환은 맨 오른쪽 자릿수에 3^0을 곱하지만, 작성한 코드의 for 문을 보면

  • i = 0일 때 : num2[0](맨 왼쪽 숫자)에 3^0을 곱함
  • 결과적으로 : 숫자 배열은 정방향(1200)이지만, 가중치를 거꾸로(3^0부터) 곱했기 때문에 자동으로 뒤집어서 계산한 효과가 나타난다!

4. 더 간단한 '치트키' (Python 내장 함수 활용)

파이썬의 int(string, base) 함수는 "이 문자열은 해당 진법이다"라고 알려주면 자동으로 10진수로 바꿔준다

num이 이미 뒤집힌 상태이므로 한 줄로 끝낼 수 있다!

def solution(n):
    num = ""
    while n > 0:
        num += str(n % 3)
        n //= 3
    # num("0021")을 3진법으로 해석해서 10진법으로 변환
    return int(num, 3)

 

💡 한 줄 요약

이 문제는 데이터를 뒤집을 것인가, 아니면 계산하는 순서(가중치)를 뒤집을 것인가의 싸움인데,
위에서 작성한 코드는 두 번 뒤집음으로써 결국 문제의 의도에 정확히 도달한 아주 재미있고 논리적인 코드라고 한다..!
(GPT씨가,,)

라이브 세션) 데이터 전처리/시각화 4일차 - [시각화]

차트 선택의 5가지 기준(상황 분류표)

아래 중 무엇인지 먼저 고르면, 차트가 자동으로 결정된다

  • 시간에 따른 변화(추이) → Line
  • 항목 간 크기 비교(순위) → Bar
  • 두 변수 관계(상관/패턴) → Scatter
  • 값이 어떻게 퍼져 있나(분포) → Histogram
  • 이상치/범위/안정성 비교(사분위, IQR = Interquartile Range) → Box(또는 Violin)
Time이면 Line,
Compare면 Bar,
Relation이면 Scatter,
Distribution이면 Hist,
Outlier/Spread면 Box

산점도(Scatter Plot)

두 변수 간의 관계를 보기 위해서 산점도 무조건 알아야 함!

Scatter로 “관계 + 결측 제거” 익히기

 

1) 산점도는 언제 쓸까?

  • “A가 커지면 B도 커지나?”
  • 두 변수의 관계/패턴(양/음/무관)을 볼 때

2) 산점도에서 결측 제거가 중요한 이유

산점도는 x, y가 둘 다 필요하다!

price 또는 qty가 결측이면 점을 찍을 수 없어 결과가 왜곡되거나 에러가 난다

산점도는 dropna(subset=[x,y])가 기본!

Box Plot

이상치 탐색!

1) Box Plot은 언제 쓸까?

  • 이상치(outlier)가 있는지 확인
  • 그룹별(지점/메뉴 등)로 “어느 쪽이 더 안정적인지” 비교할 때

2) Box Plot이 보여주는 것

  • 가운데 선 : 중앙값
  • 박스 : 가운데 50%(Q1~Q3)
  • 수염 : 일반 범위
  • 점(또는 튀는 값) : 이상치 후보
Lower whisker : Q1 − 1.5 × IQR 이상인 값 중 가장 작은 값
Upper whisker : Q3 + 1.5 × IQR 이하인 값 중 가장 큰 값
📌 진짜 최소/최대값은 Outlier일 수도 있음

 

3) Box Plot 구성 요소

 

4) Box Plot과 분포


오늘은 Kaggle의 House Price 데이터를 활용해 본격적인 시각화 작업을 진행했다.
그동안 다뤄왔던 데이터에 비해 컬럼 수가 훨씬 많아, 데이터 구조를 파악하는 것부터 쉽지 않았다.
부트캠프가 끝난 이후에는 방대한 데이터를 대상으로 결측치와 이상치를 어떤 기준으로 처리할지 스스로 판단하고, 원하는 방향으로 전처리와 시각화를 할 수 있어야 할 텐데, 아직 갈 길이 멀다는 것을 다시 한 번 느꼈다.
앞으로 진행하게 될 네 번의 프로젝트를 통해 완벽한 수준까지는 아니더라도, 데이터를 어느 정도는 스스로 다룰 수 있는 사람으로 성장하기 위해 꾸준히 노력해야겠다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함