Tools/- SQL

[SQL] πŸ“Œ 쀑앙값 κ΅¬ν•˜κΈ° | median ν•¨μˆ˜ 없이 κ΅¬ν•˜λŠ” 방법 | ν™œμš© 예제

μŠ€μœ„λ―Ό 2025. 5. 10. 15:58

쀑앙값 κ΅¬ν•˜κΈ°

 

쀑앙값(median)은 데이터λ₯Ό 크기 μˆœμ„œλŒ€λ‘œ λ‚˜μ—΄ν–ˆμ„ λ•Œ, 쀑앙에 μœ„μΉ˜ν•œ 값을 λ§ν•©λ‹ˆλ‹€.

데이터λ₯Ό λŒ€ν‘œν•˜λŠ” κ°’ 쀑 ν•˜λ‚˜λ‘œ, 특히 κ·Ήλ‹¨μ μΈ μ΄μƒμΉ˜μ— 영ν–₯을 덜 λ°›λŠ” νŠΉμ§•μ΄ μžˆμ–΄ 평균보닀 더 μ•ˆμ •μ μΈ μ§€ν‘œλ‘œ μ‚¬μš©λ˜κΈ°λ„ ν•©λ‹ˆλ‹€.

 

쀑앙값 κ΅¬ν•˜λŠ” 방법:

  • ν™€μˆ˜ 개의 데이터가 μžˆμ„ 경우:
    → κ°€μš΄λ° μžˆλŠ” 값이 μ€‘μ•™κ°’μž…λ‹ˆλ‹€.
    예: 3, 5, 7 → 쀑앙값은 5
  • 짝수 개의 데이터가 μžˆμ„ 경우:
    → κ°€μš΄λ° 두 κ°’μ˜ 평균이 μ€‘μ•™κ°’μž…λ‹ˆλ‹€.
    예: 2, 4, 6, 8 → 쀑앙값은 (4+6)/2 = 5

μ΄λ ‡κ²Œ λ°μ΄ν„°μ˜ κ°œμˆ˜κ°€ 홀/짝인지에 따라 μ€‘μ•™κ°’μ˜ μœ„μΉ˜μ™€ κ΅¬ν•˜λŠ” 방법이 달라지기 λ•Œλ¬Έμ—

이λ₯Ό λ°˜μ˜ν•˜μ—¬ SQL 쿼리λ₯Ό μ§œμ•Ό ν•©λ‹ˆλ‹€.

MySQL은 medianμ΄λΌλŠ” ν•¨μˆ˜λ₯Ό μ§€μ›ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ•½κ°„μ˜ 아이디어가 ν•„μš”ν•©λ‹ˆλ‹€.

 

ν™œμš© 예제

 

tipsλΌλŠ” ν…Œμ΄λΈ”μ„ ν™œμš©ν•˜μ—¬ 

각 μš”μΌλ³„ 성별 μ‹μ‚¬κΈˆμ•‘μ˜ 쀑앙값을 κ΅¬ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

속성 μ„€λͺ…
day μš”μΌ
sex 성별
total_bill 식사 κΈˆμ•‘

 

 

방법 1

 

첫번째 방법은, μ€‘μ•™κ°’μ˜ μˆœμ„œ * 2 - 전체 λ ˆμ½”λ“œ 수λ₯Ό κ΅¬ν•΄μ£ΌλŠ” κ²ƒμž…λ‹ˆλ‹€.

 

μ•„λž˜ 예제λ₯Ό 톡해 μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

row_num : 데이터λ₯Ό μˆœμ„œλŒ€λ‘œ λ‚˜μ—΄ν•΄μ„œ 번호λ₯Ό 맀겨쀀 κ°’μž…λ‹ˆλ‹€.

tot_cnt : 전체 λ ˆμ½”λ“œ μˆ˜μž…λ‹ˆλ‹€.

 

β–ͺοΈλ ˆμ½”λ“œκ°€ ν™€μˆ˜κ°œ

전체 λ ˆμ½”λ“œ μˆ˜κ°€ 9개이면, 쀑앙값은 5번째 μˆœμ„œμ— μžˆλŠ” λ ˆμ½”λ“œ 값일 κ²ƒμž…λ‹ˆλ‹€.

