g2g-crawling00: javascript 동적페이지 크롤링

현거래 매물 거래 현황을 수집하려 한다.
전 세계를 대상으로하는 게임의 거래 데이터를 수집할 예정이니 세계 규모의 사이트 중 유명한 현거래 사이트인 G2G를 크롤링할 예정이다.

G2G를 크롤링 하는데 request로 html을 가져오니 사이트 구조만 긁어오고 텍스트 내용이 들어간 태그들은 가져오지 못했다.

문제는 request의 경우 사이트에서 바로 html 코드를 긁어오기 때문에 유저의 행동에 따라 혹은 서버에서 요청을 받고 정보를 불러오는 javascript 동적페이지에 대한 대응을 할 수 없던거였다.

my work

외부링크

selenium 사용하기

크롤링 문제 발생

G2G 페이지를 크롤링하면서 원하는 html 구문이 requests 방식으로는 스크래핑이 안되서 이유를 찾아봤다.
g2g 페이지는 javascript를 활용, 동적 페이지로 구성된 녀석이라 페이지를 열어서 활성된 html 구문을 가져와야 원하는 정보를 받아올 수 있는 경우였다.

Selenium

직접 페이지를 열기 위한 라이브러리 selenium의 파이썬(or 아나콘다) 설치를 진행한다.

1
2
$ pip install selenium
$ conda install selenium

나의 경우 venv 환경을 이용할 예정이라 venv 경로에서 bash를 이용해 pip install 해줬다.

브라우저 webdriver 설치

selenium을 받으면 끝이 아니라 selenium으로 html 구문을 받아오기 위해 webdriver를 받아와서 지정해줘야한다.

사용중인 os 기준으로 driver를 다운받으면 되지만, chrome의 경우 chrome 버전에 맞춰서 받아주자.

난 비교적 깔린 확장프로그램이 적어 가벼운 Firefox로 진행했다.

사용하기

위에서 다운받은 webdriver를 압축해제 해주고 받은 브라우저에 맞춰서 driver를 지정해주자.

1
2
3
4
5
6
from selenium import webdriver

driver = webdriver.Firefox(executable_path="webdriver 경로.exe")
url = "https://www.g2g.com/categories/new-world-coins"
driver.get(url)
html = driver.page_source

일반 FirefoxNightly 버전이 같이 깔려있어서 그런지 Firefox Nightly 버전으로 열렸다.

불러온 html 구문을 BeautifulSoup, Scrapy와 같은 크롤링 라이브러리 명령어에 가져올 수 있다.

1
2
3
from bs4 import BeautifulSoup

soup = BeautifulSoup(html)

브라우저 종료

driver 명령어만 이용하면 url을 직접 넣어줘야하지만,
클릭, 새창에서 열기 등 다양한 명령어를 사용할 수 있다. 필요할 때 찾아볼 예정이다.

페이지 크롤링이 끝났고, 브라우저가 열려있을 필요 없다면 종료해주면 된다.

1
2
driver.close()  # 탭 닫기
driver.quit() # 창 닫기

로딩 대기

코드 실행 대기를 생각하면 time 모듈의 sleep()함수를 사용해 주어진 시간 동안 무조건 대기를 진행할 수 있다.

하지만 우리가 원하는건 무조건적인 대기가 아니라 html 코드를 불러오기 위해 브라우저 로딩이 끝날 때까지만 코드를 대기시키는 방법이다.

Implicit Waits,암시적 대기

옵션 설정하듯 대기시간을 한번 설정하면 전역 적용된다.
bs4같은 모듈이 아닌 driver 라이브러리를 활용해 특정 요소를 찾을 때 활용할 수 있다.

1
2
3
4
5
6
from selenium import webdriver

driver.implicitly_wait(10) # seconds

driver.get(URL)
driver.find_element_by_id(Elements)

Explicit Waits, 명시적 대기

특정 조건이 만족할 때까지 지정된 시간 동안 대기하고, timeout된 경우 TimeoutException이 발생한다.

expected_conditions 함수의 조건이 만족할 때까지 기다리는 코드를 사용할 수 있다.

