파이썬(Python) 데이터 크롤링 기초 수업 정리

개발자나 데이터분석가에게 중요한 능력중 하나를 꼽자면 무엇일까.
자신이 진행할 프로젝트의 데이터를 수집하는 능력도 꽤나 중요하지 않을까 싶다.
그러기 위해서는 크롤링을 해야 할 때가 있는데 크롤링이란 HTML(웹페이지)를 그대로 가져와 데이터를 추출하는 행위를 말한다.
그래서 이번 포스트에서는 학원에서 배운 간단한 크롤링에대해 수업 그대로를 정리하려 한다.

사전준비


크롤링을 하기위해 일단 BeautifulSoup 라는 파이썬 라이브러리를 설치해 주어야 한다.
다음 코드를 터미널에 입력해 라이브러리를 설치한다.

1
pip install beautifulsoup4

설치가 완료되었다면 예제 HTML 문서를 하나 만들어주면 준비는 끝난다.

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Crawl This Page</title>
</head>
<body>
<div class="cheshire">
<p>Don't crawl this.</p>
</div>
<div class="elice">
<p>Hello, Python Crawling!</p>
</div>
<div id="main">
<p>I am in main</p>
</div>
</body>
</html>

BeautifulSoup를 사용하여 크롤링

먼저 크롤링을 하기 위해 파이썬 파일을 만들어준다.

1
from bs4 import Beautifulsoup

이후 Beautifulsoup 라이브러리를 import하고 간단한 함수를 만들어 크롤링을 해보자.



HTML 불러오기

1
2
3
4
5
6
def crawling():
soup = BeautifulSoup(open("data/index.html"), "html.parser") # open의 괄호안에 크롤링할 HTML파일의 위치를 입력
print(soup)

if __name__ == "__main__":
crawling()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8"/>
<title>Crawl This Page</title>
</head>
<body>
<div class="cheshire">
<p>Don't crawl this.</p>
</div>
<div class="elice">
<p>Hello, Python Crawling!</p>
</div>
</body>

HTML 파일이 soup에 저장되어 출력된 것을 볼 수 있다.



필요한 태그만 가져오기

하지만 우리는 HTML 전체가 아닌 특정 텍스트, 태그만 필요로 하기 때문에 그것들을 뽑아낼 수 있어야 한다.

1
2
3
4
5
6
def crawling():
soup = BeautifulSoup(open("data/index.html"), "html.parser")
print(soup.find("p"))

if __name__ == "__main__":
crawling()
1
<p>Don't crawl this.</p>

find()함수는 가장 앞쪽의 한 태그만을 가져온다.
이처럼 p태그가 출력되었는데 두가지 의문이 생길 것이다.
‘p태그는 두개인데 왜 한개만 출력되는가?’와 ‘<p></p>없이 텍스트만 추출 할수 없는가?’라는 것이다.
물론 가능하다.
첫번째는 find()함수를 find_all()로 바꾸면 모든 p태그가 출력되고,
두번째는 find()함수 뒤에 .get_text()를 붙여주면 태그가 제외된 텍스트만 출력이 된다.
이외에도 많은 함수가 있고, Beautiful Soup Documentation 에서 찾아볼 수 있다.



class, id로 태그 가져오기

여전히 크롤링이라기엔(기초지만) 부족하다 원하는 태그를 가져올 수는 있으나 태그가 여러개면 같이 가져올 수밖에 없기 때문이다.
그래서 이번에는 각 태그에 주어진 class와 id를 통해 크롤링하는 법에 대해 알아보자.

index.html을 보면 div태그에 각각 class와 id가 있다.
이를통해 크롤링 하는 코드를 보면

1
2
3
4
5
6
def crawling():
soup = BeautifulSoup(open("data/index.html"), "html.parser")
print(soup.find("div", class_ = "elice").find("p").get_test())

if __name__ == "__main__":
crawling()

다음과 같으며 이를 실행하면

1
Hello, Python Crawling!

위와 같이 출력된다.
만약 class가 아닌 id를 지정해서 태그를 찾고싶을 경우 find()함수 내에 class_ = 가 아닌 id = 를 입력하면 된다.
class는 언더바가 붙고 id는 붙지 않는다.

OPEN API를 통해 데이터 가져오기

open API를 통해 데이터를 가져오는 법도 간단히 알아보자.
이 포스팅은 캐글을 기반으로 진행했으며 데이터는 한국도로공사 공공데이터 포털 의 교통데이터를 이용했다.

먼저 API에 접근하기 위해 인증키를 발급 받는다.

실시간 영업소간 통행시간 페이지
이후 실시간 영업소간 통행시간 페이지 에 접속해 아래의 예제 실행하기를 클릭.

실시간 영업소간 통행시간 팝업
원하는 Request Parameter를 입력하고(필자는 key(여기서 키는 방금 발급받은 인증키), type, iStartUnitCode, iEndUnitCode만 작성) URL보기를 하면 URL이 출력되고 , 아래의 예제 실행하기를 통해 직접 볼 수도 있다.

이제, 캐글에 다음과 같이 입력해준다.

1
2
3
4
5
6
7
8
import requests
key = "발급받은 인증키"
type = "json"
url = "출력된 URL"
responses = requests.get(url)
print(responses)
json = responses.json()
json

을 입력해주면
팝업에서 보았던 예제를 직접 볼 수 있다.

필요한 정보가 있는 “realUnitTrtmVO” 항목을 가져오기 위해 다음 코드 입력

1
cars = json["realUnitTrtmVO"]

이것을 반복문을 통해 리스트, 딕셔너리 형태로 만들어준다.

1
2
3
4
5
6
7
8
9
10
data = []
for car in cars:
dic_df = {}
dic_df["date"] = car["stdDate"]
dic_df["time"] = car["stdTime"]
dic_df["destination"] = car["endUnitNm"]

data.append(dic_df)

data

아래는 아웃풋

1
2
3
4
5
6
7
8
9
10
[{'date': '20220103', 'destination': '수원신갈', 'time': '05:30'},
{'date': '20220103', 'destination': '수원신갈', 'time': '05:35'},
{'date': '20220103', 'destination': '수원신갈', 'time': '05:40'},
{'date': '20220103', 'destination': '수원신갈', 'time': '05:45'},
{'date': '20220103', 'destination': '수원신갈', 'time': '05:50'},
{'date': '20220103', 'destination': '수원신갈', 'time': '05:55'},
{'date': '20220103', 'destination': '수원신갈', 'time': '06 '},
{'date': '20220103', 'destination': '수원신갈', 'time': '06:00'},
{'date': '20220103', 'destination': '수원신갈', 'time': '06:05'},
{'date': '20220103', 'destination': '수원신갈', 'time': '06:10'}]

이렇게 딕셔너리로 구성된 리스트가 생성되었다.
이것을 Pandas Dataframe으로 변환후 엑셀시트로 출력하려면 다음과 같이 하면 된다.

1
2
3
import pandas as pd
df = pd.DataFrame(data) # 판다스 데이터프레임으로 변환
df.to_csv("temp.csv",index=False,encoding="euc-kr")

이렇게하면 설정된 곳으로 캐글에서 데이터프레임이 다운로드된다.

콜백(Callback)함수의 사용법

