본문 바로가기

데이터 분석 학습

[모두의 데이터분석 With 파이썬] Unit.06 리뷰 / 다양한 그래프 시각화

2022.03.23 - [데이터 분석 학습] - [모두의 데이터분석 With 파이썬] Unit.01 리뷰 / CSV, 아나콘다, 크롬

2022.03.23 - [데이터 분석 학습] - [모두의 데이터분석 With 파이썬] Unit.02 리뷰 / 주피터, 코랩

2022.03.23 - [데이터 분석 학습] - [모두의 데이터분석 With 파이썬] Unit.03 리뷰 / 데이터 전처리

2022.03.23 - [데이터 분석 학습] - [모두의 데이터분석 With 파이썬] Unit.04 리뷰 / 기본 그래프 그리기

2022.03.24 - [데이터 분석 학습] - [모두의 데이터분석 With 파이썬] Unit.05 리뷰 / 데이터에 맞는 시각화

 

Unit. 06 기온 그래프를 다양하게 시각화하기

 

이전에 기온데이터는 꺾은선 그래프로 표현했지만 그걸로는 특별한 정보를 얻기 힘들었다. 정보를 한번에 파악 가능한 다른 형태로 시각화를 해야한다.

이번 유닛에서는 히스토그램과 상자그림으로 데이터를 시각화해보려 한다.
  • 히스토그램(histogram): 자료의 분포 상태를 직사각형 모양의 막대그래프로 나타낸 것. 데이터의 빈도에 따라 높이가 결정된다.
  • hist()함수: 파이썬(matplotlib, pyplot)에서 데이터를 히스토그램으로 그린다.

hist()함수로 파이썬에서 히스토그램을 그려보자

import matplotlib.pyplot as plt # 시각화 라이브러리인 matplotlib에서 .으로 pyplot만 가져오고 plt로 이름을 정해준다
plt.hist([1,1,2,3,4,5,6,6,7,8,10]) # plt(pyplot)라이브러리에서 .으로 hist()함수를 불러오고 함수안에 1,1,2..로 구성된 리스트('[]')를 넣어준다.
plt.show() # plt라이브러리를 보여준다

결과값: 

 

그래프를 보면 수가 2개(1,6)인 지표는 y값이 2로 나오고 9는 없는걸 발견할 수 있다.

 

본격적인 그래프 그리기를 시작하기 전 주사위 굴리기로 기본 내용을 알아보자

 

주사위 시뮬레이션

히스토그램 표현 연습을 위해 임의의 수를 뽑는 랜덤 함수를 활용해 주사위 시뮬레이션을 해보면 다음과 같다

  1. 주사위를 굴린다
  2. 나온 결과를 기록한다.
  3. 이전 과정을 n번 반복한다.
  4. 주사위의 눈이 나온 횟수를 히스토그램으로 그린다.

주사위를 시뮬레이션하기 위해 random 모듈의 randint()함수를 사용한다. 또한 다음 코드가 실행되면 1~6 중 하나의 숫자가 무작위로 출력된다.

*결과값은 어차피 랜덤임으로 따로 기재하지 않을 예정

import random # 랜덤 값을 출력할 수 있는 라이브러리 가져오기
print(random.randint(1,6)) #random 라이브러리에서 .으로 randint를 추가하여 숫자의 범위를 정해준다.

주사위를 여러번 굴리는 상황을 시뮬레이션하려면 for 반복문을 사용해야한다.

5번 굴려 생성된 랜덤 숫자를 dice라는 변수의 리스트에 순서대로 저장해보자

import random # 랜덤 값을 출력할 수 있는 라이브러리 가져오기
dice = [] # dice라는 리스트('[]')를 담는 변수 만들기
for i in range(5): # i라는 변수를 5라는 범위(range(5))안에 넣어둔다.
   dice.append(random.randint(1,6)) #dice라는 변수 안에 .으로 append()함수를 실행한다. random 라이브러리에서 .으로 randint를 추가하여 숫자의 범위를 정해준다.
print(dice)

무작위 숫자(1~6)가 dice 변수 안에 5번 반복하여 저장되어 있는 것을 발견할 수 있다.

이 리스트를 히스토그램으로 표현해보면

import matplotlib.pyplot as plt # 시각화 라이브러리인 matplotlib에서 .으로 pyplot만 가져오고 plt로 이름을 정해준다
plt.hist(dice, bins=6) # plt(pyplot)라이브러리에서 .으로 hist()함수를 불러오고 dice 변수를 넣어준다. 구간 개수(bins)는 6개로 설정한다.
plt.show()

결과값:

 