1
2
3
4
5
6
7
8
9
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 30)
# 특정 요소를 찾을 수 있을 때까지
element = EC.presence_of_element_located((By.CLASS_NAME , "offers-bottom-attributes.offer__content-lower-items"))
wait.until(element)

동적 페이지의 경우 하나의 URL에서 구조가 바뀌는 경우도 있다.

이런 경우 클릭이 가능할 때까지 기다리는 함수를 사용할 수 있다.

1
2
# 클릭이 가능할 때까지
element = EC.element_to_be_clickable((By.XPATH, '//a[@href="#!-1"]'))

아래의 조건들을 활용할 수 있다.

  • title_is
  • title_contains
  • presence_of_element_located
  • visibility_of_element_located
  • visibility_of
  • presence_of_all_elements_located
  • text_to_be_present_in_element
  • text_to_be_present_in_element_value
  • frame_to_be_available_and_switch_to_it
  • invisibility_of_element_located
  • element_to_be_clickable
  • staleness_of
  • element_to_be_selected
  • element_located_to_be_selected
  • element_selection_state_to_be
  • element_located_selection_state_to_be
  • alert_is_present

try ~ finally 구문을 활용하면 true인 경우 다음 단계로 넘어가는 코드를 활용할 수 있다.

외부링크

파이참 파일 확장자 타입 변경하기

파이참에서 파일을 만들다보면 확장자명을 지정하지 않고 파일을 만드면서 text 형식으로 지정하는 경우가 있다.
보통의 경우 auto-detected이지만, 잘못 지정하면 확장자를 바꿔도 기존에 지정한 형식으로 읽어온다.
이 경우 지정한 형식을 제거해주면 된다.

Setting - File Types

파이참 가상환경 생성

가상환경

라이브러리를 불러오다보면 의존성 패키지의 과거 버전이 필요할 때가 있다.
모든 패키지에 대응할 수는 없으니 파이썬이나 해당 패키지를 재설치 혹은 Downgrade를 진행해야 할 것이다.
위 문제를 해결할 수 있는 방법이 여러 파이썬을 생성해 각각 환경에 맞는 패키지를 설치해 따로 불러오는 방법으로 가상환경이라 한다.

가상환경 생성

설정에서 생성하기

프로젝트를 생성하고 코드 작업을 진행하다 버전 에러가 났을 때,

Files - Settings - Project:

- Python Interpreter 에서 Add... 를 눌러 추가할 수 있다.

가장 기본적인 Venv를 설치할 수 있다. anaconda 설치가 됐다면 conda 환경도 추가할 수 있지만, 기본적인 파이썬 Venv 설치를 진행할 예정.
파이썬 exe 경로와 가상환경 dir 경로를 잘 확인해주자

프로젝트와 같이 생성하기

보통의 경우는 파이참에서 프로젝트를 생성하며 가상환경 설치를 진행할 것이다.

터미널에서 생성하기

파이참같은 개발환경을 사용하지 못하고 리눅스 등 환경의 터미널에서 생성해야할 경우는 venv — 가상 환경 생성를 참조한다.

패키지 설치

파이썬 가상환경을 생성했다면 사용할 패키지를 설치하면 된다.

정상적으로 설치했다면 문제 없겠지만 어느 파이썬을 참조하고 있는지 터미널에서 확인해보자.

1
2
3
4
 # window
$ where python
# mac, linux .ect
$ which python

가상환경을 참조하지 못하고 있다면 아래 명령어로 (venv) 강제 진입해보자.

1
2
3
4
 # window
$ source ./venv/Scripts/activate
# mac, linux .ect
$ source ./venv/bin/activate

설정 interpreter에서

파이참 같은 경우엔 Python Interpreter - install 버튼을 선택해 설치할 패키지와 버전을 선택할 수 있다.

requirements.txt

버전을 일일히 기억하고 있기도 뭐하고 설치할 패키지가 많다면 하나하나 설치하는 것도 일이다.

pycaret같은 패키지를 보면 requirements.txt로 관리한다.