Plotly 를 사용하여 대시보드를 만드는 동안에 직면했던 여러 문제들중 해결에 꽤 오랜시간이 걸린 문제가 있었다.
바로 콜백함수에 대한 부분인데 처음보게된 콜백함수가 데코레이터가 적용된 그런 함수였기 때문에 굉장히 헷갈렸다.

콜백함수


콜백함수란 다른함수의 인자로써 이용되는함수, 어떤 이벤트에 의해 호출되어지는 함수를 말한다.
함수가 다른함수의 인자로 사용될 수 있다니, 잘 이해가 되지 않을 수 있다.

내가 접했던 콜백함수를 가져와 보면 이렇다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@app.callback(
Output('id_fig_age', 'Figure'),
Input('country-filter', 'value'))
def age_chart_func(value):
obj = px.bar(data,
x=data[data['Q3'] == value]['Q1'][1:].value_counts().sort_index().index,
y=data[data['Q3'] == value]['Q1'][1:].value_counts().sort_index().values,
)
obj.update_traces(hovertemplate='%{x}: %{y:.0f}',
marker_color='#E08E79',
marker_line_width=0,)
obj.update_layout(paper_bgcolor=colors['content-background'],
font_color=colors['text'],
plot_bgcolor=colors['plot_background'],
autosize=True)
obj.update_xaxes(title_text='Age Distribution')
obj.update_yaxes(title_text='Counts')
return obj

전체코드의 일부분이지만 함수를 설명하는데에는 문제가 없다.

먼저 가장 위에 있는 @app.callback가 무슨뜻인지 이해하지 못한다면 데코레이터 에 대해 간략히 보고 오면 좋다.
그리고 Output과 Input은 dash.dependencies 라이브러리에서 import 한 것인데, 기능을 살펴보기위해 코드대로 해석하자면

Input의 괄호안의 첫번째 인자(country-filter)와 ID가 같은 항목을 찾아 두번째 인자(country-filter 의 파라미터 value)를 아래에 있는 함수의 인자로 넣어주며, 인자를 통해 함수를 실행 후 나온 리턴을,
Output의 괄호안의 첫번째 인자(id_fig_age)와 ID가 같은 항목의 두번째 인자(Figure)로 입력되는 것이다.

위 코드에서 복잡해 보일 수 있는 부분인 px.bar ~ obj.update_yaxes 까지는 그냥 그래프를 그려주는 함수라고 생각하면된다.

이때 이 함수를 모두 실행하고 리턴하는 값인 obj 가 반환되어 Output인 ‘id_fig_age’의 ‘Figure’속성으로 입력되고 ‘id_fig_age’는 함수에서 작성된 그래프를 ‘Figure’로 갖게되어 화면에 표현해준다.

파이썬 데코레이터 (Python Decorator)

데코레이터란?


파이썬으로된 소스코드들을 보면, 가끔 다음과 같은 구문을 볼 수 있다.

1
2
3
@decorator
def func()
print("How to use Python")

본적은 있는것 같으나 어디에 어떻게 사용되는지 처음보는사람은 모를 수 있다.
데코레이터는 함수를 수정하지 않은 상태에서 추가기능을 구현할 때 사용한다.
일단 다음의 예시를 보자

1
2
3
4
5
6
7
8
def func1():
print("func1")

def func2():
print("func2")

func1()
func2()

위 두개의 함수에 각각 시작부분과 끝부분을 표기하고싶다면 아래와 같이 함수 시작, 끝부분에 print를 따로 넣어주어야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
def func1():
print("func1 start")
print("func1")
print("func1 end")

def func2():
print("func2 start")
print("func2")
print("func2 end")

func1()
func2()

함수가 한개, 두개라면 부담이 되지않겠지만 만약 10개, 100개, 1000개의 함수가 있고 그것을 수정해야한다면 여간 귀찮은 일이 아닐것이다.

이런 경우에 데코레이터를 사용하면 편리하다.
바로 다음 예시를 보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def dec(func):
def wrapper(func):
print(func.__name__, "start")
func()
print(func.__name__, "end")
return wrapper

def func1():
print("func1")

def func2():
print("func2")

dec_func1 = dec(func1)
dec_func1()
dec_func2 = dec(func2)
dec_func2()

'''
또는
dec(func1)()
dec(func2)()
'''
1
2
3
4
5
6
7
<Output>
func1 start
func1
func1 end
func2 start
func2
func2 end

위처럼 입력하게 될 경우 먼저 만들어졌던 dec 함수에 의해 func1, func2의 함수 출력부분에 시작과 끝을 나타내는 print가 같이 출력되게 할 수 있다.

위 코드에서 start 와 end 앞에 있는 func1,func2의 경우에는 함수의 이름이 출력되는것이고, 그 사이에 있는 func1,func2은 함수의 print 열이 출력된 것이다.

@가 있는 데코레이터 사용하기


그런데 처음에 예시로 보았던 @로 시작하는 데코레이터는 어디에도 보이지 않는다.
@를 사용하는 데코레이터는 어떻게 만드는 것일까?

@를 사용하는 데코레이터는 다음과 같이 작성한다.

1
2
3
@dec
def func1():
print("func1")

간결하게 적어서 이해가 어려울 수 있으니 이전 챕터의 가장 뒷부분 코드를 가져와 수정해보면 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def dec(func):
def wrapper():
print(func.__name__, "start")
func()
print(func.__name__, "end")
return wrapper

@dec
def func1():
print("func1")

@dec
def func2():
print("func2")

func1()
func2()

결과는 같지만 무엇보다 출력부분이 매우 간결해졌다.

만약 한개의 함수에 데코레이터 여러개를 사용해야한다면 다음과 같이 할 수 있다.

1
2
3
4
5
@dec1
@dec2
@dec3
def func1():
print("func1")

코드가 위와 같을 때 여러개의 데코레이터가 지정되며 이때 데코레이터가 실행되는 순서는 위에서 아래 순으로 실행된다.

마지막으로 데코레이터를 그림으로 표현하면 다음과 같다.

데코레이터의 실행 과정

Reference

https://bluese05.tistory.com/30

파이썬(Python) with 구문

with 구문이 무엇인가?


보통 프로그램은 파일에 접근해서 파일 내용등을 읽고 쓰고 수정하는등의 일을 수행한 뒤 다시 그 파일을 마운트 해제 하는 패턴을 따른다.
예시로 사용자가 워드파일 문서 작업을 하고 있을 때 그 파일을 열고있는 동안 파일관리자가 그 문서의 이름을 바꾼다던지 파일의 경로를 변경한다던지 같은 파일에 접근을 필요로하는 행동을 타 프로그램에서 할 수 없게 된다.

결론만 말하자면 파일에 접근했으면(열었으면) 해제하는(닫아주는) 일을 빼먹지 않고 해주어야 한다는 것이다.
보통 close() 같은 메소드를 사용하여 파일을 닫아주지만 이는 문제점이 있다.
파일 처리를 수행하는 도중에 오류가 발생하게되면 아래에있는 close() 문을 실행할 수 없고 파일을 닫을수 없게된다.

with 문은 그 구문을 실행했을 때 오류가 발생하던 하지않던 마지막에 close 를 해주도록 하는 것이다.

with문 사용 예시


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 일반적인 코드
file = open('textfile.txt', 'r')
contents = file.read()
file.close()

# try finally를 사용한 코드
try:
file = open('textfile.txt', 'r')
contents = file.read()
finally:
file.close()