나는 2가 한번도 나오지 않았다 이걸 100번 해보면?

import random # 랜덤 값을 출력할 수 있는 라이브러리 가져오기
dice = [] # dice라는 리스트('[]')를 담는 변수 만들기
for i in range(100): # i라는 변수를 100이라는 범위(range(100))안에 넣어둔다.
   dice.append(random.randint(1,6)) #dice라는 변수 안에 .으로 append()함수를 실행한다. andom 라이브러리에서 .으로 randint를 추가하여 숫자의 범위를 정해준다.
plt.hist(dice, bins=6) # plt(pyplot)라이브러리에서 .으로 hist()함수를 불러오고 dice 변수를 넣어준다. 구간 개수(bins)는 6개로 설정한다.(숫자 범위)
plt.show()

아마 이전보다 꽤 고르게 분포될 것이다. 100만 번 한다면 더더욱 그렇고. 이를 큰 수가 나올수록 수학적 확률에 수렴하는 '큰 수의 법칙'이라 부른다

 

이제 실제 기온데이터를 히스토그램으로 표현해보자

구간은 100개로 나눈다.

import csv # csv 관련 라이브러리 가져오기
import matplotlib.pyplot as plt # pylpot 라이브러리 가져오고 plt로 이름 설정
f = open('/content/seoul.csv', encoding='cp949') # f라는 변수는 seoul.csv 파일을 cp949라는 형식으로 읽는(open) 변수다
data = csv.reader(f) # data라는 변수는 csv라이브러리의 reader() 함수를 이용해 f변수 안에 담긴 seoul.csv를 읽어오는 것이다.
next(data) # data 변수의 데이터의 첫 번째 데이터 행 읽어오고 탐색 위치를 다음 행으로 이동시킴
result = [] #result 변수는 리스트형태로 설정

for row in data: # data 안의 row(행)에 대해 반복적인 작업을 수행한다.
  if row[-1] != '' : # 만약(if) row의 전체(row[-1])를 보았을 때 그것이 비어있지 않은 행(!='')이라면 
    result.append(float(row[-1])) # result 변수에 row데이터 전체(row[-1])를 추가한다(append). 소수형태로!(float)

plt.hist(result, bins=100, color='r') # plt 라이브러리의 hist() 함수의 내용물을 result 데이터(result)로 넣어주고, 구간을 100개로(bins=100) 두며, 색을 빨간색으로 설정한다(color='r') 
plt.show() # 이제 보여줘(show())

결과값: 

 

쌍봉낙타같은 그래프가 나왔다. 여기서 8월 데이터만 뽑아서 그려보자

aug = [] # aug 변수는 리스트형태로 설정

for row in data: # data 안의 row(행)에 대해 반복적인 작업을 수행한다.
  month = row[0].split('-')[1] # 행의 첫번째 값에서(row[0]), -로 구분된 값(split('-')) 중 2번째 값[1]을 month 변수에 저장
  if row[-1] != '' : # 만약(if) row의 전체(row[-1])를 보았을 때 그것이 비어있지 않은 행(!='')이고
    if month == '08': # 만약(if) month 변수의 데이터가 8월달('== 08')이라면 
      aug.append(float(row[-1])) # aug 변수에 row데이터 전체(row[-1])를 추가한다(append). 소수형태로!(float)

plt.hist(aug, bins=100, color='r') # plt 라이브러리의 hist() 함수의 내용물을 aug 데이터(result)로 넣어주고, 구간을 100개로(bins=100) 두며, 색을 빨간색으로 설정한다(color='r') 
plt.show() # 이제 보여줘(show())

결과값:

8월달만 보니 정규분포처럼 나타난다. 그래프를 해석하면 8월에는 30도 정도였던 날이 가장 많았고 20도 이하이거나 40도에 가까웠던 적은 거의 없었다는 것을 알 수 있다.

 

1월과 8월을 동시에 뽑으려면?

aug = [] # aug 변수는 리스트 형태로 설정, 8월의 최고 기온 값을 저장한다. 
jan = [] # jan 변수는 리스트 형태로 설정, 1월의 최고 기온 값을 저장한다.
for row in data: # data 안의 row(행)에 대해 반복적인 작업을 수행한다.
  month = row[0].split('-')[1] # 행의 첫번째 값에서(row[0]), -로 구분된 값(split('-')) 중 2번째 값[1]을 month 변수에 저장
  if row[-1] != '' : # 만약(if) row의 전체(row[-1])를 보았을 때 그것이 비어있지 않은 행(!='')이고
    if month == '08': # 만약(if) month 변수의 데이터가 8월달('== 08')이라면 
      aug.append(float(row[-1])) # aug 변수에 row데이터 전체(row[-1])를 추가한다(append). 소수형태로!(float)
    if month == '01': # 만약(if) month 변수의 데이터가 1월달('== 01')이라면 
      jan.append(float(row[-1])) # jan 변수에 row데이터 전체(row[-1])를 추가한다(append). 소수형태로!(float)