λ”°λΌμ„œ μœ„μ˜ μ€‘μ•™κ°’μ˜ μˆœμ„œ * 2 - 전체 λ ˆμ½”λ“œ 수 곡식을 ν™œμš©ν•˜λ©΄

(5 * 2) - 9 = 1μž…λ‹ˆλ‹€.

즉, μ€‘μ•™κ°’μ˜ μˆœμ„œ * 2 - 전체 λ ˆμ½”λ“œ μˆ˜κ°€ 1인 경우λ₯Ό ꡬ해주면 쀑앙값을 ꡬ할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

β–ͺοΈλ ˆμ½”λ“œκ°€ 짝수개

전체 λ ˆμ½”λ“œ μˆ˜κ°€ 10개이면, 쀑앙값은 5번째, 6번째 λ ˆμ½”λ“œμ˜ 평균일 κ²ƒμž…λ‹ˆλ‹€.

μœ„μ˜ 곡식에 μ μš©ν•΄λ³΄λ©΄

(5 * 2) - 10 = 0 | (6 * 2) - 10 = 2μž…λ‹ˆλ‹€.

즉, μ€‘μ•™κ°’μ˜ μˆœμ„œ * 2 - 전체 λ ˆμ½”λ“œ μˆ˜κ°€ 0κ³Ό 2인 λ ˆμ½”λ“œμ˜ 평균을 ꡬ해주면 쀑앙값을 ꡬ할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

방법 1 : SQL μ½”λ“œ

with rownum_table as
(select
  day,
  sex,
  total_bill,
  row_number() over (partition by day, sex order by total_bill asc) as row_num, -- day, sexλ³„λ‘œ λ ˆμ½”λ“œ μˆœμ„œ 맀겨주기
  count(*) over (partition by day, sex) as tot_cnt -- day, sexλ³„λ‘œ 전체 λ ˆμ½”λ“œ 수 κ΅¬ν•˜κΈ°
from tips)

select
  day,
  sex,
  avg(total_bill) as median_total_bill -- 짝수인 경우λ₯Ό κ³ λ €ν•΄μ„œ κΈˆμ•‘μ˜ 평균을 κ΅¬ν•΄μ€Œ
from rownum_table
where row_num * 2 - tot_cnt between 0 and 2 -- ν™€μˆ˜ : 1, 짝수 : 0,2 을 λͺ¨λ‘ 만쑱
group by 1, 2

 

β–ͺ️ROW_NUMBER()

ROW_NUMBER() ν•¨μˆ˜λŠ” μ •λ ¬ μˆœμ„œμ— 따라 1λΆ€ν„° μ‹œμž‘ν•΄ 순차적으둜 λ§€κ²¨μ£ΌλŠ” μœˆλ„μš° ν•¨μˆ˜(Window Function)μž…λ‹ˆλ‹€.

RANK(), DENSE_RANK()와 달리 같은 값이 μ—¬λŸ¬ 개 μžˆμ–΄λ„ μˆœλ²ˆμ€ 쀑볡 없이 λ²ˆν˜Έκ°€ λ§€κ²¨μ§‘λ‹ˆλ‹€.

 

κ²°κ³Ό ν…Œμ΄λΈ”μ„ 보면 μš”μΌ, μ„±λ³„λ³„λ‘œ 식사 κΈˆμ•‘μ˜ 쀑앙값이 잘 ꡬ해진 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

방법 2

 

λ‘λ²ˆμ§Έ 방법은, μ€‘μ•™κ°’μ˜ μˆœμ„œμ™€ (전체 λ ˆμ½”λ“œ 수 + 1 / 2)λ₯Ό λΉ„κ΅ν•΄μ£ΌλŠ” κ²ƒμž…λ‹ˆλ‹€.

 

 

β–ͺοΈλ ˆμ½”λ“œκ°€ ν™€μˆ˜κ°œ

(전체 λ ˆμ½”λ“œ 수 + 1 / 2)을 ꡬ해보면 (9 + 1) / 2 = 5μž…λ‹ˆλ‹€.

즉, 쀑앙값은 λ ˆμ½”λ“œμ˜ μˆœμ„œμ™€ (전체 λ ˆμ½”λ“œ 수 + 1 / 2)κ°€ μΌμΉ˜ν•˜λŠ” 경우일 κ²ƒμž…λ‹ˆλ‹€.

 

