2021. 2. 3. 23:04ㆍ교육과정/KOSMO
키워드 : 판다스 seaborn 라이브러리 / 다나와 무선청소기 데이터 크롤링 분석
****
1. 판다스 seaborn 라이브러리
히스토그램과 막대그래프 비교¶
1. 히스토그램 ( seaborn 라이브러리 이용 )¶
(1) matplotlib의 subplot() / subplots()으로 기본 틀을 만들고
(2) seabron의 distplot()에 데이타를 전달한다
a) 밀집도 그래프(실선그래프)를 제외 : kde속성
axes = sb.distplot(tips['total_bill'], kde=False)
밀집도 그래프는 주어진 데이타를 정규화시켜 넓이가 1이되도록 그린 그래프
(*) 데이타 정규화 : 데이타의 분포가 너무 한 쪽으로 치우지 않도록 하는 작업(?)
b) 밀집도 그래프만 출력 : hist=False
axes = sb.distplot(tips['total_bill'], hist=False)
c) rug 양탄자그래프(?) : 그래프의 축에 동일한 선으로 데이타 밀집정도를 표현
axes = sb.distplot(tips['total_bill'], rug=True)
2. 막대그래프 (*)¶
` 히스토그램 : 연속형 데이타(수치) - 온도, 체중
` 막대그래프 : 이산형 데이타(명목데이타) - 성별, 출신지
(1) matplotlib의 subplots()으로 기본 틀을 만들고 plot(kind='bar')
(2) seabron의 countplot()에 데이타를 전달한다.
[참고]¶
seaborn 상세한 스타일 세팅 방법은 왼쪽의 링크를 통해 확인하실 수 있다.
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import font_manager, rc
plt.rcParams['axes.unicode_minus']=False
rc('font', family='Malgun Gothic')
tips = sns.load_dataset('tips')
tips
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
0 | 16.99 | 1.01 | Female | No | Sun | Dinner | 2 |
1 | 10.34 | 1.66 | Male | No | Sun | Dinner | 3 |
2 | 21.01 | 3.50 | Male | No | Sun | Dinner | 3 |
3 | 23.68 | 3.31 | Male | No | Sun | Dinner | 2 |
4 | 24.59 | 3.61 | Female | No | Sun | Dinner | 4 |
... | ... | ... | ... | ... | ... | ... | ... |
239 | 29.03 | 5.92 | Male | No | Sat | Dinner | 3 |
240 | 27.18 | 2.00 | Female | Yes | Sat | Dinner | 2 |
241 | 22.67 | 2.00 | Male | Yes | Sat | Dinner | 2 |
242 | 17.82 | 1.75 | Male | No | Sat | Dinner | 2 |
243 | 18.78 | 3.00 | Female | No | Thur | Dinner | 2 |
244 rows × 7 columns
1. 히스토그램 ( seaborn 라이브러리 이용 )¶
(1) matplotlib의 subplots()으로 기본 틀을 만들고
(2) seabron의 distplot()에 데이타를 전달한다
# axes = sns.distplot(tips['total_bill']); # 히스토그램 + 라인 그래프
# axes = sns.distplot(tips['total_bill'], kde=False); # 히스토그램만 출력
# axes = sns.distplot(tips['total_bill'], hist=False); # 라인 그래프만 출력
axes = sns.distplot(tips['total_bill'], rug=True); # 밀집도 그래프 출력
axes.set_title('Total Bill');
2. 막대그래프 (*)¶
` 히스토그램 : 연속형 데이타(수치) - 온도, 체중
` 막대그래프 : 이산형 데이타(명목데이타) - 성별, 출신지
(1) matplotlib의 subplots()으로 기본 틀을 만들고
(2) seabron의 countplot()에 데이타를 전달한다. # 윈도우 환경에서는 (2)만 해도 된다.
sns.countplot(tips['day']);
2. 판다스 라이브러리
1. 산점도 그래프¶
(1) matplotlib의 subplots()로 기본 틀 만들고 seaborn의 그래프 함수 이용
회귀선 제거 : fit_reg=False
axes = sb.regplot(x='total_bill', y='tip', data=tips, fit_reg=False)
[예] 지불 금액과 팁에 대한 그래프
(2) 산점도와 히스토그램을 한번에 - jointplot()
` 산점도 그래프의 데이타를 육각(hexbin)으로 구분 : kind='hex'속성
( 개수가 많아지면 진한 색으로 표시 )
axes = sb.jointplot(x='total_bill', y='tip', data=tips, kind='hex')
2. 이차원 밀집도 그래프 (등고선그래프) - kdeplot()¶
3. 막대그래프 - barplot()¶
* 이산형 데이타(명목데이타)
[예] 시간대에 따라 지불한 비용의 평균 그래프
4. 박스그래프 - boxplot()¶
: 최소값, 4분위수, 평균값, 이상치 등의 통계량을 한번에 표현하는 그래프
- 1분위수와 3분위수를 박스로 그려짐
- 박스 내부의 선은 중앙값이다 (평균값이 아님)
- 박수 외부의 선은 최솟값과 최댓값인데
` 최솟값 : 1분위수 - ( IQR * 1.5 )
` 최댓값 : 3분위수 + ( IQR * 1.5 )
` IQR = 3분위수 - 1분위수
- 선 밖의 점은 outlier로 결측치이다
5. 바이올린 그래프 - violinplot()¶
: 박스 그래스는 통계 수치를 확인하기 위해 자주 사용되지만 데이터 분산이 모호하기에
박스 그래스에 커널 밀도를 표현한 그래프
6. 관계 그래프 - pairplot()¶
[그래프 분석]
1. 데이타셋에서 연속형 데이타로 된 컬럼
2. 동일 컬럼인경우 즉 하나의 변수이기에 히스토그램으로 나옴
3. 다른 변수와의 결합은 이차원 그래프로 산점도 그래프로 나옴
4. 대각선으로는 동일한 결과
import matplotlib.pyplot as plt
import seaborn as sns
tips = sns.load_dataset('tips')
# plt.subplots() # 윈도우 환경이라 기본틀 만들기 생략 가능
# axes = sns.regplot(x=tips['total_bill'], y=tips['tip']);
# axes = sns.regplot(x='total_bill' , y='tip' , data=tips);
axes = sns.regplot(x='total_bill' , y='tip' , data=tips, fit_reg=False); # 선형 그래프 생략
(2) 산점도와 히스토그램을 한번에 - jointplot()¶
` 산점도 그래프의 데이타를 육각(hexbin)으로 구분 : kind='hex'속성
( 개수가 많아지면 진한 색으로 표시 )
axes = sb.jointplot(x='total_bill', y='tip', data=tips, kind='hex')
# 지불 금액과 팁에 대한 그래프
# axes = sns.jointplot(x='total_bill', y='tip', data=tips)
axes = sns.jointplot(x='total_bill', y='tip', data=tips, kind='hex')
2. 이차원 밀집도 그래프 (등고선그래프) - kdeplot()¶
[참고] 함수의 도움말 : 함수에 커서 놓고 ctrl + tab
axes = sns.kdeplot(x='total_bill', y='tip', data=tips, shade=True);
axes.set_title('Relation of bill and tip');
axes.set_xlabel('Bill');
axes.set_ylabel('Tip');
3. 막대그래프 - barplot()¶
* 이산형 데이타(명목데이타)
[예] 시간대에 따란 비불한 비용의 평균 그래프
시간대 : Lunch, Dinner (명목형 데이터)
- countplot() : 인자 1개
- barplot() : 인자 2개
sns.barplot(x='time', y='total_bill', data=tips);
sns.countplot(x=tips['time']);
4. 박스그래프 - boxplot()¶
: 최소값, 4분위수, 평균값, 이상치 등의 통계량을 한번에 표현하는 그래프
- 1분위수와 3분위수를 박스로 그려짐
- 박스 내부의 선은 중앙값이다 (평균값이 아님)
- 박수 외부의 선은 최솟값과 최댓값인데
` 최솟값 : 1분위수 - ( IQR * 1.5 )
` 최댓값 : 3분위수 + ( IQR * 1.5 )
` IQR = 3분위수 - 1분위수
- 선 밖의 점은 outlier로 결측치이다
→ 이상치는 데이터에서 제외시켜야, 최종 결과에 영향을 미치지 않는다.