텍스트 파일에 원하는 패키지와 버전을 입력했다면 pip 명령어로 한번에 설치를 진행할 수 있다.

1
$ pip install -r requirements.txt

Jupyter ipynb에서 가상환경 사용

가상환경 이름과 표시될 이름을 설정해준다.

난 각각

과 <파이썬버전(dir 이름)>으로 설정했다.

1
ipython kernel install --user --name python_ml --display-name "Python3(python_ml)"

정상적으로 등록됐다면 jupyter 환경으로 접속하면 된다.

1
2
$ jupyter lab
$ jupyter notebook

주피터 랩을 추천한다.

외부링크

파이참, 파이썬 인코딩 설정

어떤 작업환경을 사용하든 가능하다면 UTF-8 포멧으로 작업하는걸 추천한다.

pycharm에서도 인코딩 방식 변경을 지원함.

file - setting - editor - file encodings에서 Global Encoding이 UTF-8이 아니라면 변경해주자.

프로젝트 인코딩과 속성 파일 인코딩 방식은 OS가 윈도우라면 새 프로젝트를 열 때 마다 기본값으로 돌아갈 것이다.
필요한 설정이 아니라면 크게 신경쓰진 말자.

encoding

※ 코드 파일 맨 윗단에 아래 주석을 추가하면 자동 UTF-8 인코딩 해준다.

1
# -*- coding: utf-8 -*-

pycharm에서 UTF-8로 변경했고 python 3++를 사용중이기에 추가할 필요는 없지만 작업 파일을 공유하거나 먼 미래에 열어볼 나를 위해 추가한다.

Kaggle-Survey03-B: replace가 작동 안함 [pandas]

Japan & China: IDE’s

Program_Language 과정과 동일

Treemap을 뽑으려니 “Jupyter (JupyterLab, Jupyter Notebooks, etc)”와 “Visual Studio / Visual Studio Code”가 너무 길어 플롯에서 식별하기 난감하다.

각각 “Jupyter”와 “VS / VSCode”로 간략화 하려한다.

1차 시도

1
2
df_IDEs_JnC_21n19.replace(to_replace = 'Jupyter (JupyterLab, Jupyter Notebooks, etc)', value =  'Jupyter', inplace = True)
df_IDEs_JnC_21n19.replace(to_replace = 'Visual Studio / Visual Studio Code', value = 'VS / VSCode', inplace = True)

둘 다 변경되지 않았다.

2차 시도

실제 string을 확인해보자.

1
2
print(df_IDEs_JnC_21n19.loc[0].tolist())
print(df_IDEs_JnC_21n19.iloc[29].tolist())
['2021', 'Japan', 'Jupyter (JupyterLab, Jupyter Notebooks, etc) ', 200]
['2019', 'China', ' Visual Studio / Visual Studio Code ', 200]

뒤(혹은 앞뒤)로 공백이 들어간 상황임을 알 수 있다.

1
2
df_IDEs_JnC_21n19.replace(to_replace = 'Jupyter (JupyterLab, Jupyter Notebooks, etc) ', value =  'Jupyter', inplace = True)
df_IDEs_JnC_21n19.replace(to_replace = ' Visual Studio / Visual Studio Code ', value = 'VS / VSCode', inplace = True)

성공적으로 변경됐다.

더 간편하게

근대 이럴거면 그냥 요소를 뽑아내서 직접 삽입하는게 편할 듯하다.

1
2
df_IDEs_JnC_21n19.replace(to_replace = df_IDEs_JnC_21n19.loc[0,"IDE\'s"], value =  'Jupyter', inplace = True)
df_IDEs_JnC_21n19.replace(to_replace = df_IDEs_JnC_21n19.loc[29,"IDE\'s"], value = 'VS / VSCode', inplace = True)

인덱스와 칼럼명을 직접 지정해 뽑아낸 string과 동일한 요소를 모두 변경한다.

정규식?

‘ Visual Studio / Visual Studio Code ‘ 요소는 실제로는 아니지만 정규 표현식으로 해석할 여지가 있다.
정 방법을 못찾겠다면 regex 요소를 True로 지정해보는것도 방법이 될 수 있다.