plt.hist(aug, bins=100, color='r', label='Aug') # plt 라이브러리의 hist() 함수의 내용물을 aug 데이터(result)로 넣어주고, 구간을 100개로(bins=100) 두며, 색을 빨간색으로 설정한다(color='r') 
plt.hist(jan, bins=100, color='b', label='Jan') # plt 라이브러리의 hist() 함수의 내용물을 jan 데이터(result)로 넣어주고, 구간을 100개로(bins=100) 두며, 색을 파란색으로 설정한다(color='b') 
plt.legend() # 그래프에 범례를 추가한다
plt.show() # 이제 보여줘(show())

결과값:

이처럼 1월과 8월의 데이터는 서로 다른 범위에 있다는 것을 확인할 수 있다.

 

 

BoxPlot 그리기

 

이번에는 상자그림(boxplot) 형태의 시각화다.

 

박스플롯은 보통 최댓값, 최솟값, 상위 1/4, 2/4(중앙), 3/4에 위치한 값을 보기 위한 그래프로 데이터 분포를 한 눈에 보기에 적합하다. 
 

이제 박스플롯 그래프를 그려보자. 일단 randint()함수를 이용해 데이터를 만들고 박스플롯을 그린다

import matplotlib.pyplot as plt
import random
result = []
for i in range(13) : # i라는 변수를 13개의 범위 안에(in range(13)) 넣는다.
  result.append(random.randint(1, 1000)) # 1에서 1000까지의 범위 안의 숫자를 랜덤으로 추가한다.
print(sorted(result)) #결과를 출력하는데.... sorted() 함수로 result값을 일렬로 배열한다.

plt.boxplot(result) #boxplot 함수로 result 변수를 그린다.
plt.show()

결과값:

최솟값인 1, 최댓값인 983, 중앙값인 582, 데이터가 주로 있는 부분 등이 한 눈에 들어온다. 

 

만약 다른 값이 알고 싶다면 넘파이 라이브러리를 이용해 확인할 수 있다.
 
import numpy as np
result_ov = np.array(result) # 넘파이 배열 안에 result 데이터 값을 넣는다.
print("1/4: " +str(np.percentile(result_ov,25))) # 출력한다. ("1/4: 값") 형태로. 값은 str()함수로 문자열 형태로 저장한다. 또한 넘파이의 percentile()함수를 활용해 백분위 값으로 저장한다. 여기선 1/4분위(25) 값
print("2/4: " +str(np.percentile(result_ov,50))) # 출력한다. ("2/4: 값") 형태로. 값은 str()함수로 문자열 형태로 저장한다. 또한 넘파이의 percentile()함수를 활용해 백분위 값으로 저장한다. 여기선 2/4분위(50) 값
print("3/4: " +str(np.percentile(result_ov,75))) # 출력한다. ("3/4: 값") 형태로. 값은 str()함수로 문자열 형태로 저장한다. 또한 넘파이의 percentile()함수를 활용해 백분위 값으로 저장한다. 여기선 3/4분위(75) 값

결과값: 1/4: 337.0 2/4: 582.0 3/4: 827.0

 

각 사분면에 위치한 데이터의 값을 알 수 있다.

for row in data: 
  if row[-1] != '' : # 만약(if) row의 전체(row[-1])를 보았을 때 그것이 비어있지 않은 행(!='')이면
   result.append(float(row[-1])) # result 변수에 row데이터 전체(row[-1])를 추가한다(append). 소수형태로!(float)

plt.boxplot(result) # 박스플롯으로 result 데이터 그리기 
plt.show() # 이제 보여줘(show())

그냥 플롯차트를 그릴 때와 똑같다.

히스토그램 그렸던 방식이랑 거의 같다. 1월과 8월도 이전 코드를 가지고 바꿔서 그려본다.

 

import csv 
import matplotlib.pyplot as plt 
f = open('/content/seoul.csv', encoding='cp949') 
data = csv.reader(f)
next(data) 
aug = [] 
jan = [] 
for row in data: 
  month = row[0].split('-')[1]
  if row[-1] != '' :
    if month == '08':
      aug.append(float(row[-1])) 
    if month == '01':
      jan.append(float(row[-1])) 

plt.boxplot(aug) 
plt.boxplot(jan)  
plt.show()