# 시간대별 지불 금액을 boxplot으로 출력
sns.boxplot(x='time', y='total_bill', data=tips)
<AxesSubplot:xlabel='time', ylabel='total_bill'>
# sns.boxplot(x=tips['day'], y=tips['total_bill'])
# sns.boxplot(x=tips['day'], y=tips['total_bill'], hue=tips['smoker']) # 추가 인자를 비교할 때 hue 사용
sns.boxplot(x=tips['day'], y=tips['total_bill'], hue=tips['smoker'], palette='Set3')
<AxesSubplot:xlabel='day', ylabel='total_bill'>
→ 위 그래프에서는 흡연자가 이상데이터가 더 많이 보인다.
5. 바이올린 그래프 - violinplot()¶
: 박스 그래스는 통계 수치를 확인하기 위해 자주 사용되지만 데이터 분산이 모호하기에
박스 그래스에 커널 밀도를 표현한 그래프
# axes = sns.violinplot(x='day', y='total_bill', data=tips)
axes = sns.violinplot(x='day', y='total_bill', data=tips, hue='smoker')
6. 관계 그래프 - pairplot()¶
[그래프 분석]
1. 데이타셋에서 연속형 데이타로 된 컬럼
2. 동일 컬럼인경우 즉 하나의 변수이기에 히스토그램으로 나옴
3. 다른 변수와의 결합은 이차원 그래프로 산점도 그래프로 나옴
4. 대각선으로는 동일한 결과
axes = sns.pairplot(tips)
pair = sns.PairGrid(tips);
pair.map_upper(sns.regplot); # 대각선 위로는 산점도 그래프
pair.map_lower(sns.kdeplot); # 대각선 아래로는 밀집도 그래프
pair.map_diag(sns.histplot); # 대각선은 히스토그램
3. 판다스 라이브러리
추가 그래프 확인
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset('tips')
(1) 시간대별로 지불금액의 평균을 나타내는 바이올린 그래프에 성별을 추가한다면
sns.violinplot(x='time', y='total_bill', data=tips, hue='sex');
(2) 지불금액과 팁에 대한 산점도 그래프에 성별을 추가한다면
sns.regplot(x='total_bill', y='tip', data=tips);
sns.lmplot(data=tips, x='total_bill', y='tip',hue='sex',fit_reg=False);
sns.scatterplot(x='total_bill', y='tip', data=tips, hue='sex');
FacetGrid()를 이용한 그룹별 그래프
-
시간대별로 지불 금액을 표현한 그래프
-
요일별로 지불 금액을 표현한 그래프에 성별 구분 추가
-
시간대별 흡연여부별 지불 금액을 표현한 그래프에 성별 구분 추가
# 시간대별로 지불 금액을 표현한 그래프
facet = sns.FacetGrid(tips, col='time');
facet.map(sns.histplot, 'total_bill');
# 요일별로 지불 금액을 표현한 그래프에 성별 구분 추가
facet = sns.FacetGrid(tips, col='day', hue='sex');
facet.map(sns.scatterplot, 'total_bill', 'tip');
# 시간대별 흡연여부별 지불 금액을 표현한 그래프에 성별 구분 추가
facet = sns.FacetGrid(tips, col='day', row='smoker', hue='sex');
facet.map(sns.scatterplot, 'total_bill', 'tip');
facet.add_legend();
4. 판다스 라이브러리
seabron 시각화¶
seaborn은 matplotlib을 기반으로 작동합니다.
matplotlib에 비해 그래프의 스타일이 이쁘고, 약간의 통계 기능과 사용의 편의성 때문에 널리 쓰이고 있습니다.
하지만, seaborn을 호출하는 순간 matplotlib의 기본 스타일을 바꾸기 때문에 그래프의 모양이 바뀌는 것을 염두에 두셔야 합니다.
#matplotlib을 주피터 화면 내에 띄우도록 해주는 주피터 명령어
%matplotlib inline
import pandas as pd
import matplotlib as mpl #matplotlib을 이용해 그래프를 그릴 수 있는 pyplot (인터페이스 역할)
import matplotlib.pyplot as plt
import seaborn as sns
pd.options.display.max_columns = 50
Load Dataset¶
시각화 수업의 데이터는 대한민국 국민의 2016년 건강검진 데이터을 이용하도록 하겠습니다. data 폴더에 제공된 '2016년_건강검진데이터.csv' 는 위 링크 주소의 2016년 대한민국 국민 100만명의 건강검진 데이터 중 실습을 위해 1000개만 샘플링한 데이터입니다. 원본 데이터는 노트북으로는 처리하는데 많은 시간이 들기 때문에 집에서 복습하실 때 다운받아 실습해보시길 바랍니다 :)
data = pd.read_csv('data/health_2016.csv', nrows=1000)
print(data.shape)
data.head(3)
(1000, 34)
기준년도 | 가입자일련번호 | 성별코드 | 연령대코드(5세단위) | 시도코드 | 신장(5Cm단위) | 체중(5Kg단위) | 허리둘레 | 시력(좌) | 시력(우) | 청력(좌) | 청력(우) | 수축기혈압 | 이완기혈압 | 식전혈당(공복혈당) | 총콜레스테롤 | 트리글리세라이드 | HDL콜레스테롤 | LDL콜레스테롤 | 혈색소 | 요단백 | 혈청크레아티닌 | (혈청지오티)AST | (혈청지오티)ALT | 감마지티피 | 흡연상태 | 음주여부 | 구강검진 수검여부 | 치아우식증유무 | 결손치유무 | 치아마모증유무 | 제3대구치(사랑니)이상 | 치석 | 데이터공개일자 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2016 | 465969 | 1 | 8 | 41 | 170.0 | 70.0 | 74.0 | 0.7 | 0.7 | 1.0 | 1.0 | 118.0 | 70.0 | 85.0 | 194.0 | 97.0 | 54.0 | 120.0 | 14.6 | 2.0 | 1.1 | 31.0 | 36.0 | 96.0 | 3.0 | NaN | 1 | NaN | NaN | NaN | NaN | 2.0 | 20171219 |
1 | 2016 | 565871 | 1 | 10 | 41 | 160.0 | 60.0 | 81.0 | 1.2 | 1.0 | 1.0 | 1.0 | 134.0 | 84.0 | 105.0 | 143.0 | 84.0 | 33.0 | 93.0 | 16.2 | 1.0 | 1.0 | 20.0 | 23.0 | 14.0 | 1.0 | NaN | 0 | NaN | NaN | NaN | NaN | NaN | 20171219 |
2 | 2016 | 115718 | 2 | 11 | 11 | 160.0 | 55.0 | 71.0 | 1.0 | 1.0 | 1.0 | 1.0 | 129.0 | 85.0 | 86.0 | 203.0 | 52.0 | 51.0 | 141.0 | 14.0 | 1.0 | 0.8 | 17.0 | 14.0 | 20.0 | 1.0 | NaN | 1 | NaN | NaN | NaN | NaN | 0.0 | 20171219 |
countplot¶
countplot을 그리기에 앞서, matplotlib은 한글 폰트를 지원하지 않기 때문에 폰트를 설정해주어야 합니다. 다음과 같은 명령어로 현재 사용 가능한 한글 폰트 경로와 파일명을 확인 할 수 있습니다.
# mac OS를 이용하면 AppleGothic을 설면합니다.
from matplotlib import rc
rc('font', family='Malgun Gothic')
# 별도로, 폰트를 바꿀 경우 마이너스가 표시되지 않는 경우도 있는데 이를 막아주는 코드입니다.
rc('axes', unicode_minus=False)
plt.figure(figsize=[7,4])
sns.countplot(x='성별코드', data=data);
글씨체가 조금 깨지는 것을 확인할 수 있습니다. 이는 matplotlib 그래프의 해상도를 높이는 방법으로 해결할 수 있습니다.
# retina 옵션을 통해 해상도를 올립니다.
from IPython.display import set_matplotlib_formats
set_matplotlib_formats('retina')
#또는 %config InlineBackend.figure_format = 'retina' 사용 가능
plt.figure(figsize=[7,4])
sns.countplot(x='성별코드', data=data);
sns.set_style('whitegrid')
# `whitegrid`,`darkgrid`,`dark`,`white`,`ticks`
sns.countplot(x='성별코드', data=data);
위에 한글이 다시 깨진다. style을 불러오는 경우 설정한 rcparameter(font, font size) 등이 리셋되기 때문에 다시 설정해주어야 합니다.
from matplotlib import rc
rc('font', family='Malgun Gothic')
rc('axes', unicode_minus=False) # 별도로, 폰트를 바꿀 경우 마이너스가 표시되지 않는 경우도 있는데 이를 막아주는 코드입니다.
from IPython.display import set_matplotlib_formats
set_matplotlib_formats('retina')
sns.countplot(x='성별코드', data=data);
subplots¶
matplotlib의 subplots를 이용해 미리만들어둔 figure에 그래프가 어떤 모양으로 들어갈지 지정할 수 있습니다.
figure, ((ax1, ax2)) = plt.subplots(nrows=1, ncols=2)
figure.set_size_inches(24, 4)
sns.countplot(x="성별코드", data=data, ax=ax1);
sns.countplot(x="시도코드", data=data, ax=ax2);
# plt.figure의 figsize를 이용해 특정 그래프의 사이즈를 지정할 수 있습니다. 그래프를 그리기 전에 입력해주세요.
plt.figure(figsize=[10,4])
sns.countplot(x='연령대코드(5세단위)', hue="성별코드", data=data, color='r');
#RGB 칼라 코드를 이용해서 그래프를 그릴 수도 있습니다. hue 옵션의 경우 밝기와 채도 조절을 통해 자동으로 세팅이 됩니다.
plt.figure(figsize=[10,4])
sns.countplot(x='연령대코드(5세단위)', hue="성별코드", data=data, color='#80e666');
color_palette 사용하기¶
색을 팔레트를 이용하여 다채롭게 표현하고 싶은 경우에는 seaborn의 팔레트를 이용합니다.
sns.palplot(sns.color_palette("deep", 10))
sns.palplot(sns.color_palette("colorblind", 10))
sns.palplot(sns.color_palette("coolwarm", 10))
sns.palplot(sns.color_palette("BuGn_r", 10))
위 색 이외에도 내장된 팔레트가 많습니다. 팔레트의 리스트는 다음과 같습니다.
Accent, Accent_r, Blues, Blues_r, BrBG, BrBG_r, BuGn, BuGn_r, BuPu, BuPu_r, CMRmap, CMRmap_r, Dark2, Dark2_r, GnBu, GnBu_r, Greens, Greens_r, Greys, Greys_r, OrRd, OrRd_r, Oranges, Oranges_r, PRGn, PRGn_r, Paired, Paired_r, Pastel1, Pastel1_r, Pastel2, Pastel2_r, PiYG, PiYG_r, PuBu, PuBuGn, PuBuGn_r, PuBu_r, PuOr, PuOr_r, PuRd, PuRd_r, Purples, Purples_r, RdBu, RdBu_r, RdGy, RdGy_r, RdPu, RdPu_r, RdYlBu, RdYlBu_r, RdYlGn, RdYlGn_r, Reds, Reds_r, Set1, Set1_r, Set2, Set2_r, Set3, Set3_r, Spectral, Spectral_r, Wistia, Wistia_r, YlGn, YlGnBu, YlGnBu_r, YlGn_r, YlOrBr, YlOrBr_r, YlOrRd, YlOrRd_r, afmhot, afmhot_r, autumn, autumn_r, binary, binary_r, bone, bone_r, brg, brg_r, bwr, bwr_r, cividis, cividis_r, cool, cool_r, coolwarm, coolwarm_r, copper, copper_r, cubehelix, cubehelix_r, flag, flag_r, gist_earth, gist_earth_r, gist_gray, gist_gray_r, gist_heat, gist_heat_r, gist_ncar, gist_ncar_r, gist_rainbow, gist_rainbow_r, gist_stern, gist_stern_r, gist_yarg, gist_yarg_r, gnuplot, gnuplot2, gnuplot2_r, gnuplot_r, gray, gray_r, hot, hot_r, hsv, hsv_r, icefire, icefire_r, inferno, inferno_r, jet, jet_r, magma, magma_r, mako, mako_r, nipy_spectral, nipy_spectral_r, ocean, ocean_r, pink, pink_r, plasma, plasma_r, prism, prism_r, rainbow, rainbow_r, rocket, rocket_r, seismic, seismic_r, spring, spring_r, summer, summer_r, tab10, tab10_r, tab20, tab20_r, tab20b, tab20b_r, tab20c, tab20c_r, terrain, terrain_r, viridis, viridis_r, vlag, vlag_r, winter, winter_r
상세한 사용 방법은 다음의 링크에서 확인해주세요.
#palette 옵션을 이용해 그래프에 팔레트 색상을 넣어줄 수 있습니다.
fig, (ax1, ax2) = plt.subplots(ncols=2, nrows=1)
fig.set_size_inches([12,3])
sns.countplot(x='시도코드', data=data, palette=sns.color_palette("coolwarm", 17), ax=ax1);
sns.countplot(x='시도코드', data=data, palette=sns.color_palette("deep"), ax=ax2);
light_palette는 지정한 색을 희석시키는 역할을 합니다. 단계를 지정할 수도 있고, 그 방향을 정할 수도 있습니다.
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
plt.tight_layout() #글씨가 겹치는 것을 막아주는 코드입니다.
fig.set_size_inches([12,5])
sns.countplot(x='시도코드', data=data, palette=sns.light_palette("#09CE20",n_colors=20, reverse=True), ax=ax1);
sns.countplot(x='시도코드', data=data, palette=sns.light_palette("#09CE20",n_colors=3, reverse=True), ax=ax2); #3개의 색상이 반복됩니다.
sns.countplot(x='시도코드', data=data, palette=sns.light_palette("#09CE20",n_colors=20, reverse=False), ax=ax3);
sns.countplot(x='시도코드', data=data, palette=sns.light_palette("blue",n_colors=20, reverse=False), ax=ax4);
countplot의 경우 그래프를 밑으로 길게 늘이고 싶은 경우 x 옵션이 아닌 y옵션을 주면 됩니다.
plt.figure(figsize=[4, 7])
sns.countplot(y="시도코드", data=data);
#그린 그래프를 저장하고 싶을 때는 plt.savefig()를 이용합니다.
# plt.savefig('테스트.png')
distplot¶
distplot은 scipy의 stats 모듈을 이용하여 데이터의 분포를 확률분포(pdf, probability distribution function)로 만들어주고, 동시에 히스토그램을 그릴 수 있는 plot입니다. 데이터의 분포를 가장 정확하게 볼 수 있는 plot이지만, 다른 plot들과 옵션이 통일되지 않아 사용법을 따로 익혀야 합니다.
# 다른 plot과는 다르게, pandas의 Series 나 numpy의 array를 넣어주어야 합니다. 데이터프레임 전체를 넣으면 오류가 납니다.
sns.distplot(data["허리둘레"]);
#histogram을 생략하기 위해서는 hist 옵션을 줄 수 있습니다.
sns.distplot(data["허리둘레"], hist=False);
# 확률분포를 생략하기 위해서는 kde 옵션을 줄 수 있습니다.
sns.distplot(data["허리둘레"], kde=False);
# rug 옵션을 통해 데이터의 정확한 위치를 파악할 수 있습니다.
sns.distplot(data["허리둘레"], rug=True);
#다른 plot은 orient 옵션을 사용하지만, 방향을 바꿀 때 distplot은 vertical 옵션을 활용합니다.
sns.distplot(data["허리둘레"], vertical=True);
여러가지의 distplot을 하나의 figure에 그리고 싶은 경우에는 다음과 같이 하나의 코드블럭 안에 여러번의 distplot을 그려주시면 됩니다.
one = data[data["흡연상태"] == 1]
two = data[data["흡연상태"] == 2]
three = data[data["흡연상태"] == 3]
sns.distplot(one["허리둘레"], label="흡연 안 함", hist=False);
sns.distplot(two["허리둘레"], label="흡연 했지만 그만 둠", hist=False);
sns.distplot(three["허리둘레"], label="흡연 하고 있음", hist=False);
plt.legend();
Barplot¶
Bootstrap percentile confidential interval 이론 설명
간략히 요약을 하자면 데이터를 반복추출하여 중앙값들을 모은 뒤, 중앙값들의 분포를 바탕으로 중앙값의 신뢰구간 (기본 옵션은 95% 신뢰구간)을 구하는 것입니다.
# 각 연령대별로 수축기혈압의 평균에 대한 신뢰구간을 확인할 수 있습니다. 신뢰구간 80%
plt.figure(figsize=[15,3])
sns.barplot(x='연령대코드(5세단위)', y='수축기혈압',ci=80, data=data);
# 신뢰 구간을 늘릴 경우 막대기가 길어지는 것을 파악할 수 있다.
plt.figure(figsize=[15,3])
sns.barplot(x='연령대코드(5세단위)', y='수축기혈압',ci=99, data=data);
위 신뢰구간의 경우 분포가 아닌 순전히 중앙값만의 신뢰구간을 구하는 것이기 때문에 참고용으로만 보시면 됩니다. 만약 연령대별 혈압의 분포를 보고 싶은 경우, 표준편차를 사용하도록 'sd'옵션을 넣으면 됩니다.
plt.figure(figsize=[15,3])
# ci를 standard deviation으로 변경
sns.barplot(x='연령대코드(5세단위)', y='수축기혈압', data=data, ci='sd');
평균(mean)이 아닌 다른 값 띄우기
barplot은 각 x값에 해당하는 y값들을 array (vector)로 만든 뒤, 이를 평균(scalar)을 내어 그래프에 표시합니다. 평균 이외에 다른 값을 표시하고 싶은 경우, function을 정의해 estimator 옵션에 넣을 수 있습니다.
import numpy as np
plt.figure(figsize=[15,3])
# mean을 사용하는 경우 기존과 같은 그래프가 나옵니다.
sns.barplot(x='연령대코드(5세단위)', y='수축기혈압', data=data, ci=99, estimator=np.mean);
plt.figure(figsize=[15,3])
# sum으로 변환해 카테고리별 누적합을 구할 수도 있습니다.
sns.barplot(x='연령대코드(5세단위)', y='수축기혈압', data=data, ci=99, estimator=np.sum);
numpy array를 받아 scalar (특정 값)으로 변환하는 함수를 직접 정의할 수도 있습니다.
def return_range(array):
return array.argmax()-array.argmin()
plt.figure(figsize=[15,3])
# sum으로 변환해 카테고리별 누적합을 구할 수도 있습니다.
sns.barplot(x='연령대코드(5세단위)', y='수축기혈압', data=data, ci=99, estimator=return_range);
barplot은 orient 옵션을 통해 그래프의 방향을 수정할 수 있습니다. 'v'는 vertical, 'h'는 horizon의 약자입니다.
plt.figure(figsize=[5,8])
sns.barplot(x='수축기혈압', y='연령대코드(5세단위)', data=data, orient='h');
데이터 사전 처리를 통해 평균으로부터 얼마나 떨어져 있는지를 직관적으로 나타낼 수도 있습니다.
data["수축기혈압(copy)"] = data["수축기혈압"]
data["수축기혈압(copy)"] = data["수축기혈압"] - data["수축기혈압"].mean()
plt.figure(figsize=[15,4])
sns.barplot(x='연령대코드(5세단위)', y='수축기혈압(copy)', data=data);
order 옵션을 이용하여 보고 싶은 값만 선택하거나, x값의 순서를 조정할 수 있습니다.
plt.figure(figsize=[15,4])
# ci를 standard deviation으로 변경
sns.barplot(x='연령대코드(5세단위)', y='수축기혈압(copy)', order=[16, 12, 8, 5], data=data);
pointplot¶
pointplot은 barplot과 거의 동일한 기능을 하나, 막대기 대신 각 평균값들을 선으로 잇는 특징을 가집니다. 이는 x값들 사이에 상관(correlation)이 있을 경우 적합한 시각화 방식입니다. 예를 들면, 시간의 흐름 또는 시험 등급 등이 들어갈 수 있습니다.
plt.figure(figsize=[15,3])
sns.pointplot(x='연령대코드(5세단위)', y='수축기혈압', data=data);
plt.figure(figsize=[15,3])
sns.pointplot(x='연령대코드(5세단위)', y='수축기혈압', markers=['*','s'], hue='성별코드', data=data);
linestyle을 바꾸는 것도 가능합니다. 사전 지정된 linestyle은 아래의 링크를 참조해주세요.
plt.figure(figsize=[15,3])
# ci를 standard deviation으로 변경
sns.pointplot(x='연령대코드(5세단위)', y='수축기혈압',hue='성별코드', linestyles=[':', '--'], data=data);
비슷하게 marker 옵션을 바꾸는 것도 가능합니다.
plt.figure(figsize=[15,3])
sns.pointplot(x='연령대코드(5세단위)', y='수축기혈압',hue='성별코드', markers=['.', ','], data=data);
boxplot¶
barplot은 평균을 직관적으로 보여주지만 각 클래스별 데이터의 분포를 정확히 보여주지 못합니다. boxplot이나 violinplot은 데이터의 분포를 조금 더 세밀하게 볼 수 있는 plot입니다. 하지만, 동시에 직관적으로 이해가 되지 않는다는 단점도 있습니다.
우선, boxplot을 이해하기 위해서는 분위수라는 개념을 알아야 합니다.
분위수 (Quantile)¶
중앙값이 데이터를 2개의 동일한 사이즈로 나누었을 때, 그 경계값을 의미한다면, 4분위수는 데이터를 4개의 동일한 사이즈로 나누었을 때 그 경계값들을 의미합니다.
자료의 1/4 (25%) 지점을 1분위수, 2/4 (50%) 지점을 2분위수 (=중앙값), 3/4 (75%) 지점을 3분위수라고 합니다. 이를 그림으로 나타내면 아래와 같습니다.
# pandas.Series에서 바로 quantile 메소드를 이용해 분위수를 구할 수 있습니다.
print(f"1분위수는 {data['식전혈당(공복혈당)'].quantile(q=0.25)}입니다.")
print(f"2분위수는 {data['식전혈당(공복혈당)'].quantile(q=0.50)}입니다.")
print(f"3분위수는 {data['식전혈당(공복혈당)'].quantile(q=0.75)}입니다.")
1분위수는 88.0입니다.
2분위수는 96.0입니다.
3분위수는 105.0입니다.
Boxplot의 사각형의 3가지 선이 각각 1,2,3분위수를 나타내고 막대기의 양끝 선은 최댓값, 최솟값을 의미합니다. 식전혈당 상자 그림을 다시 그려보면 아래와 같습니다.
#orient 옵션을 이용해 방향을 바꾸어 horizon하게 상자수염을 그립니다.
plt.figure(figsize=[12,1.5])
sns.boxplot(data['식전혈당(공복혈당)'], orient='h');
분명히 막대기의 왼쪽 끝과 오른쪽 끝이 최댓값과 최솟값을 의미한다고 했는데, 그 밖에 데이터가 점으로 존재하는 이유가 무엇일까요? 아래 그림을 참조하면 쉽게 이해할 수 있습니다.