1
2
df_IDEs_JnC_21n19.replace(to_replace = 'Jupyter (JupyterLab, Jupyter Notebooks, etc) ', value = 'Jupyter', inplace = True)
df_IDEs_JnC_21n19.replace(to_replace = 'Visual Studio / Visual Studio Code', value = 'VS / VSCode', inplace = True, regex = True)

IDE’s Treemap

IDE's

외부링크

Kaggle_Survey03-A: value_counts error size 0 [pandas]

Kaggle_Survey03 - Treemap 시각화 [plotly]에서 넘어왔다.

오류 식별

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
df21_Ch = df21_ChJp[df21_ChJp.Q3.isin(["China"])]
df21_Jp = df21_ChJp[df21_ChJp.Q3.isin(["Japan"])]


## Q7(Program_Language): 칼럼번호 8~20 - others
df21_Jp_PL = pd.DataFrame()
df21_Jp_PL['Program_Language'] = [df21_Jp[col][1:].value_counts().index[0] for col in df21_Jp.columns[7:20]]
df21_Jp_PL['counts'] = [df21_Jp[col][1:].value_counts().values[0] for col in df21_Jp.columns[7:20]]


## Q7(Program_Language): 칼럼번호 8~20 - others
df21_Ch_PL = pd.DataFrame()
df21_Ch_PL['Program_Language'] = [df21_Ch[col][1:].value_counts() .index[0] for col in df21_Ch.columns[7:20]]
df21_Ch_PL['counts'] = [df21_Ch[col][1:].value_counts() .values[0] for col in df21_Ch.columns[7:20]]



## 제거된 나라 칼럼과 value를 각각 삽입 및 통합
df21_Jp_PL.insert(0, 'Country', 'Japan')
df21_Ch_PL.insert(0, 'Country', 'China')

df21_PL_JnC = pd.concat([df21_Jp_PL,df21_Ch_PL], ignore_index=True)
---------------------------------------------------------------------------

IndexError                                Traceback (most recent call last)

<ipython-input-5-89d86f0a4d0b> in <module>
     11 ## Q7(Program_Language): 칼럼번호 8~20 - others
     12 df21_Ch_PL = pd.DataFrame()
---> 13 df21_Ch_PL['Program_Language'] = [df21_Ch[col][1:].value_counts() .index[0] for col in df21_Ch.columns[7:20]]
     14 df21_Ch_PL['counts'] = [df21_Ch[col][1:].value_counts() .values[0] for col in df21_Ch.columns[7:20]]
     15 


<ipython-input-5-89d86f0a4d0b> in <listcomp>(.0)
     11 ## Q7(Program_Language): 칼럼번호 8~20 - others
     12 df21_Ch_PL = pd.DataFrame()
---> 13 df21_Ch_PL['Program_Language'] = [df21_Ch[col][1:].value_counts() .index[0] for col in df21_Ch.columns[7:20]]
     14 df21_Ch_PL['counts'] = [df21_Ch[col][1:].value_counts() .values[0] for col in df21_Ch.columns[7:20]]
     15 


E:\Sadness\anaconda3\lib\site-packages\pandas\core\indexes\base.py in __getitem__(self, key)
   4295         if is_scalar(key):
   4296             key = com.cast_scalar_indexer(key, warn_float=True)
-> 4297             return getitem(key)
   4298 
   4299         if isinstance(key, slice):


IndexError: index 0 is out of bounds for axis 0 with size 0

결측 column 식별 및 제거

*IndexError: index 0 is out of bounds for axis 0 with size 0
오류가 식별됐다.
아마 China, Program_Language의 특정 응답이 없어서 발생한거같다.

N/A 개수를 식별해보자

