시각화를 하다보면 기존의 그래프에 가독성을 높이기 위해 여러가지 종류의 도형을 추가해야 할 때가 있습니다.  Python  matplotlib.artist 의 Patch 클래스는 아래의 화면 캡쳐에서 보는 것처럼 Shadow, Spine, Wedge, Circle, Ellipse, Arrow, Rectangle, RegularPolygon, FancyArrowPatch, PathPatch 등의 다양한 도형을 쉽게 그릴 수 있는 클래스를 지원합니다. 

 

 

[ matplotlib artist inheritance diagrams, reference for matplotlib artists ]

 

matplotlib.artist Inheritance Diagrams and Reference for Matplotlib artists (* source: matplotlib.org)

 

이들 Reference for Matplotlib artists 중에서 이번 포스팅에서는 matplotlib의 add_patch() 함수matplotlib.patches 클래스의 다양한 artists 를 사용해서 

 

(1) matplotlib 그래프에 직사각형 추가하기 (adding a Rectangle in matplotlib's plot)

(2) matplotlib 그래프에 원 추가하기 (adding a Circle in matplotlib's plot)

(3) matplotlib 그래프에 타원 추가하기 (adding a Ellipse in matplotlib's plot)

(4) matplotlib 그래프에 다각형 추가하기 (adding a Polygon in matplotlib's plot)

(5) matplotlib 그래프에 화살표 추가하기 (adding a Arrow in matplotlib's plot)

 

하는 방법을 소개하겠습니다. 

 

 

먼저, 시각화를 위해 필요한 Python 모듈을 불러오고, 예제로 사용할 간단한 샘플 데이터셋을 만들어보겠습니다. 

 

## importing modules for visualization and data generation
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np


## generating sample datasets
np.random.seed(1004)
x1 = np.random.uniform(low=1.5, high=4.5, size=100)
y1 = np.random.uniform(low=1.5, high=2.5, size=100)
x2 = np.random.normal(loc=7, scale=0.6, size=100)
y2 = np.random.normal(loc=7, scale=0.6, size=100)
x = np.concatenate((x1, x2), axis=0)
y = np.concatenate((y1, y2), axis=0)

 

 

(1) matplotlib 그래프에 직사각형 추가하기 (adding a Rectangle in matplotlib's plot)

 

코드가 추가적인 설명이 필요 없을 정도로 간단하므로 예제 코드와 그 결과로 그려진 그래프 & 추가된 도형 위에 matplotlib.matches 클래스의 각 artist 별 매개변수를 표기해놓는 것으로 설명을 갈음하겠습니다. 

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding a Rectangle in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (1) a Rectangle patch
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Rectangle.html
ax.add_patch(
   patches.Rectangle(
       (1.0, 1.0),      # (x, y) coordinates of left-bottom corner point
       4, 2,            # width, height
       edgecolor = 'blue',
       linestyle = 'dashed', 
       fill = True,
       facecolor = 'yellow',
   ))

plt.show()

adding a patch of a rectangle in matplotlib

 

 

 

(2) matplotlib 그래프에 원 추가하기 (adding a Circle in matplotlib's plot)

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding a Circle in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (2) a Circle patch
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Circle.html
ax.add_patch(
   patches.Circle(
       (7.0, 7.0),      # (x, y) coordinates of center point
       radius = 2.,     # radius
       edgecolor = 'black',
       linestyle = 'dotted', 
       fill = True,
       facecolor = 'lightgray',
   ))

plt.show()

adding a patch of a circle in matplotlib

 

 

 

(3) matplotlib 그래프에 타원 추가하기 (adding a Ellipse in matplotlib's plot)

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding an Ellipse in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (3) A scale-free ellipse.
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Ellipse.html
ax.add_patch(
   patches.Ellipse(
       xy = (5, 5), # xy xy coordinates of ellipse centre.
       width = 5,   # width Total length (diameter) of horizontal axis.
       height = 10, # height Total length (diameter) of vertical axis.
       angle = -40, # angle Rotation in degrees anti-clockwise. 0 by default
       edgecolor = 'black',
       linestyle = 'solid', 
       fill = True,
       facecolor = 'yellow',
   ))

plt.show()

adding a patch of an ellipse in matplotlib

 

 

 

(4) matplotlib 그래프에 다각형 추가하기 (adding a Polygon in matplotlib's plot)

 

다각형(Polygon)을 추가하려면 다각형의 각 꼭지점의 xy 좌표의 모음을 numpy array 형태로 입력해주면 됩니다. 그리고 closed=True 를 설정해주면 xy 좌표들의 모음의 처음 좌표와 마지막 좌표를 연결해서 다각형의 닫아주어서 완성해줍니다. 

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding a Polygon in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (4) a Polygon patch
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Polygon.html
ax.add_patch(
   patches.Polygon(
       # xy is a numpy array with shape Nx2.
       np.array([[1, 1], [5, 1], [9, 5], [9, 9], [6, 9], [1, 4]]), # xy
       closed=True,
       edgecolor = 'black',
       linestyle = 'dashdot', 
       fill = True,
       facecolor = 'lightgray',
   ))

plt.show()

adding a patch of a polygon in matplotlib

 

 

 

(5) matplotlib 그래프에 화살표 추가하기 (adding a Arrow in matplotlib's plot)

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding an Arrow in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (5) a Arrow patch
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Arrow.html
ax.add_patch(
   patches.Arrow(
       x = 3, # x coordinate of the arrow tail.
       y = 5, # y coordinate of the arrow tail.
       dx = 0, # dx Arrow length in the x direction.
       dy = -2, # dy Arrow length in the y direction.
       # width: Scale factor for the width of the arrow. 
       width = 1, # With a default value of 1, the tail width is 0.2 and head width is 0.6.
       edgecolor = 'black',
       linestyle = 'solid', 
       fill = True,
       facecolor = 'yellow',
   ))

plt.show()

adding a patch of an arrow in matplotlib

 

 

 

[ Reference ]

* Python matplotlib's Inheritance Diagrams 

   : https://matplotlib.org/stable/api/artist_api.html#artist-api

* Reference for matplotlib artists

   : https://matplotlib.org/stable/gallery/shapes_and_collections/artist_reference.html#sphx-glr-gallery-shapes-and-collections-artist-reference-py 

* Python matplotlib's Rectangle patch
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Rectangle.html

* Python matplotlib's Circle patch
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Circle.html

* Python matplotlib's scale-free Ellipse
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Ellipse.html

* Python matploblib's Polygon patch
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Polygon.html

* Python matplotlib's Arrow patch
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Arrow.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 numpy 2D 행렬에서 행 기준, 열기준 백분율을 구하여 히트맵을 그리는 방법을 소개하겠습니다. 

 

(1) numpy 2D array 의 행 기준 백분율을 구해서 히트맵 그리기

     (Plotting the heatmap of percentages by row in numpy 2D array)

(2) numpy 2D array 의 열 기준 백분율을 구해서 히트맵 그리기 

     (Plotting the heatmap of percentages by column in numpy 2D array)

 

 

먼저, 예제로 사용할 numpy 2D array 를 0~4 사이의 정수 난수를 생성해서 만들어보겠습니다. 

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

## creating a sample dataset of numpy 2D array
np.random.seed(1004)
mat = np.random.randint(low=0, high=5, size=(5, 5))

print(mat)
# [[2 3 3 4 1]
#  [2 0 0 4 4]
#  [0 2 4 2 3]
#  [3 0 4 3 0]
#  [0 2 4 1 4]]

 

 

(1) numpy 2D array 의 행 기준 백분율을 구해서 히트맵 그리기

     (Plotting the heatmap of percentages by row in numpy 2D array)

 

 

numpy 2D array 에서 행 기준 백분율을 구하기 위해서는 행 기준의 합(summation by row, sum(axis=1, keepdims=True)) 이 필요합니다.  

 

이때 mat.sum(axis=1, keepdims=True) 처럼 keepdims=True 옵션을 표기해줌으로써 shape(5, 1) 의 각 행별 합계를 구할 수 있습니다. 만약 keepdims 옵션을 명기하지 않을 경우 디폴트로서 keepdims=False (by default) 가 적용이 되어서 shape(5,)  의 array([13, 10, 11, 10, 11]) 의 계산 결과가 나오며, 이걸로 나누어 주면 행 기준 백분율이 아니라 element-wise 나눗셈이 되어서 전혀 엉뚱한 계산결과가 나오므로 주의가 필요합니다. 

 

## (1) plotting heatmap of numpy percentages along axis 1 in 2D array

## row sums in matrix using 'keepdims=True' option
row_sum = mat.sum(axis=1, keepdims=True)

print(row_sum)
# [[13]
#  [10]
#  [11]
#  [10]
#  [11]]


## when 'keepdims=False' (by default)
mat.sum(axis=1)
# array([13, 10, 11, 10, 11])

 

 

 

위에서 구한 shape (5, 1) 의 행 기준 합계  row_sum 행렬로 원래의 2D array 행렬 mat  을 나누어주면, numpy 2D array에서 행 기준의 백분율을 구할 수 있습니다. np.round(x, decimals=2) 함수를 사용해서 백분율 계산 결과를 소수점 2째 자리까지만 나오도록 반올림을 해주었습니다. 

 

## calculating row percentage in matrix
mat_row_pct = mat / row_sum

## Round array elements to the given number of decimals
mat_row_pct = np.round(mat_row_pct, decimals=2)

print(mat_row_pct)
# [[0.15 0.23 0.23 0.31 0.08]
#  [0.2  0.   0.   0.4  0.4 ]
#  [0.   0.18 0.36 0.18 0.27]
#  [0.3  0.   0.4  0.3  0.  ]
#  [0.   0.18 0.36 0.09 0.36]]

 

 

 

이제 matplotlib 의 matshow() 함수와 colormap 을 사용해서, 위에서 구한 행 기준 백분율에 대해 히트맵을 그려보겠습니다. colormap 으로는 순차적으로 파란색이 진해지(sequential colormaps)는 camp='Blues' 를 사용하였습니다. 그리고 해석을 편리하게 할 수 있도록 plt.colorbar() 함수를 사용해서 칼라의 강도별 백분율을 legend colorbar 를 만들어주었습니다. 아래에 히트맵을 보면 한눈에 행 기준의 백분율이 어디 셀이 높고 어디 셀이 낮은지 한눈에 금방 알아볼 수 있습니다. 

 

## plotting the heatmap of matrix row percentage (axis=1) using colormaps
## ref: https://matplotlib.org/stable/tutorials/colors/colormaps.html

plt.rcParams['figure.figsize'] = (8, 8) # figure size
plt.matshow(mat_row_pct, cmap='Blues') # sequential colormaps
plt.title('Heatmap of row percentage in matrix', fontsize=16)
plt.colorbar(label='percentages along axis 1') # colorbar legend
plt.show()

heatmap of row percentage in numpy 2D array

 

 

 

 

(2) numpy 2D array 의 열 기준 백분율을 구해서 히트맵 그리기 

     (Plotting the heatmap of percentages by column in numpy 2D array)

 

이번에는 mat.sum(axis=0, keepdims=True)  로 numpy 2D array에서 열 기준으로 백분율(percentages by column in numpy 2D array)을 구해보겠습니다. 앞에서와는 다르게 열 기준 합을 구할 때는 axis = 0 을 사용하였습니다. 

(열 기준 백분율을 구할 때는 keepdims=False 로 해도 결과는 동일합니다.)

 

## (2) plotting heatmap of numpy percentages along axis 0 in 2D array
## row sums in matrix
print(mat)
# [[2 3 3 4 1]
#  [2 0 0 4 4]
#  [0 2 4 2 3]
#  [3 0 4 3 0]
#  [0 2 4 1 4]]

col_sum = mat.sum(axis=0, keepdims=True)

print(col_sum)
# [[ 7  7 15 14 12]]

 

 

 

이제 위에서 구한 열 기준 합으로 원래의 2D array 를 나누어주어서 열 기준 백분율을 구하겠습니다. 그리고 np.round(array, decimals=2) 함수를 사용해서 백분율 행렬의 원소 값을 소수점 2째 자리까지만 나오도록 반올림 해보겠습니다. 

 

## calculating row percentage in matrix
mat_col_pct = mat / col_sum

## Round array elements to the given number of decimals
mat_col_pct = np.round(mat_col_pct, decimals=2)

print(mat_col_pct)
# [[0.29 0.43 0.2  0.29 0.08]
#  [0.29 0.   0.   0.29 0.33]
#  [0.   0.29 0.27 0.14 0.25]
#  [0.43 0.   0.27 0.21 0.  ]
#  [0.   0.29 0.27 0.07 0.33]]

 

 

 

2D array 에서 열 기준 백분율이 준비가 되었으니 matplotlib의 plt.matshow() 함수에 순차적으로 빨간색의 강도가 진해지는 colormap 으로서 cmap='Reds' 를 사용하여 히트맵을 그려보겠습니다. 

 

## plotting the heatmap of matrix column percentage (axis=0) using colormaps
## ref: https://matplotlib.org/stable/tutorials/colors/colormaps.html
plt.rcParams['figure.figsize'] = (8, 8)
plt.matshow(mat_col_pct, cmap='Reds') # sequential colormaps
plt.title('Heatmap of row percentage in matrix', fontsize=16)
plt.colorbar(label='percentages along axis 0') # colorbar legend
plt.show()

colormap of percentages by column in numpy 2D array

 

 

[Reference]

* choosing colormaps in matplotlib: https://matplotlib.org/stable/tutorials/colors/colormaps.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 pandas DataFrame을 대사응로 matplotlib 그래프를 그렸을 때, X축 범주형 항목의 순서(changing the order of x-axis ticks in python matplotlib plot)를 변경하는 방법을 소개하겠습니다. 

 

(1) pandas DataFrame의 x 축으로 사용되는 범주형 값을 loc 를 사용해 재정렬하기

(2) index 가 재정렬된 pandas.DataFrame에 대해서 matplotlib 으로 그래프 그리기

 

changing the order of x-axis ticks in matplotlib plot

 

먼저, 예제로 사용할 간단한 pandas DataFrame을 만들어 보겠습니다. 요일(weekday dates) 별 값(value) 으로 구성된 DataFrame에 대해서 groupby() 연산자를 사용해서 요일별 값의 합을 집계(aggregation by weekday) 한 DataFrame을 만들었습니다. 

 

import pandas as pd
import matplotlib.pyplot as plt

## making a sample pandas DataFrmae
df = pd.DataFrame({
    'weekday': ['Monday', 'Saturday', 'Tuesday', 'Sunday', 'Wednesday', 'Thursday', 
                'Friday', 'Saturday', 'Sunday', 'Friday', 'Tuesday'], 
    'value': [2, 3, 4, 2, 6, 7, 5, 2, 1, 6, 4]
})

print(df)
#       weekday  value
# 0      Monday      2
# 1    Saturday      3
# 2     Tuesday      4
# 3      Sunday      2
# 4   Wednesday      6
# 5    Thursday      7
# 6      Friday      5
# 7    Saturday      2
# 8      Sunday      1
# 9      Friday      6
# 10    Tuesday      4


## aggregation of value by weekday --> this will be used for visualization
df_agg = df.groupby('weekday').sum('value')
print(df_agg)
#            value
# weekday         
# Friday        11
# Monday         2
# Saturday       5
# Sunday         3
# Thursday       7
# Tuesday        8
# Wednesday      6

 

 

 

(1) pandas DataFrame의 x 축으로 사용되는 범주형 값을 loc 를 사용해 재정렬하기

 

위에서 df.groupby('weekday').sum('value') 로 집계한 df_agg DataFrame의 결과를 보면, 알파벳(alphabet) 순서대로 요일의 순서("Friday", "Monday", "Satruday", "Sunday", "Thursday", "Tuesday", "Wednesday")가 정해져서 집계가 되었습니다. 이 DataFrame에 대해 matplotlib 으로 막대그래프를 그리면 아래와 같이 요일이 알파벳 순서대로 정렬이 된 상태에서 그래프가 그려집니다. 

 

plt.figure(figsize=(12, 8))
plt.bar(df_agg.index, df_agg.value)
plt.title("Bar plot without reordering x-axis label", fontsize=20)
plt.xlabel("Weekday", fontsize=18)
plt.xticks(fontsize=16)
plt.show()

matplotlib: before changing the order of x-axis label

 

 

(2) index 가 재정렬된 pandas.DataFrame에 대해서 matplotlib 으로 그래프 그리기
       (matplotlib 그래프의 x-axis 의 ticks 순서 바꾸기)

 

요일의 순서가 우리가 일상적으로 사용하는 순서와는 다르기 때문에 눈에 잘 안들어오고 거슬립니다. 이럴 때는 pandas DataFrame의 index 순서를 먼저 바꾸어주고, 순서가 재정렬된 후의 DataFrame에 대해서 matplotlib 으로 그래프를 그려주면 됩니다.

 

아래 예제에서는 요일(weekday)을 알파벳 순서가 아니라, 우리가 일상적으로 사용하는 ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday") 의 순서로 DataFrame의  index 를 loc 를 사용해서 바꾸어 준후에, matplotlib 막대그래프(bar graph)를 그려보았습니다. 

 

matplotlib의 X 축 레이블의 크기 

## changing the order of x-axis label using loc
weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
df_agg = df_agg.loc[weekday_order]

print(df_agg)
#            value
# weekday         
# Monday         2
# Tuesday        8
# Wednesday      6
# Thursday       7
# Friday        11
# Saturday       5
# Sunday         3


## box-plot after changing the order of x-axis ticks
plt.figure(figsize=(12, 8))
plt.bar(df_agg.index, df_agg.value)
plt.title("Bar plot after reordering x-axis label", fontsize=20)
plt.xlabel("Weekday", fontsize=18)
plt.xticks(fontsize=16)
plt.show()

matplotlib: after changing the order of x-axis label

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

여러개의 그래프를 위/아래 또는 왼쪽/오른쪽으로 붙여서 시각화를 한 후에 비교를 해보고 싶을 때가 있습니다. 그리고 측정 단위가 서로 같을 경우에는 x축, y축을 여러개의 그래프와 공유하면서 동일한 scale의 눈금으로 시각화 함으로써 그래프 간 비교를 더 쉽고 정확하게 할 수 있습니다. 

 

이번 포스팅에서는

  (1) 여러개의 하위 플롯 그리기: plt.subplots(nrows=2, ncols=2)

  (2) X축과 Y축을 공유하기
        : plt.subplots(sharex=True. sharey=True)

  (3) 여러개의 하위 플롯들 간의 간격을 조절해서 붙이기

        : (3-1) plt.subplots_adjust(wspace=0, hspace=0)

        : (3-2) plt.tight_layout(h_pad=-1, w_pad=-1)

하는 방법을 소개하겠습니다. 

 

 

 

(1) 여러개의 하위 플롯 그릭: plt.subplots(nrows=2, ncols=2)

 

먼저, plt.subplots(nrows=2, ncols=2) 로 2개 행, 2개 열의 layout 을 가지는 하위 플롯을 그려보겠습니다. 평균=0, 표준편차=1을 가지는 표준정규분포로 부터 100개의 난수를 생성해서 파란색(color='b')의 약간 투명한(alpha=0.5) 히스토그램을 그려보겠습니다.

 

for loop 을 돌면서 2 x 2 의 레이아웃에 하위 플롯이 그려질때는 왼쪽 상단이 1번, 오른쪽 상단이 2번, 왼쪽 하단이 3번, 오른쪽 하단이 4번의 순서로 하위 그래프가 그려집니다.  

 

이렇게 하위 플롯이 그려질 때 자동으로 하위 플롯 간 여백(padding)을 두고 그래프가 그려지며, X축과 Y축은 공유되지 않고 각각 축을 가지고 그려집니다.  

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

## subplots with 2 by 2
## (1) there are paddings between subplots
## (2) x and y axes are not shared
fig, axes = plt.subplots(
    nrows=2, 
    ncols=2, 
    figsize=(10, 10))

for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color='b', 
            alpha=0.5)

subplots with padding and x, y axes

 

 

(2) X축과 Y축을 공유하기
      : plt.subplots(sharex=True. sharey=True)

(3) 여러개의 하위 플롯들 간의 간격을 조절해서 붙이기

      : (3-1) plt.subplots_adjust(wspace=0, hspace=0)

 

여러개의 하위 플롯 간에 X축과 Y축을 공유하려면 plt.subplots()의 옵션 중에서 sharex=True, sharey=True를 설정해주면 됩니다. 

 

그리고 하위 플롯들 간의 간격을 없애서 그래프를 서로 붙여서 그리고 싶다면 두가지 방법이 있습니다. 먼저, plt.subplots_adjust(wspace=0, hspace=0) 에서 wspace 는 폭의 간격(width space), hspace 는 높이의 간격(height space) 을 설정하는데 사용합니다. 

 

fig, axes = plt.subplots(
    nrows=2, ncols=2, 
    sharex=True, # sharing properties among x axes
    sharey=True, # sharing properties among y axes 
    figsize=(10, 10))

for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color='b', 
            alpha=0.5)
        
## adjust the subplot layout 
plt.subplots_adjust(
    wspace=0, # the width of the padding between subplots 
    hspace=0) # the height of the padding between subplots

matplotlib subplots adjust

 

 

아래 예제에서는 plt.subplots(sharex=False, sharey=True) 로 해서 X축은 공유하지 않고 Y축만 공유하도록 했습니다. 

그리고 plt.subplots_adjust(wspace=0, hspace=0.2) 로 해서 높이의 간격(height space)에만 0.2 만큼의 간격을 부여해주었습니다. 

 

fig, axes = plt.subplots(
    nrows=2, ncols=2, 
    sharex=False, # sharing properties among x axes
    sharey=True, # sharing properties among y axes 
    figsize=(10, 10))

for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color='b', 
            alpha=0.5)
        
## adjust the subplot layout 
plt.subplots_adjust(
    wspace=0, # the width of the padding between subplots 
    hspace=0.2) # the height of the padding between subplots

 

 

 

(3) 여러개의 하위 플롯들 간의 간격을 조절해서 붙이기

      : (3-2) plt.tight_layout(h_pad=-1, w_pad=-1)

 

하위 플롯 간 간격을 조절하는 두번째 방법으로는 plt.tight_layout(h_pad, w_pad) 을 사용하는 방법입니다. plt.tight_layout(h_pad=-1, w_pad=-1)로 설정해서 위의 2번에서 했던 것처럼 4개의 하위 플롯 간에 간격이 없이 모두 붙여서 그려보겠습니다. (참고: h_pad=0, w_pad=0 으로 설정하면 하위 플롯간에 약간의 간격이 있습니다.)

 

fig, axes = plt.subplots(
    nrows=2, ncols=2, 
    sharex=True, # sharing properties among x axes
    sharey=True, # sharing properties among y axes 
    figsize=(10, 10))

for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color='b', 
            alpha=0.5)

## adjusting the padding between and around subplots
plt.tight_layout(
    h_pad=-1, # padding height between edges of adjacent subplots
    w_pad=-1) # padding width between edges of adjacent subplots

matplotlib subplots tight_layout

 

 

X축은 공유하지 않고 Y축만 공유하며, plt.tight_layout(h_pad=3, w_pad=0) 으로 설정해서 높이 간격을 벌려보겠습니다. 그리고 하위 플롯이 그려지는 순서대로 'blue', 'red', 'yellow', 'black' 의 색깔을 입혀보겠습니다. 

 

fig, axes = plt.subplots(
    nrows=2, ncols=2, 
    sharex=False, # sharing properties among x axes
    sharey=True, # sharing properties among y axes 
    figsize=(10, 10))

color = ['blue', 'red', 'yellow', 'black']
k=0
for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color=color[k], 
            alpha=0.5)
        k +=1

## adjusting the padding between and around subplots
plt.tight_layout(
    h_pad=3, # padding height between edges of adjacent subplots
    w_pad=0) # padding width between edges of adjacent subplots

 

[ Reference ]

- plt.subplots(): https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots.html
- plt.subplots_adjust(): https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots_adjust.html
- plt.tight_layout(): https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.tight_layout.html

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 matplotlib 모듈을 사용해서 

 

  (1) 그래프에 수평선 추가하기 (adding horizontal lines)

  (2) 그래프에 수직선 추가하기 (adding vertical lines) 

 

하는 3가지 방법을 소개하겠습니다. 

 

예제로 사용할 샘플 데이터셋을 정규분포로부터 난수를 생성해서 100개 샘플을 추출하고, 점 그래프를 그려보겠습니다. 

이 기본 점 그래프에 수평선과 수직선을 차례대로 추가해보겠습니다. 

 

import numpy as np
import matplotlib.pyplot as plt

## generating random numbers
np.random.seed(1004)
x = np.random.normal(0, 1, 100)

## plotting the original data
plt.figure(figsize = (10, 6))
plt.plot(x, linestyle='none', marker='o', color='gray')
plt.show()

 

 

 (1) 그래프에 수평선 추가하기 (adding horizontal lines)

 

(a) plt.axhline(y, xmin, xmax) : 축을 따라서 수평선을 추가, xmin 과 xmax 는 0~1 사이의 값을 가짐

(b) plt.hlines(y, xmin, xmax) : xmin ~ xmax 까지 각 y 값의 수평선을 추가

(c) plt.plot((x1, x2), (y1, y2)) : (x1, x2), (y1, y2) 좌표를 연결하는 선 추가

 

(a) 번의 plt.axhline() 은 y축에서 부터 수평선이 시작하고, xmin~xmax 로 0~1 사이의 비율 값을 가지는 반면에, (b)번의 plt.hlines() 는 xmin 값 위치에서 부터 수평선이 시작하고, xmin~xmax 값으로 좌표값을 받는다는 차이점이 있습니다. 

(c) 번의 plt.plot() 은 단지 수평선, 수직선 뿐만이 아니라 범용적으로 두 좌표를 연결하는 선을 추가할 수 있습니다. 

 

plt.figure(figsize = (10, 6))
plt.plot(x, linestyle='none', marker='o', color='gray')
plt.title("Plot with Horizontal Lines", fontsize=16)

## (1) adding a horizontal line across the axis
## xmin and xmax should be b/w 0 and 1
plt.axhline(y=3, xmin=0, xmax=1, color='blue', linestyle='solid')
plt.axhline(y=2, xmin=0.1, xmax=0.9, color='blue', linestyle='dashed')

## (2) adding a horizontal line at each y from xmin to xmax
plt.hlines(y=0, xmin=0, xmax=50, color='red', linestyle='dotted')

## (3) adding a horizontal line using (x1, x2), (y1, y2) coordinates
plt.plot((50, 100), (-2, -2), color='black', linestyle='dashdot')

plt.show()

 

horizontal lines using matplotlib

 

 

(2) 그래프에 수직선 추가하기 (adding vertical lines) 

 

(a) plt.axvline(x, ymin, ymax) : 축을 따라서 수직선을 추가, ymin 과 ymax 는 0~1 사이의 값을 가짐

(b) plt.vlines(x, ymin, ymax) : ymin ~ ymax 까지 각 x 값의 수평선을 추가

(c) plt.plot((x1, x2), (y1, y2)) : (x1, x2), (y1, y2) 좌표를 연결하는 선 추가

 

(a) 번의 plt.axvline() 은 x축에서 부터 수평선이 시작하고, ymin~ymax 로 0~1 사이의 비율 값을 가지는 반면에, (b)번의 plt.vlines() 는 ymin 값 위치에서 부터 수평선이 시작하고, ymin~ymax 값으로 좌표값을 받는다는 차이점이 있습니다. 

(c) 번의 plt.plot() 은 단지 수평선, 수직선 뿐만이 아니라 범용적으로 두 좌표를 연결하는 선을 추가할 수 있습니다. 

 

plt.figure(figsize = (10, 6))
plt.plot(x, linestyle='none', marker='o', color='gray')
plt.title("Plot with vertical Lines", fontsize=16)

## (1) adding a vertical line across the axis
## ymin and ymax should be b/w 0 and 1
plt.axvline(x=0, ymin=0, ymax=1, color='blue', linestyle='solid')
plt.axvline(x=10, ymin=0.1, ymax=0.9, color='blue', linestyle='dashed')

## (2) adding a vertical line at each y from xmin to xmax
plt.vlines(x=50, ymin=0, ymax=3, color='red', linestyle='dotted')

## (3) adding a vertical line using (x1, x2), (y1, y2) coordinates
plt.plot((100, 100), (0, -3), color='black', linestyle='dashdot')

plt.show()

 

vertical lines using matplotlib

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 matplotlib 모듈을 사용하여, X축의 값은 동일하지만 Y축의 값은 척도가 다르고 값이 서로 크게 차이가 나는 2개의 Y값 데이터에 대해서 이중축 그래프 (plot with 2 axes for a dataset with different scales)를 그리는 방법을 소개하겠습니다. 

 

먼저 간단한 예제 데이터셋을 만들어보겠습니다. 

 

  * x 축은 2021-10-01 ~ 2021-10-10 일까지의 10개 날짜로 만든 index 값을 동일하게 사용하겠습니다. 

  * y1 값은 0~9 까지 정수에 표준정규분포 Z~N(0, 1) 로 부터 생성한 난수를 더하여 만들었습니다. 

  * y2 값은 정수 0~9에 지수를 취하여 만들었습니다. 

 

## importing modules
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


## generating sample dataset

np.random.seed(123) # for reproducibility
idx = pd.date_range("10 1 2021", 
                     periods=10, 
                     freq="d", 
                     name="Date")

y1 = np.arange(10) + np.random.normal(0, 1, 10)
y2 = np.exp(range(10))

df = pd.DataFrame({'y1': y1, 
                   'y2': y2}, 
                  index = idx)


print(df)
#                   y1           y2
# Date                             
# 2021-10-01 -1.085631     1.000000
# 2021-10-02  1.997345     2.718282
# 2021-10-03  2.282978     7.389056
# 2021-10-04  1.493705    20.085537
# 2021-10-05  3.421400    54.598150
# 2021-10-06  6.651437   148.413159
# 2021-10-07  3.573321   403.428793
# 2021-10-08  6.571087  1096.633158
# 2021-10-09  9.265936  2980.957987
# 2021-10-10  8.133260  8103.083928

 

 

먼저, 스케일이 다른 2개의 y값을 1개의 축을 사용하여 그렸을 때 문제점을 살펴보고, 

다음으로 이를 해결하기 위한 방법 중의 하나로서 matplotlib을 사용해 2중축 그래프를 그려보겠습니다. 

 

(* 참고로, 2중축 그래프 외에 서로 다른 척도(scale)의 두개 변수의 값을 표준화(standardization, scaling) 하여 두 변수의 척도를 비교할 수 있도록 변환해준 후에 하나의 축에 두 변수를 그리는 방법도 있습니다.)

 

 

(1)  스케일이 다른 2개의 y값 변수를 1중축 그래프에 그렸을 때 문제점

==> 스케일이 작은 쪽의 y1 값이 스케일이 큰 쪽의 y2 값에 압도되어 y1 값의 패턴을 파악할 수 없음. (스케일이 작은 y1값의 시각화가 의미 없음)

 

## scale이 다른 데이터를 1개의 y축만을 사용해 그린 그래프
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot() 

ax.plot(df.index, df.y1, marker='s', color='blue')
ax.plot(df.index, df.y2, marker='o', color='red')

plt.title('Plot with 1 Axis', fontsize=16)
plt.xlabel('Date', fontsize=14)
plt.ylabel('Value', fontsize=14)
plt.legend(['y1', 'y2'], fontsize=12, loc='best')
plt.show()

plot with 1 axis for a dataset with the different scales

 

 

(2) 스케일이 다른 2개의 변수에 대해 2중축 그래프 그렸을 때

==> 각 y1, y2 변수별 스케일에 맞게 적절하게 Y축이 조정이 되어 두 변수 값의 패턴을 파악하기가 쉬움

 

이때, 가독성을 높이기 위해서 각 Y축의 색깔, Y축 tick의 색깔과 그래프의 색깔을 동일하게 지정해주었습니다. (color 옵션 사용)

 

## plot with 2 different axes for a dataset with different scales
# left side
fig, ax1 = plt.subplots()
color_1 = 'tab:blue'
ax1.set_title('Plot with 2 Axes for a dataset with different scales', fontsize=16)
ax1.set_xlabel('Date')
ax1.set_ylabel('Y1 value (blue)', fontsize=14, color=color_1)
ax1.plot(df.index, df.y1, marker='s', color=color_1)
ax1.tick_params(axis='y', labelcolor=color_1)

# right side with different scale
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
color_2 = 'tab:red'
ax2.set_ylabel('Y2 value (red)', fontsize=14, color=color_2)
ax2.plot(df.index, df.y2, marker='o', color=color_2)
ax2.tick_params(axis='y', labelcolor=color_2)

fig.tight_layout()
plt.show()

plot with 2 axes for a dataset with different scales

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터과학자 되세요!   :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python 의 matplotlib 라이브러리를 사용해서 사인 곡선, 코사인 곡선을 그려보겠습니다. 

 

(1) matplotlib, numpy 라이브러리를 사용해서 사인 곡선, 코사인 곡선 그리기

    (Plotting sine, cosine curve using python matplotlib, numpy) 

(2) x축과 y축의 눈금값(xticks, yticks), 범례(legend) 를 설정하기

(3) 그래프에 가로, 세로 그리드 선 추가하기 (adding grid lines)

 

이번 포스팅은 그리 어렵지는 않은데요, 눈금값, 범례, 그리드 선 추가와 같은 소소한 팁들이 필요할 때 참고하라고 정리해 놓습니다. 

 

 

 

(1) matplotlib, numpy 라이브러리를 사용해서 사인 곡선, 코사인 곡선 그리기

    (Plotting sine, cosine curve using python matplotlib, numpy) 

 

 numpy 로 0부터 4 pi 까지 0.1씩 증가하는 배열의 x축 값을 만들고, 이들 값들을 대입하여서  사인 y값, 코사인 z값을 생성하였습니다. 

 이들 x 값들에 대한 사인 y값, 코사인 z값을 가장 간단하게 matplotlib 으로 시각화하면 아래와 같습니다. 

 

import numpy as np
import matplotlib.pyplot as plt
# using Jupyter Notebook
%matplotlib inline

## generating x from 0 to 4pi and y using numpy library
x = np.arange(0, 4*np.pi, 0.1) # start, stop, step
y = np.sin(x)
z = np.cos(x)

## ploting sine and cosine curve using matplotlb
plt.plot(x, y, x, z)
plt.show()

 

 

 

 

(2) x축과 y축의 눈금값(xticks, yticks), 범례(legend) 를 설정하기

 

위의 (1)번 사인 곡선, 코사인 곡선을 보면 그래프 크기가 작고, x축 눈금값과 y축 눈금값이 정수로 되어있으며, x축과 y축의 라벨도 없고, 범례가 없다보니 사인 곡선과 코사인 곡선을 구분하려면 신경을 좀 써야 합니다. 

 

사인 곡선, 코사인 곡선 그래프를 보기에 좀더 가독성을 높이기 위해서 

  (a) 그래프 크기를 [12, 6] 의 가로, 세로 크기로 더 키우고, 

  (b) x축과 y축의 눈금값을 지정해주며 (xticks, yticks)

  (c) 그래프 이름, x축과 y축의 라벨 지정해주기 (title, xlabel, ylabel)

  (d) 범례 지정해주기 (legend) 

를 추가로 해보겠습니다. 

 

## 가로, 세로 그래프 크기 설정해주기
plt.rcParams['figure.figsize'] = [12, 6]

plt.plot(x, y)
plt.plot(x, z)

## xticks, yticks 값 지정해주기
plt.xticks([np.pi, np.pi*2, np.pi*3, np.pi*4],
           [r'$\pi$',r'2$\pi$',r'3$\pi$',r'4$\pi$'])
plt.yticks([-1,-0.5, 0, 0.5, 1])

## title, xlabel, ylabel지정해주기
plt.title('Sine and Cosine Plot')
plt.xlabel('x from 0 to 4pi')
plt.ylabel('sin(x) and cos(x)')

## 범례 지정해주기
plt.legend(['sin(x)', 'cos(x)'])

plt.show()

 

sine curve, cosine curve using matplotlib

 

 

 

 

(3) 그래프에 가로, 세로 그리드 선 추가하기 (adding grid lines)

 

위의 (2)번 사인, 코사인 그래프가 앞서의 (1)번 그래프 보다 훨씬 보기에 좋지요? 그럼, 여기에서 조금만 더 친절하게 가로와 세로의 그리드 점선을 추가해 보겠습니다. 

 

plt.rcParams['figure.figsize'] = [12, 6]
plt.plot(x, y)
plt.plot(x, z)
plt.xticks([np.pi, np.pi*2, np.pi*3, np.pi*4],[r'$\pi$',r'2$\pi$',r'3$\pi$',r'4$\pi$'])
plt.yticks([-1,-0.5, 0, 0.5, 1])
plt.xlabel('x from 0 to 4pi')
plt.ylabel('sin(x) and cos(x)')
plt.title('Sine and Cosine Plot')
plt.legend(['sin(x)', 'cos(x)'])

## grid line 추가하기
plt.grid(b=True, 
         which='major', # both/major/minor
         color='gray', 
         linestyle='--')

plt.show()

 

sine and cosine plot with grid, legend using matplotlib

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 1. plt.subplot() 2. plt.subplots() 를 이용해서 


(1) (nrows=1, ncols=3)개의 복수의 하위 막대 그래프를 그리는 방법

     (multiple subplots of bar plots using plt.subplot())

(2) 복수의 막대 그래프 간에 y 축의 단위를 고정하는 방법 (fix y axis scale)

(3) 복수의 막대 그래프 간에 y 축의 이름을 공유하는 방법 (share y axis label)

(4) 복수의 하위 그래프 간 여백을 작게 하여 밀착해서 그리는 방법 (plots in tight layout)


을 소개하겠습니다. 


그리고 마지막에는 복수의 옆으로 누운 막대 그래프 (multiple horizontal bar plots) 를 그리는 방법을 소개하겠습니다. 


먼저 예제로 사용할 간단한 DataFrame을 만들어보겠습니다. 



import numpy as np

import pandas as pd

import matplotlib.pyplot as plt


# make a sample DataFrame

grp = ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c']

col = ['x1', 'x2', 'x3'] * 3

val = [3.5, 4.2, 2.9, 0.5, 0.2, 0.3, 1.5, 2.5, 2.6]


df = pd.DataFrame({'grp': grp, 

                  'col': col, 

                  'val': val})


df

grpcolval
0ax13.5
1ax24.2
2ax32.9
3bx10.5
4bx20.2
5bx30.3
6cx11.5
7cx22.5
8cx32.6

 




  1. plt.subplot() 으로 복수의 막대 그래프 그리기


(1-1) (행 1개, 열 3개) 로 구성된 복수의 하위 막대 그래프 그리기 

        (multiple subplots of bar plots using plt.subplot())


plt.subplot(nrow, ncol, position) 의 형태로 행과 열의 위치에 여러개의 하위 그래프를 그릴 수 있습니다. 


아래의 예제에서는 고쳤으면 하는 3가지 문제점이 보이네요. 

첫째 문제는 Y 축의 단위(y axis scale)가 서로 다르다 보니 비교를 하기가 힘듭니다. 

둘째 문제는 X축과 Y축의 이름(label)과 단위 눈금(ticks)이 반복해서 나타나서 지저분해 보입니다. 

셋째 문제는 복수의 하위 그래프 간 간격이 떨어져 있어서 좀 작게 보입니다. 


아래에 차례대로 이들 문제를 해결해 보겠습니다. 



# (1-1) multiple bar plots with different y axis scale

plt.rcParams['figure.figsize'] = [12, 6]


for i, grp in enumerate(['a', 'b', 'c']):

    df_i = df[df['grp'] == grp]

    plt.subplot(1, 3, i+1)

    plt.bar(df_i['col'], df_i['val'], color='blue', alpha=0.5)

    plt.title('Group: %s' %grp, fontsize=18)

    plt.xlabel('X variable', fontsize=14)

    plt.ylabel('Value', fontsize=14)

    plt.xticks(fontsize=12)

    plt.yticks(fontsize=12)






(1-2) 복수의 막대 그래프 간에 y 축의 단위를 고정하는 방법 (fix y axis scale)


plt.ylim(min, max) 의 형태로 y 축의 단위를 설정 또는 고정 (set/ fix y axis scale) 할 수 있습니다. 



 # (1-2) Set fixed y axis scale and Share it together

plt.rcParams['figure.figsize'] = [12, 6]

max_val = np.ceil(max(df['val']))


for i, grp in enumerate(['a', 'b', 'c']):

    df_i = df[df['grp'] == grp]

    plt.subplot(1, 3, i+1)

    plt.bar(df_i['col'], df_i['val'], color='blue', alpha=0.5)

    plt.title('Group: %s' %grp, fontsize=18)

    plt.xlabel('X variable', fontsize=14)

    plt.ylabel('Value', fontsize=14)

    plt.xticks(fontsize=12)

    plt.yticks(fontsize=12)

    # set fixed y axis scale

    plt.ylim(0, max_val)





(1-3) 복수의 막대 그래프 간에  X, Y 축의 이름을 공유하는 방법 (share X and Y axis label)


if 조건문을 사용하여 X축의 이름(X label)은 중간 위치 (index = 1)의 하위 그래프만 나타나게 하고, Y축의 이름(Y label)은 첫번째 그래프(index = 0) 에만 나타나게 하면 X축과 Y축의 이름을 서로 공유한 것과 같은 효과를 낼 수 있습니다. 



# (1-3) Display only 1 X and Y label

plt.rcParams['figure.figsize'] = [12, 6]

max_val = np.ceil(max(df['val']))


for i, grp in enumerate(['a', 'b', 'c']):

    df_i = df[df['grp'] == grp]

    plt.subplot(1, 3, i+1)

    plt.bar(df_i['col'], df_i['val'], color='blue', alpha=0.5)

    plt.title('Group: %s' %grp, fontsize=18)

    

    # display only 1 X and Y label

    if i == 1:

        plt.xlabel('X variable', fontsize=14)

    if i == 0:

        plt.ylabel('Value', fontsize=14)

    if i != 0:

        plt.yticks([])

    

    plt.xticks(fontsize=12)

    plt.yticks(fontsize=12)

    plt.ylim(0, max_val)





(1-4) 복수의 하위 그래프 간 여백을 작게 하여 밀착해서 그리는 방법 (plots in tight layout)


plt.tight_layout() 메소드를 이용하여 복수의 하위 그래프 간 여백 간격을 좁게하여 밀집된 형태로 좀더 크게 복수의 그래프를 그릴 수 있습니다. 



# (1-4) Display multiple plots in Tight Layout

plt.rcParams['figure.figsize'] = [12, 6]

max_val = np.ceil(max(df['val']))


for i, grp in enumerate(['a', 'b', 'c']):

    df_i = df[df['grp'] == grp]

    plt.subplot(1, 3, i+1)

    plt.bar(df_i['col'], df_i['val'], color='blue', alpha=0.5)

    plt.title('Group: %s' %grp, fontsize=18)

    

    # display only 1 X and Y label

    if i == 1:

        plt.xlabel('X variable', fontsize=14)

    if i == 0:

        plt.ylabel('Value', fontsize=14)

    if i != 0:

        plt.yticks([])

    

    plt.xticks(fontsize=12)

    plt.yticks(fontsize=12)

    plt.ylim(0, max_val)


# display in tight layout

plt.tight_layout()





  2. plt.subplots() 로 복수의 막대 그래프 그리기


plt.subplots() 는 위의 plt.subplot() 보다 좀더 적은 코드로 깔끔하게 복수의 하위 막대그래프를 그릴 수 있습니다. 

  • (nrows, ncols) 로 하위 그래프를 그릴 행과 열을 지정해줍니다. 
  • sharey=True 로 y 축 공유, sharex=True 로 복수 그래프 간 x 축을 공유할 수 있습니다. (편리!)
  • if 조건문과 함께 사용해서 ax.set_ylabel(), ax.set_xlabel() 로 y축과 x축의 이름을 하나만 나타나게 하였습니다. 
  • plt.tight_layout() 로 복수의 하위 그래프 간 여백을 좁게 해서 그래프를 그렸습니다. 


# (2-1) Multiple bar plots

# (2-2) Use fixed y axis scale

# (2-3) Display only 1 X and Y label

# (2-4) Display multiple plots in Tight Layout

fig, axes = plt.subplots(nrows=1

                         , ncols=3

                         , sharey=True

                         , figsize=(12,6))


grp = ['a', 'b', 'c']


for i, ax in enumerate(axes):

    df_i = df[df['grp'] == grp[i]]

    ax.bar(df_i['col'], df_i['val'], color='blue', alpha=0.5)

    ax.set_title("Group: {grp}".format(grp=grp[i]), fontsize=18)

    if i == 0:

        ax.set_ylabel("Value", fontsize=14)

    if i == 1:

        ax.set_xlabel("X variable", fontsize=14)

        

plt.tight_layout()





  3. 복수의 옆으로 누운 막대 그래프 (multiple horizontal bar plot) 그리기


ax.barh() 를 사용하여 옆으로 누운 막대 그래프 (horizontal bar plot)을 그렸으며, 복수개의 하위 그래프 그리는 방법은 2번에서 소개한 plt.subplots() 함수를 차용하였습니다. 


이때 옆으로 누운 막대 그래프이기 때문에, 가독성을 높이기 위해서 그룹의 이름을 제목(title)이 아니라 Y 축 이름(y label) 에 표시를 하였습니다. 



# Horizontal Multiple Bar Plots using plt.subplots()

fig, axes = plt.subplots(nrows=3

                         , ncols=1

                         , sharex=True

                         , figsize=(8,10))


grp = ['a', 'b', 'c']


for i, ax in enumerate(axes):

    df_i = df[df['grp'] == grp[i]]

    ax.barh(df_i['col'], df_i['val'], color='blue', alpha=0.5)

    #ax.set_title("Group: {grp}".format(grp=grp[i]), fontsize=18)

    ax.set_ylabel("Group: {grp}".format(grp=grp[i]), fontsize=18)

    if i == 2:

        ax.set_xlabel("Value", fontsize=14)

        

plt.tight_layout()


 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

이번 포스팅은 두 개의 연속형 변수에 대한 관계를 파악하는데 유용하게 사용할 수 있는 산점도(Scatter Plot) 의 세번째 포스팅으로서 4개의 연속형 변수를 사용하여 X축, Y축, 점의 색깔(color)과 크기(size)을 다르게 하는 방법을 소개합니다. 


즉, 산점도를 사용하여 4차원의 데이터를 2차원에 시각화하는 방법입니다. 




(1) 산점도 (Scatter Plot)

(2) 그룹별 산점도 (Scatter Plot by Groups)

(3) 4개 변수로 점의 크기와 색을 다르게 산점도 그리기 (Scatter plot with different size, color)

(4) 산점도 행렬 (Scatter Plot Matrix)

 



예제로 활용할 데이터는 iris 의 4개의 연속형 변수들입니다. 



# importing libraries

import numpy as np

import pandas as pd


import matplotlib.pyplot as plt

import seaborn as sns

plt.rcParams['figure.figsize'] = [10, 8] # setting figure size

 


 

# loading 'iris' dataset from seaborn

iris = sns.load_dataset('iris')

iris.shape

(150, 5)


iris.head()

sepal_lengthsepal_widthpetal_lengthpetal_widthspecies
05.13.51.40.2setosa
14.93.01.40.2setosa
24.73.21.30.2setosa
34.63.11.50.2setosa
45.03.61.40.2setosa





  (1) matplotlib에 의한 4개 연속형 변수를 사용한 산점도 (X축, Y축, 색, 크기)


plt.scatter() 함수를 사용하며, 점의 크기는 s, 점의 색깔은 c 에 변수를 할당해주면 됩니다. 



# 4 dimensional scatter plot with different size & color

plt.scatter(iris.sepal_length, # x

           iris.sepal_width, # y

           alpha=0.2, 

           s=200*iris.petal_width, # marker size

           c=iris.petal_length, # marker color

           cmap='viridis')

plt.title('Scatter Plot with Size(Petal Width) & Color(Petal Length)', fontsize=14)

plt.xlabel('Sepal Length', fontsize=12)

plt.ylabel('Sepal Width', fontsize=12)

plt.colorbar()

plt.show()





점(marker)의 모양을 네모로 바꾸고 싶으면 marker='s' 로 설정해주면 됩니다. 



# 4 dimensional scatter plot with different size & color

plt.scatter(iris.sepal_length, # x

           iris.sepal_width, # y

           alpha=0.2, 

           s=200*iris.petal_width, # marker size

           c=iris.petal_length, # marker color

           cmap='viridis'

           marker = 's') # square shape

plt.title('Size(Petal Width) & Color(Petal Length) with Square Marker', fontsize=14)

plt.xlabel('Sepal Length', fontsize=12)

plt.ylabel('Sepal Width', fontsize=12)

plt.colorbar()

plt.show()

 





  (2) seaborn에 의한 4개 연속형 변수를 사용한 산점도 (X축, Y축, 색, 크기)


seaborn 의 산점도 코드는 깔끔하고 이해하기에 쉬으며, 범례도 잘 알아서 색깔과 크기를 표시해주는지라 무척 편리합니다. 



# 4 dimensional scatter plot by seaborn

sns.scatterplot(x='sepal_length', 

                y='sepal_width', 

                hue='petal_length',

                size='petal_width',

                data=iris)

plt.show()





  (3) pandas에 의한 4개 연속형 변수를 사용한 산점도 (X축, Y축, 색, 크기)


pandas의 DataFrame에 plot(kind='scatter') 로 해서 color=iris['petal_length']로 색깔을 설정, s=iris['petal_width'] 로 크기를 설정해주면 됩니다. pandas 산점도 코드도 깔끔하고 이해하기 쉽긴 한데요, 범례 추가하기가 쉽지가 않군요. ^^; 



iris.plot(kind='scatter'

          x='sepal_length', 

          y='sepal_width', 

          color=iris['petal_length'],

          s=iris['petal_width']*100)


plt.title('Size(Petal Width) & Color(Petal Length) with Square Marker', fontsize=14)

plt.show()




참고로, 산점도의 점(marker)의 모양(shape)을 설정하는 심벌들은 아래와 같으니 참고하시기 바랍니다. 



# set the size

plt.rcParams['figure.figsize'] = [10, 8]


# remove ticks and values of axis

plt.xticks([])

plt.yticks([])


# markers' shape

all_shape=['.','o','v','^','>','<','s','p','*','h','H','D', 'd', '', '', '']


num = 0

for x in range(1, 5):

    for y in range(1, 5):

        num += 1

        

        plt.plot(x, y, 

                 marker = all_shape[num-1], 

                 markerfacecolor='green', 

                 markersize=20, 

                 markeredgecolor='black')

        

        plt.text(x+0.1, y, 

                 all_shape[num-1], 

                 horizontalalignment='left', 

                 size='medium', 

                 color='black', 

                 weight='semibold')

        

plt.title('Markers', fontsize=20)        

plt.show()



많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. ^^


다음번 포스팅에서는 산점도 행렬 (Scatter Plot Matrix) 에 대해서 소개하겠습니다. 



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 하나의 연속형 변수에 대해서 분포 형태, 퍼짐정도, 이상치 여부 등을 시각화하고, 하나의 그룹 혹은 여러개의 그룹 간 비교하는데 유용한 상자 그림 (Box plot, Box-and-Whisker Plot)을 그리는 방법을 소개하겠습니다. 


상자 그림은 연속형 변수에 대해서 최소값(min), 제 1사분위수(Q1), 중앙값(Q2, median), 제 3사분위수(Q3), 최대값(max) 의 요약통계량을 계산하는 것에서 시작합니다. 



[상자 그림 그리는 순서 및 방법]


  1. 주어진 데이터에서 각 사분위수를 계산한다.
  2. 그래프에서 제1 사분위와 제3 사분위를 밑변으로 하는 직사각형을 그리고, 제 2사분위에 해당하는 위치에 선분을 긋는다.
  3. 사분위수 범위(IQR, Interquartile range, )를 계산한다.
  4. 과 차이가 1.5*IQR 이내인 값 중에서 최댓값을 과 직선으로 연결하고, 마찬가지로 과 차이가 1.5*IQR 이내인 값 중에서 최솟값을 과 연결한다.
  5. 보다 1.5*IQR 이상 초과하는 값과 보다 1.5*IQR 이상 미달하는 값은 점이나, 원, 별표등으로 따로 표시한다(이상치 점).

* source: https://ko.wikipedia.org/wiki/상자_수염_그림

 



위의 상자 그림 그리는 방법에 대한 내용은 아래의 표준정규분포 확률밀도함수 분포 곡선에 대한 상자 그림 매핑을 보면 좀더 이해가 쉬울 것입니다. 


[Boxplot and a probability density function (pdf) of a Normal N(0,1σ2) Population]


* source: https://en.wikipedia.org/wiki/Box_plot




상자 그림을 (1) matplotlib, (2) seaborn, (3) pandas 패키지를 이용해서 그리는 방법을 차례대로 소개하겠습니다. 



import numpy as np

import pandas as pd


import matplotlib.pyplot as plt

import seaborn as sns

plt.rcParams['figure.figsize'] = [10, 6]

%matplotlib inline




예제로 사용할 데이터셋은 seaborn 패키지에 내장되어 있는 tips 라는 DataFrame 을 사용하겠습니다. 



# loading 'tips' dataset

tips = sns.load_dataset('tips')


tips.shape

(244, 7)


tips.head()

total_billtipsexsmokerdaytimesize
016.991.01FemaleNoSunDinner2
110.341.66MaleNoSunDinner3
221.013.50MaleNoSunDinner3
323.683.31MaleNoSunDinner2
424.593.61FemaleNoSunDinner4


tips.groupby(['sex', 'day']).size()

sex     day 
Male    Thur    30
        Fri     10
        Sat     59
        Sun     58
Female  Thur    32
        Fri      9
        Sat     28
        Sun     18
dtype: int64




tips DataFrame의 tip 연속형 변수에 대해서 상자 그림을 그려보겠습니다. 


  (1) matplotlib 으로 상자 그림 그리기


boxplot() 함수를 사용해서 default 세팅으로 상자 그림을 그려보겠습니다. 


# Basic box plot

plt.boxplot(tips['tip'])

plt.show()



이번에는 상자 그림의 이상치(outlier) 모양과 색깔, 제목(title), X축 이름(X label)을 설정해보겠습니다. 



# setting outlier symbol, title, xlabel

plt.boxplot(tips['tip'], sym="bo")

plt.title('Box plot of tip')

plt.xticks([1], ['tip'])

plt.show()




다음으로 상자그림을 가로로 눕히고, 가운데 상자는 중앙값 부근에서 V자 형태로 골이 패이게(notch) 그려보겠습니다. 


 

# Horizontal Box plot with notched box & red color outliers

plt.boxplot(tips['tip'], 

            notch=1, # if 'True' then notched box plot

            sym='rs', # symbol: red square

            vert=0 # vertical : if 'False' then horizontal box plot

           )

plt.show()




한 개의 축에 여러 개의 연속형 변수(total_bill, tip)에 대한 여러 개의 상자 그림을 한꺼번에 그려보겠습니다. (다수 개의 연속형 변수, 한개의 그룹)



# Multiple box plots on one Axes

fig, ax = plt.subplots()

ax.boxplot([tips['total_bill'], tips['tip']], sym="b*")

plt.title('Multiple box plots of tips on one Axes')

plt.xticks([1, 2], 

           ['total_bill', 'tip'])

plt.show()

 




  (2) seaborn 으로 여러개 그룹에 대한 상자 그림 그리기 

      (Grouped box plots by seaborn)


요일(day) 그룹별러 팁(tip)에 대한 상자 그림을 같이 그려서 비교를 해보겠습니다. (한 개의 연속 형 변수, 다수 개의 그룹)



# Grouped boxplots by seaborn

import seaborn as sns


sns.boxplot(x="day"

            y="tip"

            data=tips)

plt.show()





위의 요일(day: Thur, Fri, Sat, Sun) 그룹에 더해 hue 옵션을 사용하여 성별(sex: Male, Female)에 따른 그룹을 추가하여 (총 4개 그룹 x 2개 그룹 = 8개 그룹) 별 팁(tip) 에 대한 상자 그림을 그려서 비교해보겠습니다. 



#  2 by 4 multi-Grouped boxplots by seaborn

sns.boxplot(x="day", 

            y="tip", 

            hue="sex",

            data=tips)


plt.show()

 





  (3) pandas 로 상자 그림 그리기


pandas의 DataFrame에 df.boxplot() 함수를 사용하여 상자 그림을 그릴 수 있습니다. (2)번에서 그렸던 그룹 별 상자 그림을 pandas 의 boxplot() 함수로 그려보면 아래와 같습니다. 



# Grouped boxplots by pandas

tips.boxplot(column=["tip"], by=["day", "sex"])

plt.show()


제 개인적인 생각으로는, 그래프의 가독성은 seaborn이 더 나아 보이고, 코드의 가독성은 pandas가 더 우수해보이네요. 


많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요.


다음번 포스팅에서는 막대 그래프 그리는 방법을 소개하겠습니다. 



728x90
반응형
Posted by Rfriend
,