# with 구문을 사용한 코드
with open('textfile.txt', 'r') as file:
contents = file.read()

위의 세가지 코드는 모두 같은 내용이지만 첫번째 코드를 실행할때 contents = file.read() 행에서 에러가 난다면 file.close()를 실행하지 못해 파일을 닫을 수 없다.
그러나 아래의 두가지 코드는 에러가 발생해도 코드를 닫을 수 있을 뿐더러 with문은 더욱 간결하여 보기 편하다.



여러개의 파일 관리하기


두 개 이상의 파일을 동시에 사용할 때 with as 문을 사용하는 방법이다.
두개의 with as 문을 겹쳐도 되고, 하나의 with문에 두 개 이상의 파일을 열어도 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
with open('textfile_a.txt', 'r') as a:
with open('textfile_b.txt', 'w') as b:
a_file = a
b_file = b.write('hello wolrd')

with open('textfile_a.txt', 'r') as a, open('textfile_b.txt', 'w') as b:
a_file = a
b_file = b.write('hello world')

# 파이썬 3.10.0 b1 버전부터는 아래처럼도 가능하다
with (
open('textfile_a','w') as a,
open('textfile_b','w') as b,
open('textfile_c','w') as c
):
a.write('apple')
b.write('banana')
c.write('count')

class로 context manager 구현하기


1
2
3
4
5
6
7
class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, type, value, trace_back):
self.file_obj.close()

위와 같이 정의를 하고 아래처럼 실행해 보면

1
2
with File('demo.txt', 'wb') as opened_file:
opened_file.write('Hola!')
  1. with 문은 File class의 __exit__메소드를 저장
  2. File class의 __enter__메소드를 호출
  3. __enter__메소드는 파일을 열고 파일을 반환
  4. 열려진 파일은 변수명 opened_file에 저장
  5. .write()을 통해 내용 작성
  6. with문 이기 때문에 __exit__문을 호출
  7. __exit__문을 통해 파일 닫음

위와같은 실행순서로 context manager를 구현 할 수 있다.

References.

https://tempdev.tistory.com/22
https://ddanggle.gitbooks.io/interpy-kr/content/ch24-context-manager.html

plotly를 사용하여 막대그래프 만들기

캐글 대회에 참가하면서 파이썬 문법이나 plotly의 특성에 대해 구글링하는 시간이 훨씬 늘었다.
앞으로도 대회에 참가하거나 시각화를 할 때에 이렇게나 많은 특성을 모두 외울수는 없으니 구글링을 하게 될 텐데, 자주쓰는 속성이나 기본틀에 대해서는 포스팅을 해두고 바로바로 찾아보는것이 좋겠다는 생각이 들었다.

그래서 이번 포스팅에서는 앞으로 자주 사용하게 될 차트 중 하나인 막대그래프의 기본 틀과 자주 사용되는 속성에 대해 포스팅 하려 한다.

데이터는 2021 Kaggle Machine Learning & Data Science Survey 대회의 데이터를 사용한다.

import 및 데이터 불러오기

1
2
3
4
5
import plotly.graph_objects as go
import pandas as pd
from plotly.subplots import make_subplots

df21 = pd.read_csv('../input/kaggle-survey-2021/kaggle_survey_2021_responses.csv')

필요한 라이브러리를 import 해주고 데이터를 불러와 저장해준다.
필자는 캐글노트북에서 작성하여 data add 기능으로 kaggle에 있는 데이터를 바로 불러왔지만 로컬이나 colab, jupyter등의 다른 노트북을 사용중이라면 데이터를 다운받은후 괄호안의 경로를 재설정해주어야 한다.

출력할 데이터 확인

