2021. 2. 4. 22:56ㆍ교육과정/KOSMO
키워드 : 데이터 연결하기 / 누락값 처리 / datetime 날짜 추출 / 에볼라 데이터 분석 / 파산은행 데이터 분석 / 미주별 인구 데이터 분석 / 컴퓨존 크롤링 분석
****
1. 데이터 연결하기
데이타 연결하기¶
(1) concat 메소드 연결¶
(2) merge()¶
[참고] 데이타사이언스 스쿨
https://datascienceschool.net/view-notebook/7002e92653434bc88c8c026c3449d27b/
import pandas as pd
df01 = pd.read_csv("data/transaction/201701.csv")
df02 = pd.read_csv("data/transaction/201702.csv")
df = pd.concat([df01, df02])
df
Name | date | amount | product | result | |
---|---|---|---|---|---|
0 | Kang | 2017-01-01 | 500 | apple | confirmed |
1 | Kim | 2017-01-03 | 700 | banana | confirmed |
2 | Choi | 2017-01-05 | 800 | apple | confirmed |
3 | Park | 2017-01-07 | 500 | cereal | canceled |
4 | Kang | 2017-01-08 | 1200 | kiwi | confirmed |
5 | Choi | 2017-01-09 | 100 | melon | confirmed |
6 | Lee | 2017-01-09 | 700 | banana | confirmed |
7 | Yoon | 2017-01-10 | 200 | cereal | canceled |
0 | Kim | 2017-02-04 | 100 | cereal | confirmed |
1 | Kang | 2017-02-12 | 200 | kiwi | canceled |
2 | Kang | 2017-02-15 | 650 | banana | confirmed |
3 | Park | 2017-02-28 | 420 | cereal | confirmed |
import pandas as pd
# 1. 데이타 가져오기
person = pd.read_csv('./data/survey_person.csv')
site = pd.read_csv('./data/survey_site.csv')
survey = pd.read_csv('./data/survey_survey.csv')
visited = pd.read_csv('./data/survey_visited.csv')
person.head(3)
ident | personal | family | |
---|---|---|---|
0 | dyer | William | Dyer |
1 | pb | Frank | Pabodie |
2 | lake | Anderson | Lake |
3 | roe | Valentina | Roerich |
4 | danforth | Frank | Danforth |
site.head(3)
name | lat | long | |
---|---|---|---|
0 | DR-1 | -49.85 | -128.57 |
1 | DR-3 | -47.15 | -126.72 |
2 | MSK-4 | -48.87 | -123.40 |
survey.head(3)
taken | person | quant | reading | |
---|---|---|---|---|
0 | 619 | dyer | rad | 9.82 |
1 | 619 | dyer | sal | 0.13 |
2 | 622 | dyer | rad | 7.80 |
3 | 622 | dyer | sal | 0.09 |
4 | 734 | pb | rad | 8.41 |
visited.head(3)
ident | site | dated | |
---|---|---|---|
0 | 619 | DR-1 | 1927-02-08 |
1 | 622 | DR-1 | 1927-02-10 |
2 | 734 | DR-3 | 1939-01-07 |
방법1
redata = pd.merge(site, visited, left_on='name', right_on='site')
redata.tail(3)
name | lat | long | ident | site | dated | |
---|---|---|---|---|---|---|
5 | DR-3 | -47.15 | -126.72 | 751 | DR-3 | 1930-02-26 |
6 | DR-3 | -47.15 | -126.72 | 752 | DR-3 | NaN |
7 | MSK-4 | -48.87 | -123.40 | 837 | MSK-4 | 1932-01-14 |
방법2
redata2 = site.merge(visited, left_on='name', right_on='site')
redata2.tail(3)
name | lat | long | ident | site | dated | |
---|---|---|---|---|---|---|
5 | DR-3 | -47.15 | -126.72 | 751 | DR-3 | 1930-02-26 |
6 | DR-3 | -47.15 | -126.72 | 752 | DR-3 | NaN |
7 | MSK-4 | -48.87 | -123.40 | 837 | MSK-4 | 1932-01-14 |
- person 과 survey 를 합친다면?
print(person.shape) # (5, 3)
print(survey.shape) # (21, 4)
redata3 = person.merge(survey, left_on='ident', right_on='person')
print(redata3.shape) # (19, 7)
# ---> 최소 21개의 행이 존재해야 하지만,
# merge 과정에서 null 값이 있는 행을 제외하고 합쳐져서 19개의 행이 redata3에 들어간다.
(5, 3)
(21, 4)
(19, 7)
# outer join 처럼 null값도 합쳐주어 누락되는 행 없이 redata3에 들어간다.
redata3 = person.merge(survey, how='outer', left_on='ident', right_on='person')
print(redata3.shape) # (22, 7)
redata3.head()
(22, 7)
ident | personal | family | taken | person | quant | reading | |
---|---|---|---|---|---|---|---|
0 | dyer | William | Dyer | 619.0 | dyer | rad | 9.82 |
1 | dyer | William | Dyer | 619.0 | dyer | sal | 0.13 |
2 | dyer | William | Dyer | 622.0 | dyer | rad | 7.80 |
3 | dyer | William | Dyer | 622.0 | dyer | sal | 0.09 |
4 | pb | Frank | Pabodie | 734.0 | pb | rad | 8.41 |
- visited 와 survey 를 합친다면?
print(visited.shape) # (8, 3)
print(survey.shape) # (21, 4)
redata4 = visited.merge(survey, left_on='ident', right_on='taken')
print(redata4.shape)
redata4 = visited.merge(survey, left_on='ident', right_on='taken', how='outer')
print(redata4.shape)
# ---> 현재 연결에서는 누락값이 없어 how옵션을 주지 않아도 되지만,
# 데이터를 확인하여 누락값에 대한 처리를 꼭 지정해줘야 한다.
(8, 3)
(21, 4)
(21, 7)
(21, 7)
(2-2). concat¶
import numpy as np
carddf = [
{"payment_method": "카드 결제", "installment": "일시불"},
{"payment_method": "카드 결제", "installment": "3개월"},
{"payment_method": "무통장 입금", "installment": np.nan},
{"payment_method": "카드 결제", "installment": "일시불"},
]
carddf = pd.DataFrame(carddf)
carddf
payment_method | installment | |
---|---|---|
0 | 카드 결제 | 일시불 |
1 | 카드 결제 | 3개월 |
2 | 무통장 입금 | NaN |
3 | 카드 결제 | 일시불 |
df02
Name | date | amount | product | result | |
---|---|---|---|---|---|
0 | Kim | 2017-02-04 | 100 | cereal | confirmed |
1 | Kang | 2017-02-12 | 200 | kiwi | canceled |
2 | Kang | 2017-02-15 | 650 | banana | confirmed |
3 | Park | 2017-02-28 | 420 | cereal | confirmed |
- 데이터 연결
result = pd.concat([df02, carddf])
result
Name | date | amount | product | result | payment_method | installment | |
---|---|---|---|---|---|---|---|
0 | Kim | 2017-02-04 | 100.0 | cereal | confirmed | NaN | NaN |
1 | Kang | 2017-02-12 | 200.0 | kiwi | canceled | NaN | NaN |
2 | Kang | 2017-02-15 | 650.0 | banana | confirmed | NaN | NaN |
3 | Park | 2017-02-28 | 420.0 | cereal | confirmed | NaN | NaN |
0 | NaN | NaN | NaN | NaN | NaN | 카드 결제 | 일시불 |
1 | NaN | NaN | NaN | NaN | NaN | 카드 결제 | 3개월 |
2 | NaN | NaN | NaN | NaN | NaN | 무통장 입금 | NaN |
3 | NaN | NaN | NaN | NaN | NaN | 카드 결제 | 일시불 |
→ 같은 인덱스끼리 합쳐지지 않은채 합쳐져서, 누락값이 데이터만큼 발생한다.
result = pd.concat([df02, carddf], axis=1) # index 가 같은 데이터들을 붙여줌
result
Name | date | amount | product | result | payment_method | installment | |
---|---|---|---|---|---|---|---|
0 | Kim | 2017-02-04 | 100 | cereal | confirmed | 카드 결제 | 일시불 |
1 | Kang | 2017-02-12 | 200 | kiwi | canceled | 카드 결제 | 3개월 |
2 | Kang | 2017-02-15 | 650 | banana | confirmed | 무통장 입금 | NaN |
3 | Park | 2017-02-28 | 420 | cereal | confirmed | 카드 결제 | 일시불 |
2. 누락값 처리
# 누락값 개념
from numpy import NaN, nan, NAN
print(NaN == False) # False
print(nan == 0 ) # False
print(NAN == '') # False
print(NaN == NaN) # False
# ---> null값은 null값이 아니다.
# ---> 누락값은 값 자체가 없기 때문에 자신과 비교 자체가 불가능
False
False
False
False
# 누락값 비교 방법 : pandas 메소드 이용
import pandas as pd
print( pd.isnull(NAN) ) # True
print( pd.isnull(False) ) # False
print( pd.isnull(0) ) # False
print( pd.isnull('') ) # False
# ---> 판다스에서 누락값을 확인하려면 isnull()을 사용해야 한다.
True
False
False
False
""" 누락값이 생기는 이유
1- 데이타 자체적으로 존재
2- 데이타 연결하면서
3- 데이타 입력시
4- 데이타 추출하여 연산할 때
"""
import pandas as pd
# (1) 데이터 불러오기
survey = pd.read_csv('./data/survey_survey.csv')
survey
visited = pd.read_csv('./data/survey_visited.csv')
visited
# (2) 데이터 연결하기
result = visited.merge(survey, left_on='ident', right_on='taken')
result.tail(9)
# ---> 13 ~ 16행에서 누락값이 발생했음을 확인할 수 있다.
ident | site | dated | taken | person | quant | reading | |
---|---|---|---|---|---|---|---|
12 | 751 | DR-3 | 1930-02-26 | 751 | lake | sal | 0.10 |
13 | 752 | DR-3 | NaN | 752 | lake | rad | 2.19 |
14 | 752 | DR-3 | NaN | 752 | lake | sal | 0.09 |
15 | 752 | DR-3 | NaN | 752 | lake | temp | -16.00 |
16 | 752 | DR-3 | NaN | 752 | roe | sal | 41.60 |
17 | 837 | MSK-4 | 1932-01-14 | 837 | lake | rad | 1.46 |
18 | 837 | MSK-4 | 1932-01-14 | 837 | lake | sal | 0.21 |
19 | 837 | MSK-4 | 1932-01-14 | 837 | roe | sal | 22.50 |
20 | 844 | DR-1 | 1932-03-22 | 844 | roe | rad | 11.25 |
Countires.tsv 파일에서¶
-
년도별 각 나라의 기대수명의 평균을 구한다
-
1번의 데이타 셋에서 2000년도에서 2010년 동안의 데이타 셋을 추출
-
값이 있는 데이타만 출력한다면
countries = pd.read_csv('./data/countries.tsv', sep='\t')
countries.isnull().sum()
# (1) 년도별 각 나라의 수명의 평균을 구한다
life_y = countries.groupby(countries['year'])
life_y[['lifeExp']].mean().head()
# (2) 2000~2010 동안의 데이타를 추출하고자 한다면
# (3) 범위 지정으로 인해 NaN값이 생긴 경우 해결책 -> 해당 데이타만 추출하고자
lifeExp | |
---|---|
year | |
1952 | 49.057620 |
1957 | 51.507401 |
1962 | 53.609249 |
1967 | 55.678290 |
1972 | 57.647386 |
""" 누락값 확인 """
ebola = pd.read_csv('./data/ebola_timeseries.csv')
# 제일 먼저 데이타 확인
ebola.shape # (122, 18)
# 누락값이 아닌 값의 개수 확인
ebola.count()
# 누락값의 수 구하기
ebola.shape[0] - ebola.count()
ebola.isnull().sum()
# 전체적인 누락값의 수를 얻으려면 - numpy 라이브러리의 count_nonzero() 이용
import numpy as np
np.count_nonzero(ebola.isnull()) # NaN 총 갯수 1214개
print(ebola.isnull().sum().sum())
1214
""" (1) 누락값 삭제하기 """
ebola.shape
redata = ebola.dropna()
redata
Date | Day | Cases_Guinea | Cases_Liberia | Cases_SierraLeone | Cases_Nigeria | Cases_Senegal | Cases_UnitedStates | Cases_Spain | Cases_Mali | Deaths_Guinea | Deaths_Liberia | Deaths_SierraLeone | Deaths_Nigeria | Deaths_Senegal | Deaths_UnitedStates | Deaths_Spain | Deaths_Mali | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
19 | 11/18/2014 | 241 | 2047.0 | 7082.0 | 6190.0 | 20.0 | 1.0 | 4.0 | 1.0 | 6.0 | 1214.0 | 2963.0 | 1267.0 | 8.0 | 0.0 | 1.0 | 0.0 | 6.0 |
""" (2) 누락값 대치하기 """
# 원본
print(ebola['Cases_Guinea'])
print(ebola['Cases_Liberia'])
ebola['hap'] = ebola['Cases_Guinea'] + ebola['Cases_Liberia']
print(ebola['hap'].sum())
print('-'*30)
#수정본
redata = ebola.fillna(0)
redata
print(redata['Cases_Guinea'])
print(redata['Cases_Liberia'])
redata['hap'] = redata['Cases_Guinea'] + redata['Cases_Liberia']
print(redata['hap'].sum())
0 2776.0
1 2775.0
2 2769.0
3 NaN
4 2730.0
...
117 103.0
118 86.0
119 86.0
120 86.0
121 49.0
Name: Cases_Guinea, Length: 122, dtype: float64
0 NaN
1 NaN
2 8166.0
3 8157.0
4 8115.0
...
117 8.0
118 NaN
119 NaN
120 NaN
121 NaN
Name: Cases_Liberia, Length: 122, dtype: float64
124294.0
------------------------------
0 2776.0
1 2775.0
2 2769.0
3 0.0
4 2730.0
...
117 103.0
118 86.0
119 86.0
120 86.0
121 49.0
Name: Cases_Guinea, Length: 122, dtype: float64
0 0.0
1 0.0
2 8166.0
3 8157.0
4 8115.0
...
117 8.0
118 0.0
119 0.0
120 0.0
121 0.0
Name: Cases_Liberia, Length: 122, dtype: float64
278562.0
그럼 누락값 처리로 Data Cleaning 후 데이터 처리(Data Process)를 진행한다면?
3. 누락값 처리
[ 연습 ] 누락값처리¶
import pandas as pd
df = pd.read_csv('data/nan_sample.csv', encoding='utf-8')
df
가수 | 곡명 | 좋아요 | |
---|---|---|---|
0 | 방탄소년단 | IDOL | 166128.0 |
1 | 선미 | 사이렌 | 80982.0 |
2 | 방탄소년단 | I'm Fine | 127021.0 |
3 | 벤 | 열애중 | NaN |
4 | 레드벨벳 | 뚜루뚜루 | 190526.0 |
# 좋아요의 평균값을 구하고자 한다면?
df['좋아요'].mean() # ---> 누락값이 있는 행을 제외하고 연산
141164.25
df['좋아요'].sum() / 5
112931.4
df['좋아요'].sum() / 4
141164.25
누락값(NaN)을 처리¶
df2 = df.fillna(0)
df2
가수 | 곡명 | 좋아요 | |
---|---|---|---|
0 | 방탄소년단 | IDOL | 166128.0 |
1 | 선미 | 사이렌 | 80982.0 |
2 | 방탄소년단 | I'm Fine | 127021.0 |
3 | 벤 | 열애중 | 0.0 |
4 | 레드벨벳 | 뚜루뚜루 | 190526.0 |
df2['좋아요'].mean()
112931.4
데이타가 많은 경우 NaN 확인하는 방법 - missngno¶
https://github.com/ResidentMario/missingno
라이브러리 설치 (Anaconda Prompt 관리자 권한으로 실행)
-
conda install missingno (안됨)
-
pip install missingno (됨)
Jupyter에서 바로 실행
-
!pip install missingno
# !pip install missingno
import numpy as np
df = pd.DataFrame( np.random.rand(100,100) )
df.head()
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.642715 | 0.777387 | 0.168464 | 0.662101 | 0.543614 | 0.244840 | 0.321924 | 0.528949 | 0.310633 | 0.712680 | ... | 0.075068 | 0.019455 | 0.889791 | 0.377738 | 0.784231 | 0.961710 | 0.917670 | 0.929737 | 0.130915 | 0.817256 |
1 | 0.767233 | 0.443309 | 0.093483 | 0.246049 | 0.168959 | 0.723528 | 0.990685 | 0.962938 | 0.772284 | 0.061267 | ... | 0.065645 | 0.600878 | 0.398327 | 0.266636 | 0.890757 | 0.685085 | 0.476743 | 0.841533 | 0.368682 | 0.299402 |
2 | 0.756465 | 0.998117 | 0.216216 | 0.645011 | 0.358663 | 0.195503 | 0.123516 | 0.157675 | 0.993299 | 0.270070 | ... | 0.763514 | 0.161213 | 0.711039 | 0.944474 | 0.886062 | 0.121209 | 0.515431 | 0.564180 | 0.874130 | 0.891172 |
3 | 0.289189 | 0.404608 | 0.792425 | 0.939494 | 0.913526 | 0.308734 | 0.821341 | 0.705629 | 0.178098 | 0.338269 | ... | 0.478900 | 0.535607 | 0.470904 | 0.240481 | 0.363744 | 0.710792 | 0.258685 | 0.917311 | 0.257667 | 0.506537 |
4 | 0.786843 | 0.396049 | 0.031413 | 0.736458 | 0.318608 | 0.954852 | 0.063411 | 0.398033 | 0.536616 | 0.215378 | ... | 0.161473 | 0.146894 | 0.868877 | 0.737248 | 0.547930 | 0.625596 | 0.584313 | 0.930165 | 0.301114 | 0.548086 |
5 rows × 100 columns
df = df[ df>0.1 ] # ---> 0.3보다 작은 데이터는 값이 들어오지 않아 누락값이 발생한다.
df
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.642715 | 0.777387 | 0.168464 | 0.662101 | 0.543614 | 0.244840 | 0.321924 | 0.528949 | 0.310633 | 0.712680 | ... | NaN | NaN | 0.889791 | 0.377738 | 0.784231 | 0.961710 | 0.917670 | 0.929737 | 0.130915 | 0.817256 |
1 | 0.767233 | 0.443309 | NaN | 0.246049 | 0.168959 | 0.723528 | 0.990685 | 0.962938 | 0.772284 | NaN | ... | NaN | 0.600878 | 0.398327 | 0.266636 | 0.890757 | 0.685085 | 0.476743 | 0.841533 | 0.368682 | 0.299402 |
2 | 0.756465 | 0.998117 | 0.216216 | 0.645011 | 0.358663 | 0.195503 | 0.123516 | 0.157675 | 0.993299 | 0.270070 | ... | 0.763514 | 0.161213 | 0.711039 | 0.944474 | 0.886062 | 0.121209 | 0.515431 | 0.564180 | 0.874130 | 0.891172 |
3 | 0.289189 | 0.404608 | 0.792425 | 0.939494 | 0.913526 | 0.308734 | 0.821341 | 0.705629 | 0.178098 | 0.338269 | ... | 0.478900 | 0.535607 | 0.470904 | 0.240481 | 0.363744 | 0.710792 | 0.258685 | 0.917311 | 0.257667 | 0.506537 |
4 | 0.786843 | 0.396049 | NaN | 0.736458 | 0.318608 | 0.954852 | NaN | 0.398033 | 0.536616 | 0.215378 | ... | 0.161473 | 0.146894 | 0.868877 | 0.737248 | 0.547930 | 0.625596 | 0.584313 | 0.930165 | 0.301114 | 0.548086 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
95 | 0.908515 | 0.392919 | 0.888173 | NaN | 0.481006 | 0.984725 | 0.828146 | 0.348857 | 0.941827 | 0.146011 | ... | 0.268430 | 0.100696 | 0.533775 | 0.477834 | 0.721789 | 0.897507 | 0.467466 | NaN | 0.121018 | 0.260464 |
96 | 0.950592 | NaN | 0.827415 | 0.717786 | 0.644170 | 0.503814 | 0.349139 | 0.603200 | 0.376063 | 0.339445 | ... | 0.890000 | NaN | 0.387085 | 0.994576 | 0.461619 | NaN | 0.210131 | 0.382241 | 0.665861 | 0.831750 |
97 | 0.402902 | 0.363249 | 0.134057 | NaN | 0.282914 | 0.947744 | 0.786524 | 0.711066 | 0.484069 | 0.633954 | ... | 0.341608 | 0.466547 | 0.740708 | 0.200742 | 0.658105 | 0.987811 | NaN | 0.182027 | 0.869835 | 0.105007 |
98 | NaN | 0.686285 | 0.210794 | 0.595374 | 0.861281 | 0.464900 | 0.956364 | 0.865276 | 0.450008 | 0.647631 | ... | 0.155437 | 0.106954 | 0.629965 | 0.240878 | 0.879401 | 0.325972 | 0.581642 | 0.813561 | 0.686166 | 0.139609 |
99 | 0.769512 | 0.307608 | 0.880494 | 0.550659 | 0.837059 | 0.338718 | 0.751095 | 0.491510 | 0.816108 | 0.938137 | ... | 0.660130 | 0.284210 | 0.663467 | 0.380044 | 0.345768 | 0.584015 | 0.475505 | 0.942920 | NaN | 0.148064 |
100 rows × 100 columns
import missingno
missingno.matrix(df); # ---> 결측치의 위치가 흰색으로 표시됨
4. Datetime 날짜 추출
datetime 라이브러리¶
- date
- time
- datetime
[ df ]
문자열 처리를 위해 str 접근자를 사용하듯이 datetime 오브젝트는 dt 접근자 사용해야 datetime 속성이나 메소드를 사용가능
""" datetime 라이브러리 : date / time / datetime 오브젝트 """
import datetime
datetime.datetime.now() # 현재 년월일시분초 출력
from datetime import datetime # 패키지명을 쓰지 않기 위해 from 사용
print(datetime.now())
print(datetime.today())
today = datetime.now() # 변수를 사용하여 연,월,일 추출
print(today.year)
print(today.month)
print(today.day)
aday = datetime(2019,1,1)
print(aday)
print(today - aday) # 날짜 차이 연산 가능
print(today.strftime('%Y-%m-%d')) # 문자열로 형 변환하여 추출 가능
2021-02-04 12:38:49.614233
2021-02-04 12:38:49.614234
2021
2
4
2019-01-01 00:00:00
765 days, 12:38:49.614233
2021-02-04
[도서] Do it 데이타분석을 위한 판다스 입문
[ 연습 ] datatime 오브젝트로 변경하여 필요한 데이타 추출 및 계산¶
import pandas as pd
ebola = pd.read_csv('./data/ebola_timeseries.csv')
ebola.head()
Date | Day | Cases_Guinea | Cases_Liberia | Cases_SierraLeone | Cases_Nigeria | Cases_Senegal | Cases_UnitedStates | Cases_Spain | Cases_Mali | Deaths_Guinea | Deaths_Liberia | Deaths_SierraLeone | Deaths_Nigeria | Deaths_Senegal | Deaths_UnitedStates | Deaths_Spain | Deaths_Mali | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1/5/2015 | 289 | 2776.0 | NaN | 10030.0 | NaN | NaN | NaN | NaN | NaN | 1786.0 | NaN | 2977.0 | NaN | NaN | NaN | NaN | NaN |
1 | 1/4/2015 | 288 | 2775.0 | NaN | 9780.0 | NaN | NaN | NaN | NaN | NaN | 1781.0 | NaN | 2943.0 | NaN | NaN | NaN | NaN | NaN |
2 | 1/3/2015 | 287 | 2769.0 | 8166.0 | 9722.0 | NaN | NaN | NaN | NaN | NaN | 1767.0 | 3496.0 | 2915.0 | NaN | NaN | NaN | NaN | NaN |
3 | 1/2/2015 | 286 | NaN | 8157.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 3496.0 | NaN | NaN | NaN | NaN | NaN | NaN |
4 | 12/31/2014 | 284 | 2730.0 | 8115.0 | 9633.0 | NaN | NaN | NaN | NaN | NaN | 1739.0 | 3471.0 | 2827.0 | NaN | NaN | NaN | NaN | NaN |
# datetime 오브젝트로 변경하여 필요한 데이타 추출 및 계산
# ebola.info() # 데이터타입이 ocject 라서 연월일 추출이 어렵다. ( str 로 / 기준 잘라야 함)
ebola['date_dt'] = pd.to_datetime(ebola['Date']) # 형변환 후 새 컬럼으로 추가
ebola.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 122 entries, 0 to 121
Data columns (total 19 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Date 122 non-null object
1 Day 122 non-null int64
2 Cases_Guinea 93 non-null float64
3 Cases_Liberia 83 non-null float64
4 Cases_SierraLeone 87 non-null float64
5 Cases_Nigeria 38 non-null float64
6 Cases_Senegal 25 non-null float64
7 Cases_UnitedStates 18 non-null float64
8 Cases_Spain 16 non-null float64
9 Cases_Mali 12 non-null float64
10 Deaths_Guinea 92 non-null float64
11 Deaths_Liberia 81 non-null float64
12 Deaths_SierraLeone 87 non-null float64
13 Deaths_Nigeria 38 non-null float64
14 Deaths_Senegal 22 non-null float64
15 Deaths_UnitedStates 18 non-null float64
16 Deaths_Spain 16 non-null float64
17 Deaths_Mali 12 non-null float64
18 date_dt 122 non-null datetime64[ns]
dtypes: datetime64[ns](1), float64(16), int64(1), object(1)
memory usage: 18.2+ KB
ebola[['Date', 'date_dt']] # Date는 object 타입 (단순문자열), date_dt는 datetime 타입
ebola['year'] = ebola['date_dt'].dt.year # datetime 에서 연도만 추출하기 위해 dt를 사용하여 접근 (str 과 유사)
ebola[['Date', 'date_dt', 'year']]
Date | date_dt | year | |
---|---|---|---|
0 | 1/5/2015 | 2015-01-05 | 2015 |
1 | 1/4/2015 | 2015-01-04 | 2015 |
2 | 1/3/2015 | 2015-01-03 | 2015 |
3 | 1/2/2015 | 2015-01-02 | 2015 |
4 | 12/31/2014 | 2014-12-31 | 2014 |
... | ... | ... | ... |
117 | 3/27/2014 | 2014-03-27 | 2014 |
118 | 3/26/2014 | 2014-03-26 | 2014 |
119 | 3/25/2014 | 2014-03-25 | 2014 |
120 | 3/24/2014 | 2014-03-24 | 2014 |
121 | 3/22/2014 | 2014-03-22 | 2014 |
122 rows × 3 columns
[ 연습 ] 에볼라 최초 발병일 구하기¶
firstday = ebola['date_dt'].min()
print(firstday)
lastday = ebola['date_dt'].max()
print(lastday)
print(lastday - firstday)
2014-03-22 00:00:00
2015-01-05 00:00:00
289 days 00:00:00
5. 파산은행 데이터 분석
파산한 은행 데이타집합에서 분기별 은행수를 계산하기¶
1. 먼저 데이타 구조를 파악하기
2. 'Closing Date' 날짜 항목을 datetime 오브젝트로 변기하기
3. 'Closing Date' 날짜 정보에서 dt 접근자를 이용하여 연도(year)와 분기(quarter)를 구하기
4. 연도별 파산은행 수 구하기
5. 연도별, 분기별 파산은행 수 구하기
6. 정보 시각화
[도서] Do it 데이타분석을 위한 판다스 입문
import pandas as pd
banks = pd.read_csv('./data/banklist.csv')
(1) 먼저 데이타 구조를 파악하기¶
banks
Bank Name | City | ST | CERT | Acquiring Institution | Closing Date | Updated Date | |
---|---|---|---|---|---|---|---|
0 | Washington Federal Bank for Savings | Chicago | IL | 30570 | Royal Savings Bank | 15-Dec-17 | 20-Dec-17 |
1 | The Farmers and Merchants State Bank of Argonia | Argonia | KS | 17719 | Conway Bank | 13-Oct-17 | 20-Oct-17 |
2 | Fayette County Bank | Saint Elmo | IL | 1802 | United Fidelity Bank, fsb | 26-May-17 | 26-Jul-17 |
3 | Guaranty Bank, (d/b/a BestBank in Georgia & Mi... | Milwaukee | WI | 30003 | First-Citizens Bank & Trust Company | 5-May-17 | 26-Jul-17 |
4 | First NBC Bank | New Orleans | LA | 58302 | Whitney Bank | 28-Apr-17 | 5-Dec-17 |
... | ... | ... | ... | ... | ... | ... | ... |
550 | Superior Bank, FSB | Hinsdale | IL | 32646 | Superior Federal, FSB | 27-Jul-01 | 19-Aug-14 |
551 | Malta National Bank | Malta | OH | 6629 | North Valley Bank | 3-May-01 | 18-Nov-02 |
552 | First Alliance Bank & Trust Co. | Manchester | NH | 34264 | Southern New Hampshire Bank & Trust | 2-Feb-01 | 18-Feb-03 |
553 | National State Bank of Metropolis | Metropolis | IL | 3815 | Banterra Bank of Marion | 14-Dec-00 | 17-Mar-05 |
554 | Bank of Honolulu | Honolulu | HI | 21029 | Bank of the Orient | 13-Oct-00 | 17-Mar-05 |
555 rows × 7 columns
banks.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 555 entries, 0 to 554
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Bank Name 555 non-null object
1 City 555 non-null object
2 ST 555 non-null object
3 CERT 555 non-null int64
4 Acquiring Institution 555 non-null object
5 Closing Date 555 non-null object
6 Updated Date 555 non-null object
dtypes: int64(1), object(6)
memory usage: 30.5+ KB
(2) Closing Date를 datetime 오브젝트로 변경¶
banks['Closing Date_dt'] = pd.to_datetime(banks['Closing Date'])
banks['Closing Date_dt']
0 2017-12-15
1 2017-10-13
2 2017-05-26
3 2017-05-05
4 2017-04-28
...
550 2001-07-27
551 2001-05-03
552 2001-02-02
553 2000-12-14
554 2000-10-13
Name: Closing Date_dt, Length: 555, dtype: datetime64[ns]
(3) 날짜 정보에서 dt 접근자를 이용하여 연도(year)와 분기(quarter)를 구한다¶
banks['year'] = banks['Closing Date_dt'].dt.year
banks['quarter'] = banks['Closing Date_dt'].dt.quarter
banks.head()
Bank Name | City | ST | CERT | Acquiring Institution | Closing Date | Updated Date | Closing Date_dt | year | quarter | |
---|---|---|---|---|---|---|---|---|---|---|
0 | Washington Federal Bank for Savings | Chicago | IL | 30570 | Royal Savings Bank | 15-Dec-17 | 20-Dec-17 | 2017-12-15 | 2017 | 4 |
1 | The Farmers and Merchants State Bank of Argonia | Argonia | KS | 17719 | Conway Bank | 13-Oct-17 | 20-Oct-17 | 2017-10-13 | 2017 | 4 |
2 | Fayette County Bank | Saint Elmo | IL | 1802 | United Fidelity Bank, fsb | 26-May-17 | 26-Jul-17 | 2017-05-26 | 2017 | 2 |
3 | Guaranty Bank, (d/b/a BestBank in Georgia & Mi... | Milwaukee | WI | 30003 | First-Citizens Bank & Trust Company | 5-May-17 | 26-Jul-17 | 2017-05-05 | 2017 | 2 |
4 | First NBC Bank | New Orleans | LA | 58302 | Whitney Bank | 28-Apr-17 | 5-Dec-17 | 2017-04-28 | 2017 | 2 |
(4) 연도별 파산은행 수 구하기¶
# banks.groupby('year').count()[['Closing Date']]
banks.groupby('year').count()[['Closing Date']].head()
Closing Date | |
---|---|
year | |
2000 | 2 |
2001 | 4 |
2002 | 11 |
2003 | 3 |
2004 | 4 |
(5) 연도별, 분기별 파산은행 수 구하기¶
closing_yq = banks.groupby(['year','quarter']).count()[['Closing Date']]
closing_yq.head()
Closing Date | ||
---|---|---|
year | quarter | |
2000 | 4 | 2 |
2001 | 1 | 1 |
2 | 1 | |
3 | 2 | |
2002 | 1 | 6 |
(6) 결과를 시각화 ( 간단하게 )¶
closing_yq.plot();
6. 미주별 인구 데이터 분석
미국 주와 주별 인구 데이타 예제¶
- 2010년도 인구 밀도 기준으로 미국 주와 지역 순위를 출력
- 데이타를 로딩 후 데이타 정보 확인
-
데이타 연결
1- 인구 데이타와 주이름 약어 데이타를 연결 ( 각 주의 전체 이름과 인구를 볼 수 있도록 ) 2- 각 주의 인구 데이타와 면적 데이타를 연결 ( 각 주의 면적당 인구를 계산하여 인구 밀도를 구하기 )
-
데이타 정제(데이타 전처리)
-
누락값 처리
- 삭제한다
- 대체한다
실제로 2000년 이전의 PR(푸에르토리토)의 인구 데이타가 없는 것이다. 여기서는 2010년 데이타를 구할 것이기에 삭제해도 되지만 대체하는 방식을 연습한다 또한 전체 미국을 뜻하는 USA에 state와 abbreviation이 없다.
-
- 2010년 인구밀도 계산하고 표현
인구밀도 = 인구 / 면적
[도서] 파이썬 데이타 사이언스 핸드북 - 위키북스/오렐리
1. 데이타를 로딩 후 데이타 정보 확인¶
# 1. 데이타를 로딩 후 데이타 정보 확인
import pandas as pd
pop = pd.read_csv('./data/state-population.csv') # 주별 인구
areas = pd.read_csv('./data/state-areas.csv') # 주의 면적
abbrevs = pd.read_csv('./data/state-abbrevs.csv') # 주이름 약자
# 데이타 살펴보기
print(pop.count())
pop.head()
state/region 2544
ages 2544
year 2544
population 2524
dtype: int64
state/region | ages | year | population | |
---|---|---|---|---|
0 | AL | under18 | 2012 | 1117489.0 |
1 | AL | total | 2012 | 4817528.0 |
2 | AL | under18 | 2010 | 1130966.0 |
3 | AL | total | 2010 | 4785570.0 |
4 | AL | under18 | 2011 | 1125763.0 |
print(areas.count())
areas.head()
state 52
area (sq. mi) 52
dtype: int64
state | area (sq. mi) | |
---|---|---|
0 | Alabama | 52423 |
1 | Alaska | 656425 |
2 | Arizona | 114006 |
3 | Arkansas | 53182 |
4 | California | 163707 |
print(abbrevs.count())
abbrevs.head()
state 51
abbreviation 51
dtype: int64
state | abbreviation | |
---|---|---|
0 | Alabama | AL |
1 | Alaska | AK |
2 | Arizona | AZ |
3 | Arkansas | AR |
4 | California | CA |
2. 데이타 연결¶
1- 인구 데이타(pop)와 주이름 약어 데이타(abbrevs)를 연결
( 각 주의 전체 이름과 인구를 볼 수 있도록 )
2- 각 주의 인구 데이타(pop)와 면적 데이타(areas)를 연결
( 각 주의 면적당 인구를 계산하여 인구 밀도를 구하기 )
# (1)
redata1 = pop.merge(abbrevs, left_on='state/region', right_on='abbreviation', how='outer')
print(pop.shape) # (2544, 4)
print(abbrevs.shape) # (51, 2)
print(redata1.shape) # (2544, 6)
redata1
# (2)
redata2 = redata1.merge(areas, left_on='abbreviation', right_on='state', how='outer')
redata2
(2544, 4)
(51, 2)
(2544, 6)
state/region | ages | year | population | state_x | abbreviation | state_y | area (sq. mi) | |
---|---|---|---|---|---|---|---|---|
0 | AL | under18 | 2012.0 | 1117489.0 | Alabama | AL | NaN | NaN |
1 | AL | total | 2012.0 | 4817528.0 | Alabama | AL | NaN | NaN |
2 | AL | under18 | 2010.0 | 1130966.0 | Alabama | AL | NaN | NaN |
3 | AL | total | 2010.0 | 4785570.0 | Alabama | AL | NaN | NaN |
4 | AL | under18 | 2011.0 | 1125763.0 | Alabama | AL | NaN | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... |
2591 | NaN | NaN | NaN | NaN | NaN | NaN | West Virginia | 24231.0 |
2592 | NaN | NaN | NaN | NaN | NaN | NaN | Wisconsin | 65503.0 |
2593 | NaN | NaN | NaN | NaN | NaN | NaN | Wyoming | 97818.0 |
2594 | NaN | NaN | NaN | NaN | NaN | NaN | District of Columbia | 68.0 |
2595 | NaN | NaN | NaN | NaN | NaN | NaN | Puerto Rico | 3515.0 |
2596 rows × 8 columns
3. 데이타 정제 - 누락값 처리¶
(1) 삭제한다
(2) 대체한다
실제로 2000년 이전의 PR(푸에르토리토)의 인구 데이타가 없는 것이다.
여기서는 2010년 데이타를 구할 것이기에 삭제해도 되지만 대체하는 방식을 연습한다
또한 전체 미국을 뜻하는 USA에 state와 abbreviation이 없다.
# (1) null 값 확인
redata.isnull().any()
# (2) 우선 state 컬럼의 null 확인
redata1['state'].isnull()
redata1
# (3) state/region의 값이 USA 인 state 컬럼을 찾아서 United State 라고 지정
redata1.loc[ redata1['state/region'] == 'USA', 'state' ] = 'United State'
# redata1.loc[ redata1['state/region'] == 'USA' ]
# (4) state/region의 값이 PR 인 state 컬럼을 찾아서 Puerto Rico 라고 지정
redata1.loc[ redata1['state/region'] == 'PR', 'state' ] = 'Puerto Rico'
# redata1.loc[ redata1['state/region'] == 'PR' ]
# 각 주의 인구를 연결한 데이타셋에 주의 면적 데이타 셋을 연결하기
# 1- pop과 abbreviation의 연결한 데이타셋과 areas(면적) 데이타셋을 연결
# state 컬럼이 양쪽에 존재하므로 각각 누락값이 추가 발생하므로 outer 대신 left 를 사용한다.
redata2 = redata1.merge(areas, left_on='state', right_on='state', how='outer')
redata2
# 2-널 값 여부 확인
redata2.isnull().any()
# 3- 널값 확인 : 면적값이 널인 주를 찾으면
redata2[['area (sq. mi)']]
area (sq. mi) | |
---|---|
0 | 52423.0 |
1 | 52423.0 |
2 | 52423.0 |
3 | 52423.0 |
4 | 52423.0 |
... | ... |
2539 | NaN |
2540 | NaN |
2541 | NaN |
2542 | NaN |
2543 | NaN |
2544 rows × 1 columns
# 4- 널값 처리 : 데이타 삭제하기
# 미국 전역에 대한 데이타는 인구 밀도와 관련이 없기에 삭제
print(redata2.shape)
# redata2.loc[ redata2['area'].isnull() ]
redata2.dropna(inplace=True)
print(redata2.shape)
(2544, 7)
(2448, 7)
# 5- [결과확인] 면적이 null 값이 있는지 없는지 확인
redata2.isnull().any()
state/region False
ages False
year False
population False
state False
abbreviation False
area (sq. mi) False
dtype: bool
2010년 인구밀도 계산하고 표현¶
인구밀도 = 인구 / 면적
필요한 데이타는 2010년 전체 인구에 대한 데이타집합이다.
# 필요한 데이타집합 추출
redata3 = redata2[redata2['year']==2010]
redata3 = redata3[redata2['ages']=='total']
# print(data)
# 주(state)를 기준으로 데이타 인덱스를 정하고 인구밀도 계산
redata3.set_index('state', inplace=True)
# print(data)
density = redata3['population']/redata3['area (sq. mi)']
density
density.sort_values(ascending=False, inplace=True)
print(density.head())
state
District of Columbia 8898.897059
New Jersey 1009.253268
Rhode Island 681.339159
Connecticut 645.600649
Massachusetts 621.815538
dtype: float64
[결과]¶
state
District of Columbia 8898.897059
New Jersey 1009.253268
Rhode Island 681.339159
Connecticut 645.600649
Massachusetts 621.815538
Maryland 466.445797
Delaware 460.445752
New York 356.094135
Florida 286.597129
Pennsylvania 275.966651
Ohio 257.549634
California 228.051342
Illinois 221.687472
Virginia 187.622273
Indiana 178.197831
North Carolina 177.617157
Georgia 163.409902
Tennessee 150.825298
South Carolina 144.854594
New Hampshire 140.799273
Hawaii 124.746707
Kentucky 107.586994
Michigan 102.015794
Washington 94.557817
Texas 93.987655
Alabama 91.287603
Louisiana 87.676099
Wisconsin 86.851900
Missouri 86.015622
West Virginia 76.519582
Vermont 65.085075
Mississippi 61.321530
Minnesota 61.078373
Arizona 56.214497
Arkansas 54.948667
Iowa 54.202751
Oklahoma 53.778278
Colorado 48.493718
Oregon 39.001565
Maine 37.509990
Kansas 34.745266
Utah 32.677188
Nevada 24.448796
Nebraska 23.654153
Idaho 18.794338
New Mexico 16.982737
South Dakota 10.583512
North Dakota 9.537565
Montana 6.736171
Wyoming 5.768079
Alaska 1.087509
[분석결과]
인구 밀도가 가장 높은 주는 District of Columbia(DC) 워싱톤DC 이고 그 다음은 Puerto Rico 이다.
인구 밀도가 가장 작은 Alaska는 1제곱마일당 평균 주민수가 1만명정도이다.
[시각화작업]
7. 컴퓨존 크롤링 분석
1 데이터 수집 1 – 한 페이지 크롤링¶
컴퓨존 사이트에서 제품별로 용량, 크기 같은 조건을 설정해서 검색하여
복합기 제품의 가격과 정보를 수집한다.
[도서] 데이터실무분석 with파이썬 예제
### 6.1.1 패키지 설치
# 1) selenimu 패키지 설치
# !pip install selenium
# webdriver 다운로드 http://chromedriver.chromium.org/downloads
# chromedirver_win32.zip 파일 다운로드 받고 압축풀기
# 크롬버전을 맞춰서 다운받아야 한다
컴퓨존 검색 페이지 접속¶
# 2) selenium으로 컴퓨존 검색 결과 URL에 접속
from selenium import webdriver
# 3) webdriver 설치 경로 확인
driver = webdriver.Chrome('C:/Python/cWebConn/cWebConn/4_selenium_class/webdriver/chromedriver.exe')
driver2 = driver
# 4) 컴퓨존 사이트에서 실제 '복합기' 검색 후 url 확인
url = "https://www.compuzone.co.kr/search/search.htm?Seargbl=1&hidden_Txt=&IsEventSearch=&SearchProductKey=복합기"
driver.get(url)
컴퓨존 검색 웹 페이지에서 상품 정보 가져오기¶
크롬에서 F12(개발자모드)를 보면서 확인합시다
from bs4 import BeautifulSoup
# 5) 웹 페이지의 HTML 정보 가져오기
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
# 6) 1페이지에 대한 복합기 정보 가져오기 0 - 상품정보
prod_items = soup.select('ul.product_list > li')
len(prod_items)
20
# 6) 1페이지에 대한 복합기 정보 가져오기 1 - 상품명
# prod_name = soup.select('a.prd_info_name')
prod_name = soup.select('a.prdTxt')
len(prod_name)
20
# 6) 1페이지에 대한 복합기 정보 가져오기 2 - 스펙
prod_info = soup.select('div.prd_subTxt > a')
len(prod_info)
20
# 6) 1페이지에 대한 복합기 정보 가져오기 3 - 가격
prod_price = soup.select('span.won strong.number')
len(prod_price)
20
# 7) 상품 정보 가져오기 - 상품명
prod_name = prod_name[0].text.strip()
print(prod_name)
[삼성전자] SL-J1660 잉크젯복합기 (잉크포함)
# 7) 상품 정보 가져오기 - 스펙
prod_list = prod_info[0].text.strip()
print(prod_list)
컬러 잉크젯 복합기 / 인쇄+복사+스캔 / 컬러 속도: 16ppm / 흑백 속도: 20ppm / 컬러 속도(ISO): 5.5ipm / 흑백 속도(ISO): 7.5ipm / 인쇄 해상도: 4800 x 1200dpi / A4 출력 / 광학 스캔 해상도: 1200dpi / 연결방식: USB / 월 최대 인쇄량: 1000매 / 가로: 425mm / 세로: 304mm / 높이: 149mm
# 7) 상품 정보 가져오기 - 가격
prod_price = prod_price[0].text.strip().replace(",","")
print(prod_price)
64900
# 8) 반복문으로 검색 결과의 1페이지에 대한 상품 정보 추출
prod_data = []
for prod_item in prod_items:
try: # ① 상품명 가져오기
prod_name = prod_item.select('a.prdTxt')[0].text.strip()
except:
prod_name = ''
try: # ② 스펙 목록 가져오기
prod_list = prod_item.select('div.prd_subTxt > a')[0].text.strip()
except:
prod_list = ''
try: # ③ 가격 정보 가져오기
prod_price = prod_item.select('span.won strong.number')[0].text.strip().replace(",","")
except:
prod_price = 0
prod_data.append([prod_name, prod_list, prod_price])
print(len(prod_data))
# print(prod_data)
20
# 8) 상품 정보 태그에서 원하는 정보를 추출하는 함수
def get_prod_items(prod_items):
prod_data = []
for prod_item in prod_items:
# ① 상품명 가져오기
try:
prod_name = prod_item.select('a.prdTxt')[0].text.strip()
except:
prod_name = ''
# ② 스펙 목록 가져오기
try:
prod_list = prod_item.select('div.prd_subTxt > a')[0].text.strip()
except:
prod_list = ''
# ③ 가격 정보 가져오기
try:
prod_price = int(prod_item.select('span.won strong.number')[0].text.strip().replace(",",""))
except:
prod_price = 0
prod_data.append([prod_name, prod_list, prod_price])
return prod_data
# 9) 상품 정보를 가져오는 함수 테스트
prod_data = get_prod_items(prod_items)
print(len(prod_data))
20
6.2 데이터 수집 2 - 여러 페이지에 걸친 컴퓨존 검색 페이지 크롤링¶
현재 컴퓨존 검색 결과는 한 페이지당 20개씩 상품 정보를 보여주고 있다.
2 페이지를 선택했을 때의 변하는 URL 주소를 잘 확인해야 한다
6.2.1 컴퓨존 검색 결과 페이지 URL 분석¶
# 10) 컴퓨존 검색 URL을 만들어주는 함수
def get_search_page_url(keyword):
return 'http://www.compuzone.co.kr/search/search.htm?Seargbl=1&hidden_Txt=&IsEventSearch=&SearchProductKey={}'.format(keyword)
keyword = '복합기'
url = get_search_page_url(keyword)
print(url)
http://www.compuzone.co.kr/search/search.htm?Seargbl=1&hidden_Txt=&IsEventSearch=&SearchProductKey=복합기
6.2.2 주피터 노트북의 진행표시줄 처리¶
시간이 소요되는 진행을 확인하기 위해 표시한다.
# 10) tqdm 라이브러리 설치
# ! pip install tqdm
# 11) tqdm 사용법
import time
from tqdm import tqdm_notebook
total_page = 10
for page in tqdm_notebook(range(1, total_page + 1)):
# 페이지가 로딩 완료되기 위한 시간을 5초로 준다.
time.sleep(5)
6.2.3 여러 페이지에 걸친 상품 정보 수집¶
# 12) 실전 다나와 크롤링
from selenium import webdriver
import time
from bs4 import BeautifulSoup
from tqdm import tqdm_notebook
driver = webdriver.Chrome('C:/Python/cWebConn/cWebConn/4_selenium_class/webdriver/chromedriver.exe')
# 암묵적으로 웹 자원 로드를 위해 3초까지 기다림
driver.implicitly_wait(3)
keyword = '복합기'
total_page = 10
prod_data_total = []
# 진행 정도를 표현하는 tqdm을 적용
url = get_search_page_url(keyword)
driver.get(url)
for page in tqdm_notebook(range(1, total_page + 1)):
# ① 검색 페이지 이동
driver.find_elements_by_css_selector('.num')[page-1].click()
# 페이지가 로딩 완료되기 위한 시간으로 5초를 할당
time.sleep(5)
# ② 현재 페이지의 HTML 정보 가져오기
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
# ③ 상품 정보 추출
prod_items = soup.select('.product_list > li')
prod_item_list = get_prod_items(prod_items)
# ④ 추출 데이터 저장
prod_data_total = prod_data_total + prod_item_list
6.2.4 수집 데이터 저장¶
# 13) 데이터 저장
import pandas as pd
data = pd.DataFrame(prod_data_total)
data.columns = ['상품명', '스펙 목록', '가격']
data.to_excel('./files/compuzone_crawling_result.xlsx', index = False)
6.3 컴퓨존 크롤링 데이터 전처리¶
6.3.1 컴퓨존 크롤링 데이터 불러오기¶
# 14) 컴퓨존 크롤링 결과 가져오기
import pandas as pd
data = pd.read_excel('./files/compuzone_crawling_result.xlsx');
data
상품명 | 스펙 목록 | 가격 | |
---|---|---|---|
0 | [삼성전자] SL-J1660 잉크젯복합기 (잉크포함) | 컬러 잉크젯 복합기 / 인쇄+복사+스캔 / 컬러 속도: 16ppm / 흑백 속도: ... | 64900 |
1 | [EPSON] L6190 완성형 정품무한잉크 복합기 (잉크포함) | 컬러 잉크젯 복합기 / 인쇄+스캔+복사+팩스 / 컬러 속도: 20ppm / 흑백 속... | 359000 |
2 | [삼성전자] SL-C483FW 컬러레이저복합기 (토너포함) | 컬러 레이저 복합기 / 인쇄+복사+스캔+팩스 / 컬러 속도: 4ppm / 흑백 속도... | 359000 |
3 | [KYOCERA] ECOSYS M5521CDN 컬러레이저복합기 (토너포함) | 컬러 레이저 복합기 / 인쇄+복사+스캔+팩스 / 컬러 속도: 21ppm / 흑백 속... | 320000 |
4 | [삼성전자] SL-C483W 컬러레이저복합기 (토너포함) | 컬러 레이저 복합기 / 인쇄+스캔+복사 / 컬러 속도: 4ppm / 흑백 속도: 1... | 289000 |
... | ... | ... | ... |
195 | [Brother] DCP-T220 정품무한잉크 복합기 (잉크포함) ◆ 중고 상품 ◆... | 복합기 / 잉크젯 / 컬러 출력 / 복사 / 스캔 / 컬러 속도(ISO): 9ipm... | 114560 |
196 | [HP(병행)] DeskJet 2130 잉크젯복합기 (잉크포함) ◆ 중고 상품 ◆ | 잉크젯 복합기/ 컬러 출력/ 복사/ 스캔/ 컬러 속도: 16ppm / 흑백 속도: ... | 40250 |
197 | [FUJIXEROX] DocuPrint M285Z 흑백레이저 (토너포함) | 복합기 / 레이저젯 / 흑백(모노) 출력 / 복사 / 스캔 / 팩스 / 흑백 속도:... | 335000 |
198 | [후지제록스] DocuPrint M375z A4 흑백 복합기 | 흑백 / 팩스포함 / 스캔포함 / 30PPM이상 / A4 / 자동급지장치(ADF) ... | 689500 |
199 | [제이알테크] HP Officejet 6954 복합기 (병행수입) + 1200ml ... | 고속인쇄,무선네트워크,자동양면인쇄,대량출력 | 189000 |
200 rows × 3 columns
# 15) 누락값 확인
print(data.isnull().sum())
data.info()
data.head()
상품명 0
스펙 목록 2
가격 0
dtype: int64
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 200 entries, 0 to 199
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 상품명 200 non-null object
1 스펙 목록 198 non-null object
2 가격 200 non-null int64
dtypes: int64(1), object(2)
memory usage: 4.8+ KB
상품명 | 스펙 목록 | 가격 | |
---|---|---|---|
0 | [삼성전자] SL-J1660 잉크젯복합기 (잉크포함) | 컬러 잉크젯 복합기 / 인쇄+복사+스캔 / 컬러 속도: 16ppm / 흑백 속도: ... | 64900 |
1 | [EPSON] L6190 완성형 정품무한잉크 복합기 (잉크포함) | 컬러 잉크젯 복합기 / 인쇄+스캔+복사+팩스 / 컬러 속도: 20ppm / 흑백 속... | 359000 |
2 | [삼성전자] SL-C483FW 컬러레이저복합기 (토너포함) | 컬러 레이저 복합기 / 인쇄+복사+스캔+팩스 / 컬러 속도: 4ppm / 흑백 속도... | 359000 |
3 | [KYOCERA] ECOSYS M5521CDN 컬러레이저복합기 (토너포함) | 컬러 레이저 복합기 / 인쇄+복사+스캔+팩스 / 컬러 속도: 21ppm / 흑백 속... | 320000 |
4 | [삼성전자] SL-C483W 컬러레이저복합기 (토너포함) | 컬러 레이저 복합기 / 인쇄+스캔+복사 / 컬러 속도: 4ppm / 흑백 속도: 1... | 289000 |
# 16) 누락값 처리
data = data.fillna(0)
print(data.isnull().sum())
data.info()
상품명 0
스펙 목록 0
가격 0
dtype: int64
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 200 entries, 0 to 199
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 상품명 200 non-null object
1 스펙 목록 200 non-null object
2 가격 200 non-null int64
dtypes: int64(1), object(2)
memory usage: 4.8+ KB
6.3.2 회사명, 모델명 정리¶
# 17) 회사명 + 모델명 분리
company_list = []
product_list = []
for title in data['상품명']:
title_info = title.split(' ', 1)
if len(title_info) == 1 :
company_name = title_info[0]
product_name = title_info[0]
else:
company_name = title_info[0]
product_name = title_info[1]
company_list.append(company_name.replace('[','').replace(']',''))
product_list.append(product_name)
print(len(company_list))
print(len(product_list))
print(company_list[0:5])
200
200
['삼성전자', 'EPSON', '삼성전자', 'KYOCERA', '삼성전자']
6.3.4 스펙 목록에서 카테고리, 사용시간, 흡입력을 추출해서 정리¶
# 18) 첫 번째 제품의 스펙 목록 분리
spec_list = data['스펙 목록'][0].split('/')
spec_list
['컬러 잉크젯 복합기 ',
' 인쇄+복사+스캔 ',
' 컬러 속도: 16ppm ',
' 흑백 속도: 20ppm ',
' 컬러 속도(ISO): 5.5ipm ',
' 흑백 속도(ISO): 7.5ipm ',
' 인쇄 해상도: 4800 x 1200dpi ',
' A4 출력 ',
' 광학 스캔 해상도: 1200dpi ',
' 연결방식: USB ',
' 월 최대 인쇄량: 1000매 ',
' 가로: 425mm ',
' 세로: 304mm ',
' 높이: 149mm']
# 19) 카테고리 정보 추출
category = spec_list[0]
category
'컬러 잉크젯 복합기 '
# 20) ‘흑백속도’, ‘컬러속도’가 포함된 원소 추출
sbw=None
scolor=None
for spec in spec_list:
if ('흑백 속도:' in spec) or ('흑백 인쇄 속도:' in spec):
sbw=spec
elif spec=='' and '흑백 속도' in spec:
sbw=spec
if ('컬러 속도:' in spec) or ('컬러 인쇄 속도:' in spec):
scolor=spec
elif spec=='' and '컬러 속도' in spec:
scolor=spec
print(sbw)
print(scolor)
흑백 속도: 20ppm
컬러 속도: 16ppm
# 21) 정량적인 수치 추출
sbw=sbw.split(' ')[-2].strip()
scolor=scolor.split(' ')[-2].strip()
print(sbw)
print(scolor)
20ppm
16ppm
# 22) 카테고리, 흑백속도, 컬러속도 추출
category_list = []
sbw_list = []
scolor_list = []
for spec_data in data['스펙 목록']:
# '/' 기준으로 스펙 분리하기
if spec_data:
spec_list = spec_data.replace(',','/').split('/')
# 카테고리 추출하기
category = spec_list[0].strip()
category_list.append(category)
# 흑백속도, 컬러속도 추출
# 흑백속도, 컬러속도 정보가 없는 제품을 위해 변수를 생성
sbw=None
scolor=None
## spec_list의 각 원소에서 흑백속도, 컬러속도 수치 추출
for spec in spec_list:
if ('흑백 속도:' in spec) or ('흑백 인쇄 속도:' in spec):
sbw=spec
sbw = sbw.split(' ')[-2].strip()
elif sbw==None and '흑백 속도' in spec:
sbw=spec
sbw = sbw.split(' ')[-2].strip()
if ('컬러 속도:' in spec) or ('컬러 인쇄 속도:' in spec):
scolor=spec
scolor = scolor.split(' ')[-2].strip()
elif scolor==None and '컬러 속도' in spec:
scolor=spec
scolor = scolor.split(' ')[-2].strip()
sbw_list.append(sbw)
scolor_list.append(scolor)
print(len(category_list))
200
# 23) 카테고리, 흑백속도, 컬러속도에 대한 전처리 결과 확인
print("카테고리", len(category_list), category_list[0:5])
print("흑백 속도", len(sbw_list), sbw_list[0:5])
print("컬러 속도", len(scolor_list), scolor_list[0:5])
카테고리 200 ['컬러 잉크젯 복합기', '컬러 잉크젯 복합기', '컬러 레이저 복합기', '컬러 레이저 복합기', '컬러 레이저 복합기']
흑백 속도 200 ['20ppm', '33ppm', '18ppm', '21ppm', '18ppm']
컬러 속도 200 ['16ppm', '20ppm', '4ppm', '21ppm', '4ppm']
6.3.5 컬러속도 흑백속도 기준 통일시키기¶
# 24) 속도 기준을 ppm으로 조정하고 수치를 정수화 시키는 함수
def convert(speed):
try:
if 'ppm' in speed:
return int(speed.replace('ppm',''))
else:
return int(float(speed.replace('ipm',''))*3)
except:
return None
# 25) 출력 속도를 조정하는 함수 테스트
speeds = ["10ppm", "20ppm", "3.9ipm", "4.9ipm", "5ppm"]
for speed in speeds:
speed_value = convert(speed)
print(speed, "=", speed_value)
10ppm = 10
20ppm = 20
3.9ipm = 11
4.9ipm = 14
5ppm = 5
# 26) 흑백속도와 컬러속도를 ppm 으로 모두 변환
new_sbw_list = []
new_scolor_list = []
for sbw in sbw_list:
value = convert(sbw)
new_sbw_list.append(value)
for scolor in scolor_list:
value = convert(scolor)
new_scolor_list.append(value)
6.3.7 컴퓨존 전처리 결과를 엑셀로 저장¶
# 27) 전처리 데이터 확인
pd_data = pd.DataFrame()
pd_data['카테고리'] = category_list
pd_data['회사명'] = company_list
pd_data['제품'] = product_list
pd_data['가격'] = data['가격']
pd_data['흑백속도'] = new_sbw_list
pd_data['컬러속도'] = new_scolor_list
pd_data
카테고리 | 회사명 | 제품 | 가격 | 흑백속도 | 컬러속도 | |
---|---|---|---|---|---|---|
0 | 컬러 잉크젯 복합기 | 삼성전자 | SL-J1660 잉크젯복합기 (잉크포함) | 64900 | 20.0 | 16.0 |
1 | 컬러 잉크젯 복합기 | EPSON | L6190 완성형 정품무한잉크 복합기 (잉크포함) | 359000 | 33.0 | 20.0 |
2 | 컬러 레이저 복합기 | 삼성전자 | SL-C483FW 컬러레이저복합기 (토너포함) | 359000 | 18.0 | 4.0 |
3 | 컬러 레이저 복합기 | KYOCERA | ECOSYS M5521CDN 컬러레이저복합기 (토너포함) | 320000 | 21.0 | 21.0 |
4 | 컬러 레이저 복합기 | 삼성전자 | SL-C483W 컬러레이저복합기 (토너포함) | 289000 | 18.0 | 4.0 |
... | ... | ... | ... | ... | ... | ... |
195 | 복합기 | Brother | DCP-T220 정품무한잉크 복합기 (잉크포함) ◆ 중고 상품 ◆ ※ 보증기간 6개월 | 114560 | 48.0 | 27.0 |
196 | 잉크젯 복합기 | HP(병행) | DeskJet 2130 잉크젯복합기 (잉크포함) ◆ 중고 상품 ◆ | 40250 | 20.0 | 16.0 |
197 | 복합기 | FUJIXEROX | DocuPrint M285Z 흑백레이저 (토너포함) | 335000 | 34.0 | NaN |
198 | 흑백 | 후지제록스 | DocuPrint M375z A4 흑백 복합기 | 689500 | NaN | NaN |
199 | 고속인쇄 | 제이알테크 | HP Officejet 6954 복합기 (병행수입) + 1200ml 무한공급기 [리... | 189000 | NaN | NaN |
200 rows × 6 columns
# 28) 카테고리 분류 기준 및 데이터 개수 점검
pd_data['카테고리'].value_counts().head()
복합기 57
컬러 디지털 복합기 24
흑백 레이저 복합기 16
잉크젯 복합기 16
흑백 디지털 복합기 16
Name: 카테고리, dtype: int64
# 29) 복합기만 선택
pd_data_final = pd_data[pd_data['카테고리'].isin(['복합기'])]
len(pd_data_final)
57
# 30 엑셀로 저장
pd_data_final.to_excel('./files/compuzone_data_final.xlsx', index = False)
6.4 무선청소기 모델별 비교 분석¶
6.4.1 데이터 살펴보기¶
# 31) 데이터 불러오기
import pandas as pd
data = pd.read_excel('./files/compuzone_data_final.xlsx')
data.head()
카테고리 | 회사명 | 제품 | 가격 | 흑백속도 | 컬러속도 | |
---|---|---|---|---|---|---|
0 | 복합기 | EPSON | L4150 완성형 정품무한잉크 복합기 (잉크포함) | 219000 | 33.0 | 15.0 |
1 | 복합기 | EPSON | L1455 A3 정품무한잉크 복합기 (잉크포함) | 749000 | 32.0 | 20.0 |
2 | 복합기 | OKI | MC363dn 컬러레이저젯복합기 (토너포함) | 520000 | 30.0 | 26.0 |
3 | 복합기 | Brother | MFC-L5700DN 흑백레이저젯복합기 (토너포함) | 380000 | 40.0 | NaN |
4 | 복합기 | EPSON | AcuLaser CX37DN 컬러레이저복합기 (토너포함) | 390000 | 24.0 | 24.0 |
# 32) 누락값을 0으로 대체
data=data.dropna(subset=['흑백속도']).fillna(0)
data.head()
카테고리 | 회사명 | 제품 | 가격 | 흑백속도 | 컬러속도 | |
---|---|---|---|---|---|---|
0 | 복합기 | EPSON | L4150 완성형 정품무한잉크 복합기 (잉크포함) | 219000 | 33.0 | 15.0 |
1 | 복합기 | EPSON | L1455 A3 정품무한잉크 복합기 (잉크포함) | 749000 | 32.0 | 20.0 |
2 | 복합기 | OKI | MC363dn 컬러레이저젯복합기 (토너포함) | 520000 | 30.0 | 26.0 |
3 | 복합기 | Brother | MFC-L5700DN 흑백레이저젯복합기 (토너포함) | 380000 | 40.0 | 0.0 |
4 | 복합기 | EPSON | AcuLaser CX37DN 컬러레이저복합기 (토너포함) | 390000 | 24.0 | 24.0 |
# 33) 흑백속도 기준 정렬
top_list = data.sort_values(["흑백속도"], ascending = False)
top_list.head()
카테고리 | 회사명 | 제품 | 가격 | 흑백속도 | 컬러속도 | |
---|---|---|---|---|---|---|
12 | 복합기 | EPSON | L6550 완성형 정품무한잉크 복합기 (잉크포함) | 899000 | 75.0 | 36.0 |
55 | 복합기 | Brother | DCP-T220 정품무한잉크 복합기 (잉크포함) ◆ 중고 상품 ◆ ※ 보증기간 6개월 | 114560 | 48.0 | 27.0 |
15 | 복합기 | Brother | DCP-T220 정품무한잉크 복합기 (잉크포함) | 179000 | 48.0 | 27.0 |
3 | 복합기 | Brother | MFC-L5700DN 흑백레이저젯복합기 (토너포함) | 380000 | 40.0 | 0.0 |
48 | 복합기 | Canon | PIXMA G7091 빌트인 정품무한복합기(잉크포함) | 450000 | 39.0 | 20.0 |
# 34 컬러속도 기준 정렬
top_list = data.sort_values(["컬러속도"], ascending = False)
top_list.head()
카테고리 | 회사명 | 제품 | 가격 | 흑백속도 | 컬러속도 | |
---|---|---|---|---|---|---|
52 | 복합기 | HP | 컬러레이저젯 엔터프라이즈 MFP M577f (B5L47A/토너포함) | 2246750 | 38.0 | 38.0 |
44 | 복합기 | HP(병행) | HP Officejet Pro 8720 복합기 (병행수입) + 휘슬러 무칩 무한공급기 | 563000 | 37.0 | 37.0 |
12 | 복합기 | EPSON | L6550 완성형 정품무한잉크 복합기 (잉크포함) | 899000 | 75.0 | 36.0 |
43 | 복합기 | HP(병행) | HP Officejet Pro 8710 복합기 (병행수입) + 휘슬러 무칩 무한공급기 | 568000 | 35.0 | 35.0 |
41 | 복합기 | HP(병행) | HP Officejet Pro 7740 복합기 (병행수입) + 휘슬러 무칩 무한공급기 | 584500 | 34.0 | 34.0 |
# 35) 흑백속도, 컬러속도를 기준으로 정렬
# 흑백속도 & 컬러속도 TOP 리스트
top_list = data.sort_values(["컬러속도","흑백속도"], ascending = False)
top_list.head()
카테고리 | 회사명 | 제품 | 가격 | 흑백속도 | 컬러속도 | |
---|---|---|---|---|---|---|
52 | 복합기 | HP | 컬러레이저젯 엔터프라이즈 MFP M577f (B5L47A/토너포함) | 2246750 | 38.0 | 38.0 |
44 | 복합기 | HP(병행) | HP Officejet Pro 8720 복합기 (병행수입) + 휘슬러 무칩 무한공급기 | 563000 | 37.0 | 37.0 |
12 | 복합기 | EPSON | L6550 완성형 정품무한잉크 복합기 (잉크포함) | 899000 | 75.0 | 36.0 |
43 | 복합기 | HP(병행) | HP Officejet Pro 8710 복합기 (병행수입) + 휘슬러 무칩 무한공급기 | 568000 | 35.0 | 35.0 |
41 | 복합기 | HP(병행) | HP Officejet Pro 7740 복합기 (병행수입) + 휘슬러 무칩 무한공급기 | 584500 | 34.0 | 34.0 |
# 36) 평균값 정리
price_mean_value = data['가격'].mean()
sbw_mean_value = data['흑백속도'].mean()
scolor_mean_value = data['컬러속도'].mean()
print("가격 평균값", price_mean_value)
print("흑백속도 평균값", sbw_mean_value)
print("컬러속도 평균값", scolor_mean_value)
가격 평균값 397485.0
흑백속도 평균값 29.59259259259259
컬러속도 평균값 18.11111111111111
# 37) 가성비 좋은 제품 탐색
condition_data = data [
(data['가격'] <= price_mean_value) &
(data['흑백속도'] >= sbw_mean_value) &
(data['컬러속도'] >= scolor_mean_value)]
condition_data
카테고리 | 회사명 | 제품 | 가격 | 흑백속도 | 컬러속도 | |
---|---|---|---|---|---|---|
11 | 복합기 | Canon | PIXMA G7090 빌트인 정품무한잉크 복합기(잉크포함) [예약판매 2월 중순 이... | 331500 | 39.0 | 20.0 |
15 | 복합기 | Brother | DCP-T220 정품무한잉크 복합기 (잉크포함) | 179000 | 48.0 | 27.0 |
19 | 복합기 | HP(병행) | HP Officejet Pro 9010 복합기(병행수입)+ 틴텍 무칩 세트 | 375000 | 32.0 | 32.0 |
22 | 복합기 | Canon | PIXMA TS5392 컬러 잉크젯복합기(잉크포함) | 109000 | 39.0 | 20.0 |
30 | 복합기 | HP(병행) | officejet pro 6962 잉크젯복합기(HP902/잉크포함) | 169000 | 30.0 | 26.0 |
55 | 복합기 | Brother | DCP-T220 정품무한잉크 복합기 (잉크포함) ◆ 중고 상품 ◆ ※ 보증기간 6개월 | 114560 | 48.0 | 27.0 |
6.4.3 데이터 시각화¶
# 38) 라이브러리 임포트 및 한글 글꼴 설정
from matplotlib import font_manager, rc
import matplotlib.pyplot as plt
import seaborn as sns
import platform
# 그래프에서 한글 표기를 위한 글꼴 변경(윈도우, macOS에 대해 처리)
font_path = ''
if platform.system() == 'Windows':
font_path = 'c:/Windows/Fonts/malgun.ttf'
font_name = font_manager.FontProperties(fname = font_path).get_name()
rc('font', family = font_name)
elif platform.system() == 'Darwin':
font_path = '/Users/$USER/Library/Fonts/AppleGothic.ttf'
rc('font', family = 'AppleGothic')
else:
print('Check your OS system')
%matplotlib inline
# 39) 흑백시간, 컬러시간의 최댓값/평균값 정리
sbw_max_value = data['흑백속도'].max()
sbw_mean_value = data['흑백속도'].mean()
scolor_max_value = data['컬러속도'].max()
scolor_mean_value = data['컬러속도'].mean()
# 40 복합기 성능 시각화
plt.figure(figsize=(20, 10))
plt.title("복합기 차트")
sns.scatterplot(x = '흑백속도', y = '컬러속도', size = '가격', hue = data['회사명'],
data = data, sizes = (10, 1000), legend = False)
plt.plot([0, sbw_max_value],
[scolor_mean_value,scolor_mean_value],
'r--',
lw = 1 )
plt.plot([sbw_mean_value, sbw_mean_value],
[0, scolor_max_value],
'r--',
lw = 1 )
plt.show()
6.4.4 전체 제품의 데이터 시각화¶
# 41) 전체 제품 선택
chart_data_selected = data
len(chart_data_selected)
54
# 42) 흑백속도, 컬러속도의 최댓값/평균값 정리
sbw_max_value = chart_data_selected['흑백속도'].max()
sbw_mean_value = chart_data_selected['흑백속도'].mean()
scolor_max_value = chart_data_selected['컬러속도'].max()
scolor_mean_value = chart_data_selected['컬러속도'].mean()
plt.figure(figsize=(10, 8))
plt.title("복합기")
sns.scatterplot(x = '흑백속도',
y = '컬러속도',
size = '가격',
hue = chart_data_selected['회사명'],
data = chart_data_selected, sizes = (100, 2000),
legend = False)
plt.plot([0, sbw_max_value],
[scolor_mean_value, scolor_mean_value],
'r--',
lw = 1 )
plt.plot([sbw_mean_value, sbw_mean_value],
[0, scolor_max_value],
'r--',
lw = 1 )
for index, row in chart_data_selected.iterrows():
x = row['흑백속도']
y = row['컬러속도']
s = row['회사명'].split(' ')[0]
plt.text(x, y, s, size=10)
plt.show()