티스토리 뷰
SQL 코드카타
Q155. Population Density Difference
1. 문제 링크: https://www.hackerrank.com/challenges/population-density-difference/problem
2. 정답 코드:
SELECT MAX(POPULATION) - MIN(POPULATION) AS DIFF
FROM CITY;
Q136. Weather Observation Station 4
1. 문제 링크: https://www.hackerrank.com/challenges/weather-observation-station-4/problem
2. 정답 코드:
SELECT COUNT(*) - COUNT(DISTINCT CITY) AS CNT_CITY
FROM STATION;
Q148. Type of Triangle
1. 문제 링크: https://www.hackerrank.com/challenges/what-type-of-triangle/problem
2. 정답 코드:
SELECT
CASE
WHEN (A+B <= C) OR (B+C <= A) OR (C+A <= B) THEN 'Not A Triangle'
WHEN (A=B) AND (B=C) THEN 'Equilateral'
WHEN (A=B) OR (B=C) OR (C=A) THEN 'Isosceles'
ELSE 'Scalene'
END AS Triangle_type
FROM TRIANGLES;
삼각형이 될 수 없는 조건(A+B <= C 등) 먼저 CASE WHEN 문으로 작성하기!
Python 코드카타 (https://github.com/heeso0908/codekata.git)
Q46. 숫자 문자열과 영단어 ★★★
1. 문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/81301
2. 정답 코드:
def solution(strings, n):
answer = []
temp = []
for s in strings:
temp.append([s[n], s])
temp.sort()
for t in temp:
answer.append(t[1])
return answer
3. 오류 상황: 수도 코드 작성부터 막히는 상황
4. 시도 방법: 방법을 학습하기 위해 생성형 AI를 활용함
5. 최종 문제 해결 방법:
수도 코드 작성부터 막혀서 AI를 통해 학습해 보았다.
def solution(strings, n):
answer = []
temp = []
for s in strings:
temp.append(s[n], s)
temp.sort()
for t in temp:
answer.append(t[1:])
return answer
# 1차 작성 코드
TypeError: append() takes exactly one argument (2 given)
def solution(strings, n):
answer = []
temp = []
for s in strings:
temp.append([s[n], s])
temp.sort()
for t in temp:
answer.append(t[1:])
return answer
# 2차 작성 코드
실행한 결괏값 [["car"],["bed"],["sun"]]이 기댓값 ["car","bed","sun"]과 다르다
결과가 ["car", "bed", "sun"]이 아니라 [["car"], ["bed"], ["sun"]]처럼 리스트 안에 리스트가 들어간 형태로 나오는 이유는 슬라이싱([:]) 때문이다!
🚩 원인 분석
현재 코드의 t는 [str[n], str] 형태의 리스트
예를 들어 t가 ['a', 'car']일 때, t[1:]을 하면 다음과 같은 결과가 나온다:
- t[1] (인덱스 1번 요소 추출) → "car" (문자열)
- t[1:] (1번부터 끝까지 잘라서 리스트로 반환) → ["car"] (리스트)
파이썬에서 슬라이싱([:])은 원본의 형식을 유지하려는 성질이 있어서, 리스트를 슬라이싱하면 결과도 리스트가 된다
✅ 올바른 수정 코드
t[1:] 대신 인덱스로 직접 접근하는 t[1]을 사용하면 된다!
n번째 글자를 기준으로 오름차순 정렬하되, n번째 글자가 같은 문자열이 있을 경우에는 전체 문자열 기준, 사전순으로 앞에 있는 문자열이 앞에 오도록 오름차순 정렬 해주어야 한다.
그렇기 때문에 가장 먼저 n번째 글자를 따로 떼어 temp라는 변수에 원래 글자와 리스트로 묶어준다! → append() 사용
ex. strings = ["sun", "bed", "car"] 이고 n=1이면,
temp = [["u", "sun"], ["e", "bed"], ["a", "car"]]
그 다음에는 정렬을 해줘야 하는데, 첫번째는 n번째 글자 기준으로 두번째는 전체 문자열 기준으로 sort 해준다.
temp.sort()를 하면, 자동으로 temp[0] 기준으로 sort → temp[1] 기준으로 sort 해준다! ★핵심 코드★
마지막으로 정렬되어 있는 temp에서 원하는 결과물인 전체 문자열만 가져오면 된다!
→ for문을 사용해 1번 인덱스만 (ex. t[1]) 가져오기
라이브 세션) 통계 실습 4일차: 복습
· 함수 뒤에 계속 함수를 호출하는 것 : 메서드 체이닝
· mode(최빈값)를 구하면, 시리즈 형태(한개의 값이 0 221 이런식으로 짝지어서)로 나옴 → 최빈값이 1개라는 보장이 없기 때문에
# TODO 4: 차량등급(COMPACT, MID-SIZE, SUV)별 평균/중앙값 비교
group_results = emissions_grade_df.groupby('VEHICLE CLASS')['EMISSIONS'].agg([
('평균', 'mean'),
('중앙값', 'median'),
('절사평균(10%)', lambda data : stats.trim_mean(data, 0.1)),
절사평균은 왜 lambda를 써야하는가?
# TODO 4: 차량등급(COMPACT, MID-SIZE, SUV)별 평균/중앙값 비교
group_results = emissions_grade_df.groupby('VEHICLE CLASS')['EMISSIONS'].agg([
('평균', 'mean'),
('중앙값', 'median'),
('절사평균(10%)', lambda data : stats.trim_mean(data, 0.1)),
]) #set말고 리스트 쓰기! 순서보장!!
print(group_results)
display(group_results)
praictice_01_2번 한번에 계산하는 방법
# **COMB (L/100 km)** 와 **ENGINE SIZE
stats_df = df[['COMB (L/100 km)', 'ENGINE SIZE']].agg(['var', 'std', 'min', 'max',
lambda data : data.max()-data.min(),
lambda data : data.quantile(0.75)-data.quantile(0.25),
lambda data : data.std() / data.mean() * 100
])
stats_df.index = ['분산', '표준편차', '최솟값', '최댓값', '범위', 'IQR', 'CV(%)']
display(stats_df)
df[col], df[[col]] 차이점 : 시리즈와 데이터 프레임
# 3-2) Z-score
for col in ['COMB (L/100 km)', 'ENGINE SIZE'] :
z_score = np.abs(stats.zscore(df[col])) # 전체 데이터에대한 z_score (변량 - 평균) / 표준편차
z_outliers_2 = df[col][z_score > 2]
z_outliers_3 = df[col][z_score > 3]
print("z_outliers_2", len(z_outliers_2))
print("z_outliers_3", len(z_outliers_3))
이렇게 했는데 왜 결과가 2개가 아니라 4개가 나올까?
z_outliers_2 1124
z_outliers_3 219
z_outliers_2 762
z_outliers_3 41
절사 평균
# data.sort() # 비권장
sorted_data = sorted(data) #원본 유지
n = len(data) #데이터 갯수
# lower_bound 제거할 데이터 개수랑도 일치
lower_bound = int(n * proportion) #데이터 100개고, 비율이 10% = 10 --> 인덱스화
uppper_bound = n - lower_bound
trim_data = sorted_data[lower_bound : uppper_bound]
return sum(trim_data) / len(trim_data)
최빈값
if not data : # 예외 처리
return None
from collections import defaultdict
# counts = {}
counts = defaultdict(int) # 각 값별로 빈도수를 저장할 딕셔너리
freq_val = data[0] #가장 빈도수 높은 값
max_cnt = 1 # 빈도수
for i in data :
counts[i] += 1
if counts[i] > max_cnt :
max_cnt = counts[i]
freq_val = i
return freq_val
분산
if not data :
return None # 예외 상황
#편차 제곱의 평균
mean_val = sum(data) / len(data)
# dev_vals = [(x-mean_val)**2 for x in data] #분산의 분자
# var = sum(dev_vals) / (len(data) - ddof)
return sum((x-mean_val)**2 for x in data) / (len(data) - ddof)
백분위수
sorted_data = sorted(data)
n = len(data) #데이터의 개수
idx = round((n-1)*(q/100)) #상위 q%에 대한 인덱스
# (n-1)*(q/100) --> 4.6 --> 5
# 4.1 --> 4
#하위 10% --> 데이터가 100개가 있다.
# 인덱스? 10 <-- 11번쨰 인덱스
return sorted_data[idx]
베이즈 정리 활용
# TODO (c): 양성예측도 계산
# P(질병|양성) = P(양성|질병) * P(질병) / P(양성) = P(양성 N 질병) / P(양성)
ppv = sensitivity * p_disease / p_positive
print(f"(c) 양성예측도 P(질병|양성): {ppv}")
이항분포에서의 확률 계산 시 주의
# (c) 5개 이상 확률 ★이상일 때 주의!★
p_at_least_5 = binom_dist.sf(4)
#p_at_least_5 = 1- binom_dist.cdf(4)
print(f"(c) P(X ≥ 5) = {p_at_least_5:.4f}")
지수분포에서의 무기억성 활용
# (c) 무기억성 활용 — 추가 200시간 작동 확률
# **(c)** 이미 400시간 작동한 제품이 추가로 200시간 더 작동할 확률 (무기억성을 활용하여 검증)
# P(X>600 | P>400) = P(X > 200) # 무기억성 특징
# 검증 : P(X> 600) / P(X>400) = P(X>600 N P>400) / P(X>400)
p_additional_200 = expon_dist.sf(200)
print(f"\n(c) P(추가 200시간 작동) = {p_additional_200}")
# 무기억성
print("(C)무기억성", expon_dist.sf(600)/expon_dist.sf(400))
라이브 세션) 통계 이론 4일차 복습
1) 가설검정이란?
2) 판정의 기준: -값(-value)과 유의수준
3) 신뢰구간(CI)과 가설검정
(1) 신뢰구간(Confidence Interval)의 의미
95% 신뢰구간이란, 우리가 조사한 데이터뿐만 아니라 전체(모집단)를 봤을 때 "진짜 평균 차이가 이 범위 안에 있을 확률이 95%다"라는 뜻이다!
- 예를 들어, A 비타민과 B 비타민의 효과 차이를 계산했더니 신뢰구간이 [3, 7]이 나왔다고 치면,
- 이건 "A가 B보다 최소 3만큼, 많게는 7만큼 효과가 좋다"는 뜻!
(2) '0'을 포함한다는 것의 의미
여기서 '0'은 매우 특별한 숫자이다. 두 집단의 평균 차이가 '0'이라는 건 "차이가 전혀 없다"는 뜻이기 때문이다!
- 0을 포함하지 않는 경우 (예: [3, 7]):
- 범위 전체가 0보다 크다. 즉, 아무리 보수적으로 잡아도 차이가 0은 아니라는 것
- 따라서 "두 집단은 확실히 차이가 있다"고 결론 내린다. (통계적 유의성 확보)
- 0을 포함하는 경우 (예: [-2, 5]):
- 차이가 -2일 수도, 0일 수도, 5일 수도 있다는 뜻
- 차이가 0일 가능성이 포함되어 있으니, "이 차이는 우연일 뿐, 진짜 차이가 있다고 보기 어렵다"고 결론 내린다.
(3) p-value(p<0.05)와의 관계
통계에서는 보통 "우연히 이런 결과가 나올 확률이 5% 미만(p<0.05)"일 때, 그 결과를 믿을만하다고 본다.
신뢰구간에 0이 없다 = 차이가 0일 확률이 매우 희박하다 = p < 0.05
신뢰구간에 0이 있다 = 차이가 0일 가능성이 충분히 있다 = p ≥ 0.05
요약하자면
| 신뢰구간 상태 | 0 포함 여부 | 의미 | 통계적 결론 |
| [2, 10] | 포함 X | 확실히 차이가 남 | 유의함 (p < 0.05) |
| [-3, 4] | 포함 O | 차이가 없을 수도 있음 | 유의하지 않음 (p >= 0.05) |
실제 상황을 가정해서 더 직관적으로 이해해 보자!
예를 들어, '잠이 잘 오는 약'을 개발했다고 치고, 이 약을 먹은 그룹과 안 먹은 그룹의 수면 시간 차이를 분석해 보겠다!
(4) "0이 포함되지 않은" 경우 (유의미한 차이)
통계 프로그램을 돌렸더니 평균 차이의 95% 신뢰구간이 [1시간, 3시간]으로 나왔다고 해보자.
- 해석: "이 약을 먹으면 아무리 효과가 적어도 1시간은 더 자고, 많으면 3시간까지 더 잔다"는 뜻
- 0의 행방: 이 범위 안에 '0(차이 없음)'이 전혀 들어올 틈이 없다!
- 결론: "이 약은 진짜 효과가 있다!"라고 말하며, 이때 p < 0.05라는 도장을 쾅 찍어준다!
(5) "0이 포함된" 경우 (우연일 가능성)
그런데 만약 결과가 [-1시간, 2시간]으로 나왔다면 어떨까?
- 해석: "어떤 사람은 2시간 더 잘 수도 있지만, 어떤 사람은 오히려 1시간 덜 잘 수도 있다(차이가 -1)"는 뜻이다. 심지어 차이가 '0'일 가능성도 이 안에 들어있다.
- 0의 행방: 0이 범위 안에 쏙 들어가 있다.
- 결론: "이 약이 효과가 있는 것처럼 보일 때도 있지만, 그냥 우연일 수도 있겠는데?"라고 판단한다. 이때는 p >= 0.05가 되어 통계적으로 유의하지 않다고 본다.
4) 상황별 통계 검정 방법 선택 (로드맵)
|
데이터 유형
|
집단 수
|
주요 검정 방법
|
비고
|
|
수치형 (키, 점수 등)
|
1개
|
일표본 -검정
|
집단 평균 vs 기준값 비교
|
|
2개 (독립)
|
독립표본 -검정
|
남 vs 여 평균 비교
|
|
|
2개 (대응)
|
대응표본 -검정
|
같은 사람의 전/후 비교
|
|
|
3개 이상
|
ANOVA (분산분석)
|
비료 A/B/C 효과 차이 비교
|
|
|
범주형 (찬성/반대 등)
|
1개
|
이항검정
|
비율이 기준과 다른가?
|
|
2개 이상
|
카이제곱 검정
|
성별과 구매 여부의 연관성
|
5) 해석 시 주의할 점 (해석 방패)
6) 가설검정 심화 내용 더 자세히 공부
가설검정 심화 단계의 핵심은 데이터의 성격(유형, 집단 수, 분포 상황)에 맞춰 가장 적절한 '통계 도구'를 선택하는 것이다.
전체적인 흐름을 세 가지 주요 질문을 통해 정리해 보자!
Q1. "데이터가 어떤 형태인가?"
Q2. "비교하려는 집단이 몇 개인가?"
Q3. "통계적 가정을 만족하는가?"
통계(T-test 등)를 돌려보려면 데이터가 예쁘게 종 모양으로 퍼져 있는 '정규분포'를 따라야 한다.
Shapiro-Wilk와 Q-Q plot은 내 데이터가 그 자격이 있는지 확인하는 두 가지 방법!(수치 vs 그림)
(1) Shapiro-Wilk (샤피로-윌크) 검정 : "숫자로 판정"
데이터가 정규분포를 따르는지 통계적으로 계산해서 p값으로 알려주는 방법!
- 주의할 점 (중요!): 여기서는 p > 0.05여야 좋다!
- 보통은 p < 0.05여야 "차이가 있다"고 좋아하지만, 여기서는 p값이 "정규분포와 차이가 나는 정도"를 의미한다!!
- p > 0.05 : "정규분포와 차이가 없다" → 합격! (정규성 만족)
- p < 0.05 : "정규분포랑 너무 다르다" → 불합격!
일반적인 분석(T-test 등)과 Shapiro-Wilk의 목표는 서로 정반대이다!!!!!
- 일반적인 차이 분석 (T-test):
- 귀무가설(H0): "두 집단은 차이가 없다." (내가 깨뜨리고 싶은 말)
- 목표: p < 0.05가 나와서 "차이가 없다는 말은 틀렸어! 차이가 있어!"라고 외치기!
- 정규성 검정 (Shapiro-Wilk):
- 귀무가설(H0): "내 데이터는 정규분포와 차이가 없다." (내가 지키고 싶은 말)
- 목표: p > 0.05가 나와서 "차이가 없다는 말을 깨뜨릴 수 없네? 그럼 정규분포가 맞나 보다!"라고 안심..
(2) Q-Q Plot (Quantile-Quantile Plot) : "눈으로 확인"
데이터를 그래프에 점으로 찍어서 확인하는 직관적인 방법!
- 보는 법: 직선이 하나 그어져 있고, 그 위에 데이터 점들이 찍힌다.
- 합격: 점들이 직선을 따라 쪼르르 예쁘게 붙어 있으면 정규분포이다.
- 불합격: 점들이 직선을 벗어나 휘어지거나 엉뚱한 곳에 있으면 정규분포가 아니다.
비유를 하자면? 🍎
사과가 맛있는지(데이터 분석) 확인하기 전에, 이게 진짜 사과인지(정규성 확인) 보는 과정
- Shapiro-Wilk는 인공지능 센서가 "이것은 사과일 확률이 95% 이상입니다"라고 수치로 답해주는 것이고,
- Q-Q Plot은 내가 직접 눈으로 보고 "음, 모양이 동그란 게 사과처럼 생겼군" 하고 판단하는 것!
요약
| 구분 | Shapiro-Wilk | Q-Q Plot |
| 방법 | 통계적 계산 (수치) | 그래프 시각화 (그림) |
| 판단 기준 | p > 0.05 이면 정규성 인정 | 점들이 직선에 붙어 있으면 인정 |
| 특징 | 정확하지만 샘플이 너무 많으면 예민함 | 직관적이지만 주관적일 수 있음 |
보통은 두 가지를 세트로 같이 확인한다.
Q. 카이제곱 검정이 뭘까?
카이제곱(Chi-square) 독립성 검정은 '범주(이름)'들 사이에 관계가 있는지를 보는 도구이다!
쉽게 말해, "A라는 사실이 B라는 결과에 영향을 줄까?"를 확인하는 방법
(1) 언제 사용할까? (숫자가 아닌 '항목'일 때)
- 성별(남/여)에 따라 선호하는 색상(빨강/파랑)이 다를까?
- 사는 지역(서울/부산)에 따라 지지하는 정당이 다를까?
- 흡연 여부(흡연/비흡연)와 폐암 발생 여부는 관계가 있을까?
이처럼 데이터가 "3.5점", "70kg" 같은 숫자가 아니라 "남성", "흡연자", "서울" 같은 이름표(범주형 데이터)일 때 사용한다.
(2) 핵심 원리: "기대했던 것과 실제가 얼마나 다른가?"
카이제곱 검정은 '관측값'과 '기대값'을 비교한다.
- 기대값: "두 변수가 아무 상관이 없다면(독립적이라면) 당연히 이 정도 숫자가 나와야지!" 하는 예상치
- 관측값: 우리가 실제로 조사해서 얻은 데이터
결론 도출:
- 실제와 기대가 비슷하다: "상관없네! (독립적이다)"
- 실제와 기대가 너무 다르다: "오, 뭔가 관계가 있네! (독립적이지 않다)"
(3) 표로 보는 예시 (분할표)
'성별'과 '커피 선호도'의 관계를 조사한다고 가정해 보자.
| 구분 | 아메리카노 | 라떼 | 합계 |
| 남성 | 40명 | 10명 | 50명 |
| 여성 | 10명 | 40명 | 50명 |
| 합계 | 50명 | 50명 | 100명 |
만약 성별과 커피 취향이 아무 상관 없다면 남녀 모두 반반씩(25명씩) 골랐어야 한다.
하지만 실제 데이터(관측값)는 성별에 따라 확연히 다르다!
이 차이를 계산해서 "이건 우연이 아니야!"라고 말해주는 것이 카이제곱 검정이다.
(4) 그럼 이것도 p-value를 볼까?
그렇다!
- p < 0.05 : "두 변수는 서로 관계가 있다! (독립적이지 않다)"
- p >= 0.05 : "두 변수는 아무 상관이 없다! (독립적이다)"
요약하자면
- T-검정: 두 집단의 평균(숫자)이 다른지 비교
- 카이제곱 검정: 두 집단의 빈도(개수)를 토대로 관계가 있는지 비교
6) 최종 정리
| 데이터 유형 | 집단 수 | 조건 | 추천 검정 방법 |
|
수치형
|
1개
|
정규성 만족
|
일표본 t-검정
|
|
2개(독립)
|
등분산 만족
|
독립표본 t-검정
|
|
|
등분산 불만족
|
Welch t-검정
|
||
|
정규성 불만족
|
Mann-Whitney U 검정
|
||
|
2개(대응)
|
정규성 만족
|
대응표본 t-검정
|
|
|
3개 이상
|
정규성 만족
|
ANOVA (분산분석)
|
|
|
범주형
|
1개
|
비율 비교
|
이항검정
|
|
2개 이상
|
연관성 확인
|
카이제곱 독립성 검정
|
+ 주의할 점
가설검정은 진실을 100% 증명하는 도구가 아니라,
"우연일 가능성을 반증"하여 의사결정을 돕는 절차임을
이해하는 것이 심화 학습의 핵심!
라이브 세션) 통계 이론 5일차: 상관관계
산점도를 먼저 그리고 나서 상관계수를 확인해야 한다!
상관계수만 보면 모양을 파악할 수 없기 때문에 산점도 먼저 그리기!!!
1) 상관계수
상관계수를 구하려면 변수 모두 수치형이어야 한다!범주형은 카이제곱 검정으로?
- 상관계수 범위 : -1 <= r <= 1