1
df21[0:5][Q1,Q14] # Q1,Q14 항목의 값 0부터 4까지 출력
Q1 Q3
0 What is your age (# years)? In which country do you currently reside?
1 50-54 India
2 50-54 Indonesia
3 22-24 Pakistan
4 45-49 Mexico

이렇게 출력하거나

1
df21[df21['Q3'] == "South Korea"] # Q3 항목의 값이 "South Korea"인 행의 데이터프레임 출력

조건을 지정해 데이터를 출력해냄
위 코드처럼 어느 조건에 해당하는행을 출력 할 수도 있다.

1
2
KR_Age = df21[df21['Q3'] == 'South Korea']['Q1'].value_counts()
JP_Age = df21[df21['Q3'] == 'Japan']['Q1'].value_counts()

위 코드는 Q3(국가)이 South Korea인 행과 Japan인 행의 Q1(연령)값을 뽑아내는 코드로 이를 통해 그래프를 만들어 볼 것이다.

다중 차트 틀 만들기

위에서 import한 make_subplots으로 차트여러개를 출력 할 수 있다.

1
fig = make_subplots(rows=1, cols=2, specs=[[{'type':'xy'}, {'type':'xy'}]])

fig 객체를 생성해주고 차트를 1행, 2열로 만들어주는 코드이다.
막대그래프의 경우엔 'type':'xy' 파이그래프의 경우엔 'type':'domain'으로 할수있다.
specs에서 중괄호는 차트한개를 나타내며 내부의 중괄호가 여러개라면 각각 행을 구분하는 용도로 쓰인다.

각 속성으로 차트 그리기

1
2
3
4
5
6
7
8
9
10
11
fig.add_trace(go.Bar(name='Korea', x=KR_Age.index, y=KR_Age.values, marker_color='red'),1,1)
fig.add_trace(go.Bar(name='Japan', x=JP_Age.index, y=JP_Age.values, marker_color='blue'),1,2)

fig.update_layout(barmode='group', title_text='2021, Korea and Japan age distribution', showlegend=True)

fig.update_xaxes(title_text='Korea Age distribution', row=1, col=1)
fig.update_yaxes(title_text='Counts', row=1, col=1)
fig.update_xaxes(title_text='Japan Age distribution', row=1, col=2)
fig.update_yaxes(title_text='Counts', row=1, col=2)

fig.show()

파라미터값을 바꾸어 그래프를 출력
add_trace, update_layout 등의 파라미터(속성)값으로 그래프를 꾸미는 코드이다.


fig.add_trace(go.Bar(name='Korea', x=KR_Age.index, y=KR_Age.values, marker_color='red'),1,1)부터 살펴보면
go.Bar는 막대그래프를 뜻한다, 각 파라미터를 살펴보면
name는 그래프에 표현되는 항목의 이름,
x는 그래프의 x축이 나타낼 항목,
y는 그래프의 y축이 나타낼 값,
marker_color은 그래프의 색을 표현하며
가장 끝의1,1는 위 항목이 표시될 그래프의 행,열을 뜻한다.(그러니 1번째 행 1번째 열의 그래프에 Korea 항목이 들어간다.)


fig.update_layout(barmode='group', title_text='2021, Korea and Japan age distribution', showlegend=True)도 뜯어보겠다.
barmode는 한 그래프에 여러 항목이 들어있을 경우 어떻게 나타내는지에 대한 속성인데 대표적으로 groupstack이 있다.
title_text는 제목, showlegend는 범례 표기 여부를 가리킨다.


마지막의 fig.show()는 fig객체를 출력하는 코드이다.

아래 References에 막대그래프의 파라미터가 정리되있는 링크를 걸어놓을테니 활용하여 다양하게 그래프를 만들어보자.

References

2021 캐글 대회 코드 작성기 - 2

Q3. In which country do you currently reside?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
years = ['2019', '2021']
JP_country_count_19 = (df19[df19['Q3'] == 'Japan']['Q3']).count()
CN_country_count_19 = (df19[df19['Q3'] == 'China']['Q3']).count()
JP_country_count_21 = (df21[df21['Q3'] == 'Japan']['Q3']).count()
CN_country_count_21 = (df21[df21['Q3'] == 'China']['Q3']).count()

JP_country_count_19_21 = [JP_country_count_19, JP_country_count_21]
CN_country_count_19_21 = [CN_country_count_19, CN_country_count_21]

fig_country = go.Figure(data=[
go.Bar(name='Japan', x=years, y=JP_country_count_19_21, marker_color=JP_colors[0]),
go.Bar(name='China', x=years, y=CN_country_count_19_21, marker_color=CN_colors[1])
])
fig_country.update_layout(
barmode='group',
title_text='2019 & 2021, the number of Kaggler living in Japan and China',
xaxis_title='Years',
yaxis_title='Counts'
)
fig_country.show()

연도별 일본과 중국의 캐글러 수

2019년과 2021년 일본과 중국 캐글러의 수를 비교한 막대그래프이다.
일본은 빨강 중국은 노란색 막대로 표현했으며 일본의 수치가 인구 대비 상당히 높은 걸 알 수 있다.
양국 모두 캐글러의 수치가 증가했다.
2년간 일본은 약 35%, 중국은 약 40%가 증가했으며 증가한 캐글러의 수는 일본이 높지만 비율은 중국이 앞섰다.

Q14. What data visualization libraries or tools do you use on a regular basis?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
df19_JP = df19[df19.Q3.isin(['Japan'])]
df19_CN = df19[df19.Q3.isin(['China'])]
df21_JP = df21[df21.Q3.isin(['Japan'])]
df21_CN = df21[df21.Q3.isin(['China'])]
df19_JP_Q14 = pd.DataFrame()
df19_CN_Q14 = pd.DataFrame()
df21_JP_Q14 = pd.DataFrame()
df21_CN_Q14 = pd.DataFrame()
df19_JP_Q14['Q20'] = [df19_JP[col][1:].value_counts().index[0] for col in df19_JP.columns[97:109]]
df19_CN_Q14['Q20'] = [df19_CN[col][1:].value_counts().index[0] for col in df19_CN.columns[97:109]]
df21_JP_Q14['Q14'] = [df21_JP[col][1:].value_counts().index[0] for col in df21_JP.columns[59:71]]
df21_CN_Q14['Q14'] = [df21_CN[col][1:].value_counts().index[0] for col in df21_CN.columns[59:71]]
df19_JP_Q14['counts'] = [df19_JP[col][1:].value_counts().values[0] for col in df19_JP.columns[97:109]]
df19_CN_Q14['counts'] = [df19_CN[col][1:].value_counts().values[0] for col in df19_CN.columns[97:109]]
df21_JP_Q14['counts'] = [df21_JP[col][1:].value_counts().values[0] for col in df21_JP.columns[59:71]]
df21_CN_Q14['counts'] = [df21_CN[col][1:].value_counts().values[0] for col in df21_CN.columns[59:71]]

df19_JP_Q14.index = [3,0,6,4,5,2,7,1,8,9,10,11]
df19_CN_Q14.index = [3,0,6,4,5,2,7,1,8,9,10,11]
df19_JP_Q14 = df19_JP_Q14.sort_index()
df19_CN_Q14 = df19_CN_Q14.sort_index()
df21_JP_Q14['Q14'].index = [0,1,2,3,4,5,6,7,8,9,10,11]
df21_CN_Q14['Q14'].index = [0,1,2,3,4,5,6,7,8,9,10,11]
df19_JP_Q14.replace(regex = 'D3.js', value = 'D3 js', inplace = True)
df19_CN_Q14.replace(regex = 'D3.js', value = 'D3 js', inplace = True)

fig_T = make_subplots(rows=1, cols=2, specs=[[{'type':'xy'}, {'type':'xy'}]])

fig_T.add_trace(go.Bar(name=coun_years[0], x=df19_JP_Q14['Q20'].values, y=df19_JP_Q14['counts'], marker_color=coun_years_colors[0]),1,1)
fig_T.add_trace(go.Bar(name=coun_years[1], x=df19_CN_Q14['Q20'].values, y=df19_CN_Q14['counts'], marker_color=coun_years_colors[1]),1,1)
fig_T.add_trace(go.Bar(name=coun_years[2], x=df21_JP_Q14['Q14'].values, y=df21_JP_Q14['counts'], marker_color=coun_years_colors[2]),1,2)
fig_T.add_trace(go.Bar(name=coun_years[3], x=df21_CN_Q14['Q14'].values, y=df21_CN_Q14['counts'], marker_color=coun_years_colors[3]),1,2)

fig_T.update_layout(title_text='2019 & 2021, Visualization Library and Tools in Use',
showlegend=True,
autosize=True)

fig_T.update_xaxes(title_text='2019 Library and Tools', row=1, col=1)
fig_T.update_yaxes(title_test='Counts', row=1, col=1)
fig_T.update_xaxes(title_text='2021 Library and Tools', row=1, col=2)
fig_T.update_yaxes(title_text='Counts', row=1, col=2)

fig_T.show()

연도별 일본과 중국의 시각화 라이브러리 & 툴 사용 그래프

각 그래프를 연도로 나누고 그래프에는 그에 해당하는 라이브러리,툴 사용량을 국가별로 나누어 넣은 막대그래프이다.
양국 모두 전반적으로 증가하였으나 눈에띄는 부분은 중국의 None항목이 크게 늘었고, 중국은 모든 항목의 사용량이 증가한 반면에 일본은 감소세가 보이는 항목이 있었다.

Q16. Which of the following machine learning frameworks do you use on a regular basis?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fig_F = make_subplots(rows=1, cols=2, specs=[[{'type':'xy'}, {'type':'xy'}]])

fig_F.add_trace(go.Bar(name=coun_years[0], x=df19_JP_Q16['Q28'].values, y=df19_JP_Q16['counts'].sort_values(ascending=False).values, marker_color=coun_years_colors[0]),1,1)
fig_F.add_trace(go.Bar(name=coun_years[1], x=df19_CN_Q16['Q28'].values, y=df19_CN_Q16['counts'].sort_values(ascending=False).values, marker_color=coun_years_colors[1]),1,1)
fig_F.add_trace(go.Bar(name=coun_years[2], x=df21_JP_Q16['Q16'].values, y=df21_JP_Q16['counts'].sort_values(ascending=False).values, marker_color=coun_years_colors[2]),1,2)
fig_F.add_trace(go.Bar(name=coun_years[3], x=df21_CN_Q16['Q16'].values, y=df21_CN_Q16['counts'].sort_values(ascending=False).values, marker_color=coun_years_colors[3]),1,2)

fig_F.update_layout(title_text='2019 & 2021, Machine Learning Frameworks in Use',
showlegend=True,
autosize=True)

fig_F.update_xaxes(title_text='2019 Machine Learning Frameworks', row=1, col=1)
fig_F.update_yaxes(title_text='Counts', row=1, col=1)
fig_F.update_xaxes(title_text='2021 Machine Learning Frameworks', row=1, col=2)
fig_F.update_yaxes(title_text='Counts', row=1, col=2)

fig_F.show()

연도별 일본과 중국의 머신러닝 프레임워크 사용 그래프

이번 질문에대한 답도 비슷하다. 그래프는 연도로 나누고 각각의 x축에는 사용하는 머신러닝 프레임워크, y축에는 그 수가 표기되어 얼마나 많은 캐글러가 머신러닝 프레임워크를 사용하지는 나타낸다.
각 년도의 차이점은 (프레임워크의 사용자의 수는 물론이거니와)프레임워크의 종류가 19년도에 비해 21년에 약증했다는 점이 있고, 특이점이라면 21년 일본의 사이킷런Scikit-learn 사용자의 수가 급증했다는 점이 있다.

2021 캐글 대회 코드 작성기 - 1

파이썬을 배운지 며칠 되지 않아 kaggle이라는 예측모델 및 분석대회 플랫폼의 데이터 분석 대회에 참가하게 되었다.
주제는 2021년 캐글러kaggler들에게 설문조사이며, 그 설문조사 데이터셋을 바탕으로 참가자가 분석을 하여 참가자만의 주제에 그래프와 수치로 답을 찾아내는 대회이다.

데이터셋에 있는 캐글러의 수는 19년 19717명, 21년 25974명, 직업은 데이터사이언스, 데이터 엔지니어부터 학생까지 다양하며, 나이, 학력, 국적, 라이브러리, 언어 등 매우 다양한 항목들이 있다.

우리 조에서 처음으로 선정하려 했던 주제는 한국, 일본, 중국의 캐글러의 트렌드변화로 하려 했으나, 중국, 일본에 비해 데이터가 턱없이 부족해 비교가 무의미한 수준이었기에 캐글러의 수가 비슷한 두 나라인 중국과 일본로 선정했다.(의외로 중국의 캐글러 절대적인 수가 인구에 비해 상당히 적어 일본보다 조금 적은편이였다.)

import(Kaggle notebook)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objects as go
from warnings import filterwarnings
from plotly.subplots import make_subplots
filterwarnings('ignore')

colors = ['#B1EDED','#B1B2ED','#1DE7ED','#1DA5ED','#1D50ED','#16548E']
gen_colors = ['#4169E1','#B2182B','#81007F','#D1B2FF','#EFE4E2']
JP_colors = ['#D90B0B','#F24444','#EFE4E2','#FCCE88','#64807F']
CN_colors = ['#E0201B','#FFCE3F','#A63F03','#04BF33','#F2E6D8']
coun_years_colors = ['#D90B0B','#FFCE3F','#FF6161','#FFDB81']

df19 = pd.read_csv('../input/kaggle-survey-2019/multiple_choice_responses.csv')
df21 = pd.read_csv('../input/kaggle-survey-2021/kaggle_survey_2021_responses.csv')

이처럼 후에 사용할 라이브러리를 임포트, 그래프에 사용할 색을 리스트로 만들어주고 df19에 19년의 데이터셋을, df21에 21년의 데이터셋을 넣어준다.

사용자 지정 함수


1
2
3
4
5
6
7
8
9
10
def group(data, country, question_num):
return data[data['Q3'] == country][question_num].value_counts()

def go_Pie(country, label_value):
return go.Pie(title = country,
labels = label_value.index,
values = label_value.values,
textinfo = 'label+percent',
rotation=315,
hole = .3,)

앞으로 코드에서 사용될 함수이다.
사용자 지정 함수란?

Q1. What`s your age?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
JP_age_19 = group(df19,'Japan','Q1').sort_index()

JP_age_21 = group(df21,'Japan','Q1').sort_index()

CN_age_19 = group(df19,'China','Q1')
CN_age_19.loc['55-59'] = 0
CN_age_19.loc['60-69'] = 0
CN_age_19 = CN_age_19.sort_index()

CN_age_21 = group(df21,'China','Q1')
CN_age_21.loc['60-69'] = 0
CN_age_21 = CN_age_21.sort_index()

fig_age = make_subplots(rows=1, cols=2, specs=[[{'type':'xy'}, {'type':'xy'}]])

fig_age.add_trace(go.Bar(name=coun_years[0], x=JP_age_19.index, y=JP_age_19.values, marker_color='#FDB0C0'),1,1)
fig_age.add_trace(go.Bar(name=coun_years[2], x=JP_age_21.index, y=JP_age_21.values, marker_color='#FD4659'),1,1)
fig_age.add_trace(go.Bar(name=coun_years[1], x=CN_age_19.index, y=CN_age_19.values, marker_color='#FFDB81'),1,2)
fig_age.add_trace(go.Bar(name=coun_years[3], x=CN_age_21.index, y=CN_age_21.values, marker_color='#FFAB0F'),1,2)

fig_age.update_layout(barmode='group', title_text='2019 & 2021, Japan and China age distribution', showlegend=True)

fig_age.update_xaxes(title_text='Japan Age distribution', row=1, col=1)
fig_age.update_yaxes(title_text='Counts', row=1, col=1)
fig_age.update_xaxes(title_text='China Age distribution', row=1, col=2)
fig_age.update_yaxes(title_text='Counts', row=1, col=2)

fig_age.show()

일본, 중국 캐글러의 나이분포

첫번째 주제인 What`s your age?에 대한 일본과 중국의 캐글러 나이분포를 막대그래프로 나타낸 것이다.
각 그래프는 연도별로 나눈 일,중 캐글러의 나이 분포이고 그래프의 항목은 18세부터 70세 이상까지 3,4년씩을 한 항목으로 묶었다.

Q2. What`s your gender?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
JP_ndarray = df19[df19['Q3'] == 'Japan']['Q2'].values
CN_ndarray = df19[df19['Q3'] == 'China']['Q2'].values
JP_age_list = []
CN_age_list = []

for item in JP_ndarray:
if item == 'Male':
item_mod = item.replace('Male','Man')
JP_age_list.append(item_mod)
elif item == 'Female':
item_mod2 = item.replace('Female','Woman')
JP_age_list.append(item_mod2)
else :
JP_age_list.append(item)

for item in CN_ndarray:
if item == 'Male':
item_mod = item.replace('Male','Man')
CN_age_list.append(item_mod)
elif item == 'Female':
item_mod2 = item.replace('Female','Woman')
else :
CN_age_list.append(item)

JP_age_series = pd.Series(JP_age_list)
CN_age_series = pd.Series(CN_age_list)

fig = make_subplots(rows=2, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}],
[{'type':'domain'}, {'type':'domain'}]])

fig.add_trace(go_Pie('2019_Japan', JP_age_series.value_counts()),1,1)
fig.add_trace(go_Pie('2019_China', CN_age_series.value_counts()),1,2)
fig.add_trace(go_Pie('2021_Japan', group(df21,'Japan','Q2')),2,1)
fig.add_trace(go_Pie('2021_China', group(df21,'China','Q2')),2,2)

fig.update_traces(marker=dict(colors=gen_colors[0:]))
fig.update_layout(title_text='Gender Distribution',
showlegend=True,
autosize=True,
height=700)
fig.show()

일본, 중국 캐글러의 성별분포

두번쨰 질문에 답하기 위해 일본과 중국의 캐글러 성별분포를 도넛모양으로 나타낸 그래프이다.
각각의 그래프는 국가,년도(2019, 2021)별로 각각 나누었고 그래프의 항목은
Man
Woman
Prefer not to say
Nonbinary
Prefer to self-describe
이렇게 다섯가지로 나누었다.

References

plotly bar chart tutorial
plotly bar chart properties (bar traces)
plotly pie chart tutorial
plotly pie chart properties (pie traces)

산점도(scatter plot)와 박스플롯(box plot)

산점도(scatter plot)

산점도란?


산점도

산점도, 산포도scatter plot는 직교좌표계를 이용해 좌표상의 점들을 표시함으로써 두 개 변수 간의 관계를 나타내는 그래프 방법이다.
도표 위에 두 변수 X와 Y값이 만나는 지점을 표시한 그림. 이 그림을 통해 두 변수 사이의 관계를 알 수 있다.



언제 사용되는가??


산점도는 다음의 경우에 사용된다.

  1. 두 종류의 데이터의 관계를 파악할때. 즉, 양의 상관관계, 음의 상관관계, 관계없음 등의 관계를 파악한다.
  2. 산점도의 작성으로 한 변수에 대한 결과의 영향조사를 한다.



Plotly로 산점도 작성하기


  • 데이터 임포트 및 데이터셋 생성

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import numpy as np
    import pandas as pd
    # plotly 라이브러리 불러오기
    import plotly.offline as po
    import plotly.graph_objs as go

    # 임의의 숫자로 이루어진 2열,100행의 데이터프레임 생성
    df1 = pd.DataFrame(np.random.randint(0, 100, (100, 2)), columns=['A', 'B'])
    df2 = pd.DataFrame(np.random.randint(0, 100, (100, 2)), columns=['A', 'B'])
  • 하나의 산점도 그리기

1
2
3
trace1 = go.Scatter(x=df1['A'], y=df1['B'], mode='markers')
data = [trace1]
pyo.iplot(data)
Output

df1이 그대로 산점도로 출력된 모습이다.

  • 두 개 이상의 산점도 그리기

1
2
3
4
trace1 = go.Scatter(x=df1['A'], y=df1['B'], mode='markers')
trace2 = go.Scatter(x=df2['A'], y=df2['B'], mode='markers')
data = [trace1, trace2]
pyo.iplot(data)
Output

df1과 df2가 한 도표 내에 산점도로 출력된 모습이다.
이렇게 다른 데이터를 표시하고 싶을때에는 출력코드에 새로운 데이터값을 넣어주기만 하면 된다.

  • 마커 모양 변경하기

1
2
3
4
trace1 = go.Scatter(x=df1['A'], y=df1['B'], mode='markers', marker=dict(size=7, color='#D90B0B', symbol=20))
trace2 = go.Scatter(x=df2['A'], y=df2['B'], mode='markers', marker=dict(size=7, color='#F24444', symbol=23))
data = [trace1, trace2]
pyo.iplot(data)
Output

그림처럼 마커의 모양을 변경 할 수 있다.
color옵션은 헥스코드와 rgb값 모두 가능하며, 모양의 경우 매우 다양하므로 포스팅 최하단에 링크를 첨부한다.




박스플롯 (box plot)

박스플롯이란??


박스플롯

‘박스플롯’box plot,또는 ‘상자 수염 그림’은 수치적 자료를 나타내는 그래프이다. 이 그래프는 가공하지 않은 자료 그대로를 이용하여 그린 것이 아니라, 자료로부터 얻어낸 통계량인 ‘5가지 요약 수치’five-number summary 를 가지고 그린다. 이 때 5갸지 요약 수치란 최솟값, 제 1사분위(Q1),제 2사분위(Q2), 제 3사분위(Q3), 최댓값을 일컫는 말이다. 히스토그램과는 다르게 집단이 여러개인 경우에도 한 공간에 수월하게 나타낼수 있다.



언제 사용되는가??


박스 플롯을 사용하는 이유는 데이터가 매우 많을때 모든 데이터를 한눈에 확인하기 어려우니 그림을 이용해 데이터 집합의 범위와 중앙값을 빠르게 확인 할 수 있는 목적으로 사용한다.
또한, 통계적으로 이상치outlier가 있는지도 확인이 가능하다.



박스플롯의 구성


박스플롯을 처음 접한다면 박스플롯을 어떻게 해석해야 하는지 난해할수 있다.
다음 그림에서 박스플롯의 각 구성이 무엇을 의미하는지 간단하게 알아보자.
박스플롯의 구성
각 요소들을 설명하면 다음과 같다.

요소 설명
이상치(outlier) 최소값보다 작은데이터 또는 최대값보다 큰 데이터가 이상치에 해당한다
최대값(upper whisker) ‘중앙값 + 1.5 × IQR’보다 작은 데이터 중 가장 큰 값
최소값(lower whisker) ‘중앙값 - 1.5 × IQR’보다 큰 데이터 중 가장 작은 값
IQR(Inter Quartile Range) 제3사분위수 - 제1사분위수
실수 값 분포에서 1사분위수(Q1)와 3사분위수(Q3)를 뜻하고 이 3사분위수와 1사분위수의 차이(Q3 - Q1)를 IQR라고 한다.
중앙값 박스내부의 가로선, 용어 그대로 중앙값이다
whisker 상자의 상하로 뻗어있는 선




Plotly로 박스플롯 작성하기


1
2
3
4
5
6
7
8
9
10
11
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


df3 = pd.DataFrame(np.random.randint(0, 100, (100, 2)), columns=['A', 'B']) #임의로 수 생성

plt.figure(figsize=(8,7)) # 크기 지정
boxplot = df3.boxplot(column=['B']) # df3의 'B'컬럼을 박스플롯으로 생성
plt.yticks(np.arange(0,101,step=5)) # 박스플롯이 그려질 범위 지정
plt.show()
Output


위처럼 임의로 생성된 데이터프레임을 이용해 박스플롯을 만들 수 있다.




References

산점도 그리기
산점도 마커 모양
위키백과 - 산점도
위키백과 - 상자 수염 그림
박스플롯 및 요소 설명

파이썬(Python) 사용자 지정 함수

사용자 지정 함수란?


파이썬에서(다른프로그래밍 언어, 스크립트 언어에서도) 함수란, 소프르웨어에서 특정 동작을 수행하는 일정 코드부분을 의미한다.

하나의 큰 프로그램을 여러부분으로 나누어주기 때문에 같은 함수를 여러 상황에서 여러차례 호출할 수 있고, 일부분을 수정하기 쉽다는 장점을 지닌다.

파이썬에서는 미리 정의 되어있는 built in 함수(내장함수)가 있고. 또, 사용자가 필요에 의해 정의를 한 사용자 지정 함수가 존재한다.

파이썬에서의 함수 모형


파이썬 함수의 기본 모형

1
2
3
4
def 함수명() :
실행문
실행문
...

이와 같은 구조인데 함수를 정의하는 def뒤에 함수명이 오고 소괄호()를 붙인 다음 콜론:을 붙여주고, 다음줄에 원하는 실행문을 들여쓰기하여 사용하면 된다.

1
2
def func() :
print('python function')

위는 기본 모형을 응용하여 함수를 정의한 것이다. 이는 입력값도, 반환값도 없는 함수이며 값이 있는 함수에 대해서는 후술하겠다.

그러나 위 코드는 함수를 정의한 것이지 사용을 한 것이 아니다. 그렇기 때문에 함수를 사용하기 위해서는 함수의 사용법을 알아야 한다.

1
2
3
4
5
def func() :
print('python function')

#함수 호출
func()
Output
1
python function

func()함수는 입력값이 없기때문에 호출시에 입력값을 넣어주지 않고 괄호만 입력해 준다.



입력값만 있는 함수


1
2
3
4
5
6
def func2(a) :
print('입력한 문자는 ' + a + ' 입니다.')

func2("비행기")
func2("자동차")
func2("파이썬")
Output
1
2
3
입력한 문자는 비행기 입니다.
입력한 문자는 자동차 입니다.
입력한 문자는 파이썬 입니다.

func2()함수의 입력값이 a 에 입력되어 출력 된 것을 볼 수 있다.



리턴값만 있는 함수


1
2
3
4
def func3() :
return "12345"

print(func3())
Output
1
12345

func3()의 값이 리턴되어 print()를 통해 출력된 것을 알 수 있다.



입력,리턴값이 모두 있는 함수


1
2
3
4
def func4(a, b) :
return a + b

print(func4(10,20))
Output
1
30

a,b의 입력값을 더하는 함수에 10,20의 값을 주었더니 예상대로 30이 출력 되었다.



기본 인자 값


입력이 있는 함수를 여러 곳에 사용할 때 반복되는 값에 대해서는 기본적인 값을 지정할 수 있다.
기본값은 입력 = 값의 형태로 작성된다.

1
2
3
4
5
def func_b(a,b,c=5) :
return a*b+c

print(func_b(2,3,10))
print(func_b(2,3))
Output
1
2
16
11

출력에서처럼 기본값이 주어진 c 인자에 별다른 값을 넣지 않으면 기본값 5로 계산되어 10이들어간 결과보다 5가 낮아지게 된다.

이렇게 기본 인자를 줄 때는 주의 할 점이 한 가지 있는데 바로 인자의 순서에 맞게 넣어주어야 한다는 것과, 기본값과 입력값이 없으면 에러가 난다는 것이다.
다음 예시를 보자.

1
2
3
4
def func_b2(a,b=5,c) :
return a*b+c

print(func_b2(2,3))

위와 같이 값을 주게 된다면 어떨까?
b에 기본값이 있으니 a와 c에 각각 2와 3이 입력된다고 생각한 사람도 있을 것이다.
그러나 이 생각은 적어도 사용자 지정 함수에서는 옳지 않다.

이 코드가 불가능한 데에는 두 가지 이유가 있다.

첫 번째로는 a에는 2가 들어가는 것이 맞겠지만, b에 기본값이 있더라도 두 번째 입력값 자리에 3을 넣게 되는 순간 그 숫자는 b에 입력되기 때문에 결국엔 기본값이 없는 c는 어떠한 값도 입력받지 못하게 되기 때문이고,

두 번째 이유로는 c의 인자에 기본값이 없음에도 앞에 있는 b의 인자에 기본값이 있으므로 아예 성립할 수 없는 코드이다.
그러므로 기본값을 부여받은 인자는 항상 뒤에 배치해야 한다.



키워드 인자


키워드 인자는 함수를 정의할때 인자에 키워드를 부여하고 함수를 호출할 때 키워드=값의 형태로 넣어줄 수 있는 인자이다.
그냥 인자에 값을 넣어주는것과 뭐가 다르냐?라는 의문이 들 수도 있다.
다음 예시를 보자.

1
2
3
4
def score(kim=100, lee=100, park=100, choi=100) :
print('score = kim :', kim, 'lee :',lee, 'park :',park, 'choi :',choi)

score(park= 50)
Output
1
score = kim : 100 lee : 100 park : 50 choi : 100

이렇게 중간의 인자만 키워드=값의 형태로 입력해 값을 변경해 줄 수 있다.
예제처럼 여러 기본값이 있고 그중 몇 가지의 기본값만 바꿔 주어야 할 때 용이하게 쓸 수 있다.

Arbitrary Argument Lists (임의 인자 리스트)


Arbitrary Argument Lists는, 여러인자를 함수에 잔달하는 방법들중 하나이다.

함수내부에서 이 인자들은 튜플로 감싸져있으며 이는*argsarguments 생성자를 사용하여 정의 된다.

1
2
3
4
5
6
7
8
9
10
def avg_score(*numbers) :
s = 0
count = 0
for i in numbers:
s += i
count += 1
return s / count

print('국어와 수학점수의 평균은{}점 입니다'.format(avg_score(95, 87)))
print('입력된 숫자들의 평균은{} 입니다.'.format(avg_score(42,63,45,32,48)))
Output
1
2
국어와 수학점수의 평균은91.0점 입니다
입력된 숫자들의 평균은46.0 입니다.

위 코드에서는 *args*numbers로 대체했다.
이처럼 함수를 호출 할 때에 숫자를 몇가지를 넣어도 사용이 가능하다.



Lambda 함수


우리가 앞에서 배웠던 대로 사용자 지정 함수를 정의하고 사용하여 평균을 구하라고 한다면 아마 다음과 같을것이다.

1
2
3
4
5
def avg(x,y):
return(x + y) / 2
print(avg(5,13))

>> 9.0

그러나 파이썬은 Lambda함수를 통해서 이 코드를 더욱 짧게 줄일수 있다.

1
2
3
print((lambda x,y: (x + y)/2)(5,13))

>> 9.0

그러나 배우는 입장인 내 눈으로 단점을 찾아보자면 함수인데 정의를 하는 부분이 따로 없다 보니, 출력할 일이 여러 번 생긴다면 번거로워 질 것 같다.
그러니 앞으로 호출 횟수에 따라 적다면 람다 함수를, 많다면 기본적인 사용자 지정 함수를 사용하는 방식이 좋을 듯하다.



References

캐글 데이터 시각화 해석-1

임포트 및 데이터프레임 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
for filename in filenames:
print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All"
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
1
2
from plotly.offline import plot, iplot, init_notebook_mode
init_notebook_mode(connected=True)
1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objects as go
from warnings import filterwarnings
filterwarnings('ignore')

colors = ['#B1EDED','#B1B2ED','#1DE7ED','#1DA5ED','#1D50ED','#16548E']

df = pd.read_csv('../input/kaggle-survey-2021/kaggle_survey_2021_responses.csv')
df.head()

원 그래프 해석


1
2
3
4
5
6
7
8
9
10
11
fig = go.Figure( data=[ # 그래프의 형태를 나타내는 함수
go.Pie( # 원그래프
labels = df['Q2'][1:].value_counts().index, # 값에 붙일 이름 - df['Q2'][1:].value_counts()의 인덱스
values = df['Q2'][1:].value_counts().values, # 나타낼 값 - df['Q2'][1:].value_counts()의 값
textinfo = 'label+percent')]) # 그래프 항목당 나타낼 텍스트 (여기서는 항목명, 비율)
fig.update_traces(
marker=dict(colors=colors[2:])) # 그래프를 어떤 색으로 표현할 것인지
fig.update_layout( # 그래프의 레이아웃 설정
title_text='Gender Distribution', # 그래프의 제목
showlegend=False) # 범례표시여부
fig.show()
그래프

원 그래프

코드 해석
1
2
3
4
5
6
fig = go.Figure(data=[
go.Pie(
labels = df['Q2'][1:].value_counts().index,
values = df['Q2'][1:].value_counts().values,
textinfo = 'label+percent'
)])

fig = go.Figure(data=[
-> 그래프의 기본적인 틀을 설정하는 함수이고, fig에 데이터를 부여해준다.

go.Pie(
-> 그래프의 형태가 파이모양(원그래프)임을 의미한다.

labels = df['Q2'][1:].value_counts().index
-> 그래프로 표현될 값에 붙일 이름이다. 이 코드에서는 ‘df’의 ‘Q2’열에 있는 데이터의 ‘1번인덱스(질문열을 제외하기 위함) 행부터 마지막까지’ 카운트하고 값에따라 분류했을 때의 인덱스열 인 것이다.

values = df['Q2'][1:].value_counts().values
-> 그래프로 표현될 값이다. 이 코드에서는 ‘df’의 ‘Q2’열에 있는 데이터의 ‘1번인덱스 행부터 마지막까지’ 카운트하고 값에따라 분류했을 때의 값(각 값을 카운트한 값) 이다.

textinfo = 'label+percent'
-> 그래프에 표시된 항목에 나타낼 텍스트를 설정한다. 이 코드에서는 항목명(label)과 비율(percent)을 나타낸다.


1
2
3
4
5
6
fig.update_traces( 
marker=dict(colors=colors[2:])) # 그래프를 어떤 색으로 표현할 것인가를 설정
fig.update_layout( # 그래프의 부가정보 설정
title_text='Gender Distribution', # 그래프 제목
showlegend=False) # 범례표시여부
fig.show()

fig.update_traces(
-> 추가바람

marker=dict(colors=colors[2:]))
-> 그래프의 색상을 설정한다. 이 코드에서는 위에서 설정된 colors 리스트에서 2번 인덱스부터 순서대로 사용한다.

fig.update_layout(
-> 그래프의 부가정보를 설정한다.

title_text='Gender Distribution'
-> 그래프의 제목을 설정한다.

showlegend=False)
-> 범례의 표기여부를 결정한다. 이 코드에서는 False이므로 범례가 표기되지 않는다.

fig.show()
-> 화면에 그래프를 표시하는 기능을한다. 몇몇에디터에서는 자동으로 표시되기때문에 호출할 필요가 없는 경우가 있다.

막대그래프 해석


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
man = df[df['Q2'] == 'Man']['Q1'].value_counts() # 성별이 남성[df['Q2'] == 'Man']인 행에서 나이['Q1']의 값을 카운트하여 시리즈로 만듦.
woman = df[df['Q2'] == 'Woman']['Q1'].value_counts() # 성별이 여성[df['Q2'] == 'Woman']인 행에서 나이['Q1']의 값을 카운트하여 시리즈로 만듦.
textonbar_man = [ # list comprehension = [(변수를 활용한 값) for (사용할 변수 이름) in (순회 할 수 있는 값)]
round((m/(m+w))*100, 1) for m, w in zip(man.values, woman.values)] # for문을 사용하여 round함수의 계산을 하고 textonbar_man에 저장
textonbar_woman = [ # list comprehension
round((w/(m+w))*100, 1) for m, w in zip(man.values, woman.values)]

# go = graph_objects
fig = go.Figure(data=[
go.Bar( # 막대그래프
name='Man', # 그래프로 나타낼 항목
x=man.index, # x축에 man의 인덱스
y=man.values, # y축에 man의 값
text=textonbar_man, # 막대의 값을 작성해줄 텍스트
marker_color=colors[2]), #막대 색
go.Bar(
name='Woman',
x=woman.index,
y=woman.values,
text=textonbar_woman,
marker_color=colors[3])
])
fig.update_traces(
texttemplate='%{text:.3s}%', # fig(print(fig)로 출력가능)내부의 text 인자를 차례대로 출력 (그래프의 위의 텍스트를 표현)
textposition='inside') # 그래프상에서 값의 위치
fig.update_layout(
barmode='stack', # 막대의 형태
title_text='Age distribution by gender', # 그래프 제목
xaxis_title='Age', # x축 제목
yaxis_title='Counts') # y축 제목
fig.show()
그래프

막대 그래프

코드 해석
1
2
3
4
man = df[df['Q2'] == 'Man']['Q1'].value_counts()
woman = df[df['Q2'] == 'Woman']['Q1'].value_counts()
textonbar_man = [ round((m/(m+w))*100, 1) for m, w in zip(man.values, woman.values)]
textonbar_woman = [ round((w/(m+w))*100, 1) for m, w in zip(man.values, woman.values)]

man = df[df['Q2'] == 'Man']['Q1'].value_counts()
-> 성별이 남성[df['Q2'] == 'Man]인 행에서 나이['Q1']의 값을 카운트하여 시리즈를 생성한다.

woman = df[df['Q2'] == 'Woman']['Q1'].value_counts()
-> 성별이 여성[df['Q2'] == 'Woman']인 행에서 나이['Q1']의 값을 카운트하여 시리즈를 생성한다.

textonbar_man = [round((m/(m+w))*100, 1) for m, w in zip(man.values, woman.values)]
-> for문을 사용하여 round함수를 계산 하고 textonbar_man에 저장한다.

textonbar_woman = [round((uw/(m+w))*100, 1) for m, w in zip(man.values, woman.values)]
-> for문을 사용하여 round함수를 계산 하고 textonbar_woman에 저장한다.



1
2
3
4
5
6
7
8
9
10
fig = go.Figure(data=[
go.Bar(
name='Man', x=man.index, y=man.values,
text=textonbar_man, marker_color=colors[2]
),
go.Bar(
name='Woman', x=woman.index, y=woman.values,
text=textonbar_woman, marker_color=colors[3]
)
])

go.Bar(
-> 그래프의 모양이 막대모양임을 의미한다.

name='Man', x=man.index, y=man.values,
-> 순서대로 막대의 이름, x축에는 man의 인덱스, y축에는 man의 값을 나타내라는 의미이다.

text=textonbar_man, marker_color=colors[2]),
-> 각각 막대의 값을 작성해줄 텍스트, 막대의 색을 의미한다.



1
2
3
4
5
6
7
8
9
fig.update_traces(
texttemplate='%{text:.3s}%',
textposition='inside')
fig.update_layout(
barmode='stack',
title_text='Age distribution by gender',
xaxis_title='Age', yaxis_title='Counts')
fig.show()
)

texttemplate='%{text:.3s}%',
-> fig(print(fig)로 출력가능)내부의 text 인자를 차례대로 출력 (그래프의 위의 텍스트를 표현)

textposition='inside'
-> 그래프상에서의 값의 위치를 설정한다.

barmode='stack'
-> 막대의 형태를 표현한다.

title_text='Age distribution by gender'
-> 그래프의 제목을 설정한다.

xaxis_title='Age', yaxis_title='Counts'
-> 그래프의 x축 제목, y축 제목

몇몇 요소 확인법


print(type(데이터))
-> 데이터의 타입을 출력한다

데이터.head(),데이터.tail()
-> 데이터를 인덱스 순으로 출력한다. head는 처음부터 끝까지, tail은 반대로 출력하며 괄호안에 숫자를 입력하면 숫자만큼 출력한다.

References

go.Figure() properties
update_traces() properties
update_layout() properties
show() properties
go.Bar() properties