귀퉁이 서재

DATA - 28. 단변량 (Univariate) 데이터 시각화 본문

데이터 분석

DATA - 28. 단변량 (Univariate) 데이터 시각화

Baek Kyun Shin 2019. 6. 14. 19:38

단변량(uniariate) 데이터를 시각화하는 것은 아주 쉽습니다. 몇 가지 그래프를 그리는 코드를 간단히 짚어보겠습니다. 데이터와 notebooks 코드는 제 깃헙에서 볼 수 있습니다. (데이터: pokemon.csv, notebooks: Univariate Exploration of Data.ipynb)

히스토그램(histogram)과 막대 그래프(bar chart)가 비슷한 모양이라 헷갈릴 수 있습니다만 차이점이 있습니다. 히스토그램은 수치형 데이터(quantative)를, 막대그래프는 카테고리 데이터(qualitive, categorical data)를 표현하는데 쓰입니다. (Reference 1)  

import

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb
%matplotlib inline

절대량과 상대량

절대량 그래프: x축은 카테고리, y축은 각 카테고리의 갯수를 나타내는 막대그래프
상대량 그래프: x축은 카테고리, y축은 각 카테고리의 비율을 나타내는 막대그래프

절대량 (Absolute Frequency)

pokemon = pd.read_csv('./pokemon.csv')

base_color = sb.color_palette()[0]
sb.countplot(data = pokemon, x = 'generation_id', color = base_color);

상대량 (Relative Frequency)

# get proportion taken by most common group for derivation of tick marks
n_points = pokemon.shape[0]
max_count = pokemon['generation_id'].value_counts().max()
max_prop = max_count / n_points

# generate tick mark locations and names
tick_props = np.arange(0, max_prop, 0.05)
tick_names = ['{:0.2f}'.format(v) for v in tick_props]

# create the plot
sb.countplot(data = pokemon, x = 'generation_id', color = base_color);
plt.yticks(tick_props * n_points, tick_names)
plt.ylabel('proportion');

상대량 그래프는 절대량 그래프와 다 똑같지만 왼쪽 축만 다릅니다. 축을 비율로 나타낸 것입니다.

Text annotation 추가

아래 plt.text를 통해 그래프에 데이터를 입력해줄 수 있습니다. 

sb.countplot(data = pokemon, x = 'generation_id', color = base_color);

# add annotations
id_counts = pokemon['generation_id'].value_counts()
locs, labels = plt.xticks() # get the current tick locations and labels

# loop through each pair of locations and labels
for loc, label in zip(locs, labels):

	# get the text property for the label to get the correct count
    count = id_counts[int(label.get_text())]
    pct_string = '{:0.1f}%'.format(100*count/n_points)

    # print the annotation just below the top of the bar
    plt.text(loc, count-8, pct_string, ha = 'center', color = 'w')

정렬된 가로 막대그래프

x를 y로 바꾸는 것만으로 세로 그래프를 가로 그래프로 바꿀 수 있습니다. 또한 정렬된 index를 order 파라미터에 넣어줌으로써 정렬을 시킬 수 있습니다.

ordered_index = pokemon.generation_id.value_counts().index

sb.countplot(data = pokemon, y = 'generation_id', color = base_color, order = ordered_index);

히스토그램 & subplot

plt.figure(figsize = [10, 5])

# example of somewhat too-large bin size
plt.subplot(1, 2, 1) # 1 row, 2 cols, subplot 1
bin_edges = np.arange(0, pokemon['speed'].max()+20, 20)
plt.hist(data = pokemon, x = 'speed', bins = bin_edges);

# example of somewhat too-small bin size
plt.subplot(1, 2, 2) # 1 row, 2 cols, subplot 2
bin_edges = np.arange(0, pokemon['speed'].max()+5, 5)
plt.hist(data = pokemon, x = 'speed', bins = bin_edges);

위 그래프는 같은 데이터에 대해 bin 크기를 다르게 해 준 것입니다. plt.subplot(1, 2, 1)은 1행 2열로 분할한 후 1행 1열에 그래프를 그린다는 뜻입니다. 마찬가지로 plt.subplot(1, 2, 2)는 1행 2열로 분할된 창에서 1행 2열에 그래프를 그린다는 뜻입니다. 따라서 첫 번째 코드는 왼쪽에, 두 번째 코드는 오른쪽에 그래프가 그려진 것입니다.

스케일링 & 변환

데이터가 지나치게 왜곡이 되었다면 스케일링 및 변환을 해줄 수 있습니다. 아래는 weight에 대한 히스토그램입니다. 지나친 right-skewed 그래프입니다.

bins = np.arange(0, pokemon['weight'].max()+10, 10)
plt.hist(data = pokemon, x = 'weight', bins = bins);

이를 아래와 같이 로그(log)화해줌으로써 정규분포로 바꿀 수 있습니다. 로그를 취해줬을 때 정규분포로 바뀌는 분포를 로그 정규분포라고 합니다. 로그 정규분포는 right-skewed 합니다.

로그화하면 왜 정규분포가 될까요? x가 커짐에 따라 bin의 크기도 커져서 그 bin에 들어가는 y값(count)이 더 많아지기 때문입니다. 무슨 말인지 모르겠다면 아래 표를 보시기 바랍니다.

x 1 1.1 1.2 1.3
10**x 10 12.58925 15.84893 19.95262
차이   2.589254 3.259678 4.103691

x값이 1, 1.1, 1.2, 1.3이 되면서 10**x는 10, 12.59, 15.85, 19.95가 됩니다.

차이가 2.59, 3.26, 4.10으로 점점 증가합니다. bins = 10 ** np.arange(-1, 3.0+0.1, 0.1)이기 때문에 차이가 점점 증가하는 것입니다. 또한, x축을 보면 1 ~ 10과 10 ~ 30의 크기가 똑같습니다. 이는 log(1) = 0, log(10) = 1, log(100) = 2이기 때문입니다. 즉, log(1)과 log(10)의 차이가 log(10)과 log(100)의 차이와 똑같기 때문인 것입니다. 어쨌든 스케일링 및 변환을 하는 이유는 skedwed 된 그래프를 정규분포화하기 위함입니다.

bins = 10 ** np.arange(-1, 3.0+0.1, 0.1) # because -1 is min, 2.999 is max value
ticks = [0.1, 0.3, 1, 3, 10, 30, 100, 300, 1000]
labels = ['{}'.format(val) for val in ticks]

plt.hist(data = pokemon, x = 'weight', bins = bins)
plt.xscale('log')
plt.xticks(ticks, labels)
plt.xlabel('Weight (kg)');

 

Reference

Reference1: 그래프: 막대 도표(막대그래프) vs 히스토그램

 

Comments