알고리즘이나 분석가마다 기준이 다르기는 하지만, 일반적으로 IQR = 3분위수 - 1분위수 로 정의한 뒤
Upper Fence = 3분위수 + 1.5 IQR*
Lower Fence = 1분위수 - 1.5 IQR*
를 만들고, 이 범위를 벗어나는 데이터는 Outlier (이상치)라고 정의합니다. seaborn에서는 whis라는 옵션을 이용해 이 범위를 조절할 수 있습니다.
fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1)
fig.set_size_inches([12,4])
#whis 범위를 조절해 outlier처리할 범위를 조절
sns.boxplot(data['식전혈당(공복혈당)'], orient='h', whis=1.5, ax=ax1);
sns.boxplot(data['식전혈당(공복혈당)'], orient='h', whis=5, ax=ax2);
plt.figure(figsize=[15,4])
sns.boxplot(x='연령대코드(5세단위)', y='수축기혈압', data=data);
plt.figure(figsize=[15,4])
sns.boxplot(x='연령대코드(5세단위)', y='수축기혈압',hue='성별코드', data=data);
violinplot¶
violinplot은 boxplot보다 조금 더 데이터의 분포를 확실하게 볼 수 있는 plot입니다. distplot을 카테고리별로 그린 버전이라고 생각하시면 좋습니다.
plt.figure(figsize=[5,12])
sns.violinplot(y='연령대코드(5세단위)', x='수축기혈압', data=data, inner='box', orient='h');
마찬가지로 hue옵션을 사용할 수 있습니다.
plt.figure(figsize=[15,4])
sns.violinplot(x='연령대코드(5세단위)', y='수축기혈압', data=data, hue="성별코드", inner='box');
plt.figure(figsize=[15,4])
#split 옵션을 통해 성별코드 별로 violinplot을 각각 그리는 것이 아닌, 좌우로 쪼개 하나로 그릴 수 있습니다.
sns.violinplot(x='연령대코드(5세단위)', y='수축기혈압', data=data, hue="성별코드", inner='box', split=True);
lineplot¶
pointplot이 하나의 변수는 연속형, 하나의 변수는 범주형 (카테고리형)인 경우에 사용하는 그래프라면, lineplot은 둘 다 연속형, 또는 시간 정보가 있을 때 사용하기 좋은 그래프입니다. 위의 건강검진데이터에는 시간 정보가 없으므로, seaborn에 기본으로 저장되어 있는 fmri 데이터를 이용하도록 하겠습니다.
# seaborn의 fmri 데이터를 로딩합니다.
fmri = sns.load_dataset("fmri")
print(fmri.shape)
fmri.head()
(1064, 5)
subject | timepoint | event | region | signal | |
---|---|---|---|---|---|
0 | s13 | 18 | stim | parietal | -0.017552 |
1 | s5 | 14 | stim | parietal | -0.080883 |
2 | s12 | 18 | stim | parietal | -0.081033 |
3 | s11 | 18 | stim | parietal | -0.046134 |
4 | s10 | 18 | stim | parietal | -0.037970 |
fig, (ax1, ax2) = plt.subplots(ncols=2, nrows=1)
fig.set_size_inches([14,3])
# err_style 옵션을 통해 각 timepoint 에서의 신뢰구간 표시하는 방식을 바꿀 수 있습니다.
sns.lineplot(data=fmri,x='timepoint', y='signal', hue='event', err_style='band', ax=ax1);
sns.lineplot(data=fmri,x='timepoint', y='signal', hue='event', err_style='bars', ax=ax2);
# markers 옵션을 사용하거나, line의 모양을 바꾸는 옵션을 사용하고 싶은 경우 style 옵션을 구분지을 변수명으로 설정해주어야 합니다.
sns.lineplot(x="timepoint", y="signal", style="event", hue='event', markers=True, data=fmri);
scatterplot¶
scatterplot은 표현하고자 하는 두 변수가 모두 연속형인 경우에 사용하기 적합한 그래프입니다. 직관적으로 상관관계가 있는지 분석하는 경우 많이 사용됩니다.
#scatterplot을 그려줍니다.
plt.figure(figsize=[5, 5])
sns.scatterplot(x="체중(5Kg단위)", y="신장(5Cm단위)", data=data);
신장이 큰 사람은 신장이 작은 사람보다 평균체중도 더 크다는 추론을 할 수 있습니다.
데이터 비식별화로 인해 체중과 신장이 범주형 (카테고리형) 처럼 끊어져 있는 것이 아쉽습니다.
hue 옵션을 통해 색으로 하나의 변수를 더 표현할 수 있습니다. 위에서 사용한 그래프들은 hue 옵션이 항상 카테고리형이어야했지만, scatterplot에서는 연속형이어도 사용 가능합니다.
plt.figure(figsize=[5, 5])
#hue 옵션을 연속형 변수인 허리둘레를 이용해 표현합니다.
sns.scatterplot(x="체중(5Kg단위)", y="신장(5Cm단위)", hue="허리둘레", data=data);
체중이 많을수록 허리둘레가 두꺼워지는 것을 확인할 수 있습니다. 마찬가지로 색상이 아닌 원의 사이즈로도 허리둘레를 구분할 수 있습니다.
plt.figure(figsize=[5, 5])
#hue 옵션을 연속형 변수인 허리둘레를 이용해 표현합니다.
sns.scatterplot(x="체중(5Kg단위)", y="신장(5Cm단위)", size="허리둘레", data=data);
plt.figure(figsize=[5, 5])
#두 개의 옵션을 동시에 사용할 수도 있습니다.
sns.scatterplot(x="체중(5Kg단위)", y="신장(5Cm단위)",
hue="허리둘레", size="총콜레스테롤",
data=data);
plt.figure(figsize=[6, 6])
# sizes 옵션을 통해 최소 원의 크기와 최대 원의 크기를 지정해줄 수 있습니다.
sns.scatterplot(x="체중(5Kg단위)", y="신장(5Cm단위)",
hue="허리둘레", size="총콜레스테롤", sizes=(30, 450),
data=data);
x, y가 모두 연속형인 그래프를 그려보도록 하겠습니다. 식전혈당이 높을 수록 허리둘레가 두꺼워지는 (비만인) 사람들의 비중이 높아지는 것을 확인할 수 있습니다.
plt.figure(figsize=[18, 18])
sns.scatterplot(x="식전혈당(공복혈당)", y="수축기혈압",
hue="허리둘레", size="총콜레스테롤", sizes=(30, 550),
data=data[data['식전혈당(공복혈당)'] < 150]);
[ 출처 ] DSschool 데이타분석 자료
5. 판다스 라이브러리
일변량 양적 자료 분석¶
- 표
- 그래프
- 기술통계량(요약통계량)
** 양적자료는 값에 대한 빈도와 백분율을 구하지 않음 ( 데이터가 가지는 값의 종류가 많기 때문에 )
** 양적자료로 빈도와 백분율을 구하려면 구간별 새로운 자료를 생성하고 구함
# 데이타 읽어오기
import pandas as pd
# 인덱스가 2번 생성되기에 기존 인덱스를 인덱스로 지정
diamonds = pd.read_csv('data/diamonds.csv', index_col='Unnamed: 0')
가격에 대한 그래프
sns.distplot(diamonds['price']);
sns.boxplot(diamonds['price']);
[양적자료를 질적자료로 새로 생성 ]¶
diamond 데이터에 gprice 컬럼 추가하기
0 ~ 5000 : 5000
5000 ~ 10000 : 10000
10000 ~ 15000 : 15000
15000 ~ : 20000
def add_gprice(diamonds) :
gprice = 2000
if diamonds['price'] <= 5000 :
gprice = 5000
elif 5000 < diamonds['price'] <= 10000 :
gprice = 10000
elif 10000 < diamonds['price'] <= 15000 :
gprice = 15000
return gprice
diamonds['gprice'] = diamonds.apply(add_gprice, axis=1)
diamonds.tail()
carat | cut | color | clarity | depth | table | price | x | y | z | gprice | |
---|---|---|---|---|---|---|---|---|---|---|---|
53936 | 0.72 | Ideal | D | SI1 | 60.8 | 57.0 | 2757 | 5.75 | 5.76 | 3.50 | 2000 |
53937 | 0.72 | Good | D | SI1 | 63.1 | 55.0 | 2757 | 5.69 | 5.75 | 3.61 | 2000 |
53938 | 0.70 | Very Good | D | SI1 | 62.8 | 60.0 | 2757 | 5.66 | 5.68 | 3.56 | 2000 |
53939 | 0.86 | Premium | H | SI2 | 61.0 | 58.0 | 2757 | 6.15 | 6.12 | 3.74 | 2000 |
53940 | 0.75 | Ideal | D | SI2 | 62.2 | 55.0 | 2757 | 5.83 | 5.87 | 3.64 | 2000 |
명목형 데이터
빈도수 : value_counts()
막대그래프, 원그래프
data = diamonds['gprice'].value_counts();
data
2000 33930
1000 12775
1500 5486
500 1749
Name: gprice, dtype: int64
import matplotlib.pyplot as plt
import seaborn as sns
fig = plt.figure(figsize=(12,4));
ax1 = fig.add_subplot(1,2,1);
ax2 = fig.add_subplot(1,2,2);
ax1.pie(data, autopct='%1.1f%%', explode=(0.02, 0, 0, 0));
ax1.set_title('price of diamonds');
ax2.pie(data, autopct='%1.1f%%');
ax2.set_title('price of diamonds');
4
import matplotlib.pyplot as plt
import seaborn as sns
# 기본제공
data.plot(kind='bar')
<AxesSubplot:>
6. 판다스 라이브러리
일변량질적자료¶
일변량 - 변수 하나
질적자료 - 명목형(카테고리) / 이산형
(ex. 남자/여자, 고급/중급/초급 )
* 질적자료 = 명목형 = 이산형
* 양적자료 = 수치형 = 연속형
빈도와 그래프¶
-
빈도 : 자료가 가지는 각각의 값이 몇 개 있는지를 구한 수치
-
백분율 : 자료가 가지는 각각의 값이 전체를 100으로 보았을 때의 차지하고 얼마나 차지하고 있는지를 알려주는 수치
질적자료 시각화¶
-
막대 그래프
`import matplotlib.pyplot as plt
import seaborn as sns
sns.countplot(diamonds['color']); -
원 그래프
`import matplotlib.pyplot as plt
import seaborn as snsfig = plt.figure();
ax = fig.add_subplot();ax.pie(d_color, autopct='%1.1f%%');
ax.set_title('color of diamonds');
데이타 읽어오기
import pandas as pd
diamonds = pd.read_csv('data/diamonds.csv', index_col='Unnamed: 0')
diamonds.head()
carat | cut | color | clarity | depth | table | price | x | y | z | |
---|---|---|---|---|---|---|---|---|---|---|
1 | 0.23 | Ideal | E | SI2 | 61.5 | 55.0 | 326 | 3.95 | 3.98 | 2.43 |
2 | 0.21 | Premium | E | SI1 | 59.8 | 61.0 | 326 | 3.89 | 3.84 | 2.31 |
3 | 0.23 | Good | E | VS1 | 56.9 | 65.0 | 327 | 4.05 | 4.07 | 2.31 |
4 | 0.29 | Premium | I | VS2 | 62.4 | 58.0 | 334 | 4.20 | 4.23 | 2.63 |
5 | 0.31 | Good | J | SI2 | 63.3 | 58.0 | 335 | 4.34 | 4.35 | 2.75 |
데이타프레임 구조 살피기
[-] 질적자료와 양적자료 확인
diamonds.shape
(53940, 10)
diamonds.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 53940 entries, 1 to 53940
Data columns (total 10 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 carat 53940 non-null float64
1 cut 53940 non-null object
2 color 53940 non-null object
3 clarity 53940 non-null object
4 depth 53940 non-null float64
5 table 53940 non-null float64
6 price 53940 non-null int64
7 x 53940 non-null float64
8 y 53940 non-null float64
9 z 53940 non-null float64
dtypes: float64(6), int64(1), object(3)
memory usage: 4.5+ MB
diamonds.describe()
carat | depth | table | price | x | y | z | |
---|---|---|---|---|---|---|---|
count | 53940.000000 | 53940.000000 | 53940.000000 | 53940.000000 | 53940.000000 | 53940.000000 | 53940.000000 |
mean | 0.797940 | 61.749405 | 57.457184 | 3932.799722 | 5.731157 | 5.734526 | 3.538734 |
std | 0.474011 | 1.432621 | 2.234491 | 3989.439738 | 1.121761 | 1.142135 | 0.705699 |
min | 0.200000 | 43.000000 | 43.000000 | 326.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 0.400000 | 61.000000 | 56.000000 | 950.000000 | 4.710000 | 4.720000 | 2.910000 |
50% | 0.700000 | 61.800000 | 57.000000 | 2401.000000 | 5.700000 | 5.710000 | 3.530000 |
75% | 1.040000 | 62.500000 | 59.000000 | 5324.250000 | 6.540000 | 6.540000 | 4.040000 |
max | 5.010000 | 79.000000 | 95.000000 | 18823.000000 | 10.740000 | 58.900000 | 31.800000 |
1. 빈도구하기 : value_counts()
diamonds['color'].value_counts()
G 11292
E 9797
F 9542
H 8304
D 6775
I 5422
J 2808
Name: color, dtype: int64
diamonds['cut'].value_counts()
Ideal 21551
Premium 13791
Very Good 12082
Good 4906
Fair 1610
Name: cut, dtype: int64
diamonds['clarity'].value_counts()
SI1 13065
VS2 12258
SI2 9194
VS1 8171
VVS2 5066
VVS1 3655
IF 1790
I1 741
Name: clarity, dtype: int64
2. 백분율 = 빈도수/전체수 * 100
d_color = diamonds['color'].value_counts() / diamonds['color'].count() * 100
d_cut = diamonds['cut'].value_counts() / diamonds['cut'].count() * 100
d_clarity = diamonds['clarity'].value_counts() / diamonds['clarity'].count() * 100
3. 시각화
(1) 막대 그래프
(2) 원 그래프
import matplotlib.pyplot as plt
import seaborn as sns
sns.countplot(diamonds['color']);
import matplotlib.pyplot as plt
import seaborn as sns
fig = plt.figure();
ax = fig.add_subplot();
ax.pie(d_color, autopct='%1.1f%%');
ax.set_title('color of diamonds');
sns.countplot(diamonds['cut']);
sns.countplot(diamonds['clarity']);
7. 판다스 라이브러리
이변량질적자료¶
이변량 - 변수 2개
질적자료 - 명목형(카테고리) / 이산형
(ex. 남자/여자, 고급/중급/초급 )
* 질적자료 = 명목형 = 이산형
* 양적자료 = 수치형 = 연속형
- 다변량 - 변수 여러개
1. 두 질적자료에 대한 빈도수 : crosstab() 이용
2. 교차표 결과를 이용 -> 묶음 세로 막대 그래프
# 데이타 읽어오기
import pandas as pd
# 인덱스가 2번 생성되기에 기존 인덱스를 인덱스로 지정
diamonds = pd.read_csv('data/diamonds.csv', index_col='Unnamed: 0')
# 두 질적자료에 대한 빈도수
data = pd.crosstab(diamonds['cut'], diamonds['color'])
data
color | D | E | F | G | H | I | J |
---|---|---|---|---|---|---|---|
cut | |||||||
Fair | 163 | 224 | 312 | 314 | 303 | 175 | 119 |
Good | 662 | 933 | 909 | 871 | 702 | 522 | 307 |
Ideal | 2834 | 3903 | 3826 | 4884 | 3115 | 2093 | 896 |
Premium | 1603 | 2337 | 2331 | 2924 | 2360 | 1428 | 808 |
Very Good | 1513 | 2400 | 2164 | 2299 | 1824 | 1204 | 678 |
data.plot(kind='bar');
캐럿 (무게)와 가격에 대한 그래프 -> 산점도
sns.scatterplot(x='price', y='carat', data=diamonds, hue='color');
sns.scatterplot(x='price', y='carat', data=diamonds, hue='cut');
sns.regplot(x='price', y='carat', data=diamonds, fit_reg=False);
8. 다나와 무선청소기 크롤링 데이터 분석
1 데이터 수집 1 – 한 페이지 크롤링¶
다나와 사이트에서 제품별로 용량, 크기 같은 조건을 설정해서 검색하여
무선청소기 제품의 가격과 정보를 수집한다.
[도서] 데이터실무분석 with파이썬 예제
### 6.1.1 패키지 설치
# 1) selenimu 패키지 설치
# !pip install selenium
# 2) webdriver 다운로드 http://chromedriver.chromium.org/downloads
# chromedirver_win32.zip 파일 다운로드 받고 압축풀기
# 크롬버전을 맞춰서 다운받아야 한다
6.1.2 다나와 검색 페이지 접속¶
# 예제 6-1 selenium으로 다나와 검색 결과 URL에 접속
from selenium import webdriver
# webdriver 설치 경로 확인
driver = webdriver.Chrome('C:/Python/cWebConn/cWebConn/4_selenium_class/webdriver/chromedriver.exe')
# 다나와 사이트에서 실제 '무선청소기' 검색 후 url 확인
url = "http://search.danawa.com/dsearch.php?query=무선청소기&tab=main"
driver.get(url)
6.1.3 다나와 검색 웹 페이지에서 상품 정보 가져오기¶
크롬에서 F12(개발자모드)를 보면서 확인합시다
from bs4 import BeautifulSoup
#예제 6-2 웹 페이지의 HTML 정보 가져오기
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
# 예제 6-3 1페이지에 대한 무선청소기 정보 가져오기 1
prod_items = soup.select('li.prod_item')
len(prod_items)
42
# 예제 6-4 1페이지에 대한 무선청소기 정보 가져오기 2 - 상위 태그를 추가해서 확인
prod_items = soup.select('ul.product_list > li.prod_item')
len(prod_items)
42
# 예제 6-5 1페이지에 대한 무선청소기 정보 가져오기 3 - 상위 태그를 추가해서 확인
prod_items = soup.select('div.main_prodlist > ul.product_list > li.prod_item')
len(prod_items)
41
# 예제 6-6 상품명 정보 가져오기
title = prod_items[0].select('p.prod_name > a')[0].text.strip()
print(title)
샤오미 드리미 V10
# 예제 6-7 스펙 목록 정보 가져오기
spec_list = prod_items[0].select('div.spec_list')[0].text.strip()
print(spec_list)
핸디/스틱청소기 / 핸디+스틱형 / 무선형 / [성능] 멀티싸이클론 / 흡입력: 140AW / 흡입력: 22000Pa / 소비전력: 450W / [배터리] 용량: 2500mAh / 전압: 25.2V / 충전시간: 3시간30분 / 사용시간: 1시간 / [필터] 헤파필터 / H12급 / 5단계여과 / [기능] 배터리잔량표시 / [구성품] 바닥 / 침구 / 솔형 / 틈새 / 연장툴 / 거치대 / [부가] 먼지통용량: 0.5L / 색상: 화이트 / 무게: 1.5kg
# 예제 6-8 가격 정보 가져오기
price = prod_items[0].select('li.rank_one > p.price_sect > a > strong')[0].text.strip().replace(",","")
print(price)
149940
# 예제 6-9 반복문으로 검색 결과의 1페이지에 대한 상품 정보 추출
prod_data = []
for prod_item in prod_items:
try: # ① 상품명 가져오기
title = prod_item.select('p.prod_name > a')[0].text.strip()
except:
title = ''
try: # ② 스펙 목록 가져오기
spec_list = prod_item.select('div.spec_list')[0].text.strip()
except:
spec_list = ''
try: # ③ 가격 정보 가져오기
price = prod_item.select('li.rank_one > p.price_sect > a > strong')[0].text.strip().replace(",","")
except:
price = 0
prod_data.append([title, spec_list, price])
print(len(prod_data))
print(prod_data)
# 예제 6-10 상품 정보 태그에서 원하는 정보를 추출하는 함수
def get_prod_items(prod_items):
prod_data = []
for prod_item in prod_items:
# ① 상품명 가져오기
try:
title = prod_item.select('p.prod_name > a')[0].text.strip()
except:
title = ''
# ② 스펙 목록 가져오기
try:
spec_list = prod_item.select('div.spec_list')[0].text.strip()
except:
spec_list = ''
# ③ 가격 정보 가져오기
try:
price = int(prod_item.select('li.rank_one > p.price_sect > a > strong')[0].text.strip().replace(",",""))
except:
price = 0
prod_data.append([title, spec_list, price])
return prod_data
# 예제 6-11 상품 정보를 가져오는 함수 테스트
prod_items = soup.select('div.main_prodlist > ul.product_list > li.prod_item')
prod_data = get_prod_items(prod_items)
print(len(prod_data))
41
6.2 데이터 수집 2 - 여러 페이지에 걸친 다나와 검색 페이지 크롤링¶
현재 다나와 검색 결과는 한 페이지당 30개씩 상품 정보를 보여주고 있다.
2 페이지를 선택했을 때의 변하는 URL 주소를 잘 확인해야 한다
6.2.1 다나와 검색 결과 페이지 URL 분석¶
# 예제 6-12 다나와 검색 URL을 만들어주는 함수
def get_search_page_url(keyword, page):
return 'http://search.danawa.com/dsearch.php?query={}&volumeType=allvs&page={}&limit=30&sort=saveDESC&list=list&boost=true&addDelivery=N&tab=goods&tab=goods'.format(keyword, page)
keyword = '무선청소기'
page = 1
url = get_search_page_url(keyword, page)
print(url)
http://search.danawa.com/dsearch.php?query=무선청소기&volumeType=allvs&page=1&limit=30&sort=saveDESC&list=list&boost=true&addDelivery=N&tab=goods&tab=goods
6.2.2 주피터 노트북의 진행표시줄 처리¶
시간이 소요되는 진행을 확인하기 위해 표시한다.
# 예제 6-13 tqdm 라이브러리 설치
# ! pip install tqdm
# # 예제 6-14 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 여러 페이지에 걸친 상품 정보 수집¶
# 예제 6-15 실전 다나와 크롤링
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을 적용
for page in tqdm_notebook(range(1, total_page + 1)):
# ① 검색 페이지 이동
url = get_search_page_url(keyword, page)
driver.get(url)
# 페이지가 로딩 완료되기 위한 시간으로 5초를 할당
time.sleep(5)
# ② 현재 페이지의 HTML 정보 가져오기
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
# ③ 상품 정보 추출
prod_items = soup.select('div#productListArea > div.main_prodlist > ul.product_list > li.prod_item')
prod_item_list = get_prod_items(prod_items)
# ④ 추출 데이터 저장
prod_data_total = prod_data_total + prod_item_list
6.2.4 수집 데이터 저장¶
# 예제 6-16 데이터 저장
import pandas as pd
data = pd.DataFrame(prod_data_total)
data.columns = ['상품명', '스펙 목록', '가격']
data.to_excel('./files/3_1_danawa_crawling_result.xlsx', index = False)
6.3 다나와 크롤링 데이터 전처리¶
6.3.1 다나와 크롤링 데이터 불러오기¶
# 예제 6-17 다나와 크롤링 결과 가져오기
import pandas as pd
data = pd.read_excel('./files/3_1_danawa_crawling_result.xlsx')
data.info()
data.head()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 410 entries, 0 to 409
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 상품명 410 non-null object
1 스펙 목록 410 non-null object
2 가격 410 non-null int64
dtypes: int64(1), object(2)
memory usage: 9.7+ KB
상품명 | 스펙 목록 | 가격 | |
---|---|---|---|
0 | 샤오미 드리미 V10 | 핸디/스틱청소기 / 핸디+스틱형 / 무선형 / [성능] 멀티싸이클론 / 흡입력: 1... | 149940 |
1 | LG전자 코드제로 ThinQ A9S A9700 | 핸디/스틱청소기 / 핸디+스틱형 / 무선형 / 흡입+걸레겸용 / [성능] 2중터보싸... | 923930 |
2 | eS38UblSCe | eS38UblSCe904379 | 904379 |
3 | 삼성전자 제트 VS15R8500BF | 핸디/스틱청소기 / 핸디+스틱형 / 무선형 / [성능] 싸이클론 / 인버터모터 / ... | 399830 |
4 | 샤오미 미지아 차량용 핸디 무선 청소기 (해외구매) | 차량용청소기 / 무선 / 흡입력: 13,000Pa / 최대출력: 120W / 헤파필... | 33580 |
6.3.2 회사명, 모델명 정리¶
# 예제 6-18 회사명 + 모델명 분리
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)
product_list.append(product_name)
print(len(company_list))
print(len(product_list))
410
410
6.3.4 스펙 목록에서 카테고리, 사용시간, 흡입력을 추출해서 정리¶
# 예제 6-19 첫 번째 제품의 스펙 목록 분리
spec_list = data['스펙 목록'][0].split(' / ')
spec_list
['핸디/스틱청소기',
'핸디+스틱형',
'무선형',
'[성능] 멀티싸이클론',
'흡입력: 140AW',
'흡입력: 22000Pa',
'소비전력: 450W',
'[배터리] 용량: 2500mAh',
'전압: 25.2V',
'충전시간: 3시간30분',
'사용시간: 1시간',
'[필터] 헤파필터',
'H12급',
'5단계여과',
'[기능] 배터리잔량표시',
'[구성품] 바닥',
'침구',
'솔형',
'틈새',
'연장툴',
'거치대',
'[부가] 먼지통용량: 0.5L',
'색상: 화이트',
'무게: 1.5kg']
# 예제 6-20 카테고리 정보 추출
category = spec_list[0]
category
'핸디/스틱청소기'
# 예제 6-21 ‘사용시간’, ‘흡입력’이 포함된 원소 추출
for spec in spec_list:
if '사용시간' in spec:
use_time_spec = spec
elif '흡입력' in spec:
suction_spec = spec
print(use_time_spec)
print(suction_spec)
사용시간: 1시간
흡입력: 22000Pa
# 예제 6-22 정량적인 수치 추출
use_time_value = use_time_spec.split(' ')[1].strip()
suction_value = suction_spec.split(' ')[1].strip()
print(use_time_value)
print(suction_value)
1시간
22000Pa
# 예제 6-23 카테고리, 사용시간, 흡입력 추출
category_list = []
use_time_list = []
suction_list = []
for spec_data in data['스펙 목록']:
# ' / ' 기준으로 스펙 분리하기
spec_list = spec_data.split(' / ')
# 카테고리 추출하기
category = spec_list[0]
category_list.append(category)
# 사용시간, 흡입력 추출
## 사용시간, 흡입력 정보가 없는 제품을 위해 변수를 생성
use_time_value = None
suction_value = None
## spec_list의 각 원소에서 사용시간, 흡입력 수치 추출
for spec in spec_list:
if '사용시간' in spec:
use_time_value = spec.split(' ')[1].strip()
if '흡입력' in spec:
suction_value = spec.split(' ')[1].strip()
use_time_list.append(use_time_value)
suction_list.append(suction_value)
print(len(category_list))
410
# 예제 6-24 카테고리, 사용시간, 흡입력에 대한 전처리 결과 확인
print("카테고리", len(category_list), category_list[0:5])
print("사용시간", len(use_time_list), use_time_list[0:5])
# print("흡입력", len(suction_list), suction_list[0:5])
# suction_list에 None 값이 있어서 len() 함수에서 에러발생하는데
print("흡입력", suction_list[0:5])
카테고리 410 ['핸디/스틱청소기', '핸디/스틱청소기', 'eS38UblSCe904379', '핸디/스틱청소기', '차량용청소기']
사용시간 410 ['1시간', '2시간', None, '40분', '9~30분']
흡입력 ['22000Pa', '200W', None, '150W', '13,000Pa']
6.3.5 무선청소기 사용시간 단위 통일시키기¶
# 예제 6-25 사용시간을 분 단위로 조정하는 함수
def convert_time_minute(time):
try:
if '시간' in time:
hour = time.split('시간')[0]
if '분' in time:
minute = time.split('시간')[-1].split('분')[0]
else:
minute = 0
else:
hour = 0
minute = time.split('분')[0]
return int(hour)*60 + int(minute)
except:
return None
# 예제 6-26 사용시간을 분 단위로 조정하는 함수의 테스트
times = ["40분", "4분", "1시간", "3시간30분", "4시간"]
for time in times:
time_value = convert_time_minute(time)
print(time, "=", time_value)
40분 = 40
4분 = 4
1시간 = 60
3시간30분 = 210
4시간 = 240
# 예제 6-27 모델별 사용시간을 분 단위로 통일하기
new_use_time_list = []
for time in use_time_list:
value = convert_time_minute(time)
new_use_time_list.append(value)
6.3.6 무선 청소기 흡입력 단위 통일시키기¶
# 예제 6-28 흡입력 단위를 통일시키는 함수
def get_suction(value):
try:
value = value.upper()
if "AW" in value or "W" in value:
result = value.replace("A", "").replace("W","")
result = int(result.replace(",",""))
elif "PA" in value:
result = value.replace("PA","")
result = int(result.replace(",",""))/100
else:
result = None
return result
except:
return None
# 예제 6-29 흡입력 단위 통일시키기
new_suction_list = []
for power in suction_list:
value = get_suction (power)
new_suction_list.append(value)
6.3.7 다나와 전처리 결과를 엑셀로 저장¶
# 예제 6-30 전처리 데이터 확인
pd_data = pd.DataFrame()
pd_data['카테고리'] = category_list
pd_data['회사명'] = company_list
pd_data['제품'] = product_list
pd_data['가격'] = data['가격']
pd_data['사용시간'] = new_use_time_list
pd_data['흡입력'] = new_suction_list
pd_data.head()
카테고리 | 회사명 | 제품 | 가격 | 사용시간 | 흡입력 | |
---|---|---|---|---|---|---|
0 | 핸디/스틱청소기 | 샤오미 | 드리미 V10 | 149940 | 60.0 | 220.0 |
1 | 핸디/스틱청소기 | LG전자 | 코드제로 ThinQ A9S A9700 | 923930 | 120.0 | 200.0 |
2 | eS38UblSCe904379 | eS38UblSCe | eS38UblSCe | 904379 | NaN | NaN |
3 | 핸디/스틱청소기 | 삼성전자 | 제트 VS15R8500BF | 399830 | 40.0 | 150.0 |
4 | 차량용청소기 | 샤오미 | 미지아 차량용 핸디 무선 청소기 (해외구매) | 33580 | NaN | 130.0 |
# 예제 6-31 카테고리 분류 기준 및 데이터 개수 점검
pd_data['카테고리'].value_counts()
핸디/스틱청소기 302
물걸레청소기 40
차량용청소기 33
욕실청소기 11
업소용청소기 8
침구청소기 3
nU4uhSQGgy595342 1
핸디/스틱청소기+로봇청소기 1
EIbfkTD3Mu584523 1
1YEJcwbmFt192471 1
LG3ya8wkzd973525 1
CgBA3k0Mej649880 1
로봇청소기 1
C8xyj0zY40946998 1
15xqp2jVjy22371 1
jy1Vsx6Vtv592758 1
WjBWY2LmZJ627180 1
eS38UblSCe904379 1
진공청소기 1
Name: 카테고리, dtype: int64
# 예제 6-32 핸디/스틱청소기만 선택
pd_data_final = pd_data[pd_data['카테고리'].isin(['핸디/스틱청소기'])]
len(pd_data_final)
302
# 예제 6-33 엑셀로 저장
pd_data_final.to_excel('./files/3_2_danawa_data_final.xlsx', index = False)
6.4 무선청소기 모델별 비교 분석¶
6.4.1 데이터 살펴보기¶
# 예제 6-34 데이터 불러오기
import pandas as pd
danawa_data = pd.read_excel('./files/3_2_danawa_data_final.xlsx')
danawa_data.info()
danawa_data.head()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 302 entries, 0 to 301
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 카테고리 302 non-null object
1 회사명 302 non-null object
2 제품 302 non-null object
3 가격 302 non-null int64
4 사용시간 278 non-null float64
5 흡입력 167 non-null float64
dtypes: float64(2), int64(1), object(3)
memory usage: 14.3+ KB
카테고리 | 회사명 | 제품 | 가격 | 사용시간 | 흡입력 | |
---|---|---|---|---|---|---|
0 | 핸디/스틱청소기 | 샤오미 | 드리미 V10 | 149940 | 60.0 | 220.0 |
1 | 핸디/스틱청소기 | LG전자 | 코드제로 ThinQ A9S A9700 | 923930 | 120.0 | 200.0 |
2 | 핸디/스틱청소기 | 삼성전자 | 제트 VS15R8500BF | 399830 | 40.0 | 150.0 |
3 | 핸디/스틱청소기 | 삼성전자 | 제트 VS20T9258SDCS | 898990 | 120.0 | 200.0 |
4 | 핸디/스틱청소기 | 삼성전자 | 제트 VS20T9213QDCSB | 716680 | 60.0 | 200.0 |
# 예제 6-35 흡입력 기준 정렬
top_list = danawa_data.sort_values(["흡입력"], ascending = False)
top_list.head()
카테고리 | 회사명 | 제품 | 가격 | 사용시간 | 흡입력 | |
---|---|---|---|---|---|---|
28 | 핸디/스틱청소기 | 리하스 | LH20 | 299000 | 120.0 | 330.0 |
218 | 핸디/스틱청소기 | 퍼피유 | T12R | 325630 | 60.0 | 290.0 |
151 | 핸디/스틱청소기 | 캐치웰 | NEW F8 | 199000 | 25.0 | 290.0 |
50 | 핸디/스틱청소기 | 샤오미 | 드리미 V12 | 308770 | 90.0 | 270.0 |
294 | 핸디/스틱청소기 | 툴앤툴 | T300 | 198950 | 35.0 | 260.0 |
# 예제 6-36 사용시간 기준 정렬
top_list = danawa_data.sort_values(["사용시간"], ascending = False)
top_list.head()
카테고리 | 회사명 | 제품 | 가격 | 사용시간 | 흡입력 | |
---|---|---|---|---|---|---|
34 | 핸디/스틱청소기 | 삼성전자 | 제트 VS20T9257SECS | 739906 | 120.0 | 200.0 |
109 | 핸디/스틱청소기 | 삼성전자 | 제트 VS20T9257SDCS | 745280 | 120.0 | 200.0 |
38 | 핸디/스틱청소기 | 삼성전자 | 제트 VS20R9078S2 | 695270 | 120.0 | 200.0 |
166 | 핸디/스틱청소기 | 삼성전자 | 제트 VS20R9074S3 | 615300 | 120.0 | 200.0 |
207 | 핸디/스틱청소기 | 삼성전자 | 제트 VS20T9279S6 | 850680 | 120.0 | 200.0 |
# 예제 6-37 흡입력, 사용시간을 기준으로 정렬
# 흡입력 & 사용시간 TOP 리스트
top_list = danawa_data.sort_values(["사용시간","흡입력"], ascending = False)
top_list.head()
카테고리 | 회사명 | 제품 | 가격 | 사용시간 | 흡입력 | |
---|---|---|---|---|---|---|
28 | 핸디/스틱청소기 | 리하스 | LH20 | 299000 | 120.0 | 330.0 |
1 | 핸디/스틱청소기 | LG전자 | 코드제로 ThinQ A9S A9700 | 923930 | 120.0 | 200.0 |
3 | 핸디/스틱청소기 | 삼성전자 | 제트 VS20T9258SDCS | 898990 | 120.0 | 200.0 |
9 | 핸디/스틱청소기 | LG전자 | 코드제로 ThinQ A9S A9570 | 913500 | 120.0 | 200.0 |
10 | 핸디/스틱청소기 | LG전자 | 코드제로 ThinQ A9S A9500 | 780180 | 120.0 | 200.0 |
# 예제 6-38 평균값 정리
price_mean_value = danawa_data['가격'].mean()
suction_mean_value = danawa_data['흡입력'].mean()
use_time_mean_value = danawa_data['사용시간'].mean()
print("가격 평균값", price_mean_value)
print("흡입력 평균값", suction_mean_value)
print("사용시간 평균값", use_time_mean_value)
가격 평균값 364626.4900662252
흡입력 평균값 174.91017964071855
사용시간 평균값 49.2589928057554
# 예제 6-39 가성비 좋은 제품 탐색
condition_data = danawa_data [
(danawa_data['가격'] <= price_mean_value) &
(danawa_data['흡입력'] >= suction_mean_value) &
(danawa_data['사용시간'] >= use_time_mean_value)]
condition_data
카테고리 | 회사명 | 제품 | 가격 | 사용시간 | 흡입력 | |
---|---|---|---|---|---|---|
0 | 핸디/스틱청소기 | 샤오미 | 드리미 V10 | 149940 | 60.0 | 220.0 |
5 | 핸디/스틱청소기 | 샤오미 | 드리미 V11 | 242730 | 90.0 | 250.0 |
22 | 핸디/스틱청소기 | 샤오미 | SHUNZAO Z11 | 159000 | 60.0 | 260.0 |
24 | 핸디/스틱청소기 | 샤오미 | 드리미 V9 | 112500 | 60.0 | 200.0 |
28 | 핸디/스틱청소기 | 리하스 | LH20 | 299000 | 120.0 | 330.0 |
43 | 핸디/스틱청소기 | 샤오미 | SHUNZAO Z11 PRO | 198990 | 60.0 | 260.0 |
45 | 핸디/스틱청소기 | 샤오미 | 드리미 V9P | 158210 | 60.0 | 200.0 |
50 | 핸디/스틱청소기 | 샤오미 | 드리미 V12 | 308770 | 90.0 | 270.0 |
55 | 핸디/스틱청소기 | 샤오미 | 드리미 V10P | 235000 | 60.0 | 220.0 |
82 | 핸디/스틱청소기 | 로이드미 | 제로 | 221040 | 60.0 | 220.0 |
105 | 핸디/스틱청소기 | DIBEA | X20 | 217490 | 50.0 | 200.0 |
134 | 핸디/스틱청소기 | 델로라 | V11 파워 300W | 129000 | 70.0 | 220.0 |
144 | 핸디/스틱청소기 | 샤오미 | 드리미 V10 프로 | 255890 | 60.0 | 220.0 |
150 | 핸디/스틱청소기 | DIBEA | M500 퀀텀 | 238000 | 50.0 | 250.0 |
170 | 핸디/스틱청소기 | DIBEA | X30 | 229000 | 50.0 | 250.0 |
175 | 핸디/스틱청소기 | DIBEA | M500 클래스 | 246000 | 50.0 | 200.0 |
186 | 핸디/스틱청소기 | DIBEA | PLA25 | 199000 | 50.0 | 250.0 |
202 | 핸디/스틱청소기 | DIBEA | F20 맥스 | 199000 | 50.0 | 250.0 |
204 | 핸디/스틱청소기 | 샤오미 | VIOMI A9 V-HWVC12A | 243570 | 60.0 | 230.0 |
205 | 핸디/스틱청소기 | 캐치웰 | Z20 | 129000 | 60.0 | 200.0 |
206 | 핸디/스틱청소기 | 보아르 | 포스 B25 | 219000 | 60.0 | 250.0 |
216 | 핸디/스틱청소기 | 캐치웰 | 코드엑스 | 199000 | 60.0 | 220.0 |
218 | 핸디/스틱청소기 | 퍼피유 | T12R | 325630 | 60.0 | 290.0 |
261 | 핸디/스틱청소기 | 원더스리빙 | 원더스 다이나킹 Z9 | 299000 | 65.0 | 220.0 |
269 | 핸디/스틱청소기 | 캐치웰 | G20 | 129000 | 60.0 | 200.0 |
282 | 핸디/스틱청소기 | 캐치웰 | CX11 | 159000 | 60.0 | 200.0 |
6.4.3 데이터 시각화¶
# 예제 6-40 라이브러리 임포트 및 한글 글꼴 설정
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
# 예제 6-41 결측값 없애기
chart_data = danawa_data.dropna(axis = 0)
len(chart_data)
162
# 예제 6-42 흡입력, 사용시간의 최댓값/최솟값 정리
# 흡입력, 사용사간 최대, 최소
suction_max_value = chart_data['흡입력'].max()
suction_mean_value = chart_data['흡입력'].mean()
use_time_max_value = chart_data['사용시간'].max()
use_time_mean_value = chart_data['사용시간'].mean()
# 예제 6-43 청소기 성능 시각화
plt.figure(figsize=(20, 10))
plt.title("무선 핸디/스틱청소기 차트")
sns.scatterplot(x = '흡입력', y = '사용시간', size = '가격', hue = chart_data['회사명'],
data = chart_data, sizes = (10, 1000), legend = False)
plt.plot([0, suction_max_value],
[use_time_mean_value, use_time_mean_value],
'r--',
lw = 1 )
plt.plot([suction_mean_value, suction_mean_value],
[0, use_time_max_value],
'r--',
lw = 1 )
plt.show()
6.4.4 인기 제품의 데이터 시각화¶
# 예제 6-44 인기 제품 선택
chart_data_selected = chart_data[:20]
len(chart_data_selected)
20
# 예제 6-45 흡입력, 사용시간의 최댓값/최솟값 정리
# 흡입력, 사용시간의 최댓값, 최솟값 구하기
suction_max_value = chart_data_selected['흡입력'].max()
suction_mean_value = chart_data_selected['흡입력'].mean()
use_time_max_value = chart_data_selected['사용시간'].max()
use_time_mean_value = chart_data_selected['사용시간'].mean()
plt.figure(figsize=(20, 10))
plt.title("무선 핸디/스틱청소기 TOP 20")
sns.scatterplot(x = '흡입력',
y = '사용시간',
size = '가격',
hue = chart_data_selected['회사명'],
data = chart_data_selected, sizes = (100, 2000),
legend = False)
plt.plot([60, suction_max_value],
[use_time_mean_value, use_time_mean_value],
'r--',
lw = 1 )
plt.plot([suction_mean_value, suction_mean_value],
[20, use_time_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=20)
plt.show()