1
2
print("df21_Ch\'s rows_num:",len(df21_Ch))
print(df21_Ch.isnull().sum().iloc[7:20])
df21_Ch's rows_num: 814
Q7_Part_1      76
Q7_Part_2     729
Q7_Part_3     599
Q7_Part_4     588
Q7_Part_5     546
Q7_Part_6     602
Q7_Part_7     728
Q7_Part_8     810
Q7_Part_9     809
Q7_Part_10    783
Q7_Part_11    645
Q7_Part_12    814
Q7_OTHER      787
dtype: int64

Q7_Part_12 이 녀석이 문제였다.

  • 행 개수는 814개고 Part_12의 N/A 개수도 814개이다.

위 식별 과정의 시행 횟수가 많아진다면 if 문을 사용한 define을 사용해도 되겠지만 일단은 수동으로 제거하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
df21_Ch = df21_ChJp[df21_ChJp.Q3.isin(["China"])]
df21_Jp = df21_ChJp[df21_ChJp.Q3.isin(["Japan"])]


## Q7(Program_Language): 칼럼번호 8~20 - others
df21_Jp_PL = pd.DataFrame()
df21_Jp_PL['Program_Language'] = [df21_Jp[col][1:].value_counts().index[0] for col in df21_Jp.columns[7:19]]
df21_Jp_PL['counts'] = [df21_Jp[col][1:].value_counts().values[0] for col in df21_Jp.columns[7:19]]


## 2021 China: Q7_Part12(None) value == 0이므로 결측값 제거
df21_Ch_rmQ07P12 = df21_Ch.drop(['Q7_Part_12'], axis='columns')

## Q7(Program_Language): 칼럼번호 8~20 - others - Q7_Part12(None)
df21_Ch_PL = pd.DataFrame()
df21_Ch_PL['Program_Language'] = [df21_Ch_rmQ07P12[col][1:].value_counts() .index[0] for col in df21_Ch_rmQ07P12.columns[7:18]]
df21_Ch_PL['counts'] = [df21_Ch_rmQ07P12[col][1:].value_counts() .values[0] for col in df21_Ch_rmQ07P12.columns[7:18]]


## 제거된 나라 칼럼과 value를 각각 삽입 및 통합
df21_Jp_PL.insert(0, 'Country', 'Japan')
df21_Ch_PL.insert(0, 'Country', 'China')

df21_PL_JnC = pd.concat([df21_Jp_PL,df21_Ch_PL], ignore_index=True)

Kaggle_Survey03: Treemap 시각화 [plotly]

기본 설정

module

treemap으로 많은 요소를 건드리진 않을 예정이니 express 라이브러리를 사용

1
2
import pandas as pd
import plotly.express as px

Import data

2021년 자료 외에도 19년 자료를 추가

1
2
3
4
df21 = pd.read_csv("https://raw.githubusercontent.com/hangack/project-green/main/Kaggle_Survey-2021/data/kaggle-survey-2021/kaggle_survey_2021_responses.csv", dtype='unicode')
df19 = pd.read_csv("https://raw.githubusercontent.com/hangack/project-green/main/Kaggle_Survey-2021/data/kaggle-survey-2019/multiple_choice_responses.csv", dtype='unicode')
#df21 = pd.read_csv("../input/kaggle-survey-2021/kaggle_survey_2021_responses.csv", dtype='unicode')
#df19 = pd.read_csv("../input/kaggle-survey-2019/multiple_choice_responses.csv", dtype='unicode')

Japan & China: Programming_Languages

2021 Japan & China total

2021년 Q3(Country) 일본 중국 추출 dataframe

1
df21_ChJp = df21[df21.Q3.isin(["Japan","China"])]

Split Country

나라별 value_counts를 위해 각 나라로 dataframe 분리

2021

value_counts 오류 식별 결측값 제거

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
df21_Ch = df21_ChJp[df21_ChJp.Q3.isin(["China"])]
df21_Jp = df21_ChJp[df21_ChJp.Q3.isin(["Japan"])]


## Q7(Program_Language): 칼럼번호 8~20 - others
df21_Jp_PL = pd.DataFrame()
df21_Jp_PL['Program_Language'] = [df21_Jp[col][1:].value_counts().index[0] for col in df21_Jp.columns[7:19]]
df21_Jp_PL['counts'] = [df21_Jp[col][1:].value_counts().values[0] for col in df21_Jp.columns[7:19]]