2) 산점도
데이터 갯수가 많으면 산점도만으로는 해석이 어렵다+ 차트는 보는 사람마다 다르게 해석할 수 있다
산점도는 방향(+/-), 강도(약/중/강), 모양상관계수는 방향(+/-), 강도(약/중/강) 알 수 있다!
3) 회귀(Regression)
상관은 두 변수가 "같이 움직인다"는 사실만 요약하지만 아래와 같은 질문에는 답하지 못한다:- 광고비를 100만원 늘리면, 매출은 얼마나 늘까?- 공부시간이 1시간 늘면, 점수는 몇 점 올라갈까?→ 영향력의 크기를 알고 싶을 때 필요한 도구가 회귀
4) 이상치
상관계수(r)는 이상치의 영향을 많이 받는다




5) 상관계수와 가설검정

6) 비선형 상관






7) 인과와 상관
: 잘못된 해석을 방지하기 위한 사고방식



8) 무작위 통제 실험(RCT, Randomized Controlled Trial)


9) 인과 효과를 추정하는 방법


10) A/B 테스트
A/B 테스트도 무작위 대조 실험(RCT)








- A/B 테스트를 하는 방법







· A/B 테스트 실행 절차 요약

오늘로 통계 이론 세션이 마무리되었다.
5일 동안 많은 내용을 배워 정리하는 시간이 꼭 필요하다고 느끼지만, 세션 시간이 길다 보니 충분히 복습하기가 쉽지 않다,,
내일부터는 머신러닝을 배우는데, 오늘 밤에는 최대한 통계 내용을 다시 정리해 내일 수업에서 막히지 않도록 준비해야겠다.
'내일배움캠프 데이터 분석' 카테고리의 다른 글
| 37일차) 내일배움캠프 데이터 분석 TIL - 통계(7), 머신러닝(2) (0) | 2026.02.12 |
|---|---|
| 36일차) 내일배움캠프 데이터 분석 TIL - 통계(6), 머신러닝(1) (0) | 2026.02.11 |
| 34일차) 내일배움캠프 데이터 분석 TIL - 통계(4) (0) | 2026.02.09 |
| 33일차) 내일배움캠프 데이터 분석 TIL - 통계(3) (0) | 2026.02.06 |
| 32일차) 내일배움캠프 데이터 분석 TIL - 통계(2) (1) | 2026.02.05 |
- Total
- Today
- Yesterday
- 데이터분석
- github
- Python
- SQL
- 내일배움캠프
- 파이썬
- 프로그래밍입문
- 파이썬입문
- 텍스트분석
- GoogleColab
- 코드카타
- 코딩처음
- 통계
- 중학생코딩
- 판다스
- 코딩기초
- 비전공자코딩
- 구글코랩
- 데이터시각화
- 태블로
- Til
- 데이터분석입문
- Tableau
- 머신러닝
- git
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