β–ͺοΈλ ˆμ½”λ“œκ°€ 짝수개

(전체 λ ˆμ½”λ“œ 수 + 1 / 2)을 ꡬ해보면 (10 + 1) / 2 = 5.5μž…λ‹ˆλ‹€.

쀑앙값은 (전체 λ ˆμ½”λ“œ 수 + 1) / 2의 ⌊λ‚΄λ¦Όκ°’⌋κ³Ό ⌈μ˜¬λ¦Όκ°’⌉ μœ„μΉ˜μ— μžˆλŠ” κ°’λ“€μ˜ ν‰κ· μž…λ‹ˆλ‹€.

 

 

방법 2 : SQL μ½”λ“œ

 

with rownum_table as
(select
  day,
  sex,
  total_bill,
  row_number() over (partition by day, sex order by total_bill asc) as row_num, -- λ ˆμ½”λ“œ μˆœμ„œ λ§€κΈ°κΈ°
  count(*) over (partition by day, sex) as tot_cnt -- 전체 λ ˆμ½”λ“œ 수
from tips)

select
  day,
  sex,
  avg(total_bill) as median_total_bill -- 평균 κ΅¬ν•˜κΈ°
from rownum_table
where row_num between floor((tot_cnt + 1) / 2.0) and ceil((tot_cnt + 1) / 2.0) -- λ‚΄λ¦Όν•œ κ°’κ³Ό μ˜¬λ¦Όν•œ κ°’ 사이에 μžˆλŠ” κ°’
group by 1, 2

 

β–ͺ️FLOOR & CEIL

floor : λ‚΄λ¦Ό ν•¨μˆ˜

ceil (λ˜λŠ” ceiling) : 올림 ν•¨μˆ˜

 

κ²°κ³ΌλŠ” λ™μΌν•˜κ²Œ 잘 λ‚˜μ™”μŠ΅λ‹ˆλ‹€!

 

 

λ²ˆμ™Έ ) wide ν˜•νƒœμ˜ ν”Όλ²—ν…Œμ΄λΈ” κ΅¬ν•˜κΈ°

 

μ΄λ ‡κ²Œ longν•œ ν˜•νƒœμ˜ ν…Œμ΄λΈ”μ„ wide ν˜•νƒœμ˜ ν”Όλ²— ν…Œμ΄λΈ”λ‘œ λ³€ν˜•ν•˜μ—¬ 가독성을 λ†’μ—¬λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

-- μœ„μ˜ longν•œ ν˜•νƒœμ˜ 쀑앙값 ν…Œμ΄λΈ”μ„ cte long_table둜 μ •μ˜ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

SELECT
  sex,
  max(case when day = 'Thur' then median end) as 'Thursday',
  max(case when day = 'Fri' then median end) as 'Friday',
  max(case when day = 'Sat' then median end) as 'Saturday',
  max(case when day = 'Sun' then median end) as 'Sunday'
from long_table
group by sex

 

 

κ²°κ³Όκ°€ wideν•œ ν˜•νƒœμ˜ ν…Œμ΄λΈ”λ‘œ 잘 좜λ ₯이 된 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

정리

 

μ˜€λŠ˜μ€ 쀑앙값을 κ΅¬ν•˜λŠ” ν•¨μˆ˜κ°€ 없을 λ•Œ

직접 κ΅¬ν•˜λŠ” 방법을 μ•Œμ•„λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

μΆ”κ°€λ‘œ λ‘± ν˜•νƒœμ˜ ν…Œμ΄λΈ”μ„ μ™€μ΄λ“œ ν˜•νƒœλ‘œ ν”Όλ²—ν•˜λŠ” 방법도 μ•Œμ•„λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

 

막상 직접 κ΅¬ν˜„ν•˜λ €λ©΄ ν—·κ°ˆλ¦΄ 수 μžˆλŠ” λ‚΄μš©λ“€μ΄λΌ, 정리해두면 μ•žμœΌλ‘œ 뢄석 μž‘μ—…μ— 큰 도움이 될 것 κ°™μŠ΅λ‹ˆλ‹€!