## 2021 China: Q7_Part12(None) value == 0이므로 결측값 제거
df21_Ch_rmQ07P12 = df21_Ch.drop(['Q7_Part_12'], axis='columns')

## Q7(Program_Language): 칼럼번호 8~20 - others - Q7_Part12(None)
df21_Ch_PL = pd.DataFrame()
df21_Ch_PL['Program_Language'] = [df21_Ch_rmQ07P12[col][1:].value_counts() .index[0] for col in df21_Ch_rmQ07P12.columns[7:18]]
df21_Ch_PL['counts'] = [df21_Ch_rmQ07P12[col][1:].value_counts() .values[0] for col in df21_Ch_rmQ07P12.columns[7:18]]


## 제거된 나라 칼럼과 value를 각각 삽입 및 통합
df21_Jp_PL.insert(0, 'Country', 'Japan')
df21_Ch_PL.insert(0, 'Country', 'China')

df21_PL_JnC = pd.concat([df21_Jp_PL,df21_Ch_PL], ignore_index=True)

2019 Japan & China total

2019년 Q3(Country) 일본 중국 추출 dataframe

1
df19_ChJp = df19[df19.Q3.isin(["Japan","China"])]

2019

2021년과 동일 과정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
df19_Ch = df19_ChJp[df19_ChJp.Q3.isin(["China"])]
df19_Jp = df19_ChJp[df19_ChJp.Q3.isin(["Japan"])]


## Q18(Program_Language): 칼럼번호 83~95 - others & other(text)
df19_Jp_PL = pd.DataFrame()
df19_Jp_PL['Program_Language'] = [df19_Jp[col][1:].value_counts().index[0] for col in df19_Jp.columns[82:93]]
df19_Jp_PL['counts'] = [df19_Jp[col][1:].value_counts().values[0] for col in df19_Jp.columns[82:93]]


## 2019 China Q18_Part11(None) 결측값 제거
df19_Ch_rmQ18P11 = df19_Ch.drop(['Q18_Part_11'], axis='columns')

## Q18(Program_Language): 칼럼번호 83~95 - others & other(text) - Q18_Part11(None)
df19_Ch_PL = pd.DataFrame()
df19_Ch_PL['Program_Language'] = [df19_Ch_rmQ18P11[col][1:].value_counts() .index[0] for col in df19_Ch_rmQ18P11.columns[82:92]]
df19_Ch_PL['counts'] = [df19_Ch_rmQ18P11[col][1:].value_counts() .values[0] for col in df19_Ch_rmQ18P11.columns[82:92]]



df19_Jp_PL.insert(0, 'Country', 'Japan')
df19_Ch_PL.insert(0, 'Country', 'China')

df19_PL_JnC = pd.concat([df19_Jp_PL,df19_Ch_PL], ignore_index=True)

Split year{Country}

다른 csv인 2019자료와 2021자료 통합

1
2
3
4
df21_PL_JnC.insert(0, 'year',  '2021')
df19_PL_JnC.insert(0, 'year', '2019')

df_PL_JnC_21n19 = pd.concat([df21_PL_JnC,df19_PL_JnC], ignore_index=True)

Program_Language의 19년도 21년도 통합 value_counts의 정렬(연도 - 언어 - 나라)

values는 Program_Language의 value_counts

Programming_Languages [treemap]

이전 언급처럼 treemap으로 많은 요소를 건드리진 않을 예정이니 express 라이브러리를 사용했다.

path 요소의 순서는 부모자식 순서로 dataframe 칼럼 순서에 제한되지 않는다. -> path 요소 조정으로 순서를 맘대로 바꿀 수 있다.

color 기준: country

1
2
3
4
5
6
7
8
9
fig = px.treemap(df_PL_JnC_21n19, path=[px.Constant("2019n2021"),'year','Program_Language','Country'],
values='counts', color='Country',
color_discrete_map={'(?)':'lightgrey', 'China':'gold', 'Japan':'darkblue'})

