SQL 코드카타
문제 90
Confirmation Rate
The confirmation rate of a user is the number of 'confirmed' messages divided by the total number of requested confirmation messages. The confirmation rate of a user that did not request any confirmation messages is 0. Round the confirmation rate to two decimal places.
최종 답안
SELECT user_id,
ROUND(confirmed_user / total, 2) AS confirmation_rate
FROM (
SELECT S.user_id,
COUNT(*) AS total,
COUNT(CASE WHEN C.action = 'confirmed' THEN 1 ELSE NULL END) AS confirmed_user
FROM Signups S
LEFT JOIN Confirmations C
ON S.user_id = C.user_id
GROUP BY S.user_id) a;
✔️ 틀리지는 않았으나, Runtime 1098ms, Beats 5.04%로 개선 여지가 있다고 판단됨!
개선점
SQL에서 비율 구할 때 기존에 쓰던 템플릿(서브쿼리에 COUNT(CASE WHEN)) 대신 다른 템플릿 사용
새롭게 알게 된 것
[ANSI SQL] 그룹별 비율 구하는 템플릿
1. 윈도우 함수
- GROUP BY를 사용하지 않아 성능 상 이점이 있다
SELECT column1, (또는 DISTINCT column1) -- JOIN 시 중복 row 발생 가능성 있음
ROUND(
COUNT(CASE WHEN 조건 THEN 1 ELSE NULL END) OVER (PARTITION BY column1)
-- 그룹별 분자
* 100.0 / COUNT(*) OVER (PARTITION BY column1), 2) AS ratio
-- 그룹별 분모
FROM table
WHERE -- 선택 사항
-- GROUP BY 사용하지 않는 것이 핵심
ORDER BY column1; -- 선택 사항
- 이 문제를 윈도우 함수 템플릿으로 풀기
✔️ Runtime 642ms, Beats 77.80%
SELECT DISTINCT S.user_id, -- S.user_id 중복 row 발생해서 DISTINCT 사용
ROUND(
COUNT(CASE WHEN C.action = 'confirmed' THEN 1.0 ELSE NULL END)
-- 실수 1.0 곱해 정수 나눗셈 방지
OVER (PARTITION BY S.user_id) /
COUNT(*) OVER (PARTITION BY S.user_id), 2) AS confirmation_rate
FROM Signups S
LEFT JOIN Confirmations C
ON S.user_id = C.user_id;
-- GROUP BY 없음
2. AVG()
- 한 그룹의 모든 값이 1 아니면 0일 때,
(그룹의 평균 * 100) = 전체에서 1이 차지하는 비율
SELECT column1,
ROUND(AVG(CASE WHEN 조건 THEN 1.0 ELSE 0.0 END) * 100, 2
-- 1.0, 0.0 실수 사용해 정수 나눗셈 방지
) AS ratio
FROM table
GROUP BY column1;
- 이 문제를 AVG 템플릿으로 풀기
✔️ Runtime 672ms, Beats 57.81%
SELECT S.user_id,
ROUND(AVG(CASE WHEN C.action = 'confirmed' THEN 1.0 ELSE 0.0 END), 2)
AS confirmation_rate
FROM Signups S
LEFT JOIN Confirmations C
ON S.user_id = C.user_id
GROUP BY S.user_id;
3. COUNT(CASE WHEN) ✔️ 이번 문제에서 사용한 방법
SELECT column1,
ROUND(conditional_count * 100.0 / total_count, 2) -- 비율 구하는 식
FROM
( SELECT column1,
COUNT(*) AS total_count, -- 분모
COUNT(CASE WHEN 조건 THEN 1 ELSE NULL END) AS conditional_count -- 분자
FROM table
WHERE -- 선택 사항
GROUP BY column1
) AS count_data
ORDER BY column1; -- 선택 사항
참고 자료
- Gemini 검색 후 정리
Python 알고리즘 코드카타
문제 47
문자열 내 마음대로 정렬하기
문자열로 구성된 리스트 strings와, 정수 n이 주어졌을 때, 각 문자열의 인덱스 n번째 글자를 기준으로 오름차순 정렬하려 합니다. 예를 들어 strings가 ["sun", "bed", "car"]이고 n이 1이면 각 단어의 인덱스 1의 문자 "u", "e", "a"로 strings를 정렬합니다. 인덱스 1의 문자가 같은 문자열이 여럿일 경우, 사전순으로 앞선 문자열이 앞쪽에 위치합니다.
최종 답안 (구글링해서 품)
def solution(strings, n):
answer = sorted(strings, key=lambda x: (x[n], x))
return answer
틀린 이유
문제의 정렬 조건이 2개이다.
여러 개의 조건을 기준으로 정렬해야 할 때 어떻게 해야 할지 몰랐다.
새롭게 알게 된 것
여러 개의 조건을 기준으로 정렬할 때 템플릿
- sorted(iterable, key=function, reverse=False)
# 리스트
my_list = []
sorted_my_list = sorted(my_list, key=lambda x: (x[n], x))
# x는 리스트의 원소
# 딕셔너리를 원소로 갖는 리스트
data = [{}, {}, {}]
sorted_data = sorted(data, key=lambda item: (item['key1'], item['key2']))
# item 딕셔너리의 원소
# x가 숫자일 때 내림차순 정렬: -
sorted_my_list = sorted(my_list, key=lambda x: -x)
- sorted() 함수에 key=tuple이 입력되면
- 튜플의 첫번째 요소를 기준으로 비교
- 첫 번째 요소가 같다면 두 번째 요소를 기준으로 비교
데이터 전처리 & 시각화 Pandas - 1
데이터 전처리 & 시각화 강의 3주차 : Pandas 위주로 정리 - 1
1. Pandas 알아보기
1) Pandas 알아보기
- pandas 라이브러리 불러오기
import pandas as pd
# 에러날 경우 terminal에 pip install pandas
# 또는 pip3 install pandas
- 오픈소스 데이터셋 불러오기
import seaborn as sns
# seaborn은 데이터를 시각화 하는 라이브러리
data = sns.load_dataset('tips')
# tips라는 이름의 데이터셋을 불러와서 data 변수에 저장
data
# 데이터 출력
2) 데이터 불러오기
- 엑셀/CSV 데이터 불러오기
# pd.read_excel('파일경로/파일명.확장자')
# 엑셀 불러오기
pd.read_excel('파일경로/파일명.xlsx')
# 현재 위치에 있는 파일 불러옴
pd.read_excel('customer_data.xlsx')
pd.read_excel('./customer_data.xlsx')
# Users/jameskim 위치에 있는 파일 불러옴
pd.read_excel('/Users/jameskim/customer_data.xlsx')
# csv 파일 불러오기
pd.read_csv('파일경로/파일명.xlsx')
- 파일 경로 찾는 법
- Window
- 파일 탐색기(File Explorer) 사용
파일 탐색기를 열고 원하는 파일 또는 폴더를 찾은 후, 주소 표시줄(Address Bar)에 파일 경로가 표시됩니다. 이 경로를 복사하여 사용하거나, 마우스 오른쪽 버튼을 클릭하고 "속성(Properties)"을 선택하면 파일 또는 폴더의 속성 창에서 경로를 확인할 수 있습니다. - 명령 프롬프트(Command Prompt) 사용
명령 프롬프트(cmd)를 열고, cd 명령어를 사용하여 원하는 디렉토리로 이동합니다. 그리고 pwd 명령어를 입력하면 현재 디렉토리의 경로를 확인할 수 있습니다.
- 파일 탐색기(File Explorer) 사용
- Mac
- Finder 사용
Finder를 열고, 원하는 파일 또는 폴더를 찾은 후, 상단의 경로 표시줄(Address Bar)에 파일 경로가 표시됩니다. 이 경로를 복사하여 사용하거나, 파일 또는 폴더를 선택한 후 마우스 오른쪽 버튼을 클릭하여 "정보 보기(Get Info)"를 선택하면 파일 또는 폴더의 경로를 확인할 수 있습니다. - 터미널(Terminal) 사용
터미널을 열고, pwd 명령어를 입력하면 현재 작업 디렉토리의 경로를 확인할 수 있습니다.
- Finder 사용
3) 데이터 저장하기
# 엑셀로 저장
pd.to_excel(’파일경로/파일명.확장자’, index = False)
# csv로 저장
pd.to_csv(’파일경로/파일명.확장자’, index = False)
4) 인덱스
(1) 개념
- 데이터프레임(DataFrame) 또는 시리즈(Series)의 각 행 또는 각 요소에 대한 식별자
- 숫자, 문자
- 파일 불러올때 , 인덱스를 지정하는 것 가능
# 불러올 때 column1을 인덱스로 지정
pd.read_csv('./data/file.csv', index_col = 'column1')
# 불러올 때 파일의 첫 번째 열을 인덱스로 지정
pd.read_csv('./data/file.csv', index_col = 0)
# 가장 왼쪽 열 0, 그 다음이 1, ...
(2) 종류
- 기본 인덱스
데이터프레임 생성 시 자동으로 0부터 시작하는 정수 인덱스 제공됨
import pandas as pd
# 기본 정수 인덱스를 가진 데이터프레임 생성
df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']})
>>>
A B
0 1 a
1 2 b
2 3 c
- 사용자 지정 인덱스
# 사용자가 직접 인덱스를 설정한 데이터프레임 생성
df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']}, index=['level1', 'level2', 'level3'])
>>>
A B
level1 1 a
level2 2 b
level3 3 c
(3) 인덱스 활용
- .loc['row']: 특정 인덱스의 행에 접근
df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']}, index=['idx1', 'idx2', 'idx3'])
# 특정 인덱스의 행에 접근
row = df.loc['idx2']
- .sort_index(): 인덱스 기준으로 데이터프레임 정렬
df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']}, index=['idx1', 'idx2', 'idx3'])
# 인덱스를 기준으로 데이터프레임 정렬
sorted_df = df.sort_index()
>>>
index A B
idx1 1 a
idx2 2 b
idx3 3 c
# 특정 컬럼 기준으로 내림차순 정렬
sorted_df = df.sort_values(by='A', ascending=False)
>>>
index A B
idx3 3 c
idx2 2 b
idx1 1 a
- .set_index('column'): 특정 컬럼을 인덱스로 지정하기
# df가 가지고 있는 특정 컬럼 기준으로 인덱스 설정
data = df.set_index('column1')
data.head() # 기본값 상위 5개 행 보여줌
# head(n): 상위 n개 행 보여줌
- df.index: 인덱스 확인, 인덱스 새로 지정
#인덱스 확인
data.index
#리스트 형태로 인덱스를 새로 입력
data.index = ['1번' , '2번' , '3번']
- .reset_index(): 현재 인덱스를 0부터 시작하는 정수로 변경
# 0부터 시작하는 새 인덱스 제공
# 현재 인덱스는 그대로 유지되어 컬럼으로 변경됨
data.reset_index()
# 0부터 시작하는 새 인덱스 제공
# 현재 인덱스는 사라짐
data.reset_index(drop=True)
2. 데이터 전처리 - 컬럼
1) 컬럼 (Column)
(1) 예시
import pandas as pd
# 데이터프레임 생성
data = {
'이름': ['Gerri', 'Angela', 'Deric'],
'나이': [35, 40, 29],
'성별': ['여', '여', '남']
}
df = pd.DataFrame(data)
# 각 컬럼 출력
print(df['이름']) # '이름' 컬럼 출력
>>>
0 Gerri
1 Angela
2 Deric
(2) 활용
- name = ['컬럼명1', '컬럼명2', ...]: 컬럼명 변경
pd.read_csv('./data/file.csv', names = [’컬럼명1’, ‘컬럼명2’, … ,‘컬럼명15’])
- df.columns: 컬럼 확인, 컬럼명 변경
# 컬럼 확인하기
data.columns
# 리스트 형태로 컬럼명 새롭게 입력
data.columns = ['Korean_food', 'Mexican_food', 'Moroccan_food']
data
- df.rename(columns={'기존컬럼명': '새컬럼명'}): 특정 컬럼명만 변경
# 특정 컬럼명만 변경
# '기존컬럼명'을 '새컬럼명'으로 바꿉니다.
data = data.rename(columns={'기존컬럼명1': '새컬럼명1', '기존컬럼명2': '새컬럼명2'})
(3) 파일이 깨질 때
- 파일 인코딩 방식 정확하게 지정해야 올바르게 데이터 읽어올 수 있다
import pandas as pd
# UTF-8 인코딩으로 파일 불러오기
data = pd.read_csv('file.csv', encoding='utf-8')
# ASCII 인코딩으로 파일 불러오기
data = pd.read_csv('file.csv', encoding='ascii')
3. 데이터 전처리 - 데이터 확인
1) 데이터 확인 함수
- .head()
데이터를 n개 행까지 보여준다
data.head() # head(): 5개 행 데이터 보여줌
data.head(3) # ()안의 숫자만큼 데이터 보여줌
- .Info()
데이터의 정보를 파악합니다.
인덱스, 컬럼명, 컬럼의 데이터 개수, 데이터 타입
data.info()
# null 값을 확인할때도 활용
- .describe()
데이터의 기초통계량 확인
개수, 평균, 표준편차, 사분위, 중앙값
data.describe()
# 숫자값에 대해서만 기초통계량 확인 가능
2) 데이터 불러온 다음 확인할 사항
- 데이터 결측치(null)가 있는지?
# 결측치 확인: isnull()
df.isnull()
df.isnull().sum() # 결측치 몇 개 있는지 세어줌
# 결측치 제거: dropna()
df.dropna()
- 중복 데이터는 없는지?
# 중복 데이터 확인
df.duplicated(subset=['컬럼1', '컬럼2', '컬럼3'])
# 중복 데이터 제거
df.drop_duplicates(subset=['컬럼1', '컬럼2', '컬럼3'])
- 데이터 이상치는 없는지?
IQR (Interquartile Range) 방법 찾아보기
참고
# IQR 계산
Q1 = df['컬럼1'].quantile(0.25)
Q3 = df['컬럼1'].quantile(0.75)
IQR = Q3 - Q1
# 이상치 기준 설정
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 이상치 제거
df[(df['컬럼1'] >= lower_bound) & (df['컬럼1'] <= upper_bound)]
- 데이터 타입은 알맞게 들어가 있는지?
# 데이터 타입 변경
df['column_name'].astype(int)
df['column_name'].astype(float)
df['column_name'].astype(str)
df['column_name'].astype(bool)
df['column_name'].astype('category')
df['column_name'].astype('datetime64[ns]')
df['column_name'].astype(complex)
df['column_name'].astype(object)
3) 데이터 타입 변경
int64, int32 | 정수형 데이터 (64비트 또는 32비트) | 1, 42, -10, 1000 |
float64, float32 | 부동 소수점 수 (64비트 또는 32비트) | 3.14, -0.001, 2.718 |
object | 문자열 데이터 (일반적으로 문자열) | 'Hello', 'Data Science' |
bool | 불리언(참/거짓) 데이터 | True, False |
datetime64 | 날짜와 시간 데이터 | '2023-12-31 08:00:00' |
timedelta64 | 시간 간격(두 날짜 또는 시간 사이의 차이) 데이터 | '3 days', '2 hours' |
category | 카테고리형 데이터 (제한된 고유 값으로 구성) | 'Red', 'Blue', 'Green' |
- astype()
- pandas 데이터프레임의 열의 데이터 타입 변경
- DataFrame['column_name']
데이터타입 변경하고자 하는 열 지정 - new_dtype
변경하고자 하는 새로운 데이터 타입 명시
import pandas as pd
# 예시 데이터프레임 생성
data = {'integer_column': [1, 2, 3, 4, 5]}
df = pd.DataFrame(data)
# int → float
df['integer_column'] = df['integer_column'].astype(float)
print(df.dtypes) # 데이터프레임의 열 타입 확인
자료 출처
스파르타 코딩클럽 데이터 전처리 & 시각화 강의
아티클 스터디
[아티클]
파이썬 초보자가 저지르는 10가지 실수
[요약]
1. import*을 사용함
- 문제점
- 비효율적
- 변수명 충돌 위험성
- 해결법
- 기존 from math import *
- 개선 import math from math import pi
2. 예외 처리: ‘except’ 절에 예외를 지정하지 않음
- 해결법 try / except를 사용할 때 except 절에 예외 꼭 지정하기
3. 수학 계산에 Numpy 사용하지 않음
- 문제점 for loop보다 Numpy가 빠르다
- 해결법
- 기존 for loop 사용 avg = sum() / len()
- 개선 import numpy as np avg = .mean() 메서드 사용
4. 이전에 열었던 파일을 닫지 않음
- 해결법 with 구문에서 open, write/read, close 사용
5. PEP8의 가이드라인을 벗어남
- 해결법 PEP8을 읽고 가이드라인에 따른다
6. 딕셔너리를 사용할 때 .keys와 .values를 적절하게 사용하지 않음
- .keys 딕셔너리의 키만 보여주는 메서드
- .values 딕셔너리의 값만 보여주는 메서드
- 해결법
- key 얻기 .keys 메서드 대신 for loop로 for key in dictionary: print(key)
- value 얻기 for loop와 .items 메서드 조합 for key, value in dictionary.items(): print(value)
7. 컴프리헨션(comprehension)을 사용하지 않음 (혹은 언제나 사용)
- 해결법 for loop 대신 컴프리헨션 사용
8. range(len()) 사용
- 문제점 리스트를 반복할 때 range(len()) 사용
- 해결법 두 가지 리스트를 동시에 반복할 때 enumerate나 zip 사용
9. + 연산자를 사용한 문자열 연결
- 문제점 비효율적, 가독성 나쁨
- 해결법 f-strings 사용
10. Mutable value를 디폴트 매개변수로 사용할 때
- 문제점 이전 호출의 값을 계속 저장함
- 해결법 Mutable value(리스트 등)의 디폴트 값을 None으로 설정 if 구문 추가 if my_list is None: my_list = []
[핵심 개념 및 용어 정리]
Mutable
수정 가능한 것
- Immutable 객체: int, float, str, tuple
- Mutable 객체: list, dict, ndarray(Numpy의 배열)
참고 자료
디폴트 매개변수
def 함수이름(매개변수=디폴트값):
실행문
함수이름() # 값이 입력되지 않으면 디폴트값 자동 입력
함수이름(값)
참고 자료
[인사이트]
- 4월부터 파이썬을 배우기 시작했다. 파이썬 초보자의 10가지 실수 중 일부는 공감되기도, 일부는 생소하기도 했다.
- PEP8 가이드라인이 있다는 것을 처음 알았다. 코드 샘플과 가이드라인이 제공된다고 하니 읽어봐야겠다. (5번)
- 파이썬 과제를 하면서 딕셔너리에 대한 이해도가 낮다는 것을 체감했다. .keys, .values, .items 메서드와 for loop를 이용해서 어떤 작업이 가능한지 새롭게 알 수 있었다. 코테를 풀 때 활용할 수 있을 것 같다. (6번)
- for loop 대신 comprehension 사용, range(len())대신 enumerate, zip 사용하는 것. 내 코드에서 개선할 점을 Gemini와 gpt에게 질문할 때마다 자주 나왔던 답이다. 컴프리헨션은 조금씩 써보는 중이고, 나머지는 아직 생소해서 시도하지 못했다. 이번 아티클을 계기로 enumerate, zip을 사용해 코테를 풀어야겠다. (7, 8번)