이번 포스팅은 두 개의 연속형 변수에 대한 관계를 파악하는데 유용하게 사용할 수 있는 산점도(Scatter Plot) 의 두번째 포스팅으로서 그룹별로 산점도의 점의 색깔과 모양을 다르게 하는 방법을 소개합니다. 

 

(1) 산점도 (Scatter Plot)

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

(3) 산점도의 marker 크기 및 색깔, 모양 설정 (Setting Marker's size, color, shape)

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

 

 

산점도를 그리는데 사용할 데이터는 iris 로서, 'petal length'와 'petal width'의 연속형 변수에 대해서 'species' 그룹별로 점의 색깔과 모양을 다르게 설정해보겠습니다. 

 

참고로 species 에는 setosa 50개, versicolor 50개, virginica 50개씩의 관측치가 들어있습니다. 

 

 

 

# 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_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa

 

iris.groupby('species').size()

species setosa 50 versicolor 50 virginica 50 dtype: int64

 

 

 

 

  (1) matplotlib 으로 그룹별 산점도 그리기 (scatter plot by groups via matplotlib)

 

 

# Scatter plot with a different color by groups

groups = iris.groupby('species')

 

fig, ax = plt.subplots()

for name, group in groups:

    ax.plot(group.petal_length, 

            group.petal_width, 

            marker='o', 

            linestyle='',

            label=name)

ax.legend(fontsize=12, loc='upper left') # legend position

plt.title('Scatter Plot of iris by matplotlib', fontsize=20)

plt.xlabel('Petal Length', fontsize=14)

plt.ylabel('Petal Width', fontsize=14)

plt.show()

 

 

 

 

  (2) seaborn 으로 그룹별 산점도 그리기 (scatter plot by groups via seaborn)

 

코드가 깔끔하고 가독성이 좋으며, 산점도 그래프도 보기에 참 좋습니다. 

 

 

# Scatter plot by Groups

sns.scatterplot(x='petal_length', 

                y='petal_width', 

                hue='species', # different colors by group

                style='species', # different shapes by group

                s=100, # marker size

                data=iris)

plt.show()

 

 

 

 

혹시 AttributeError: module 'seaborn' has no attribute 'scatterplot' 에러가 나면 seaborn version upgrade를 해주시기 바랍니다. 

 

 

# error: AttributeError: module 'seaborn' has no attribute 'scatterplot' 

# solution: upgrade seaborn

pip install --upgrade seaborn

 

 

 

 

  (3) pandas로 그룹별 산점도 그리기 (scatter plot by groups via pandas)

 

# adding 'color' column

iris['color'] = np.where(iris.species == 'setosa', 'red', 

                         np.where(iris.species =='versicolor', 

                         'green', 

                         'blue'))

 

# scatter plot

iris.plot(kind='scatter',

          x='petal_length', 

          y='petal_width', 

          s=50, # marker size

          c=iris['color']) # marker color by group

 

plt.title('Scatter Plot of iris by pandas', fontsize=20)

plt.xlabel('Petal Length', fontsize=14)

plt.ylabel('Petal Width', fontsize=14)

plt.show()

 

 

 

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

이번 포스팅이 도움이 되었다면 아래의 '공감~

'를 꾹 눌러주세요. ^^

 

다음번 포스팅에서는 산점도의 marker 크기 및 색깔, 모양 설정 (Setting Marker's size, color, shape) 에 대해서 소개하겠습니다. 

 

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅부터는 두 개의 연속형 변수에 대한 관계를 파악하는데 유용하게 사용할 수 있는 산점도(Scatter Plot) 를 4번에 나누어서 소개를 하겠습니다. 

 

(1) 산점도 (Scatter Plot)

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

(3) 산점도의 marker 크기 및 색깔, 모양 설정 (Setting Marker's size, color, shape)

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

 

 

기본적인 산점도를 matplotlib, seaborn, pandas 패키지를 사용하여 순서대로 그려보겠습니다. 

 

사용할 데이터는 iris 데이터셋의 'petal length'와 'petal width'의 두 개 연속형 변수입니다. 

 

 

# importing libraries

import numpy as np

import pandas as pd

 

import matplotlib.pyplot as plt

import seaborn as sns

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

 

 

 

# loading 'iris' dataset from seaborn

iris = sns.load_dataset('iris')

iris.shape

(150, 5) 

 

iris.head()

  sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa

 

 

 

  (1) matplotlib을 사용한 산점도 (scatter plot by matplotlib)

 

# Basic Scatter Plot

plt.plot('petal_length',  # x

         'petal_width',  # y

         data=iris, 

         linestyle='none', 

         marker='o', 

         markersize=10,

         color='blue', 

         alpha=0.5)

plt.title('Scatter Plot of iris by matplotlib', fontsize=20)

plt.xlabel('Petal Length', fontsize=14)

plt.ylabel('Petal Width', fontsize=14)

plt.show()

 

 

 

산점도에 X, Y 좌표를 사용하여 직사각형(rectangle), 원(circle), 직선(line)을 추가하여 보겠습니다. 먼저 matplotlib.patches 를 importing 해주어야 하고, 산점도를 그린 다음에, add_patch() 함수를 사용하여 직사각형, 원, 직선을 추가합니다. 

 

 

# adding a rectangle, a circle

import matplotlib.patches as patches

import matplotlib.pyplot as plt

 

fig1 = plt.figure()

ax1 = fig1.add_subplot(111)

 

# (0) scatter plot

ax1.plot('petal_length', 'petal_width', data=iris, linestyle='none', marker='o')

 

# (1) adding a rectangle

ax1.add_patch(

    patches.Rectangle(

        (3, 1), # (x, y)

        2, # width

        1, # height

        alpha=0.2, 

        facecolor="blue", 

        edgecolor="black", 

        linewidth=2, 

        linestyle="solid", 

        angle=-10))

 

# (2) adding a circle

ax1.add_patch(

    patches.Circle(

        (1.5, 0.25), # (x, y)

        0.5, # radius

        alpha=0.2, 

        facecolor="red", 

        edgecolor="black", 

        linewidth=2, 

        linestyle='solid'))

 

# (3) adding a line

plt.plot([4, 6], [2.2, 1.1], color="green", lw=4, linestyle='solid')

 

plt.title("Adding a Rectangle, a Circle and a Line", fontsize=20)

plt.xlabel('Petal Length', fontsize=14)

plt.ylabel('Petal Width', fontsize=14)

plt.show()

 

 

 

 

 

 

 

  (2) seaborn을 사용한 산점도 (scatter plot by seaborn)

 

seaborn 패키지의 (a) regplot() 함수와 (b) scatterplot() 함수를 사용해서 산점도를 그릴 수 있습니다. 순서대로 소개합니다. 

 

(a) regplot() 함수를 사용한 산점도

 

선형회귀 적합 선을 포함시키지 않으려면 fit_reg=False 를 설정해주면 됩니다. 

 

 

# Basic Scatter Plot by seaborn

sns.regplot(x=iris['petal_length'], 

           y=iris['petal_width'], 

           fit_reg=False) # no regression line

plt.title('Scatter Plot of iris by regplot()', fontsize=20)

plt.xlabel('Petal Length', fontsize=14)

plt.ylabel('Petal Width', fontsize=14)

plt.show()

 

 

 

 

두 연속형 변수 간의 선형회귀 적합선을 산점도에 포함시키려면 fit_reg=True 를 설정해주면 됩니다. (defalt 이므로 별도로 표기를 해주지 않아도 회귀적합선이 추가됩니다)

 

 

# Scatter Plot with regression line by seaborn regplot()

sns.regplot(x=iris['petal_length'], 

           y=iris['petal_width'], 

           fit_reg=True) # default

plt.title('Scatter Plot with Regression Line by regplot()', fontsize=20)

plt.show()

 

 

 

X축과 Y축의 특정 값의 조건을 기준으로 산점도 marker의 색깔을 다르게 해보겠습니다. 가령, 'petal length' > 2.5 & 'petal width' > 0.8 이면 '빨간색', 그 이외는 '파란색'으로 설정을 해보겠습니다. 조건에 맞게 'color'라는 새로운 변수를 생성한 후에, scatter_kws={'facecolors': iris_df['color']}) 로 조건별 색을 설정하는 방법을 사용하였습니다. 

 

 

# Control color of each marker based on X and Y values

iris_df = iris.copy()

 

# Adding a 'color' column based on x and y values

cutoff = (iris_df['petal_length']>2.5) & (iris_df['petal_width'] > 0.8)

iris_df['color'] = np.where(cutoff==True, "red", "blue")

 

# Scatter Plot with different colors based on X and Y values

sns.regplot(x=iris['petal_length'], 

           y=iris['petal_width'], 

           fit_reg=False

           scatter_kws={'facecolors': iris_df['color']}) # marker color

plt.title('Scatter Plot with different colors by X & Y values', fontsize=20)

plt.show()

 

 

 

 

(b) scatterplot() 함수를 사용한 산점도

 

# scatter plot by seaborn scatterplot()

ax = sns.scatterplot(x='petal_length', 

                     y='petal_width', 

                     alpha=0.5,

                     data=iris)

plt.title('Scatter Plot by seaborn', fontsize=20)

plt.show()

 

 

 

 

  (3) pandas를 사용한 산점도 (scatter plot by pandas)

 

 

iris.plot.scatter(x='petal_length', 

                  y='petal_width', 

                  s=50, # marker size

                  c='blue', 

                  alpha=0.5)

plt.title('Scatter Plot of iris by pandas', fontsize=20)

plt.xlabel('Petal Length', fontsize=14)

plt.ylabel('Petal Width', fontsize=14)

plt.show()

 

 

 

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

 

이번 포스팅이 도움이 되었다면 아래의 '공감~

'를 꾹 눌러주세요. ^^

 

다음번 포스팅에서는 그룹별 산점도 (Scatter Plot by Groups) 의 마커 모양, 색깔을 다르게 그리는 방법을 소개하겠습니다. 

 

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 시장 점유율과 같이 구성비율을 시각화하는데 사용하는 (1) 원 그래프 (Pie Chart), (2) 하위 그룹을 포함한 도넛 그래프 (Donut Chart with Subgraphs) 그리는 방법을 소개하겠습니다. 


두 개 모두 matplotlib 의 pie() 함수를 사용합니다. 



  (1) 원 그래프 (Pie Chart)



# importing library and set figure size

import matplotlib.pyplot as plt

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

 



# information of groups

group_names = ['Group_A', 'Group_B', 'Group_C']

group_sizes = [95, 54, 25]

group_colors = ['yellowgreen', 'lightskyblue', 'lightcoral']

group_explodes = (0.1, 0, 0) # explode 1st slice

 



# Pie chart

plt.pie(group_sizes, 

        explode=group_explodes, 

        labels=group_names, 

        colors=group_colors, 

        autopct='%1.2f%%', # second decimal place

        shadow=True, 

        startangle=90,

        textprops={'fontsize': 14}) # text font size

plt.axis('equal') #  equal length of X and Y axis

plt.title('Pie Chart of Market Share', fontsize=20)

plt.show()




  (2) 하위 그룹을 포함한 도넛 그래프 (Donut Chart with Subgraphs)


상위 그룹 A, B, C가 있고, 각 상위 그룹에 딸린 하위 그룹으로 'A_1'~'A_4', 'B_1'~'B_3', 'C_1'~'C_2' 의 하위 그룹(subgroups)이 있는 경우 가운데가 뚫린 도넛 모양의 도넛 그래프로 표현해주면 효과적입니다. 


기본 원리는 간단합니다. matplotlib 으로 원 그래프를 그리되, radius 와 width 의 숫자를 적절히 조절하여 (a) 바깥쪽의 상위 그룹의 원 그래프를 반지름이 크고 폭은 작아서 가운데가 뚫린 도넛 그래프로 만들고, (b) 안쪽에는 하위 그룹의 원 그래프를 반지름이 상위 그룹의 것보다는 작고(즉, 상위 그룹 도넛 그래프의 폭 만큼을 빼줌) 폭은 상위 그룹과 같은 가운데가 뚫린 도넛 그래프를 만들여서, (a)와 (b)의 두개 도넛 그래프를 겹치게 그리는 것입니다. 



# importing library and set figure size

import matplotlib.pyplot as plt

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

 



 # info. of groups

group_names = ['Group_A', 'Group_B', 'Group_C']

group_sizes = [95, 54, 25]


# info. of subgroups

subgroup_names = ['A_1', 'A_2', 'A_3', 'A_4', 

                  'B_1', 'B_2', 'B_3', 

                  'C_1', 'C_2']

subgroup_sizes = [50, 30, 10, 5, 30, 20, 4, 20, 5]


# colors

a, b, c = [plt.cm.Reds, plt.cm.Greens, plt.cm.Blues]


# width

width_num = 0.4

 



# Outside Ring

fig, ax = plt.subplots()

ax.axis('equal')

pie_outside, _ = ax.pie(group_sizes, 

                        radius=1.3

                        labels=group_names, 

                        labeldistance=0.8,

                        colors=[a(0.6), b(0.6), c(0.6)])

plt.setp(pie_outside

         width=width_num

         edgecolor='white')


# Inside Ring

pie_inside, plt_labels, junk = \

    ax.pie(subgroup_sizes, 

           radius=(1.3 - width_num), 

           labels=subgroup_names, 

           labeldistance=0.75, 

           autopct='%1.1f%%', 

           colors=[a(0.5), a(0.4), a(0.3), a(0.2), 

                   b(0.5), b(0.4), b(0.3), 

                   c(0.5), c(0.4)])

plt.setp(pie_inside

         width=width_num

         edgecolor='white')

plt.title('Donut Plot with Subgroups', fontsize=20)

plt.show()

 



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


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



728x90
반응형
Posted by Rfriend
,

지난 포스팅에서는 Python으로 연속형 자료에 대해 히스토그램박스 그래프를 그리는 방법을 소개하였습니다. 


이번 포스팅에서는 이산형 자료에 대해 범주(category), 계급(class)별로 빈도나 합계 등을 막대로 그려서 비교하는데 유용하게 사용하는 막대 그래프(bar chart)를 Python의 matplotlib, seaborn, pandas를 사용하여 그리는 방법을 소개하겠습니다. 


예제로 사용할 데이터셋은 seaborn에 내장되어 있는 tips 데이터셋입니다. 



# importing packages

import numpy as np

import pandas as pd


import matplotlib.pyplot as plt

import seaborn as sns

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

 



# Loading 'tips' dataset from seaborn

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.61FemaleNoSunDinner

4





  (1) matplotlib으로 막대 그리프 그리기 (bar chart by matplotlib)


tips DataFrame에서 요일(day)별로 tip 의 합계를 구해서 막대 그래프로 비교를 해보겠습니다. 



# Summary Statistics

tips_sum_by_day = tips.groupby('day').tip.sum()

tips_sum_by_day

day
Thur    171.83
Fri      51.96
Sat     260.40
Sun     247.39 

Name: tip, dtype: float64

 



label = ['Thur', 'Fri', 'Sat', 'Sun']

index = np.arange(len(label))

 



# Basic Bar Chart

plt.bar(index, tips_sum_by_day)

plt.title('Sum of Tips by Day', fontsize=20)

plt.xlabel('Day', fontsize=18)

plt.ylabel('Sum of Tips', fontsize=18)

plt.xticks(index, label, fontsize=15)

plt.show()

 




막대 그래프의 막대 색깔(bar color)과 투명도(transparency, alpha)를 조절해보겠습니다. 



# bar color, transparency

plt.bar(label, tips_sum_by_day, 

        color='red', # color

        alpha=0.5) # transparency

plt.show()

 





막대그래프의 막대 폭(width)을 좀더 좁게하고, 가운데 정렬 대신에 한쪽 끝으로 정렬(align)을 변경해보겠습니다. 



# bar width, align

plt.bar(label, tips_sum_by_day, 

        width=0.5, # default: 0.8

        align='edge') # default: 'center'

plt.show()

 




X축의 라벨이 갯수가 많거나 길이가 길 경우에는 X 축 라벨을 비스듬히 눕히거나 90도로 세우는 것이 보기에 더 효과적일 때가 있습니다. X축 라벨을 90도 회전시켜 보겠습니다.  



# X tick labels rotation

plt.bar(index, tips_sum_by_day)

plt.xlabel('Day', fontsize=18)

plt.xticks(index, label, fontsize=15, 

           rotation=90) # when X tick labels are long

plt.show()

 




X축의 라벨 개수가 많다면 막대그래프를 아예 가로로 눕혀서 그리는 것이(horizontal bar chart) 보기에 효과적일 때도 있습니다. 옆으로 누운 막대그래프는 plt.barh() 함수를 사용합니다. 



# Horizontal Bar Chart

plt.barh(index, tips_sum_by_day)

plt.title('Sum of Tips by Day', fontsize=18)

plt.ylabel('Day', fontsize=15)

plt.xlabel('Sum of Tips', fontsize=15)

plt.yticks(index, label, fontsize=13, rotation=0)

plt.show()




2개의 범주형 변수를 사용하여 각 클래스별로 빈도나 합계를 막대그래프로 비교하려면 (a) 위로 쌓은 막대그래프 (stacked bar chart)나, (b) 옆으로 나란히 놓은 막대그래프 (dodged bar chart)를 사용합니다. 


먼저, (a) 요일(day)과 성별(sex)의 두개 범주형 변수의 클래스별로 팁(tip)의 합계를 비교하기 위해 위로 쌓은 막대그래프 (stacked bar chart)를 그려보겠습니다. 



# summary by group

tips_sum_by_day_male = tips[tips['sex'] == 'Male'].groupby('day').tip.sum()

tips_sum_by_day_female = tips[tips['sex'] == 'Female'].groupby('day').tip.sum()


print('--Male--')

print(tips_sum_by_day_male);

print(' ')

print('--Female--')

print(tips_sum_by_day_female);

--Male--

day
Thur     89.41
Fri      26.93
Sat     181.95
Sun     186.78
Name: tip, dtype: float64
 
--Female--
day
Thur    82.42
Fri     25.03
Sat     78.45
Sun     60.61
Name: tip, dtype: float64




# Bar Chart by 2 categorical variables

# Stacked Bar Chart

label = ['Thur', 'Fri', 'Sat', 'Sun']

N = len(tips['day'].unique())

index = np.arange(N)

alpha = 0.5


p1 = plt.bar(index, tips_sum_by_day_male, color='b', alpha=alpha)

p2 = plt.bar(index, tips_sum_by_day_female, color='r', alpha=alpha,

             bottom=tips_sum_by_day_male) # stacked bar chart

plt.title('Stacked Bar Chart of Sum of Tips by Day & Sex', fontsize=20)

plt.ylabel('Sum of Tips', fontsize=18)

plt.xlabel('Day', fontsize=18)

plt.xticks(index, label, fontsize=15)

plt.legend((p1[0], p2[0]), ('Male', 'Female'), fontsize=15)

plt.show()

 




다음으로 (b) 요일(day)과 성별(sex)의 두개 범주형 변수의 클래스별로 팁(tip)의 합계를 비교하기 위해 옆으로 나란히 놓은 막대그래프를 그려보겠습니다. 두번째 막대 그래프에서 X축의 위치를 bar_width 만큼 오른쪽으로 바로 붙여서 그려지도록 index+bar_width 로 설정(빨간색 부분)해주면 됩니다. 



# Dodged Bar Chart (with same X coordinates side by side)

bar_width = 0.35

alpha = 0.5


p1 = plt.bar(index, tips_sum_by_day_male, 

             bar_width, 

             color='b', 

             alpha=alpha,

             label='Male')

p2 = plt.bar(index + bar_width, tips_sum_by_day_female, 

             bar_width, 

             color='r', 

             alpha=alpha,

             label='Female')


plt.title('Dodged Bar Chart of Sum of Tips by Day & Sex', fontsize=20)

plt.ylabel('Sum of Tips', fontsize=18)

plt.xlabel('Day', fontsize=18)

plt.xticks(index, label, fontsize=15)

plt.legend((p1[0], p2[0]), ('Male', 'Female'), fontsize=15)

plt.show()

 




  (2) seaborn 으로 막대 그래프 그리기 (bar chart by seaborn)


seaborn 패키지를 사용하여 위의 matplotlib 으로 그린 stacked bar chart를 그려보겠습니다. pandas 의 df.plot(kind='bar', stacked=True) 함수처럼 stacked=True 옵션이 seaborn 에는 없는 것 같습니다 (제가 못찾은 걸 수도 있구요). 그래서 workaround 로서 DataFrame에 'day'와 'sex' 기준으로 정렬을 한 후에 누적 합(cumulative sum) 을 구해서, 누적합 칼럼에 대해 dodge=False 로 하여 서로 겹쳐그리게끔 해서 위로 쌓은 누적 막대그래프 (stacked bar plot by seaborn)를 그렸습니다.  (3)번의 pandas 대비 많이 복잡합니다. (댓글에 질문 남겨주신분께 감사드립니다. 덕분에 제가 잘못 알고 쓴 부분 수정할 수 있었습니다.)



tips_sum_by_day_sex = pd.DataFrame(tips.groupby(['day', 'sex']).tip.sum())

tips_sum_by_day_sex = tips_sum_by_day_sex.reset_index()

tips_sum_by_day_sex

daysextip
0ThurMale89.41
1ThurFemale82.42
2FriMale26.93
3FriFemale25.03
4SatMale181.95
5SatFemale78.45
6SunMale186.78
7SunFemale60.61



# sort dataframe by 'day' and 'sex' in descending order first

tips_sum_by_day_sex = tips_sum_by_day_sex.sort_values(by=['day', 'sex'], ascending=False)

tips_sum_by_day_sex

daysextip
7SunFemale60.61
6SunMale186.78
5SatFemale78.45
4SatMale181.95
3FriFemale25.03
2FriMale26.93
1ThurFemale82.42
0ThurMale89.41


# then, calculate cumulative summation by 'day' group

tips_sum_by_day_sex['tip_cumsum'] = tips_sum_by_day_sex.groupby(['day'])['tip'].cumsum(axis=0)

tips_sum_by_day_sex

daysextiptip_cumsum
7SunFemale60.6160.61
6SunMale186.78247.39
5SatFemale78.4578.45
4SatMale181.95260.40
3FriFemale25.0325.03
2FriMale26.9351.96
1ThurFemale82.4282.42
0ThurMale89.41171.83


# atfer that, sort agian by 'day' and 'sex' in ascending order

tips_sum_by_day_sex = tips_sum_by_day_sex.sort_values(by=['day', 'sex'], ascending=True)

tips_sum_by_day_sex

daysextiptip_cumsum
0ThurMale89.41171.83
1ThurFemale82.4282.42
2FriMale26.9351.96
3FriFemale25.0325.03
4SatMale181.95260.40
5SatFemale78.4578.45
6SunMale186.78247.39
7SunFemale60.6160.61




아래 sns.barplot() 함수에서 y 값에 누적 합 칼럼 (y='tip_cumsum') 을 사용한 점, dodge=False 로 옵션 설정한 점 유의하세요. 



# Stacked Bar Chart

sns.barplot(x='day', y='tip_cumsum', hue='sex', data=tips_sum_by_day_sex, 

           dodge=False) # stacked bar chart

plt.title('Stacked Bar Chart by Seaborn', fontsize='20')

plt.show()







seaborn으로 옆으로 나란히 두 개의 범주형 변수의 클래스별 막대그래프를 그려서 비교를 하려면 dodge=True (default setting) 을 해주면 됩니다. 



# Dodged Bar Chart

sns.barplot(x='day', y='tip', hue='sex', data=tips_sum_by_day_sex) # default : dodge=True

plt.title('Dodged Bar Chart by Seaborn', fontsize=20)

plt.legend(fontsize=12)

plt.show()





  (3) pandas 로 막대 그래프 그리기 (bar chart by pandas)


pandas로도 막대 그래프를 그릴 수 있는데요, 위의 matplotlib, seaborn으로 그릴 때 사용했던 데이터 집계 테이블 형태가 조금 다르므로 유심히 살펴보고 참고해서 사용하시기 바랍니다. 



# make a DataFrame

tips_sum_by_day = pd.DataFrame(tips.groupby('day').tip.sum())

tips_sum_by_day = tips_sum_by_day.reset_index()

daytip
0Thur171.83
1Fri51.96
2Sat260.40
3Sun247.39

 




pandas DataFrame에 df.plot.bar() 함수를 사용하거나 df.plot(kind='bar') 를 사용해서 막대그래프를 그립니다.  rot 는 X축 라벨의 회전(rotation) 각도를 설정하는데 사용합니다. 



# basic bar chart with a single column

tips_sum_by_day.plot.bar(x='day', y='tip', rot=0)

plt.show()





이번에는 pandas로 요일(day)과 성별(sex)의 두개 범주형 변수의 각 클래스별로 팁의 합계(summation of tip)를 집계하여 위로 쌓은 막대그래프(stacked bar chart)를 그려보겠습니다. 



# making a DataFrame with 'day' and 'sex' groups

tips_sum_by_day_sex = pd.DataFrame(tips.groupby(['day', 'sex']).tip.sum())

tips_sum_by_day_sex = tips_sum_by_day_sex.reset_index()


# pivot

tips_sum_by_day_sex_pivot = \

    tips_sum_by_day_sex.pivot(index='day', 

                              columns='sex', 

                              values='tip')

tips_sum_by_day_sex_pivot

sexMaleFemale
day
Thur89.4182.42
Fri26.9325.03
Sat181.9578.45
Sun186.7860.61




# Stacked Bar Chart by pandas

tips_sum_by_day_sex_pivot.plot.bar(stacked=True, rot=0)

plt.title('Stacked Bar Chart by Pandas', fontsize=20)

plt.show()




이번에는 pandas로 요일(day)별 성별(sex) 팁의 합계를 옆으로 나란히 막대그래프(dodged bar chart by pandas)로 그려서 비교를 해보겠습니다. 



# Dodged Bar Chart by pandas

tips_sum_by_day_sex_pivot.plot(kind='bar', rot=0)

plt.title('Dodged Bar Chart by Pandas', fontsize=20)

plt.legend(fontsize='12')

plt.show()

 



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


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



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
,

지난번 포스팅에서는 하나의 그룹, 하나의 변수에 대한 히스토그램, 커널밀도곡선을 그리는 방법을 소개하였습니다. 


이번 포스팅에서는

(1) 여러개의 그룹에 대한 히스토그램, 커널밀도곡선 그리기

(2) 여러개의 변수에 대한 히스토그램, 커널밀도곡선 그리기

에 대해서 알아보겠습니다. 



먼저, matlplotlib.pyplot, seaborn 패키지를 importing하고, 예제로 사용할 iris 데이터셋을 불러오겠습니다. 



import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sns


# loading 'iris' dataset

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

 

iris.groupby('species').size()

species
setosa        50
versicolor    50
virginica     50
dtype: int64




iris는 붓꽃인데요, 아래처럼 versicolor, setosa, virginica의 3개 종(species) 그룹별로 각 50개씩 꽃잎 길이와 넓이, 꽃받침 길이와 넓이의 4개 변수를 측정한 데이터셋입니다. 


* image source: https://www.datacamp.com/community/tutorials/machine-learning-in-r



  (1) 여러개 그룹의 히스토그램, 커널밀도곡선 그리기


petal_length 변수에 대해서 setosa, versicolor, virginica 종의 3개 그룹(groups)의 히스토그램과 커널밀도곡선을 그룹별로 색깔을 다르게 하여 그려보겠습니다. 



# 1-1. Multiple histograms on the same axis

sns.distplot(iris[iris.species == "setosa"]["petal_length"], 

             color="blue", label="setosa")


sns.distplot(iris[iris.species == "versicolor"]["petal_length"], 

             color="red", label="versicolor")


sns.distplot(iris[iris.species == "virginica"]["petal_length"], 

             color="green", label="virginica")


plt.legend(title="Species")

plt.show()


 




만약 그룹 개수가 많아서 위에서처럼 일일이 코딩하기가 시간이 오래걸리고 반복되는 코드가 길게 늘어서는게 싫다면 아래처럼 for loop 을 사용해주면 됩니다. 


그래프의 제목, X축 이름, Y축 이름, 범례 이름을 설정하는 방법도 같이 소개합니다. 



# 1-2. Via for loop

grp_col_dict = {'setosa': 'blue', 

                    'versicolor': 'red', 

                    'virginica': 'green'}


# for loop of species group

for group in grp_col_dict:

    

    # subset of group

    subset = iris[iris['species'] == group]

    

    # histogram and kernel density curve

    sns.distplot(subset['petal_length'], 

                    hist = True, # histogram

                    kde = True,  # density curve

                    kde_kws = {'linewidth': 2}, 

                    color = grp_col_dict[group],

                    label = group)


# setting plot format

plt.title('Histogram & Density Plot by Groups')

plt.xlabel('Petal Length(unit:cm)')

plt.ylabel('Density')

plt.legend(prop={'size': 12}, title = 'Group')

plt.show()




  (2) 여러개 변수의 히스토그램, 커널밀도곡선 그리기


이번에는 sepal_width, sepal_length, petal_width, petal_length 의 4개 변수(variable)에 대해서 히스토그램과 커널밀도곡선을 그려보겠습니다. (단, 종(species)의 구분없이 전체 사용)


for loop 을 사용하였는데요, 위의 그룹 indexing 과 이번의 변수 indexing 부분이 다르다는 점 유심히 살펴보시기 바랍니다. 



# 2-1. Multiple histograms on the same axis

var_color_dict = {'sepal_length': 'blue', 

                      'sepal_width': 'red', 

                      'petal_length': 'yellow', 

                      'petal_width': 'green'}


# for loop

for var in var_color_dict:

    sns.distplot(iris[var],                  

                    color = var_color_dict[var], 

                    hist_kws = {'edgecolor': 'gray'}, 

                    label = var)


plt.legend(title = 'Variables')

plt.show()





위의 (2-1) 그래프는 1개의 window에 동일한 축을 사용하여 4개 변수의 히스토그램과 밀도곡선을 그리다보니 중첩이 되면서 좀 헷갈리고 보기에 어려운 점이 있습니다. 


이런 경우에 그래프를 각 변수별로 분리해서 4개의 window subplots에 하나씩 그려서 비교하는 것도 좋은 방법입니다. ax=axes[0, 0] 은 좌상, ax=axes[0, 1]은 우상, ax=axes[1, 0]은 좌하, ax=axes[1, 1]은 우하 위치의 subplot 입니다. 



# 2-2. Multiple histograms at separate windows

f, axes = plt.subplots(2, 2, figsize=(8, 6), sharex=True)

sns.distplot(iris["sepal_length"], color="blue", ax=axes[0, 0])

sns.distplot(iris["sepal_width"], color="red", ax=axes[0, 1])

sns.distplot(iris["petal_length"], color="yellow", ax=axes[1, 0])

sns.distplot(iris["petal_width"], color="green", ax=axes[1, 1])

plt.show()





for loop을 사용해서 그리려면 아래 코드를 참고하세요. 



var_color_dict = {'sepal_length': 'blue', 

                      'sepal_width': 'red', 

                      'petal_length': 'yellow', 

                      'petal_width': 'green'}


i = [0, 0, 1, 1]

j = [0, 1, 0, 1]


# for loop

f, axes = plt.subplots(2, 2, figsize=(8, 6), sharex=True)

for var, i, j in zip(var_color_dict, i, j):

    sns.distplot(iris[var],                  

                    color = var_color_dict[var],

                    ax = axes[i, j])

    

plt.show()



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


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



728x90
반응형
Posted by Rfriend
,

탐색적 데이터 분석 단계에서 변수의 분포, 중심 경향, 퍼짐 정도, 치우침 정도 등을 한눈에 살펴볼 수 있는 시각화 종류로 히스토그램이 많이 사용됩니다. 


이번 포스팅에서는 Python의 matplotlib.pyplot, seaborn, pandas를 이용해서 하나의 변수, 하나의 그룹에 대한 히스토그램(Histogram)을 그리는 방법을 소개하겠습니다. 


그리고 다음번 포스팅에서는 여러개의 변수, 여러개의 그룹별 히스토그램을 그리는 방법을 다루어보겠습니다. 


필요한 패키지를 import하고 데이터셋을 loading하겠습니다. 

예제로 사용할 데이터는 seaborn 패키지에 들어있는 iris 데이터세입니다. setosa, versicolor, virginica 종별로 50개씩, 총 150개의 붖꽃 관측치에 대해서 꽃받침(sepal)과 꽃입(petal)의 길이와 넓이를 측정한 자료입니다. 기계학습 공부할 때 약방의 감초처럼 분류나 군집분석 예제로 사용되곤 하는 바로 그 데이터셋입니다. 



import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sns


# Data Loading

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


iris.groupby('species').size()

species
setosa        50
versicolor    50
virginica     50
dtype: int64

 




  (1) matplotlib.pyplot 으로 히스토그램 그리기


plt.hist() 함수에 X변수의 데이터와 bin의 개수를 입력해주면 됩니다. 이렇 히스토그램을 그리면 그래프 뿐만 아니라 아래처럼 2개의 array와 <a list of Patch objects>가 같이 반환됩니다. 


히스토그램을 그릴 때는 bin의 개수를 적당히(?) 설정하는 것이 매우 중요합니다. bin 개수가 너무 적으면 분포가 뭉뚱그려지며, bin 개수가 너무 많으면 이빨빠지 빗처럼 보기에 이상해집니다. 대개의 경우 bins 값을 입력하지 않은채로 default 세팅으로 해서 그래프를 그려도 제법 보기에 좋게 나오는데요, 혹시 마음에 들지 않는다면 bins=x 값을 변경해가면서 여러번 시도를 해보시기 바랍니다. 



plt.hist(iris['sepal_width'], bins=10)



히스토그램만 보고 싶을 때는 아래처럼 n, bins, patches = plt.hist() 처럼 관측치 값, bin 개수, patch 객체를 n, bins, patches 에 별도로 할당해주면 됩니다. 



n, bins, patches = plt.hist(iris['sepal_width'], bins=10)

 


n, bins, patches

(array([ 4.,  7., 22., 24., 37., 31., 10., 11.,  2.,  2.]),
 array([2.  , 2.24, 2.48, 2.72, 2.96, 3.2 , 3.44, 3.68, 3.92, 4.16, 4.4 ]),
 <a list of 10 Patch objects>)





Y축을 빈도수(frequency)가 아니라 density로 하고 싶을 때는 density=True 를 설정해주면 됩니다. 



# Y axis as density

n, bins, patches = plt.hist(iris['sepal_width'], bins=10, density=True)





히스토그램의 색깔은 facecolor = 'blue' 식으로 설정해주며, alpha 는 투명도(0~1)를 조절할 때 사용합니다. alpha 가 0에 가까워질수록 투명해집니다. 



# facecolor, alpha

n, bins, patches = plt.hist(iris['sepal_width'], bins=10, 

                            density=True, 

                            facecolor='blue'

                            alpha=0.5)




X축과 Y축 이름은 plt.xlabel(), plt.ylabel() 함수로 지정하며, 제목은 plt.title()를 사용하여 추가할 수 있습니다. X축과 Y축의 범위를 강제로 지정해주고 싶으면 plt.axis(X축 시작, X축 끝, Y축 시작, Y축 끝)의 순서대로 값을 입력해줍니다. 



# Setting X, Y label and Title, axis

n, bins, patches = plt.hist(iris['sepal_width'], 

                            bins=10, 

                            density=True, 

                            facecolor='blue', 

                            alpha=0.5)

plt.xlabel('X bins')

plt.ylabel('Density')

plt.title('Histogram of Sepal Width')

plt.axis([1.5, 4.5, 0, 1.1])

plt.show()




  (2) seaborn 패키지로 히스토그램 그리기


seaborn 패키지의 distplot() 함수로 히스토그램을 그리니 density 기준의 히스토그램에 kernel density curve가 겹쳐져서 그래프가 그려졌습니다. 디폴트 세팅으로 그렸는데 bin 개수도 적당해보이고, 분포 곡선까지 겹쳐서 그려주니 편리하고 좋네요. (density curve와 겹쳐그려야 하므로 히스토그램은 frequency가 아니라 density 기준입니다.) 



sns.distplot(iris['sepal_width'])

plt.show()

 




물론 세부 옵션 설정을 통해서 그래프를 자유자재로 그릴 수 있습니다. 옵션 이름만 보면 무슨 기능인지 짐작할 수 있으므로 부연설명은 생략하겠습니다. 


kde=True 에서 kde는 Kernel Density Estimate 를 하여 커널 밀도 함수 곡선을 그리라는 뜻입니다. 


sns.distplot()에서 반환된 axis를 사용해서 seaborn histogram의 제목(title), X축 이름(X axis label), Y축 이름(Y axis label)을 설정할 수 있습니다. (ax.set_title(), ax.set_xlabel(), ax.set_ylabel())



# Kernel Density Curve with histogram

ax = sns.distplot(iris['sepal_width'], 

                      hist=True, 

                      kde=True, 

                      bins=10, 

                      color='blue', 

                      hist_kws={'edgecolor': 'gray'}, 

                      kde_kws={'linewidth': 2})

ax.set_title('Histogram of Sepal Width')

ax.set_xlabel('Sepal Width(cm)')

ax.set_ylabel('Density')

plt.show()




Kernel Density Curve만 그리고 싶다면 sns.distplot(x, hist=False) 라고 해서 그려도 되는데요, 좀더 많은 옵션을 사용하고 싶으면 아래처럼 sns.kdeplot() 함수를 사용하면 편리합니다. 



# Kernel Density Estimate Plot

sns.kdeplot(iris['sepal_width'],  

               shade=True, 

               bw=2, 

               label="Sepal Width")

plt.legend()

plt.show()





  (3) pandas.DataFrame.hist 를 이용한 히스토그램 그리기


pandas의 DataFrame에 아래처럼 바로 hist() 함수를 사용해서 히스토그램을 그릴 수 있습니다. matplotlib.pyplot.hist() 함수를 pandas가 가져다가 히스토그램을 그려주므로 (1)번의 plt.hist() 의 결과와 동일하게 나왔습니다. (디폴트 세팅은 grid=True 여서 격자로 그리드가 쳐져 있음)



# Histogram by pandas.DataFrame.hist

iris['sepal_width'].hist(bins=10, grid=False)

plt.show()



저는 개인적으로는 seaborn 의 그래프가 제일 이뻐보이기는 한데요, 여러 책의 예제 코드로 가장 많이 눈에 띄이는 것은 matplotlib.pyplot 이네요. 간단하게 쓰기에는 pandas.DataFrame.hist 가 손이 제일 덜 가구요. 


다음번 포스팅에서는 여러개의 변수/그룹에 대한 히스토그램 그리는 방법을 소개하겠습니다. 


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


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



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas의 GroupBy()와 apply() 함수를 사용하여 그룹별로 무작위 표본 추출 (Random Sampling and Permutation by Groups) 하는 방법을 소개하겠습니다. 


무작위 표본 추출을 하는 데는 np.random.permutation() 함수를 사용해서 순열을 무작위로 뒤섞은 후에 n개 만큼 indexing 해오는 방법을 사용하였습니다. 



[ 그룹 별 무작위 표본 추출 (Random Sampling and Permutation by Groups) ]




먼저 예제로 사용한 'grp_1'과 'grp_2'의 두 개의 그룹을 포함한 DataFrame을 만들어보겠습니다. 



import numpy as np

import pandas as pd


# setting seed number for reproducibility

np.random.seed(123)


# Make a DataFrame

df = pd.DataFrame({'grp': ['grp_1']*10 + ['grp_2']*10, 

                          'col_1': np.random.randint(20, size=20), 

                          'col_2': np.random.randint(20, size=20)})

 

df

col_1col_2grp
01316grp_1
124grp_1
2217grp_1
363grp_1
4172grp_1
5197grp_1
6102grp_1
7115grp_1
8016grp_1
9177grp_1
10159grp_2
1193grp_2
1206grp_2
13141grp_2
1402grp_2
15151grp_2
161912grp_2
17148grp_2
1843grp_2
19010grp_2





사용자가 지정한 샘플링 비율(sample_pct) 만큼 무작위 표본 추출을 해주는 사용자 정의 함수 (User Defined Function)을 정의해보겠습니다. 



# UDF of random sampling

def sampling_func(data, sample_pct):

    np.random.seed(123)

    N = len(data)

    sample_n = int(len(data)*sample_pct) # integer

    sample = data.take(np.random.permutation(N)[:sample_n])

    return sample

 



이제 위의 무작위 샘플링 사용자 정의 함수(UDF)를 GroupBy()와 apply() 함수에 적용해서 그룹별 특정 비율(이 예에서는 80%)만큼 무작위 표본 추출(random sampling and permutation by groups)을 해보겠습니다.  



# Apply random sampling UDF to groups

sample_set = df.groupby('grp').apply(sampling_func, sample_pct=0.8)


sample_set.sort_index()

col_1col_2grp
grp
grp_101316grp_1
124grp_1
363grp_1
4172grp_1
5197grp_1
6102grp_1
7115grp_1
8016grp_1
grp_210159grp_2
1193grp_2
13141grp_2
1402grp_2
15151grp_2
161912grp_2
17148grp_2
1843grp_2

 



위의 무작위 표본 추출 결과에서 group_keys 를 없애려면 group_keys = False 를 지정해주면 됩니다. 



# disable group_keys

sample_set = df.groupby('grp', group_keys=False).apply(sampling_func, sample_pct=0.8)


sample_set.sort_index()

col_1col_2grp
01316grp_1
124grp_1
363grp_1
4172grp_1
5197grp_1
6102grp_1
7115grp_1
8016grp_1
10159grp_2
1193grp_2
13141grp_2
1402grp_2
15151grp_2
161912grp_2
17148grp_2
1843grp_2




위의 80% 무작위 샘플링이 된 sample_set 에 있는 데이터셋을 training_set 이라고 가정해보고, 위의 sample_set에는 없지만 원래 데이터에는 있던 나머지 20% 데이터를 test_set 으로 별도로 만들어보겠습니다. 



test_set = df.drop(df.index[sample_set.index])

test_set

col_1col_2grp
2217grp_1
9177grp_1
1206grp_2
19010grp_2

 



참고로, pandas DataFrame에 대한 무작위 표본 추출 방법은 https://rfriend.tistory.com/602 를 참고하세요.


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


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



728x90
반응형
Posted by Rfriend
,

여러번에 걸친 지난 포스팅에서는 groupby()와 apply()를 이용하여 그룹 별로 통계량을 구한다든지, 결측값을 대체한다든지, 변수 간 상관계수를 구하는 방법을 소개하였습니다.  


이번 포스팅에서는 groupby()와 apply()를 사용하여 그룹 별로 선회회귀모형을 적합(Group-wise Linear Regression)하는 방법을 소개하겠습니다. 


그룹 개수가 많고, 그룹별로 회귀계수를 비교하고자 할 때 이번 포스팅을 참고하면 그룹별로 일일이 하나씩 모형을 적합하지 않고도 짧은 코드로 간편하게 그룹별 회귀모형을 적합할 수 있습니다. 



[ 그룹 별 선형회귀모형 적합 (Group-wise Linear Regression) ]




예제에 사용할 diabetes (당뇨병) 데이터와 선형회귀모형을 적합할 때 사용할 sklearn의 linear_model 모듈을 importing 하겠습니다. 


당뇨병 환자 442명의 'age', 'sex', bmi', bp', 's1', 's2', 's3', 's4', 's5', 's6' 등의 10개 설명변수와, 우리가 예측하고자 하는 'target' 반응변수가 있는 데이터셋입니다. 설명변수는 표준화가 되어있군요. 



import numpy as np

import pandas as pd

from sklearn import datasets, linear_model


diabetes = datasets.load_diabetes()


diabetes.DESCR 

'Diabetes dataset
================
Notes
-----
Ten baseline variables, age, sex, body mass index, average blood

npressure, and six blood serum measurements were obtained for each of n =442 diabetes patients, as well as the response of interest, a\nquantitative measure of disease progression one year after baseline.\n\nData Set Characteristics:\n\n :Number of Instances: 442\n\n :Number of Attributes: First 10 columns are numeric predictive values\n\n :Target: Column 11 is a quantitative measure of disease progression one year after baseline\n\n :Attributes:\n :Age:\n :Sex:\n :Body mass index:\n :Average blood pressure:\n :S1:\n :S2:\n :S3:\n :S4:\n :S5:\n :S6:\n\nNote: Each of these 10 feature variables have been mean centered and scaled by the standard deviation times `n_samples` (i.e. the sum of squares of each column totals 1).\n\nSource URL:\nhttp://www4.stat.ncsu.edu/~boos/var.select/diabetes.html\n\nFor more information see:\nBradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani (2004) "Least Angle Regression," Annals of Statistics (with discussion), 407-499.\n(http://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf)\n'


diabetes.feature_names

['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']

diabetes.target[:5]
array([151.,  75., 141., 206., 135.])




이번 포스팅이 '그룹'별 선형회귀모형 분석이므로 'sex(성별)'을 'M (Male)', 'F (Female)' 의 2개 그룹으로 나누어서 'age', 'bmi' 의 2개의 설명변수만을 사용하여 'target' 을 예측하는 그룹별 회귀모형을 적합하여 보겠습니다. 필요한 변수만 선별해서 DataFrame을 만들어보겠습니다. 



# Make a DataFrame of Y ('target')

diabetes_Y = pd.DataFrame(diabetes.target, columns = ['target'])

diabetes_Y[:5]

target
0151.0
175.0
2141.0
3206.0
4135.0

 


# Make a DataFrame of X with age, sex, bmi

diabetes_X = pd.DataFrame(diabetes.data[:, 0:3], 

                          columns = ['age', 'sex', 'bmi']) # age, sex, bmi


diabetes_X[:5]

agesexbmi
00.0380760.0506800.061696
1-0.001882-0.044642-0.051474
20.0852990.0506800.044451
3-0.089063-0.044642-0.011595
40.005383-0.044642-0.036385



diabetes_df = pd.concat([diabetes_Y, diabetes_X], axis=1)

diabetes_df[:5]

targetagesexbmi
0151.00.0380760.0506800.061696
175.0-0.001882-0.044642-0.051474
2141.00.0852990.0506800.044451
3206.0-0.089063-0.044642-0.011595
4135.00.005383-0.044642-0.036385




'sex' 변수를 가지고 'M', 'F' 를 class로 가지는 'grp'라는 범주형 변수를 만든 후에, 'sex' 변수는 삭제하겠습니다. 



diabetes_df['grp'] = np.where(diabetes_df['sex'] > 0, 'M', 'F')

diabetes_df.drop(columns=['sex'], inplace=True) 

diabetes_df[:3]

targetagebmigrp
0151.00.0380760.061696M
175.0-0.001882-0.051474F
2141.00.0852990.044451M




이제 GroupBy()의 apply()에 사용할 선형회귀모형 사용자 정의 함수(UDF)를 정의해보겠습니다. 각 그룹별 age와 bmi변수의 회귀계수를 비교하고 싶다고 했으므로 사용자 정의 함수에서 그룹별 회귀모형의 회귀계수와 절편을 결과로 반환하도록 하였습니다. 


# UDF of linear regression model

def lin_regress(data, yvar, xvars):


    # output, input variables

    Y = data[yvar]

    X = data[xvars]

    

    # Create linear regression object

    linreg = linear_model.LinearRegression()

    

    # Fit the linear regression model

    model = linreg.fit(X, Y)

    

    # Get the intercept and coefficients

    intercept = model.intercept_

    coef = model.coef_

    result = [intercept, coef]

    

    return result

 



다음으로 GroupBy()와 apply()를 사용해서 성별 그룹('M', 'F')별 선형회귀모형을 적합해보겠습니다. 



# GroupBy

grouped = diabetes_df.groupby('grp')

# Apply the UDF of linear regression model by Group

lin_reg_coef = grouped.apply(lin_regress, 'target', ['age', 'bmi'])

 



남성('M')과 여성('F') 그룹별 Y절편과 age, bmi 변수의 회귀계수 적합 결과를 조회해보겠습니다. 



lin_reg_coef

grp
F    [152.40684676047456, [23.199210147823813, 814....
M    [148.21507864445124, [291.7563226838977, 1092.... 

dtype: object 


lin_reg_coef['M']

[148.21507864445124, array([ 291.75632268, 1092.80118705])]


lin_reg_coef['F']

[152.40684676047456, array([ 23.19921015, 814.50932703])]




위의 그룹별 선형회귀무형 적합 결과로부터 우리는 아래의 모형을 얻었습니다. 

  • 남성('M') 그룹의 당뇨병 진단 target = 148.2 + 291.8*age + 1,092.8*bmi
  • 여성('F') 그룹의 당뇨병 진단 target = 152.4 + 23.2*age + 814.5*bmi

(이때 age, bmi 는 표준화한 후의 input 값임)


따라서 다른 변수가 고정되었다고 했을 때 (표준화한) bmi 값이 한 단위 증가할 때 '남성('M')' 그룹의 당뇨병 진단 target 은 1,092.8 만큼 증가하는 반면에 '여성('F') 그룹의 당뇨병 진단 target은 814.5 만큼 증가하는 것으로 나왔습니다. 


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


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



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 그룹 별로 변수 간 상관관계 분석 (correlation with columns by groups) 하는 방법을 소개하겠습니다. 


Python pandas는 Pearson Correlation Coefficient를 구할 수 있는 corr(), corrwith() 함수를 제공합니다. 이들 함수를 groupby() 와 apply(lambda func)를 함께 사용함으로써 그룹 별 변수 간 상관계수를 구할 수 있습니다. 



[ 피어슨 상관계수 ( Pearson Correlation Coefficient) ]



먼저 예제로 사용할 'group 1'과 'group 2'의 2개의 그룹을 가진 간단한 DataFrame을 만들어보겠습니다. 




import numpy as np

import pandas as pd

from pandas import DataFrame


# making DataFrame with 4 random variables

np.random.seed(123) # for reproducibility

df= DataFrame(np.random.randn(10, 4), 

                    columns=['a', 'b', 'c', 'd'])

# setting index with 2 group, 'grp1' and 'grp2'

df['group'] = ['grp1', 'grp1', 'grp1', 'grp1', 'grp1', 

                  'grp2', 'grp2', 'grp2', 'grp2', 'grp2']


df = df.set_index('group')


df

abcd
group
grp1-1.0856310.9973450.282978-1.506295
grp1-0.5786001.651437-2.426679-0.428913
grp11.265936-0.866740-0.678886-0.094709
grp11.491390-0.638902-0.443982-0.434351
grp12.2059302.1867861.0040540.386186
grp20.7373691.490732-0.9358341.175829
grp2-1.253881-0.6377520.907105-1.428681
grp2-0.140069-0.861755-0.255619-2.798589
grp2-1.771533-0.6998770.927462-0.173636
grp20.0028460.688223-0.8795360.283627







  (1) 'd' 변수와 나머지 모든 변수 간 그룹 별 상관계수 구하기 : x.corrwith(x['d'])



# correlation with columns: corrwith()

corr_with_d = lambda x: x.corrwith(x['d'])


grouped = df.groupby('group')


grouped.apply(corr_with_d) 

abcd
group
grp10.8468220.0994170.0892051.0
grp20.3074770.832473-0.3904691.0





  (2) 'a'변수와 'd'변수 간 그룹 별 상관계수 구하기 : g['a'].corr[g['d'])



# inter-column correlation: corr()

corr_a_d = lambda g: g['a'].corr(g['d'])


grouped = df.groupby('group')


DataFrame(grouped.apply(corr_a_d))

0
group
grp10.846822
grp20.307477

 



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


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



728x90
반응형
Posted by Rfriend
,