fig.data[0].textinfo = 'label+percent parent+value'

fig.update_layout(margin = dict(t=0, l=0, r=0, b=0))

fig.show()

color 참조 값을 counts로 넣었을 때는 plotly.express 설정대로 colorbar가 나온다.

color 기준: counts

1
colors = ['#D2691E','#E19B50','#E6C17B','#F0CB85','#F5D08A','#FFEFD5']
1
2
3
4
5
6
7
8
fig = px.treemap(df_PL_JnC_21n19, path=[px.Constant("2019n2021"),'year','Program_Language','Country'],
values='counts', color='counts', color_continuous_scale=colors)

fig.data[0].textinfo = 'label+percent parent+value'

fig.update_layout(margin = dict(t=0, l=0, r=0, b=0))

fig.show()

Kaggle_Survey02: Bar 시각화 [plotly]

이전 포스팅들과 동일 과정

1
2
3
4
import pandas as pd
import plotly.graph_objects as go

colors = ['#FF0000','#FFBB00','#ffff00','#00FF00','#0000FF','#9C009C']
1
2
df = pd.read_csv("https://raw.githubusercontent.com/hangack/project-green/main/Kaggle_Survey-2021/data/kaggle-survey-2021/kaggle_survey_2021_responses.csv", dtype='unicode')
#df = pd.read_csv("../input/kaggle-survey-2021/kaggle_survey_2021_responses.csv")
1
2
3
4
df_ChJp = df[df.Q3.isin(["Japan","China"])]

df_Ch = df_ChJp[df_ChJp.Q3.isin(["China"])]
df_Jp = df_ChJp[df_ChJp.Q3.isin(["Japan"])]
1
2
3
def indiQ_value_counts(dataframe, indi_Qnum):
df = dataframe[indi_Qnum][1:].value_counts()
return df
1
2
df_Jp_age = indiQ_value_counts(df_Jp, 'Q1')
df_Ch_age = indiQ_value_counts(df_Ch, 'Q1')

Age [plotly: Bar]

01에 이어 이번에도 두 나라의 값을 비교할 예정이니 아예 처음부터 데이터를 합쳐서 표현한 그래프를 뽑아내겠다.

1
2
3
4
5
6
7
8
Bar_C = go.Bar(name='China',
x=df_Ch_age.index,
y=df_Ch_age.values
)
Bar_J = go.Bar(name='Japan',
x=df_Jp_age.index,
y=df_Jp_age.values
)
1
2
3
4
5
6
7
8
fig = go.Figure(data=[Bar_C,
Bar_J])

fig.update_layout(title='Age: Japan & China',
xaxis_title="Age", yaxis_title='Counts'
)

fig.show()

Bar1

bar chart에서 stack 형식을 사용하거나 group과 stack을 동시에 사용하지 않는 이상 하나의 plot에 여러 bar data를 넣으면 default는 group 형식으로 뽑힌다.

index 정렬

나이 순으로 출력하고 싶었지만, value_counts 할 때 원본 dataframe의 요소 순서로 index가 들어가버렸다.

그래서 value_counts된 dataframe을 sort_index를 사용해 오름차순 정렬한다.

1
2
df_Ch_sortAge = df_Ch_age.sort_index()
df_Jp_sortAge = df_Jp_age.sort_index()

Bar2

60-69 구간에 더미값 추가

정렬은 문제없어 보였지만 70+와 60-69 순서가 이상하다.

1
df_Ch_sortAge
18-21    206
22-24    274
25-29    159
30-34    109
35-39     39
40-44     14
45-49      8
50-54      1
55-59      2
70+        1
Name: Q1, dtype: int64

아무래도 China에 60-69 구간에 해당하는 value가 없어서 index 추가가 안된거같다.

index가 전부 있는 Japan을 앞 순서로 바꿔도 되겠지만, 그냥 China 60-69 index를 추가하고 임의로 값 0를 넣겠다.

1
2
df_Ch_sortAge.loc['60-69'] = 0
df_Ch_sortAge = df_Ch_sortAge.sort_index()

Bar3

외부링크