def solution(dartResult):
squared = {
'S':1
, 'D':2
, 'T':3
}
temp = ''
score = []
for i in dartResult:
if i.isdigit():
temp += i
elif i.isalpha():
score.append(pow(int(temp), squared[i]))
temp = ''
elif i == '*':
if len(score) > 1:
score[-2] *= 2
score[-1]*=2
elif i == '#':
score[-1] *= -1
return sum(score)
import re
def solution(dartResult):
# 1. 정규식을 사용하여 점수와 보너스, 옵션을 추출
rounds = re.findall(r'(\d+)([SDT])([*#]?)', dartResult)
scores = [] # 점수를 저장할 리스트
for i, (score, bonus, option) in enumerate(rounds):
# 2. 점수 변환 및 보너스 처리
score = int(score)
if bonus == 'S':
score **= 1
elif bonus == 'D':
score **= 2
elif bonus == 'T':
score **= 3
# 3. 옵션 처리
if option == '*':
score *= 2
if i > 0: # 이전 점수도 2배
scores[i - 1] *= 2
elif option == '#':
score *= -1
# 점수를 리스트에 저장
scores.append(score)
# 총점 반환
return sum(scores)
\d+: 숫자(점수) 추출[SDT]: 보너스 구분 추출[*#]?: 옵션 추출 (없을 수도 있음)1D#2S*3S를 넣으면:
[('1', 'D', '#'), ('2', 'S', '*'), ('3', 'S', '')] 생성
1D2S3T*를 넣으면:
[('1', 'D', ''), ('2', 'S', ''), ('3', 'T', '*')]
*)*)의 점수만 2배#)import re
def SDT(num, c):
if c == 'S' : return num
elif c == 'D' : return num ** 2
else : return num ** 3
def solution(dartResult):
answer = []
dart = re.findall(r'\d+|[a-zA-Z][^\d\s]*',dartResult) # 숫자 또는 문자+특수문자 조합 찾음
for idx, chars in zip(range(1,len(dart)+1,2),dart[1::2]):
answer.append(SDT(int(dart[idx-1]),chars[0])) # 숫자,(Single/Double/Triple 연산)
if len(chars) == 2 :
if chars[1] == '*':
answer[len(answer)-1] *= 2
if len(answer) != 1 : answer[len(answer)-2] *= 2
elif chars[1] == '#':
answer[len(answer)-1] *= (-1)
return sum(answer)
import re
def solution(dartResult):
bonus = {'S' : 1, 'D' : 2, 'T' : 3}
option = {'' : 1, '*' : 2, '#' : -1}
p = re.compile('(\d+)([SDT])([*#]?)')
dart = p.findall(dartResult)
for i in range(len(dart)):
if dart[i][2] == '*' and i > 0:
dart[i-1] *= 2
dart[i] = int(dart[i][0]) ** bonus[dart[i][1]] * option[dart[i][2]]
answer = sum(dart)
return answer
def solution(dartResult):
shots = [''] # shots[i] = i회차의 결과(str). shots[0] = ''
points = [0] # points[i] = i회차로 얻은 점수(int). points[0] = 0
muls = {'S':1,'D':2,'T':3} # (i영역 점수) **= muls[i]
s, p = '', '' # 각각 dartResult의 결과 부분, 점수 부분 (str)
strs = {'S','D','T','*','#'} # 결과 부분을 이루는 문자들
split = False # 회차가 구분되는 지점인지의 여부.
for i in dartResult:
if i.isdigit():
if split: # 만약 회차가 구분되는 지점이라면
shots.append(s) # shots에 결과 부분(str) 저장
points.append(int(p)) # point에 점수 부분(int) 저장
s, p = '', '' # 이후 결과 부분, 점수 부분과
split = False # '회차가 구분되는 지점인지' 여부를 초기화한다
p += i
else:
s += i
split = True
shots.append(s) # 리스트: shots 완성
points.append(int(p)) # 리스트: points 완성
for i in range(1,4): # 1 ~ 3회차 점수 계산
points[i] **= muls[shots[i][0]]
if len(shots[i]) == 2:
if shots[i][1] == '*': # 스타상 효과 적용
points[i-1] *= 2
points[i] *= 2
elif shots[i][1] == '#': # 아차상 효과 적용
points[i] *= -1
return sum(points)
SELECT
id
, SUM(IF(month="Jan", revenue, null)) AS Jan_Revenue
, SUM(IF(month="Feb", revenue, null)) AS Feb_Revenue
, SUM(IF(month="Mar", revenue, null)) AS Mar_Revenue
, SUM(IF(month="Apr", revenue, null)) AS Apr_Revenue
, SUM(IF(month="May", revenue, null)) AS May_Revenue
, SUM(IF(month="Jun", revenue, null)) AS Jun_Revenue
, SUM(IF(month="Jul", revenue, null)) AS Jul_Revenue
, SUM(IF(month="Aug", revenue, null)) AS Aug_Revenue
, SUM(IF(month="Sep", revenue, null)) AS Sep_Revenue
, SUM(IF(month="Oct", revenue, null)) AS Oct_Revenue
, SUM(IF(month="Nov", revenue, null)) AS Nov_Revenue
, SUM(IF(month="Dec", revenue, null)) AS Dec_Revenue
FROM
Department
GROUP BY
id
;
select id,
sum(case when month = "Jan" then revenue else NULL end) as Jan_Revenue,
sum(case when month = "Feb" then revenue else NULL end) as Feb_Revenue,
sum(case when month = "Mar" then revenue else NULL end) as Mar_Revenue,
sum(case when month = "Apr" then revenue else NULL end) as Apr_Revenue,
sum(case when month = "May" then revenue else NULL end) as May_Revenue,
sum(case when month = "Jun" then revenue else NULL end) as Jun_Revenue,
sum(case when month = "Jul" then revenue else NULL end) as Jul_Revenue,
sum(case when month = "Aug" then revenue else NULL end) as Aug_Revenue,
sum(case when month = "Sep" then revenue else NULL end) as Sep_Revenue,
sum(case when month = "Oct" then revenue else NULL end) as Oct_Revenue,
sum(case when month = "Nov" then revenue else NULL end) as Nov_Revenue,
sum(case when month = "Dec" then revenue else NULL end) as Dec_Revenue
from Department
group by id
order by id
;
SELECT
DISTINCT id
, SUM(CASE WHEN month = "Jan" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Jan_Revenue
, SUM(CASE WHEN month = "Feb" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Feb_Revenue
, SUM(CASE WHEN month = "Mar" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Mar_Revenue
, SUM(CASE WHEN month = "Apr" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Apr_Revenue
, SUM(CASE WHEN month = "May" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS May_Revenue
, SUM(CASE WHEN month = "Jun" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Jun_Revenue
, SUM(CASE WHEN month = "Jul" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Jul_Revenue
, SUM(CASE WHEN month = "Aug" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Aug_Revenue
, SUM(CASE WHEN month = "Sep" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Sep_Revenue
, SUM(CASE WHEN month = "Oct" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Oct_Revenue
, SUM(CASE WHEN month = "Nov" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Nov_Revenue
, SUM(CASE WHEN month = "Dec" THEN revenue END) OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS Dec_Revenue
FROM
Department
;
linear_model = LinearRegression()

RMSEMAEmean_squared_error)오차 기반 평가 지표)mean_absolute_error)
# 가설함수 h(x)
def h(w, x):
return w*x + 0
# 비용함수, 손실함수 (mse)
def loss (data, target, weight):
# 예측할 데이터의 X: data, 실제 답: target, 예측가중치: weight
y_pre = h(weight, data)
mse = np.mean((target - y_pre)**2)
return mse
# 예측한 가중치가 10
loss(score["시간"], score["성적"], 10) # np.float64(0.0)
# 예측한 가중치가 5
loss(score["시간"], score["성적"], 5) # np.float64(1031.25)
# 예측한 가중치가 15
loss(score["시간"], score["성적"], 15) # np.float64(1031.25)

# x축의 범위 (예측가중치의 변화) -> -10~30
w_arr = range(-10, 30+1)
# mse를 리스트에 저장 (반복)
loss_list = []
for w in w_arr:
mse = loss(score["시간"], score["성적"], w)
loss_list.append(mse)
# 간단한 반복문 -> 리스트 안에 누적할 때 사용
# 리스트명 = [반복하여 담을 데이터 for i in range()]
loss_list = [loss(score["시간"], score["성적"], w) for w in w_arr]
plt.subplots()
plt.plot(w_arr, loss_list) # x축, y축
plt.xlabel("w")
plt.ylabel("loss")
# 그래프를 보는 사람이 이해할 수 있도록 label 적는 습관 가지기
plt.show()


from sklearn.linear_model import SGDRegressor
sgd_model = SGDRegressor()
sgd_model.fit(score[["시간"]], score["성적"])


print("가중치(w):", sgd_model.coef_)
print("절편(b):", sgd_model.intercept_)
# R2 score 확인하기
sgd_model.score(X = score[["시간"]], y = score["성적"])
# 1에 가까울수록 성능이 좋은 모델~
sgd_model2 = SGDRegressor(
eta0 = 0.0001, # 러닝레이트: 기본값 0.01
max_iter = 5000, # w, b값 업데이트 횟수, 기본값 1000
verbose = 1 # 학습진행상황 출력 -> 학습 업데이트마다의 결과값 확인
)
sgd_model2.fit(score[["시간"]], score["성적"])
# Epoch: 학습 반복 횟수
sgd_model2.score(score[["시간"]], score["성적"])
sgd_model_tuning = SGDRegressor(
eta0 = 0.0001,
max_iter = 10000,
verbose=1
)
sgd_model_tuning.fit(X = X_train, y = y_train)
sgd_model_tuning.score(X_test, y_test)
거리 계산을 하는 모델은 데이터의 스케일이 중요합니다!
# 강사님 코드
from sklearn.preprocessing import StandardScaler
# 스케일러 객체 생성
st_scaler = StandardScaler()
# 우리의 데이터에 맞게 학습 후 데이터를 반환
X_train_scaled = st_scaler.fit_transform(X_train)
# test 데이터도 변환
# 주의! test 데이터는 변환만! (학습 X)
X_test_scaled = st_scaler.transform(X_test)
# 모델 생성 및 학습
sgd_model4 = SGDRegressor(eta0=0.0001)
sgd_model4.fit(X_train_scaled, y_train)
sgd_model4.score(X_test_scaled, y_test)









→ 1 epoch 과정마다 오차를 확인해 오차가 줄어드는 방향으로 학습하는 것이 전체적인 모델의 원리

오차가 상대적으로 작게 느껴져 문제가 될 수 있음
예시: 타이타닉 생존 여부 예측(label 0, 1)
| y | ||
|---|---|---|
| 1 | 1 | 0 |
| 0 | 1 | 1 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
→ accuracy: 1/4 = 0.25
→ mse: 3/4 = 0.75 -> 4개 중 3개나 틀린 모델을 0.75만큼만 수정한다는 의미
: mse는 많이 틀리면(오차 크면) 많이 수정해야 하고 오차 작으면 조금만 수정함 → 오차를 크게 보기 위해 제곱하는 건데 1은 제곱해도 1이라 오차가 작게 느껴짐



# 라이브러리 불러오기
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# warning 제거
import warnings
warnings.filterwarnings('ignore')
# 데이터 불러오기
df = pd.read_csv("./data/job_transfer.csv")
# 형태 확인
df.shape
| 컬럼명 | 설명 |
|---|---|
| Age | 직원의 나이 |
| Attrition | 이직 여부 (Yes, No) |
| BusinessTravel | 출장을 얼마나 자주 가는지 (Non-Travel, Travel_Rarely, Travel_Frequently) |
| DailyRate | 일일 급여 |
| Department | 부서 (Sales, Research & Development, Human Resources) |
| DistanceFromHome | 집에서 직장까지의 거리 |
| Education | 교육 수준 (1: Below College, 2: College, 3: Bachelor, 4: Master, 5: Doctor) |
| EducationField | 전공 분야 (Life Sciences, Other, Medical, Marketing, Technical Degree, Human Resources) |
| EmployeeCount | 직원 수 (모든 행이 1로 동일) |
| EmployeeNumber | 직원 식별 번호 |
| EnvironmentSatisfaction | 직무 환경 만족도 (1: Low, 2: Medium, 3: High, 4: Very High) |
| Gender | 성별 (Male, Female) |
| HourlyRate | 시간당 급여 |
| JobInvolvement | 직무 몰입도 (1~4) |
| JobLevel | 직무 레벨 (직위 수준) |
| JobRole | 직무 유형 (Sales Executive, Research Scientist, Laboratory Technician, 등) |
| JobSatisfaction | 직무 만족도 (1: Low, 2: Medium, 3: High, 4: Very High) |
| MaritalStatus | 결혼 여부 (Single, Married, Divorced) |
| MonthlyIncome | 월 급여 |
| MonthlyRate | 월 급여 비율 |
| NumCompaniesWorked | 근무했던 회사 수 |
| Over18 | 18세 이상 여부 (모든 값이 Y) |
| OverTime | 초과 근무 여부 (Yes, No) |
| PercentSalaryHike | 급여 인상 비율 (%) |
| PerformanceRating | 성과 평가 (1~4) |
| RelationshipSatisfaction | 동료 및 상사와의 관계 만족도 (1~4) |
| StandardHours | 표준 근무 시간 (모든 값이 80) |
| StockOptionLevel | 스톡옵션 수준 (0~3) |
| TotalWorkingYears | 총 경력 연수 |
| TrainingTimesLastYear | 작년에 받은 교육 횟수 |
| WorkLifeBalance | 워라밸 수준 (1: Bad, 2: Good, 3: Better, 4: Best) |
| YearsAtCompany | 현 회사 근속 연수 |
| YearsInCurrentRole | 현재 직무 근속 연수 |
| YearsSinceLastPromotion | 마지막 승진 이후 경과 연수 |
| YearsWithCurrManager | 현재 매니저와 함께 일한 연수 |
# 정답 데이터 확인
df["Attrition"].value_counts()
# 이직률 확인
df["Attrition"].value_counts(normalize=True)
sum(df["Attrition"] == "Yes")/len(df["Attrition"])*100
# 16.12% 이직률
# 학습 및 집계함수를 사용하기 위하여 정답 데이터를 수치 데이터로 변경
# 이직: 1, 이직 안 함: 0
# np.where(조건, 조건에 해당 시 반환할 값, 미해당 시 반환할 값)
df["Attrition"] = np.where(df["Attrition"]=="Yes", 1, 0)
df["Attrition"].value_counts()
# 연령 데이터 범주화
np.sort(df["Age"].unique())
# 세분화 되어있는 연령 데이터를 범주화
# 30세 미만, 30세~39세 -> 30대, 40세 이상
df["Age_gp"] = np.where(df["Age"]<30, "30대 미만", np.where(df["Age"]<40, "30대", "40대 이상"))
df["Age_gp"].value_counts()
# 연령대별 이직률 현황
df.groupby("Age_gp")["Attrition"].value_counts()
# 이직률을 어떻게 구할까?
# 이직률 = 이직한 직원 수 / 전체 인원 수 * 100
# 연령대별 이직률 구하는 법
# 1. Age_gp 기준 groupby
# 2. 이직한 직원 수 sum → Attrition이 1이기 때문에 더하면 됨
# 3. 전체 인원 수 count
df_gp = df.groupby("Age_gp")["Attrition"].agg(["count", "sum"])
df_gp["ratio"] = round((df_gp["sum"]/df_gp["count"])*100, 1)
df_gp["ratio"].sort_values(ascending=False)
# 나이가 어릴수록 이직률이 높아진다!
# 성별에 따른 이직률 현황
df_gd = df.groupby("Gender")["Attrition"].agg(["count", "sum"])
df_gd["ratio"] = round((df_gd["sum"]/df_gd["count"])*100, 1)
df_gd.sort_values(by="ratio", ascending=False)
# 해석:
# 남자에 비해 여자의 이직률이 낮은 편
# 컬럼에 따라 이직률 현황 -> 유의미한 결과가 나올 수 있는 컬럼 찾기

유의미한 결과가 있어 보이는 컬럼
point
# 인간 관계 만족도, 업무 만족도 컬럼 가져오기 -> 숫자가 클수록 만족도가 높음
df[["RelationshipSatisfaction", "JobSatisfaction", "Attrition"]].head()
# 인간 관계 만족도, 업무 만족도에 따른 이직률 확인
df_eda = df.groupby(["RelationshipSatisfaction", "JobSatisfaction"])["Attrition"].agg(["count", "sum"])
df_eda["ratio"] = round((df_eda["sum"]/df_eda["count"])*100, 1)
df_eda["ratio"]
# 인간관계 만족도가 높다고 하여 이직률이 적은 것은 아님
# 업무만족도가 높은 직원은 인간 관계에 영향을 덜 받는다
# 업무만족도가 낮은 직원은 인간 관계가 나쁠수록 이직률이 증가하는 경향을 보인다~
# 업무만족도를 높여주는 것이 더 효과적이겠다!
# YearsInCurrentRole: 직원이 현재 역할에서 근무한 기간
# YearsAtCompany: 직원이 현재까지 근무한 기간
df[["YearsInCurrentRole", "YearsAtCompany"]]
# 현재 회사는 업무의 변경이 적은 편 -> 적으니까 이직을 많이 하지 않을까?
# 근속년수 대비 한 가지 업종에서 업무를 한 비중
df["Role_Company"] = df["YearsInCurrentRole"]/df["YearsAtCompany"]
df["Role_Company"].fillna(0, inplace=True)
df["Role_Company"]
# 0 / 0은 연산 X -> NaN 출력 -> 0으로 대체(fillna(0))
# 분포와 밀도를 확인
plt.subplots()
sns.distplot(x=df["Role_Company"])
plt.show()

# Role_company 값을 범주화: 0.4 미만, 0.4 이상 0.7 미만, 0.7 이상
# cut은 bin 지정해 줘야 하고 좀 복잡한데 where는 조건만 넣으면 됨
df["Role_Company_gp"] = np.where(df["Role_Company"]<0.4, "0.4 미만", np.where(df["Role_Company"]<0.7, "0.4 이상 0.7 미만", "0.7 이상"))
df["Role_Company_gp"].unique() # NaN이 남아 있지 않은지 확인용
# 근속년수 대비 같은 일을 오래 한 경우 이직률이 낮다 -> 위 범주에 대한 이직률 확인
df_eda = df.groupby("Role_Company_gp")["Attrition"].agg(["count", "sum"])
df_eda["ratio"] = round((df_eda["sum"]/df_eda["count"])*100, 1)
df_eda.sort_values(by="ratio", ascending=False)
# 근속년수 대비 동일한 업무를 오래 한 경우 이직률이 낮다!
df.corr(numeric_only=True)["Attrition"].abs().sort_values(ascending=False)
흩어져 있는 연속형 데이터들의 분포를 확인할 때 그래프를 그려주면 좋음 → violin plot, distplot, histogram, …







# 계속 쓸 거니까 함수로 만들었음
def get_attrition_ratio(df, *args):
# args가 1개이고, 그 값이 리스트/튜플이면 풀어서 전달
if len(args) == 1 and isinstance(args[0], (list, tuple)):
group_cols = args[0]
else:
group_cols = list(args)
df_eda = df.groupby(group_cols)["Attrition"].agg(["count", "sum"])
df_eda["ratio"] = round((df_eda["sum"]/df_eda["count"])*100, 1)
return df_eda["ratio"].sort_values(ascending=False)
# 야근 여부에 따른 이직률 현황
get_attrition_ratio(df, "OverTime")
# 야근을 많이 할수록 이직률이 높다!
# PercentSalaryHike: 연봉인상률
get_attrition_ratio(df, "OverTime", "PercentSalaryHike")
# 야근 Yes 사람들의 이직률 평균 확인
# 야근 Yes 직원 Data 분석 -> 연봉인상률(x)에 따른 이직률 (y)
df_gp_plot = get_attrition_ratio(df, "OverTime", "PercentSalaryHike").reset_index()
df_gp_yes = df_gp_plot[df_gp_plot['OverTime'] == 'Yes']
plt.subplots()
sns.barplot(data=df_gp_yes, x="PercentSalaryHike", y="ratio")
plt.ylim(10,)
plt.show()
