Data 원본 출처
Target Data(CSV): Global Internet Usage(국가별 인터넷 사용률)
참고사항
- 위 3개의 Data들은 생성 시기가 다르므로 이 Test에서 도출되는 결과는 실제와 일치하지 않습니다.
- 문제에 hint가 있을 경우, 해당 hint를 이용하지 않으셔도 무방합니다.
- 해당 Test는 Wiki의 Online Data를 가져오는 내용을 포함하고 있습니다
우선 csv 파일을 읽어 DataFrame으로 변환 하겠습니다.
import pandas as pd
# 채점을 위한 코드입니다. 반드시 실행해주세요.
from grading import *
df_target = pd.read_csv('./datas/gapminder_internet.csv')
df_target
1단계: Target Data 불러오기 & 전처리
문제 1-1) Target Data 전처리 01 (5점)
- 위에서 읽은 DataFrame에서 Null값을 처리하고자 합니다. 아래 조건에 맞게 Null값을 처리하세요.
- 조건1: 'incomeperperson', 'internetuserate', 'urbanrate' Column(열)에 하나라도 Null값이 있다면 그 row(행)를 삭제(drop)하세요.
- 조건2: Index와 순서(order)는 변경하지 마세요.
# 1-1
df_target.dropna(axis=0, inplace=True)
check_01_01(df_target)
Nan값이 있는 해당 행을 삭제하기 위해 dropna와 axis = 0을 활용하였습니다.
문제 1-2) Target Data 전처리 02 (5점)
- 1-1의 DataFrame(df_target)과 아래의 df_target_change_list를 이용하여 아래 조건에 맞게 국가명(컬럼명: 'country')을 변경하세요.
- 참고: 국가명을 변경하는 이유는 추후(문제 2-4) pycountry Library를 사용하여 국가코드(ex: 대한민국-KR)를 얻기 위함입니다.
- 아래 df_target_change_list는 변경 대상인 df_target의 index와 그에 맞는 국가명이 쌍(tuple)들을 값으로 가지고 있습니다.
- 조건1: df_target_change_list를 이용하여 df_target의 국가명을 변경하세요.
- 조건2: Index 또는 순서(order)는 변경하지 마세요.
df_target_change_list = [
(33, 'Cabo Verde'),
(35, 'Central African Republic'),
(41, 'Congo, The Democratic Republic of the'),
(42, 'Congo'),
(45, "Côte d'Ivoire"),
(49, 'Czech Republic'),
(53, 'Dominican Republic'),
(83, 'Hong Kong'),
(100, 'Korea, Republic of'),
(103, "Lao People's Democratic Republic"),
(112, 'Macao'),
(113, 'Republic of North Macedonia'),
(125, 'Federated States of Micronesia'),
(183, 'Eswatini'),
(210, 'Yemen')
]
# 1-2
for v in df_target_change_list:
df_target['country'].loc[v[0]] = v[1]
check_01_02(df_target)
우선 위의 df_target_change_list의 국가명으로 변환 시키기 위해 해당 리스트에 있는 인덱스 값을 사용하기위해 loc함수를 이용하였습니다.
그리고 for 문을 통해 df_target_change_list의 0번 인덱스(바꿔야 할 국가명이 있는 인덱스 번호)를 활용해 바꾸고 싶은 인덱스가 있는 곳으로 이동 후 1번 인덱스에 있는 국가명으로 바꿔주는 작업을 하였습니다.
loc, iloc : 인덱스를 활용하고 싶을 때 사용하는 함수인 것을 생각해주시면 매우 좋을 것 같습니다.
문제 1-3) Target Data 전처리 03 (10점)
- 1-2의 DataFrame(df_target)과 pycountry Library를 이용하여 아래 조건에 맞게 국가코드를 구하세요.
- 참고: pycountry.countries
- ISO 3166-2(전 세계 나라 및 부속 영토의 주요 구성 단위의 명칭에 고유 부호(코드)를 부여하는 국제 표준) 기준 국가별 코드를 얻을 수 있는 Python Library
- 참고: pycountry.countries
- 국가명 표기 방식에 따라 잘못된 값 또는 값 검색이 안되는 경우가 많아 주의가 필요함
- 예시: 대한민국의 경우 'south korea', 'republic of korea', 'korea', 'korea, republic of' 등으로 표기되는데, 이 중 'korea, republic of' 로만 정확한 국가 코드를 얻을 수 있음 - 혼선을 줄이기 위하여 문제 1-2와 같이 변경할 국가명을 제공함
- 사용법(상세 사용법은 2.Datas의 참고사항 내 링크 참조)
- 국가 검색 방법
- 일반 검색: pycountry.countries.get(name=country_name) -> 하나의 결과값을 return - fuzzy 검색: pycountry.countries.search_fuzzy(country_name) -> 하나 이상의 결과값을 list형태로 return
- 조건1: df_target에 'code'컬럼을 추가하여 각 row(행)의 국가명에 맞는 2글자 국가코드(alpha_2)를 입력하세요.
- 조건2: 일반검색(pycountry.countries.get(name=country_name))을 우선 이용 해보고, 결과값이 안 나올 경우 fuzzy 검색(pycountry.countries.search_fuzzy(country_name))을 활용하여 검색하세요.
- 조건3: fuzzy 검색을 이용할 경우, 결과값 list의 첫번째 값(index=0)의 국가코드를 입력하세요.
- 조건4: Index 또는 순서(order)는 변경하지 마세요.
- hint: Python 예외처리(try-except)를 활용해 보세요. -> 아마 일반 검색으로 안되는 경우 except를 활용하여 fuzzy검사로 하는 방법을 떠올리면 될 것 같습니다.
# 1-3
import pycountry
country_list = []
for i in range(0, len(df_target['country'])):
country_name = df_target['country'].iloc[i]
# 국가명이 바뀐 관계로 계속 Turkey에서 오류 발생 -> Türkiye로 변경
if country_name == 'Turkey':
country_name = 'Türkiye'
try:
country = pycountry.countries.get(name=country_name)
country_list.append(country.alpha_2)
except:
country = pycountry.countries.search_fuzzy(country_name)
country_list.append(country[0].alpha_2)
df_target['code'] = country_list
check_01_03(df_target)
국가 검색 방법을 사용하기 위해 country 컬럼에 있는 값을 하나하나 가져와서 country_name 변수에 저장하여 일반 검색을 해보았습니다.
하지만 일반 검색으로만 할 경우 중간에 오류가 생기게 됩니다.
그래서 except를 활용하여 fuzzy 검색을 넣어서 일반 검색으로는 안되는 경우 fuzzy 검색으로 하여 모든 국가가 검색되도록 만들었습니다.
여기서 alpha_2 : 국가 코드입니다. ex) 한국 -> KR
이제 국가코드를 뽑아내어 country_list에 모두 저장 시킨 후 새로운 컬럼인 'code'에 추가해주었습니다.
여기서 문제를 푸는 중 'LookupError: turkey'라는 에러가 발생하였습니다. 발생 원인이 'turkey' 라는 국가명이 사라지고 'Türkiye'로 국가명이 바뀌어 오류가 발생하는 것이였습니다.
그래서 코드 중간에 if문을 활용하여 ' turkey' -> ' Türkiye'로 바꿔주는 작업을 하였습니다.
2단계: Reference Data01: 불러오기 & 전처리 & 합치기
문제 2-1) Web Data 가져오기 (10점)
- 아래 제시된 Link에 있는 국가별 인구에 대한 Table을 아래 조건에 맞게 예시와 같이 Pandas DataFrame으로 불러오세요.
- 조건: 해당 DataFrame의 변수명을 'df_population'으로 지정해주세요.
- hint: pandas의 기능 중에 웹페이지의 Table들을 읽어와 DataFrame으로 만들어주는 method가 있습니다.
table = pd.read_html('https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population')
df_population = table[0]
df_population.columns
df_population.rename(columns={'Unnamed: 0':'Rank', 'Location':'Country / Dependency'}, inplace=True)
check_02_01(df_population)
pandas의 read_html을 활용하면 해당 웹페이지의 표로 구성된 모든 데이터를 가져올 수 있습니다.
여기서 표로 구성된 데이터를 어떻게 구별하는 가?
-> html에 표는 <table></table>로 구성되어 있는데 해당 구간을 가져오는 것입니다.
표가 여러개라면 리스트로 저장되게 됩니다. 그래서 위의 코드에서 저희가 가져오고 싶은 데이터가 0번 인덱스에 존재하여 갸져왔으며, 정답과 같은 컬럼을 유지하기 위해 컬럼이름을 바꿔주는 작업가지 하였습니다.
문제 2-2) Population Data 전처리 01 (5점)
- 2-1의 DataFrame(df_population)을 아래의 조건에 맞게 변경하세요.
- 조건1: Column(열) 중 'Country / Dependency', 'Population' 2개만 남기세요.
- 조건2: 컬럼명을 아래와 같이 변경하세요.
- 'Country / Dependency' -> 'country'
- 'Population' -> 'population'
- 조건3: 'country' Column(열)의 값이 'World'인 첫번째 인덱스(index=0)을 삭제하세요.
- 조건4: Index 또는 순서(order)는 변경하지 마세요.
# 2-2
columns = ['Country / Dependency', 'Population']
df_population = df_population[columns]
df_population.rename(columns={'Country / Dependency':'country', 'Population':'population'}, inplace=True)
df_population.drop(index=0, axis=0, inplace=True)
check_02_02(df_population)
이제 두 개의 컬럼만 남기고 나머지는 없애 줘야 하기 때문에 columns 라는 변수에['Country / Dependency', 'Population'] 해당 값들을 저장 해줍니다.
그리고 rename을 사용하여 이름을 조건에 맞게 변환 해주고 첫번째 인덱스의 행의 값을 삭제해야하니 drop을 이용하여 삭제 해줍니다.
문제 2-3) Population Data 전처리 02 (5점)
- 2-2의 DataFrame(df_population)과 아래의 df_population_change_dict를 이용하여 아래 조건에 맞게 국가명(컬럼명: 'country')을 변경하세요.
- 참고: 국가명을 변경하는 이유는 추후 문제에서 1단계에서 만들었던 df_target과 합치기 위함입니다.
- 변경 국가는 1단계의 df_target에 있는 국가만 해당됩니다.
- 일부 국가명의 표현이 다르기 때문에 df_target을 기준으로 표기를 통일합니다.
- 참고: 국가명을 변경하는 이유는 추후 문제에서 1단계에서 만들었던 df_target과 합치기 위함입니다.
- 아래 df_population_change_dict는 변경 대상인 df_population의 기존 국가명과 변경할 국가명이 key-value로 이루어져 있습니다.
- 조건1: df_population_change_dict를 이용하여 df_population의 국가명을 변경하세요. - 조건2: Index 또는 순서(order)는 변경하지 마세요.
df_population_change_dict = {
'Bermuda (United Kingdom)': 'Bermuda',
'Cape Verde': 'Cabo Verde',
'DR Congo': 'Congo, The Democratic Republic of the',
'Ivory Coast': "Côte d'Ivoire",
'Greenland (Denmark)': 'Greenland',
'Hong Kong (China)': 'Hong Kong',
'South Korea': 'Korea, Republic of',
'Laos': "Lao People's Democratic Republic",
'Macau (China)': 'Macao',
'North Macedonia': 'Republic of North Macedonia',
'Micronesia': 'Federated States of Micronesia',
'Puerto Rico (United States)': 'Puerto Rico',
'Slovakia': 'Slovak Republic',
'East Timor': 'Timor-Leste',
}
# 2-3
for idx, value in df_population.iterrows():
for k, v in df_population_change_dict.items():
if value['country'] == k:
df_population.loc[idx, 'country'] = v
check_02_03(df_population)
우선 iterrows를 이용하여 해당 행들을 모두 체크 하면서 df_population_change_dict에서의 키 값과 동일한 국가 이름이 확인 되면 해당 국가이름을 df_population_change_dict의 value값으로 변경해주는 코드입니다.
.loc[인덱스 값, 컬럼명] -> 해당 인덱스의 컬럼에 해당하는 값을 의미합니다.
문제 2-4) Data 합치기(10점)
- 1단계의 DataFrame(df_target)과 2-3의 DataFrame(df_population)을 아래의 조건에 맞게 합치세요
- df_target에 df_population의 Data를 추가하려 합니다.
- 조건1: df_target, df_population의 국가명('country' Column(열)의 value)이 같은 Data끼리 합쳐주세요
- df_target에 df_population의 Data를 추가하려 합니다.
- 조건2: 결합 방식은 교집합(겹치는 국가명이 있는 경우만 추출)으로 지정해 주세요.
- 조건3: Column(열)방향으로 DataFrame을 합쳐주세요.
- 조건4: 'code' Column(열)을 기준으로 오름차순으로 정렬해주세요.
- 조건5: index를 reset해주세요.
- 조건6: 결과 DataFrame의 Column은 6개('country', 'incomeperperson', 'internetuserate', 'urbanrate', 'code', 'population')이어야 합니다.
- 조건7: 결과 DataFrame의 변수는 'df' 로 지정해 주세요
# 2-4
df = pd.merge(df_target, df_population, on='country', how='inner')
df = df.sort_values(by='code')
df = df.reset_index(drop=True)
df
check_02_04(df)
1. 처음에 만든 DataFrame과 웹사이트에서 가져온 DataFrame을 country를 기준으로 합쳐줄 것입니다.
2. 저는 합칠 때 merge를 사용하였습니다. 그리고 문제에서 교집합으로 지정하라 하였으니 inner를 사용하였습니다.
3. 마지막으로 조건 4, 조건 5에 맞게 오름차순과 reset_index()를 해주었습니다.
3단계: Reference Data02: 불러오기 & 전처리 & 합치기
새로운 데이터프레임을 가져와줍니다.
df_region = pd.read_csv('./datas/continents2.csv')
df_region
문제 3-1) Region Data 전처리 01 (10점)
- 위의 DataFrame(df_region)을 아래의 조건에 맞게 변경하세요.
- 조건1
- 특정 국가('Namibia')의 국가코드가 'NA'이기 때문에 위와 같이 DataFrame을 불러올경우 해당 국가코드가 NaN값으로 처리됩니다.
- 해당 국가의 국가코드를 'NA'로 변경하세요.(해당 값의 type이 string이어야 합니다.)
- 참고
- 아래 조건3에 따른 Column(열)명을 변경하기 전에는 'alpha-2', 변경 후에는 'code' Column(열)에 국가코드가 있습니다.
- 국가명 Column(열)명은 'name'입니다.
- 조건1
- 조건2 - 아래 df_region_drop_col은 Data 분석시 필요 없는 Column(열)입니다. - df_region_drop_col을 참고하여 df_region의 Column(열)들을 삭제(drop)하세요.
- 조건3 - 아래 df_region_rename_dict는 기존 컬럼명 - 변경할 컬럼명이 key-value로 이루어져 있습니다. - df_region_rename_dict를 참고하여 df_region의 Column(열)명을 변경하세요.
- 조건4: Index 또는 순서(order)는 변경하지 마세요.
df_region_drop_col = [
'country-code',
'alpha-3',
'iso_3166-2',
'region-code',
'sub-region-code',
'intermediate-region-code'
]
df_region_rename_dict = {
'alpha-2': 'code',
'sub-region': 'sub_region',
'intermediate-region': 'intermediate_region',
}
# 3-1
df_region[df_region['name'] == 'Namibia']
df_region['alpha-2'].loc[153] = 'NA'
df_region.drop(columns=df_region_drop_col, axis=1, inplace=True)
df_region.rename(columns=df_region_rename_dict, inplace=True)
df_region.info()
check_03_01(df_region)
우선 조건 1을 만족하기 위해 저는 Namibia국가의 인덱스를 찾기 위해 코드를 실행 시킨 후 153번이라는 인덱스가 확인 되어 해당 행의 국가 코드를 NA로 직접 바꿔 주었습니다.
그리고 조건 2를 위해 drop을 이용하였고 조건 3을 위해 rename을 사용하여 이름을 바꿔주었습니다.
문제 3-2) Data 합치기(10점)
- 2단계의 DataFrame(df)과 3-1의 DataFrame(df_region)을 아래의 조건에 맞게 합치세요.
- df에 df_region의 Data를 추가하려 합니다.
- 조건1: df, df_region 국가코드('code' Column(열)의 value)가 같은 Data끼리 합쳐주세요.
- df에 df_region의 Data를 추가하려 합니다.
- 조건2: 결합 방식은 교집합(겹치는 국가코드가 있는 경우만 추출)으로 지정해 주세요.
- 조건3: Column(열)방향으로 DataFrame을 합쳐주세요.
- 조건4: 'code' Column(열)을 기준으로 오름차순으로 정렬해주세요.
- 조건5 - 아래 df_rename_dict는 기존 컬럼명 - 변경할 컬럼명이 key-value로 이루어져 있습니다. - df_rename_dict를 참고하여 df의 Column(열)명을 변경하세요.
- 조건6 - 아래 new_col_order는 변경하고자하는 Column(열)의 순서대로 Column(열)명을 값으로 가지고 있습니다. - new_col_order를 참고하여 df의 Column(열)의 순서를 변경하세요.
- 조건7: 'name' Column(열)은 삭제(drop)해 주세요
- 조건8: index를 reset해주세요.
- 조건9: 결과 DataFrame의 Column은 6개('country', 'incomeperperson', 'internetuserate', 'urbanrate', 'code', 'population')이어야 합니다.
- 조건10: 결과 DataFrame의 변수는 'df' 로 지정해 주세요.
df_rename_dict = {
'incomeperperson': 'income_per_person',
'internetuserate': 'internet_use_rate',
}
df_drop_col = ['name']
new_col_order = [
'code',
'country',
'population',
'income_per_person',
'internet_use_rate',
'urbanrate',
'region',
'sub_region',
'intermediate_region'
]
# 데이터 합치기
df_merge = pd.merge(df, df_region, on='code', how='inner')
# 3-2
# 참고: 'name' Column(열)은 new_col_order 순서로 맞출 때 사라짐.
# 조건 4
df_merge.sort_values(by='code', inplace=True)
# 조건 5
df_merge.rename(columns=df_rename_dict, inplace=True)
# 조건 6
df_merge = df_merge[new_col_order]
# 조건 7 : 미리 위에서 없애서 삭제할 name이 없음.
# df_merge = df_merge.drop(columns=df_drop_col, inplace=True)
# 조건 8
df_merge = df_merge.reset_index(drop=True)
df = df_merge.copy()
df
해당 코드 내용은 조건에 맞게 주석을 맞춰놨습니다.
여기서 특이한 점은 없고 조건에 맞게 모든 값들을 설정해주시면 될 거 같습니다.
4단계: Data 분석하기(가중 평균 & 분산)
문제 4-1) 지역대륙별 가준 평균 구하기 (15점)
- 3단계 DataFrame(df)의 국가별 인터넷 사용률(internet_use_rate) 및 인당 소득(income_per_person)을 대륙(region) 또는 지역대륙(sub_region)별로 평균을 합산하여 보기 위해서는 단순 평균이 아닌 국가별 인구수(population)을 가중한 평균을 계산하여야 합니다.
- 3단계 DataFrame(df)을 이용하여 지역대륙(sub_region)별 조건에 맞게 가중평균을 구하세요.
- 참고
- 가중 평균: 자료의 평균을 구할 때 자료 값의 중요도나 영향 정도에 해당하는 가중치를 반영하여 구한 평균값
- 조건1: 가중치(weights)는 population Column(열)을 이용하세요.
- 조건2: 지역대륙(sub_region)의 인터넷 사용률(internet_use_rate) 및 인당 소득(income_per_person)을 아래 표와 같은 index, column 형태로 나타내주세요.
- index = ['region', 'sub_region']
- column = ['weighted_ave_internet', 'weighted_ave_income']
- 조건3: 결과 DataFrame의 변수는 'df_result' 로 지정해 주세요.
import numpy as np
# 4-1
def weighted_avg(values, weights):
return np.average(values, weights=weights, axis=0)
df_group = df.groupby(['region', 'sub_region']).apply(lambda x: weighted_avg(x[['internet_use_rate', 'income_per_person']], x['population'])).to_frame()
df_list = df_group[0].to_list()
internet_list = []
income_list = []
for i in df_list:
internet_list.append(i[0])
income_list.append(i[1])
df_group['weighted_ave_internet'] = internet_list
df_group['weighted_ave_income'] = income_list
df_group.drop(columns=0, inplace=True)
df_result = df_group
check_04_01(df_result)
1. 위의 그림처럼 출력하기 위해서는 group by를 통해 (region, sub_region)을 그룹핑하였습니다.
2. 이제 집계함수(mean())를 사용해야 해당 집계함수는 가중 평균을 구하는 기능이 없어 numpy에 있는 np.average함수를 이용하기 위해 함수를 만듭니다.
3. weighted_avg라는 함수를 직접 만들고 return 값으로 weights에 가중값을 넣도록 만들어 주었습니다.
4. apply() 함수에 lambda를 이용하여 weighted_avg 들어갈 value값과 weight값을 넣어 주었습니다.
5. 그룹핑과 집계를 끝낸 값을 to_frame을 이용하여 DataFrame으로 만들어줍니다.
6. 여기서 문제가 생겼습니다. 컬럼명이 0이고 해당 값들은 [weighted_ave_internet 값, weighted_ave_income] 이렇게 리스트에 묶여서 입력되어있는 것 확인되었습니다.
7. 그래서 해당 컬럼 값들을 먼저 모두 to_list()를 활용해 리스트에 저장을 해주었습니다.
8. 이제 for문을 통해 각각의 리스트에 넣어줍니다. 그리고 해당 리스트를 통해 weighted_ave_internet 컬럼, weighted_ave_income 컬럼에 추가하였습니다.
9. 이제 쓸데없는 컬럼인 컬럼명이 0인 데이터들을 모두 삭제하기 위해 drop을 사용하여 최종적으로 위의 사진처럼 만들어 주었습니다.
문제 4-2) 특정 조건의 가준 평균 구하기 (15점)
- 중국과 인도를 제외한 Eastern Asia, Southern Asia의 인터넷 사용률(internet_use_rate) 및 인당 소득(income_per_person)을 아래 조건에 맞게 구하세요.
- 참고: 4-1의 참고사항 참조
- 조건1: 가중치(weights)는 population Column(열)을 이용하세요.
- 조건2: 중국(국가코드(code): 'CN)과 인도(국가코드: 'IN')를 제외한 Asia(region) - Eastern Asia, Southern Asia (sub_region)의 인터넷 사용률(internet_use_rate) 및 인당 소득(income_per_person)을 아래 표와 같은 index, column 형태로 나타내주세요.
- index = ['region', 'sub_region']
- column = ['weighted_ave_internet', 'weighted_ave_income']
- 조건1: 가중치(weights)는 population Column(열)을 이용하세요.
# 중국 제외
df[(df['code'] == 'CN')]
df.drop(labels=33, axis=0, inplace=True)
# 인도 제외
df[(df['code'] == 'IN')]
df.drop(labels=77, axis=0, inplace=True)
# 4-2
import numpy as np
def weights_Avg(value, weights):
return np.average(value, weights=weights, axis=0)
df_group_Asia = df[df['region'] == 'Asia']
df_group_Asia = df[(df['sub_region'] == 'Eastern Asia') | (df['sub_region'] == 'Southern Asia')]
df_group_Asia = df_group_Asia.groupby(['region', 'sub_region']).apply(lambda x:weighted_avg(x[['internet_use_rate', 'income_per_person']], x['population'])).to_frame()
df_group_Asia['weighted_ave_internet'] = [i[0] for i in df_group_Asia[0]]
df_group_Asia['weighted_ave_income'] = [i[1] for i in df_group_Asia[0]]
df_group_Asia.drop(columns=0, inplace=True)
df_result = df_group_Asia
check_04_02(df_result)
이번 코드는 4-1에서 했던 것과 거의 동일합니다.
다른 점으로는 중국 제외, 인도 제외하는 코드들을 제외하고는 거의 모두 같습니다.
저는 이번에는 우선 리스트로 만들지 않으면서 for문 또한 따로 두지 않고, 위의 코드 12 ~ 16열의 내용을 리스트 컴프리헨션을 사용하여 한 번에 작업 하도록 만들어 주었습니다.
이상으로 EDA 분석 테스트를 마치겠습니다.
이 글은 제로베이스 데이터 분석 취업 스쿨의 강의 자료 일부를 발췌하여 작성되었습니다.
'Project > EDA 연습' 카테고리의 다른 글
[Zero-base] EDA 5회차 테스트 (10) | 2024.10.02 |
---|---|
[Zero-base] EDA 3회차 테스트 (0) | 2024.09.24 |