위 그래프를 보면 동그라미가 그려져 있는데 이상치(outlier) 값을 표현한 것으로, 다른 수치에 비해 너무 크거나 작은 값을 자동으로 나타낸 것이다.

 

이번에는 이 두 데이터를 리스트 형태로 넣어서 boxplot형태로 보자

 

plt.boxplot([jan, aug])

오? 월별 데이터도 확인 가능할 것 같다.

 

1. 데이터를 월별로 분류해 리스트 형태로 저장
2. 월별 데이터를 상자그림으로 그림

 

요렇게 해보자

month = [[],[],[],[],[],[],[],[],[],[],[],[]] # 큰 하나의 리스트를 담는 month 변수에 하위 12개 리스트 형태를 담는다. 
for row in data: 
  if row[-1] != '' : # row 데이터가 비어있지 않다면
    month[int(row[0].split('-')[1])-1].append(float(row[-1])) # month 변수는 정수 형태로(int) row데이터의 첫 번째 값인 날짜 데이터(row[0])를 split() 함수를 이용해 ('-')로 구분하여 2번째 위치의([1])의 값에 1을 빼 월과 같은 번호의 인덱스에 월별 데이터를 저장한다. 1월은 month[0]으로!
plt.boxplot(month) 
plt.show()

플롯차트와 비슷한 추세의 그림이 그려진다.

 

여기서 잠깐!

여기서 중요한 것은 숫자를 리스트의 인덱스 값으로 다루기 위해 int값으로 month의 월별 데이터에 -1를 넣어줬다. 인덱스는 0부터 시작하기 때문에 월별 데이터인 1월과 맞지 않기 때문이다. 만약 여기서 -1을 안해줬다면

next(data) 
month = [[],[],[],[],[],[],[],[],[],[],[],[]] 
for row in data: 
  if row[-1] != '' : 
    month[int(row[0].split('-')[1])].append(float(row[-1]))
plt.boxplot(month) 
plt.show()

결과값: list index out of range

 

이처럼 인덱스 에러가 발생한다. 인덱스의 범위를 벗어나는 것이다. 그럼 리스트가 13개라면?

month = [[],[],[],[],[],[],[],[],[],[],[],[],[]] 
for row in data: 
  if row[-1] != '' : 
    month[int(row[0].split('-')[1])].append(float(row[-1]))
plt.boxplot(month) 
plt.show()

이처럼 정상출력은 되지만 첫번째 인덱스에는 값이 들어가지 않았다는 걸 볼 수 있다. 이렇게 궁금한게 있으면 요리조리 확인해서 확실히 이해하고 넘어가자

 

이제 8월 일별 기온데이터를 상자 그림으로 표현해본다.
day = []
for i in range(31):
  day.append([]) # day 리스트 안에 31개 리스트 생성

for row in data:
  if row[-1] != '':
    if row[0].split('-')[1] == '08': #8월이라면
      day[int(row[0].split('-')[2])-1].append(float(row[-1])) # 이번에는 3번째 위치(날짜)를 기준으로 분리한다! 역시 인덱스 값과 맞추기 위해 -1을 붙여준다.

plt.style.use('ggplot') # 그래프 스타일 지정
plt.figure(figsize=(10,5), dpi=300) # 그래프 크기 수정
plt.boxplot(day, showfliers=False) # day 데이터의 아웃라이어값 생략
plt.show()

박스플롯으로 일별 데이터를 한 눈에 확인 가능하다

 

이번에 배운 명령어 SET

append(): 데이터를 추가하는 명령어. 데이터의 형식도 지정 가능하다(float...) split(): 값을 구분하는 기준을 정하는 명령어, 값의 위치도 설정 가능

  • ex) row[0].split('-')[1]

[라이브러리] matplotlib의 pyplot 모듈 명령어

bins= : 가로축의 구간 개수를 설정하는 속성 'bins = 구간개수'

boxplot(): 박스플롯 그래프를 그린다.

showfliers= True/False: 박스플롯에서 이상치를 보여주거나(True) 사라지게 하는 것(False)이다.

style: 그래프의 스타일을 지정하는 코드로 style.use('스타일')형태로 사용한다. 참고로 ggplot은 격자형 무늬를 보여준다.

figure(): 그래프의 모양을 결정한다.

figsize=(가로,세로): figure()에서 그래프의 크기를 결정한다 dpi='dpi크기': dpi 크기를 결정한다.

[라이브러리] random 라이브러리 명령어

randint(): 랜덤 숫자의 범위 정하기 randint(첫 번째 숫자, 마지막 숫자)