Statistics

Python 통계 - 비모수 통계

강의 홍보

분포에 대한 가정을 만족 못할 시의 문제점

  • 1종 오류의 값이 커지거나, 분석 결과 자체에 대한 신뢰성이 떨어짐
  • 모수 통계 분석 적용 못할 시, 비모수 통계 분석 활용

(1) 언제 적용할까?

  • 분포 가정 불만족 (예: 정규분포 아님)
  • 분석기법의 전제조건 불만족 (예: 다중공선성)
  • 표본 수 불만족 (예: 설문조사 20개)
  • 데이터 척도 단순화 (예: 명목 또는 서열 척도로만 측정)

(2) 비모수 통계분석 유형

  • 비모수 통계분석의 유형은 아래와 같다. (p. 357)

통계분석 실습 - 적합도 검정

(1) 적합도 검정: RUN 검정

  • 가정된 확률이 정해져 있는 경우, 예) 제품의 A등급(6), B등급(3), C등급(1)
  • 비모수 통계는 반대로, 표본의 배열이 무작위로 구성되어 있는지 검정
  • (교재, p. 357)
    • 마케팅 행사가 공정하게 이루어졌는지 RUN 검정 실시
  • 가설검정
    • 귀무가설: 멤버십 소지 고객과 비소지 고객의 방문은 무작위로 이루어짐
    • 연구가설: 멤버십 소지 고객과 비소지 고객의 방문은 무작위로 이루어지지 않음
from statsmodels.sandbox.stats.runs import Runs
import numpy as np

x = [1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0]
x = np.array(x)

Runs(x).runs_test()
(-1.8277470669267506, 0.06758752074917526)
  • z값의 공식은 (RUN의 개수 - RUN의 평균) + 0.5 / RUN의 표준편차 (p.358) 참조
    • 평균 및 표준편차를 구하는 방식은 모수의 방식과 다르다 (주의)
  • -1.8은 z값을 의미하고, p 값은 0.067로 도출됨으로 유의수준 0.1 수준에서 유의하다.
    • 즉, 연구가설을 채택하는데, 이 말의 함의는 공정하게 이루어지지 않았음을 의미

(2) Kolmogorov-Smirnov 검정

  • 표본의 분포가 가정한 분포와 적합한지 검정 $$D_{n} = max|F_{n}(x) - F_{0}(x)$$

Python 통계 - 확률의 정의

강의 홍보

확률기초이론

  • 이산확률분포: 베르누이분포, 이항분포, 포아송분포
  • 연속확률분포: 정규분포, 카이제곱분포, t-분포, F-분포
  • 확률이란? 경험 또는 실험의 결과로 특정한 사건(event)이나 결과가 발생할 가능성
    • 예1) 주사위 던져서 1이 나올 가능성 1/6
    • 예2) 비가 올 가능성 30%

(1) 확률의 정의

  • 사건 A의 확률 = $\frac{n(A)}{N}$
    • N = 표본공간(=sample space) = 특정 실험에서 일어날 수 있는 모든 가능성

Python 통계 - T검정 예제

강의 홍보

통계분석을 활용한 문제해결 과정

  • 비즈니스에서 통계는 그저 툴이다.
    • 통계를 몰라도 물건을 파는데 전혀 문제가 없다.
  • 통계는 객관적인 근거를 확보하여 유효한 의사결정을 내리기 위한 그저 도구 (Tool) 이다.
  • 따라서, 마케팅이나 CRM과 같은 경영이슈에서도 통계는 문제해결을 이한 체계적인 절차를 제공한다.
    • 문제정의
    • 가설수립 및 분석방법 설정
    • 유의수준 및 임계치 설정
    • 분석 및 검정 통계량 산출
    • 결과 해석 및 가설 검증

예제를 활용한 통계분석 예제

  • 데이터 분석에서 가장 자주 사용되는 통계 기법 중의 하나는 t-검정이다.
  • 처음 t-검정을 배울 때는 비슷한 용어들이 많아서 혼동이 오기도 했다.
    • z-검정, t-검정, 분산분석
    • 이 중 2개 이하의 집단에서 평균을 비교하는 것은 z-검정, t-검정은 사실 동일한 분석방법이다.
    • 그러나, 실무에서는 t-검정을 자주 사용한다 (모집단의 분산을 알 수 있는 방법이 없다, 이는 자세히 한번 얘기하도록 하겠다)

(1) 문제 정의

  • 이제 부터 상상의 나래를 펼쳐보자.
    • 이제 온라인 쇼핑몰을 운영하는 사장이다.
    • 마케팅 부서에서는 콜센터를 통해 접수된 클레임 고객에 대한 타겟마케팅을 기획한다.
    • 클레임 고객은 상대적으로 매장을 찾는 횟수가 적어져 이탈위험도가 높을 것이라고 예상되기 때문이다.

(2) 가설설정 및 분석방법

  • 이제 가설 설정을 한다.
  • t검정을 실시할 때는 보통의 경우 평균의 차이는 없는 것으로 정한다.
    • $H_{0}$(귀무가설) = A쇼핑 클레임 고객들과 비클레임 고객들의 방문 횟수 차이는 없다.
    • $H_{1}$(연구가설) = A쇼핑 클레임 고객들과 비클레임 고객들의 방문횟수 차이는 있다.
  • 즉, 두 그룹간의 평균 비교이다.

(3) 데이터 수집 및 분석방법

  • 독립표본 t-검정을 수행하기 위해서는 평균과 등분산 여부, 그리고 t-value(검정 통계량)p-value(유의확률)을 출력한다.
    • 가장 중요한 것은 두 그룹간의 분산이 동일한지 확인할 필요가 있다.
  • 먼저 데이터를 불러온다.
# Mount Google Drive
from google.colab import drive # import drive from google colab

ROOT = "/content/drive"     # default location for the drive
print(ROOT)                 # print content of ROOT (Optional)
drive.mount(ROOT)           # we mount the google drive at /content/drive
/content/drive
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
# import join used to join ROOT path and MY_GOOGLE_DRIVE_PATH
from os.path import join  

# path to your project on Google Drive
MY_GOOGLE_DRIVE_PATH = 'My Drive/Colab Notebooks/inflearn/Python/Kaggle_Edu/05_statistics'

PROJECT_PATH = join(ROOT, MY_GOOGLE_DRIVE_PATH)
print(PROJECT_PATH)
/content/drive/My Drive/Colab Notebooks/inflearn/Python/Kaggle_Edu/05_statistics
%cd "{PROJECT_PATH}"
/content/drive/My Drive/Colab Notebooks/inflearn/Python/Kaggle_Edu/05_statistics
import pandas as pd
data = pd.read_csv('python_stat/Ashopping.csv', sep=",", encoding='CP949')
data.head()
고객ID 이탈여부 총_매출액 방문빈도 1회_평균매출액 할인권_사용 횟수 총_할인_금액 고객등급 구매유형 클레임접수여부 구매_카테고리_수 거주지역 성별 고객_나이대 거래기간 할인민감여부 멤버쉽_프로그램_가입전_만족도 멤버쉽_프로그램_가입후_만족도 Recency Frequency Monetary 상품_만족도 매장_만족도 서비스_만족도 상품_품질 상품_다양성 가격_적절성 상품_진열_위치 상품_설명_표시 매장_청결성 공간_편의성 시야_확보성 음향_적절성 안내_표지판_설명 친절성 신속성 책임성 정확성 전문성
0 1 0 4007080 17 235711 1 5445 1 4 0 6 6 1 4 1079 0 5 7 7 3 4 6 5 6 7 7 6 7.0 6.0 6 7 6 6 6 6 6 6 6 6
1 2 1 3168400 14 226314 22 350995 2 4 0 4 4 1 1 537 0 2 3 2 3 3 2 5 4 6 7 6 6.0 NaN 7 7 6 6 6 5 3 6 6 6
2 3 0 2680780 18 148932 6 186045 1 4 1 6 6 1 6 1080 0 6 6 7 3 2 4 6 7 6 7 6 7.0 NaN 6 6 6 6 6 7 7 6 6 7
3 4 0 5946600 17 349800 1 5195 1 4 1 5 5 1 6 1019 0 3 5 7 3 5 3 5 5 6 6 6 5.0 6.0 6 6 5 6 6 6 6 6 5 6
4 5 0 13745950 73 188301 9 246350 1 2 0 6 6 0 6 1086 0 5 6 7 6 7 5 6 6 5 6 6 5.0 6.0 5 6 6 6 5 5 6 6 5 6
  • 데이터를 확인했다면, 이제, 클레임 접수여부에 따라 클레임이 없는 (0)고객과, 클레임이 있는 고객(1) 객체에 저장후 두 그룹의 방문빈도를 추출한다.
  • 우선 클레임이 없는 고객을 뽑자.
import numpy as np
no_claim = data[data.클레임접수여부 == 0]
no_claim_array = np.array(no_claim.방문빈도)
no_claim_array
array([ 17,  14,  73,  26,   6,  17,  19,  88,  39,  12,  27,  21,  56,
        16,  21,  28,  17,  15,  48,  42,  33,   9,  32,  15,  23,  24,
        18,  18,  38,  15,  16,  36,   8,  33,  41,  34,  12,  56,  60,
        27,  10,  39,  48,  24,   8,   7,  25,  19,  33,  22,  30,   9,
        80,  18,  15,  50,  71,  15,  66,  41,  18,   5,  78,  30,  14,
        10,  54,  21,  43,  33,  24,  21,  13,  25,  37,  25,  47,  60,
        21,  23,  18,  26,  28,  31,  58,  88,  19,  38,  37,  20,  33,
        18,  15,   7,  29,  30,   6,  16,  10,  23,  10,  14,  18,  11,
        15,  14,  90,   9,   9,   5,  15,  11,  33,   9,  20,   6,  42,
        17,  78,   9,  20,  37,  14,  17,  25,  52,  19,   9,  26,  16,
        49,  32,  24,  40,  44,  13,  16,  25,  16,  47,  27,   9,  15,
         5,  26,  34,  39,  18,  28,  16,  32,  81,  15,  41,   9,  10,
         9,   8,  56,  12,  65,  10,  45,  60,  22,  52,  15,  61,  18,
        12,  23,  93,  28,  12,  39, 103,  11,  28,   6,  35,   6,  86,
        17, 185,  16,  37,  21,  22,  50,  37,  14,  15,  28,  14,  11,
        31,  31,  59,  26,  20,  29,  23,  11,  29,  11, 108,   6,  37,
        29,  12,  11,  10,  18,  36,  73,  18,  22,  14,  22,  13,  31,
        15,  31,  25,  52,  10,  35,   9,   9,   5,  27,  12,  79,  21,
         8,  11,  64,  22,  60,  31,  15,  48,  18,   8,  31,  14,  46,
       102,  10,  34,   4,  23,  43,  45,   9,  18,  26,  12,   8,  81,
        51,  14,  28,  18,  24,  28,  15,  29,   8,   5,  17,  16,  28,
        36,  27,  11,  10,  20,  13,  54,  23,  14,  27,  13,  38,  38,
        79,  27,  97,   8,  10,  41,   6,  19,  16,  21,  46,  23,  39,
        38,  19,   7,   6,  33,  61,  15,  10, 114,  71,  33,  25,  79,
        13,  29,  34,  30,  19,  23,  40,  15,  17,   8,   3,   7,  21,
         9,  40,  46,  24,  73,  38,  56,  13,  13,  30,  46,   6,  30,
        19,   5,  20,  23,   7,  20,  26,  15,  58,  15,  11,  31,  17,
        10,  14,  20,  10,  50,  21,  37,  30,   6,  16,  23,  21, 102,
        26,  43,   8,  15,  10,  14,  71,  60,  45,  25,  49,  50,   9,
        18,  23,  29, 106,  35,  22,  12, 203,  12,  17,  14,  39,  11,
        19,  29,  22,  21,   9,  22,  11,  21,  58,  10,   5,  16,  39,
        13,  33,  13,  14,  13,  18,  42,  11,  29,  28,  35,   9,  21,
        26,  17,  24,   5,  23,  71,  22,  20,  20,   7,  14,  12,  10,
        16,  18,  30,  25,  22,  15,  18,  43,  33,  46,  14,  12,  24,
        23,  18,  23,   9,  13,  17,  22,   7,  18,  15,  39,  22,  22,
        22,   9,  15,  36,  24,  32,  38, 109,  28,  23,  20,  76,  43,
        25,   5,  46,  10,  22,  18,  30,  75,  34,  11,  17,  43,  39,
        84,  42,  41,  26,  32,  31,  37,  15,  50,  22,  19,   6,  19,
        11,   7,  25,  63,  21,  16,  67,  27,   5,  32,  31, 114,  20,
        21,  14,  80,  19,  14,  32,   4,  15,  37,  24,  14,  11,  10,
        20,  30,  10,  19,   6,  26,  26,   9,   4,  66,  16,  24,  29,
        33,  20,  19,  20,  49,  10,  15,  23])
  • 두번째, 클레임이 있는 고객을 뽑아본다.
claim = data[data.클레임접수여부 == 1]
claim_array = np.array(claim.방문빈도)
claim_array
array([ 18,  17, 109,  15,  21,   9,  12,  28,   5,  12,  12,  13,   8,
        18,  27,  34,  23,  19,  29,  10,  19,  28,  23,  30,  14,  20,
         7,  14,  32,  32,  18,  17,  10,  23,  12,   9,  43,  42,   7,
        12,  73,  26,  27,  25,  29,  37,  32,  45,  34,   7,  29,  26,
        27,  33,  27,  28,   8,  45,  66,  14,  73,  24,  13,  54,  24,
        17,  29,  29,  57,  18,  17,  27,  15,  23,  11,   4,  15,  31,
        21,  21,  11,  19,  34,  41,  18,  45,  10,  72,  18,  82,  10,
        26,  17,  37,  17,   8,  26,  11,  28,  17,  12,  23,  37,  19,
         7,  20,   9,  14,  13,  36,  26,  23,  23,  16,  30,  13,  45,
        30,   6,  44,  32,  19,  20,  24,   7,  14,   7,  65,  31,   6,
        37,  91,  17,  12,  23,  61, 162,  23,  21,  12,  14,  10,  18,
        14,   4,   6,  26,  19,  27,  14,   7,  32,  11,  11,  20,   7,
         7,   7,  27,  16,  26,  10,  12,   8,  12,  25,  14,  18,  30,
        33,  86,  20,  22,  10,  34,  36,   9,  68,  28,  14,  61,   9,
        17,   6,  30,  14,  25,  32,  19,  34,  26,  17,  24,  18,  30,
        15,  15,  96,  17,  18,  19,  24,  16,   6,  27,  31,  15,  38,
        11,   7,  65,  15,   7,  23,   7,  15,  34,  17,  20,  19,  10,
        14,  19,  69,   4,  21,  13,  20,  45,  50,  58,   6,  15,  10,
        10,  19,  32,   6,  38,  36,   6,  39,  16,  20,  48,  26,  21,
        22,   7,   8,  28,   8,  31,  13,  46,  20,   8,  49,   9,  48,
        20,  25,  54,  21,   6,  30,  40,  11,  12,  28,  15,  24,  90,
        22,  15,  14,   7,  77,  39,  11,  18,  16,  55,  15,  12,  17,
        23,  15,  33,  15,  18,  28,   7,  17,  34,  27,  44,  35,  67,
        12,  54,  18,  10,  21,  28,  67,   9, 126,  23,  10,  41,  15,
        21,  21,  42,  18,  31,  13,  20,  19,  39,  26,  11,  22,  64,
         4,  27,  21,  14,  30,  25,  18,  11,  10,  70,  24,  18,  19,
        30,  30,  19, 104,  39,  92,  52,  48,  30,  79,  15,  24,  23,
        14,   8,   9,  21,  18,  11,   7,   8,  13,  21,  23,   2,  12,
        12,  52,  22,  20,   6,  22,   6,  24,  12,  18,  20,  11,  12,
        33,   8,  24,  79,  34,   8,  36,  13,  19,   5,  12,  25,  31,
        27,  17,  11,  65,  29,  18,  10,  28,  22,  12,  18,  20,  27,
        17,  20,  17,   8,  38,  18,  25,  10,   7,  18,  11,  55,   5,
        20,  11,  41,  12,  11,  26,  55,  25,   6,  35,  38,  32,  27,
        11,  10,  11,  55,  22,  12,  18,  10,  15,  32,  13,  29,   8,
        17,  83,  15,   7,   8,  11,  27,  21,  18,  15,  19,   8,  16,
        35,  15,  40,   8])
  • 그 후, stats.bartlett() 함수를 이용하여 등분산 검정을 실시한다.
  • 이 때, 주의해야 하는 것이 있다.
  • 등분산 검정의 귀무가설과 대립가설을 다음과 같다.
    • H0 : 등분산이다.
    • H1 : 이분산이다(등분산이 아니다).
  • 즉, 등분산 가정의 귀무가설을 채택하려면 유의확률인 0.05 > p 나와야 한다.
from scipy import stats
stats.bartlett(no_claim_array, claim_array)
BartlettResult(statistic=13.626177910965525, pvalue=0.00022305349806448475)
  • 여기에서 두 고객 그룹간의 등분산 검정 결과 F값은 13.626, 유의확률은 0.05미만으로 귀무가설이 기각된다.
    • 즉, 두 집단의 분산은 동일하지 않은 것으로 나타났다.
  • 등분산성이 나와야 하는데, 나오지 않아서 입문자들이 조금 당혹스러워 할 수 있다.
    • 굳이 당황할 필요는 없다. 분산이 동일하지 않으면 동일하지 않다고 표시만 해두면 된다. (check: equal_var)
  • 이제 ttest_ind를 활용해서 구하도록 하자.
print(stats.ttest_ind(no_claim_array, claim_array, equal_var=False))
Ttest_indResult(statistic=2.595726838875684, pvalue=0.009577734932789503)
  • 독립표본 t-value2.59이며, p-value 0.0095로 나왔다.
    • 이 의미는 두 그룹간의 평균 방문 빈도에 차이가 있음을 의미한다.
  • 조금 더 구체적으로 구해보자.

(4) 시각화

  • 실제 두 그룹간의 차이를 matplotlib을 활용하여 시각화로 구현해본다.
  • 이 때, 각 그룹의 평균(Mean)표준편차(Standard Deviation)를 구한다.
# 평균 계산하기
no_claim_mean = np.mean(no_claim_array)
claim_mean = np.mean(claim_array)
print("클레임이 없는 고객의 평균 방문빈도:", no_claim_mean)
print("클레임이 있는 고객의 평균 방문빈도:", claim_mean)

# 표준편차 구하기
no_claim_std = np.std(no_claim_array)
claim_std = np.std(claim_array)
print("클레임이 없는 고객의 표준편차:", no_claim_std)
print("클레임이 있는 고객의 표준편차:", claim_std)

# 라벨 정리
viz_labels = ['No Claim', 'Claim']
x_pos = np.arange(len(viz_labels))
avg = (no_claim_mean, claim_mean)
error = [no_claim_std, claim_std]
클레임이 없는 고객의 평균 방문빈도: 28.184842883548985
클레임이 있는 고객의 평균 방문빈도: 24.736383442265794
클레임이 없는 고객의 표준편차: 22.7348095052013
클레임이 있는 고객의 표준편차: 19.234427104778828
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.bar(x_pos, avg,
       yerr = error,
       align='center',
       alpha=0.5,
       ecolor='black',
       capsize=10)
ax.set_ylabel('Avg. Customer Visitation')
ax.set_xticks(x_pos)
ax.set_xticklabels(viz_labels)
ax.set_title('The Customer difference between No Claim and Claim')
ax.yaxis.grid(True)

plt.tight_layout()
plt.show()

png

삼성카드 대회 Track-2 - 포지셔닝 분석(2)

대회 소개

  • 삼성카드 데이터분석 공모전이 시행되고 있다.
    • 대회에 처음 참여하는 아시아경제-수강생들을 위해 일종의 가이드라인으로 제안하고자 한다.
  • 본 포스트에서는 기본적인 내용만 전달하고자 함을 밝힌다.
    • Track2 과정은 마케팅 전략 제안이 중요하다!

포지셔닝 분석 개요

  • 마케팅에서 자주 보는 분석 방법중의 하나는 포지셔닝(Positioning) 기법이다.
  • 포지셔닝 분석은 마케팅 통계분석 기법중의 하나로, 기업이나, 상품, 브랜드 같은 개체들의 포지셔닝을 수행하는 다차원 척도법(MDS: Multi-Dimensional Scaling)과 상응분석(Correspondence Analysis)이 있다.
  • 위 두가지 분석 방법 중 무엇을 사용해야 할까?
    • 만약 데이터셋이 주로 등간척도, 비율척도와 같이 구성되어 있다면 다차원 척도법
    • 만약 데이터셋이 주로 명목척도, 서열척도와 같이 구성되어 있다면 상응분석
  • 현재 삼성카드 대회의 주 데이터셋은 명목척도 및 서열척도로 구성되어 있기 때문에 상응분석으로 시작하면 된다.

상응분석

(1) 기본 개념

  • 상응분석을 사용하려면 빈도교차표를 만들어야 한다.
  • 요약하면, 상응분석은 범주형 변수의 빈도를 나타내고 있는 빈도교차표의 행과 열(명목변수의 범주 값들)을 그래프상의 자극점 형태로 표시하는 방법.
  • 이 때, 단순 상응분석은 2개의 변수, 다중 상응분석은 3개 이상의 변수 활용한다.
  • 이 때, 상응분석은 카이제곱 검정과 같이 범주형 변수간의 상호연관성을 바탕으로 진행된다.
    • 따라서, 두 개의 범주형 변수가 서로 연관성을 가지고 있다는 전제하에서 진행된다.

(2) 데이터 불러오기

  • 먼저 필요한 데이터를 불러온다.
  • 필자는 구글 코랩에서 데이터를 불러오기 때문에
%config InlineBackend.figure_format = 'retina'
!sudo apt-get -qq -y install fonts-nanum
The following package was automatically installed and is no longer required:
  libnvidia-common-440
Use 'apt autoremove' to remove it.
The following NEW packages will be installed:
  fonts-nanum
0 upgraded, 1 newly installed, 0 to remove and 39 not upgraded.
Need to get 9,604 kB of archives.
After this operation, 29.5 MB of additional disk space will be used.
Selecting previously unselected package fonts-nanum.
(Reading database ... 144579 files and directories currently installed.)
Preparing to unpack .../fonts-nanum_20170925-1_all.deb ...
Unpacking fonts-nanum (20170925-1) ...
Setting up fonts-nanum (20170925-1) ...
Processing triggers for fontconfig (2.12.6-0ubuntu2) ...
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.font_manager as fm
/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm
for font in fm.fontManager.ttflist:
    if 'Nanum' in font.name:
        print(font.name, font.fname)
NanumGothic /usr/share/fonts/truetype/nanum/NanumGothic.ttf
NanumSquareRound /usr/share/fonts/truetype/nanum/NanumSquareRoundR.ttf
NanumSquare /usr/share/fonts/truetype/nanum/NanumSquareR.ttf
NanumSquareRound /usr/share/fonts/truetype/nanum/NanumSquareRoundB.ttf
NanumBarunGothic /usr/share/fonts/truetype/nanum/NanumBarunGothicBold.ttf
NanumMyeongjo /usr/share/fonts/truetype/nanum/NanumMyeongjo.ttf
NanumMyeongjo /usr/share/fonts/truetype/nanum/NanumMyeongjoBold.ttf
NanumBarunGothic /usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf
NanumSquare /usr/share/fonts/truetype/nanum/NanumSquareB.ttf
NanumGothic /usr/share/fonts/truetype/nanum/NanumGothicBold.ttf
fontpath = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
font = fm.FontProperties(fname=fontpath, size=9)
plt.rc('font', family='NanumGothic') 
plt.rcParams["figure.figsize"] = (20, 10)
plt.rcParams['lines.linewidth'] = 2
plt.rcParams['lines.color'] = 'r'
plt.rcParams['axes.grid'] = True 
mpl.font_manager._rebuild()
fm._rebuild()
  • 한글 텍스트가 잘 나오는지 확인해본다.
font = {'family' : 'NanumGothic',
        'weight' : 'bold',
        'size'   : 22}

plt.rc('font', **font)
plt.text(0.3, 0.3, '한글', size=100)
Text(0.3, 0.3, '한글')

png

삼성카드 대회 Track-2 - 포지셔닝 분석(1)

대회 소개

  • 삼성카드 데이터분석 공모전이 시행되고 있다.
    • 대회에 처음 참여하는 아시아경제-수강생들을 위해 일종의 가이드라인으로 제안하고자 한다.
  • 본 포스트에서는 기본적인 내용만 전달하고자 함을 밝힌다.
    • Track2 과정은 마케팅 전략 제안이 중요하다!

포지셔닝 분석 개요

  • 마케팅에서 자주 보는 분석 방법중의 하나는 포지셔닝(Positioning) 기법이다.
  • 포지셔닝 분석은 마케팅 통계분석 기법중의 하나로, 기업이나, 상품, 브랜드 같은 개체들의 포지셔닝을 수행하는 다차원 척도법(MDS: Multi-Dimensional Scaling)과 상응분석(Correspondence Analysis)이 있다.
  • 위 두가지 분석 방법 중 무엇을 사용해야 할까?
    • 만약 데이터셋이 주로 등간척도, 비율척도와 같이 구성되어 있다면 다차원 척도법
    • 만약 데이터셋이 주로 명목척도, 서열척도와 같이 구성되어 있다면 상응분석
  • 현재 삼성카드 대회의 주 데이터셋은 명목척도 및 서열척도로 구성되어 있기 때문에 상응분석으로 시작하면 된다.

상응분석

  • Correspondence Analysis는 범주형 변수(수준)들 간의 연관성을 분석한 후, 그 결과를 시각적 해석이 용이하도록 그래프화 하는 것임

(1) 기본 개념

  • 상응분석을 사용하려면 빈도교차표를 만들어야 한다.
  • 요약하면, 상응분석은 범주형 변수의 빈도를 나타내고 있는 빈도교차표의 행과 열(명목변수의 범주 값들)을 그래프상의 자극점 형태로 표시하는 방법.
  • 이 때, 단순 상응분석은 2개의 변수, 다중 상응분석은 3개 이상의 변수 활용한다.
  • 이 때, 상응분석은 카이제곱 검정과 같이 범주형 변수간의 상호연관성을 바탕으로 진행된다.
    • 따라서, 두 개의 범주형 변수가 서로 연관성을 가지고 있다는 전제하에서 진행된다.

(2) 데이터 불러오기

  • 먼저 필요한 데이터를 불러온다.
  • 필자는 구글 코랩에서 데이터를 불러오기 때문에
%config InlineBackend.figure_format = 'retina'
!sudo apt-get -qq -y install fonts-nanum
The following package was automatically installed and is no longer required:
  libnvidia-common-440
Use 'apt autoremove' to remove it.
The following NEW packages will be installed:
  fonts-nanum
0 upgraded, 1 newly installed, 0 to remove and 39 not upgraded.
Need to get 9,604 kB of archives.
After this operation, 29.5 MB of additional disk space will be used.
Selecting previously unselected package fonts-nanum.
(Reading database ... 144579 files and directories currently installed.)
Preparing to unpack .../fonts-nanum_20170925-1_all.deb ...
Unpacking fonts-nanum (20170925-1) ...
Setting up fonts-nanum (20170925-1) ...
Processing triggers for fontconfig (2.12.6-0ubuntu2) ...
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.font_manager as fm
/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm
for font in fm.fontManager.ttflist:
    if 'Nanum' in font.name:
        print(font.name, font.fname)
NanumGothic /usr/share/fonts/truetype/nanum/NanumGothicBold.ttf
NanumBarunGothic /usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf
NanumBarunGothic /usr/share/fonts/truetype/nanum/NanumBarunGothicBold.ttf
NanumSquare /usr/share/fonts/truetype/nanum/NanumSquareB.ttf
NanumMyeongjo /usr/share/fonts/truetype/nanum/NanumMyeongjoBold.ttf
NanumSquareRound /usr/share/fonts/truetype/nanum/NanumSquareRoundB.ttf
NanumSquare /usr/share/fonts/truetype/nanum/NanumSquareR.ttf
NanumMyeongjo /usr/share/fonts/truetype/nanum/NanumMyeongjo.ttf
NanumSquareRound /usr/share/fonts/truetype/nanum/NanumSquareRoundR.ttf
NanumGothic /usr/share/fonts/truetype/nanum/NanumGothic.ttf
fontpath = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
font = fm.FontProperties(fname=fontpath, size=9)
plt.rc('font', family='NanumGothic') 
plt.rcParams["figure.figsize"] = (20, 10)
plt.rcParams['lines.linewidth'] = 2
plt.rcParams['lines.color'] = 'r'
plt.rcParams['axes.grid'] = True 
mpl.font_manager._rebuild()
fm._rebuild()
  • 한글 텍스트가 잘 나오는지 확인해본다.
font = {'family' : 'NanumGothic',
        'weight' : 'bold',
        'size'   : 22}

plt.rc('font', **font)
plt.text(0.3, 0.3, '한글', size=100)
Text(0.3, 0.3, '한글')

png

Python 통계 - 포지셔닝 분석(2)

강의 홍보

포지셔닝 분석 개요

  • 마케팅에서 자주 보는 분석 방법중의 하나는 포지셔닝(Positioning) 기법이다.
  • 포지셔닝 분석은 마케팅 통계분석 기법중의 하나로, 기업이나, 상품, 브랜드 같은 개체들의 포지셔닝을 수행하는 다차원 척도법(MDS: Multi-Dimensional Scaling)과 상응분석(Correspondence Analysis)이 있다.
  • 위 두가지 분석 방법 중 무엇을 사용해야 할까?
    • 만약 데이터셋이 주로 등간척도, 비율척도와 같이 구성되어 있다면 다차원 척도법
    • 만약 데이터셋이 주로 명목척도, 서열척도와 같이 구성되어 있다면 상응분석
  • 현재 삼성카드 대회의 주 데이터셋은 명목척도 및 서열척도로 구성되어 있기 때문에 상응분석으로 시작하면 된다.

상응분석

  • Correspondence Analysis는 범주형 변수(수준)들 간의 연관성을 분석한 후, 그 결과를 시각적 해석이 용이하도록 그래프화 하는 것임

(1) 기본 개념

  • 상응분석을 사용하려면 빈도교차표를 만들어야 한다.
  • 요약하면, 상응분석은 범주형 변수의 빈도를 나타내고 있는 빈도교차표의 행과 열(명목변수의 범주 값들)을 그래프상의 자극점 형태로 표시하는 방법.
  • 이 때, 단순 상응분석은 2개의 변수, 다중 상응분석은 3개 이상의 변수 활용한다.
  • 이 때, 상응분석은 카이제곱 검정과 같이 범주형 변수간의 상호연관성을 바탕으로 진행된다.
    • 따라서, 두 개의 범주형 변수가 서로 연관성을 가지고 있다는 전제하에서 진행된다.

(2) 데이터 불러오기

  • 먼저 필요한 데이터를 불러온다.
  • 필자는 구글 코랩에서 데이터를 불러오기 때문에
%config InlineBackend.figure_format = 'retina'
!apt -qq -y install fonts-nanum
fonts-nanum is already the newest version (20170925-1).
The following package was automatically installed and is no longer required:
  libnvidia-common-440
Use 'apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 39 not upgraded.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.font_manager as fm
/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm
for font in fm.fontManager.ttflist:
    if 'Nanum' in font.name:
        print(font.name, font.fname)
NanumMyeongjo /usr/share/fonts/truetype/nanum/NanumMyeongjo.ttf
NanumSquare /usr/share/fonts/truetype/nanum/NanumSquareR.ttf
NanumSquare /usr/share/fonts/truetype/nanum/NanumSquareB.ttf
NanumBarunGothic /usr/share/fonts/truetype/nanum/NanumBarunGothicBold.ttf
NanumMyeongjo /usr/share/fonts/truetype/nanum/NanumMyeongjoBold.ttf
NanumSquareRound /usr/share/fonts/truetype/nanum/NanumSquareRoundR.ttf
NanumSquareRound /usr/share/fonts/truetype/nanum/NanumSquareRoundB.ttf
NanumBarunGothic /usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf
NanumGothic /usr/share/fonts/truetype/nanum/NanumGothic.ttf
NanumGothic /usr/share/fonts/truetype/nanum/NanumGothicBold.ttf
fontpath = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
font = fm.FontProperties(fname=fontpath, size=9)
plt.rc('font', family='NanumGothic') 
plt.rcParams["figure.figsize"] = (20, 10)
plt.rcParams['lines.linewidth'] = 2
plt.rcParams['lines.color'] = 'r'
plt.rcParams['axes.grid'] = True 
mpl.font_manager._rebuild()
fm._rebuild()
font = {'family' : 'NanumGothic',
        'weight' : 'bold',
        'size'   : 22}

plt.rc('font', **font)
plt.text(0.3, 0.3, '한글', size=100)
Text(0.3, 0.3, '한글')

png

Python 통계 - 포지셔닝 분석(1)

강의 홍보

포지셔닝 분석 개요

  • 마케팅에서 자주 보는 분석 방법중의 하나는 포지셔닝(Positioning) 기법이다.
  • 포지셔닝 분석은 마케팅 통계분석 기법중의 하나로, 기업이나, 상품, 브랜드 같은 개체들의 포지셔닝을 수행하는 다차원 척도법(MDS: Multi-Dimensional Scaling)과 상응분석(Correspondence Analysis)이 있다.
  • 위 두가지 분석 방법 중 무엇을 사용해야 할까?
    • 만약 데이터셋이 주로 등간척도, 비율척도와 같이 구성되어 있다면 다차원 척도법
    • 만약 데이터셋이 주로 명목척도, 서열척도와 같이 구성되어 있다면 상응분석
  • 현재 삼성카드 대회의 주 데이터셋은 명목척도 및 서열척도로 구성되어 있기 때문에 상응분석으로 시작하면 된다.

상응분석

  • Correspondence Analysis는 범주형 변수(수준)들 간의 연관성을 분석한 후, 그 결과를 시각적 해석이 용이하도록 그래프화 하는 것임

(1) 기본 개념

  • 상응분석을 사용하려면 빈도교차표를 만들어야 한다.
  • 요약하면, 상응분석은 범주형 변수의 빈도를 나타내고 있는 빈도교차표의 행과 열(명목변수의 범주 값들)을 그래프상의 자극점 형태로 표시하는 방법.
  • 이 때, 단순 상응분석은 2개의 변수, 다중 상응분석은 3개 이상의 변수 활용한다.
  • 이 때, 상응분석은 카이제곱 검정과 같이 범주형 변수간의 상호연관성을 바탕으로 진행된다.
    • 따라서, 두 개의 범주형 변수가 서로 연관성을 가지고 있다는 전제하에서 진행된다.

(2) 데이터 불러오기

  • 먼저 필요한 데이터를 불러온다.
  • 필자는 구글 코랩에서 데이터를 불러오기 때문에
%config InlineBackend.figure_format = 'retina'
!apt -qq -y install fonts-nanum
fonts-nanum is already the newest version (20170925-1).
The following package was automatically installed and is no longer required:
  libnvidia-common-440
Use 'apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 39 not upgraded.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.font_manager as fm
/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm
for font in fm.fontManager.ttflist:
    if 'Nanum' in font.name:
        print(font.name, font.fname)
NanumMyeongjo /usr/share/fonts/truetype/nanum/NanumMyeongjoBold.ttf
NanumSquareRound /usr/share/fonts/truetype/nanum/NanumSquareRoundR.ttf
NanumSquare /usr/share/fonts/truetype/nanum/NanumSquareB.ttf
NanumGothic /usr/share/fonts/truetype/nanum/NanumGothicBold.ttf
NanumMyeongjo /usr/share/fonts/truetype/nanum/NanumMyeongjo.ttf
NanumGothic /usr/share/fonts/truetype/nanum/NanumGothic.ttf
NanumBarunGothic /usr/share/fonts/truetype/nanum/NanumBarunGothicBold.ttf
NanumSquareRound /usr/share/fonts/truetype/nanum/NanumSquareRoundB.ttf
NanumBarunGothic /usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf
NanumSquare /usr/share/fonts/truetype/nanum/NanumSquareR.ttf
fontpath = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
font = fm.FontProperties(fname=fontpath, size=9)
plt.rc('font', family='NanumGothic') 
plt.rcParams["figure.figsize"] = (20, 10)
plt.rcParams['lines.linewidth'] = 2
plt.rcParams['lines.color'] = 'r'
plt.rcParams['axes.grid'] = True 
mpl.font_manager._rebuild()
fm._rebuild()
font = {'family' : 'NanumGothic',
        'weight' : 'bold',
        'size'   : 22}

plt.rc('font', **font)
plt.text(0.3, 0.3, '한글', size=100)
Text(0.3, 0.3, '한글')

png

ch 13 - Reliability

Intro

  • PLS-SEM의 분석과정에서 척도(측정변수와 잠재변수)의 신뢰도와 타당도를 확보하는 것은 매우 중요하며, 신뢰도와 타당도가 확보되지 않으면 모델 추정 결과가 의미가 없기 때문임
  • 즉, 구조모델의 추정을 실행하려면 사전에 반드시 측정모델에 대한 평가과정을 통해 신뢰도와 타당도 확보 필요

I. 주요 개념

(1) 신뢰도

  • 잠재변수의 측정에 있어서 얼마나 일관성이 있는가의 정도 의미
    • 검사도구의 일관성을 말하며, 일관성이란 잠재변수를 여러 번에 걸쳐 측정했을 때 매번 같은 결과를 도출할 수 있는 정도.
    • 내적 일관성 신뢰(Internal Consistency Reliability)로 평가

(2) 타당도

  • 타당도의 기본 정의는 실제 측정하고자 하는 잠재변수를 정확하게 측정하고 있는 정도
    • PLS-SEM에서는 집중타당도(Convergent Validity)와 판별타당도(Discriminant Validity)를 사용한다.
    • 전자는 하나의 잠재변수를 측정하기 위해 사용되는 척도의 구성항목들 간에 상관관계가 높아야 집중타당도가 있다고 볼 수 있고, 후자는 하나의 잠재변수와 다른 잠재변수간 상관관계가 낮을수록 판별 타당도가 높다고 판단함.

(3) PLS-SEM 분석 결과의 쳬계적인 평가 단계

  • 반영적 측정모델: 내적 일관성 신뢰도, 집중타당도, 판별타당도
  • 형성적 측정모델: 집중타당도, 다중공선성, 외부가중치와 외부적재치의 유의성과 적합성
  • 구조모델의 평가기준: 다중공선성, 결정계수 $R^2$, 효과크기 $f^2$, 예측적 적합성 $Q^2$, 경로계수의 유의성과 적합성
  • PLS-SEM의 평가 단계: 제 1단계는 측정모델(Outer Model)을 평가하는 것이며, 제 2단계는 구조모델(Inner Model)을 평가하는 것이다.

II. 설문조사 데이터 분석

  • 이제 설문지를 분석해본다.
  • 필수 패키지를 확인한다.
library(readr) 
library(dplyr)
library(kableExtra) 
library(psy) # 신뢰도
library(corrplot) # 상관계수
library(psychometric) # 타당도 

(1) 데이터 수집

  • 먼저 수집된 설문조사 데이터를 확인한다.
data <- read_csv('data/thesis_mater.csv') %>% 
  distinct() %>% # 중복데이터 제거
  rename(Position = founder_employee, # 출력을 위한 변수명 정리
         Age = age_of_respondent, 
         Education = Education_Level) %>% 
  slice(-c(1:10)) %>% 
  dplyr::select(-c(Firm_Age:Business_Area))

data %>% 
  head() %>% 
  kable() %>% 
  kable_styling("striped") %>% 
  scroll_box(width = "100%")
EI_1 EI_2 EI_3 EP_1 EP_2 EP_3 ER_1 ER_2 ER_3 SS_1 SS_2 SS_3 SC_1 SC_2 SC_3 SR_1 SR_2 SR_3 F1 F2 F3 NF1 NF2 NF3 Firm_Age Firm_Size WE1 WE2 WE3 gender founder_employee age_of_respondent Education_Level Business_Area
2 3 4 3 3 4 3 2 4 1 1 3 3 3 3 2 2 1 2 2 3 3 1 3 5 years above Above 15 members No, I don't have experience Yes Yes Female Employee 30-39 Undergraduate School Others
5 5 2 3 5 3 4 4 4 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 Less than 2 years Less than 5 members No, I don't have experience No Yes Male Employee Younger than 30 Undergraduate School Media and Entertainment
1 2 2 1 1 2 1 2 1 2 2 1 1 2 2 1 2 1 2 1 1 1 1 1 5 years above Less than 5 members As founder or employee, I have startup experiences more than 3 times No Yes Female Founder of Company Younger than 30 Undergraduate School Others
3 3 2 1 2 1 2 1 3 2 1 3 1 1 1 2 3 3 3 3 2 3 2 2 Less than 2 years Less than 5 members No, I don't have experience Yes Yes Male Employee Younger than 30 Undergraduate School Others
5 3 5 2 5 4 4 4 4 4 5 4 5 5 5 5 5 5 4 5 4 4 5 5 3-4 years Less than 5 members As founder or employee, I have startup experiences more than 3 times No Yes Male Founder of Company 30-39 Undergraduate School Others
1 3 3 1 3 3 2 3 1 4 1 2 3 3 1 2 2 1 1 2 3 1 3 1 5 years above 5-9 members As founder or employee, I have startup experience, one time No No Female Employee Younger than 30 Undergraduate School Others

(2) 상관관계 확인

  • 각 척도(Item)에서의 상관관계를 확인해본다.
M <- cor(data)

corrplot(M, type="upper", order="hclust", 
         col=RColorBrewer::brewer.pal(n=8, name="RdBu"))

ch 12 - Demographic of Respondent in R

Intro

  • 지난 시간에 설문조사 전처리에 대해 배웠다면 이번에는 경영/사회과학 논문에서 필수적으로 기재해야 하는 표본의 특성을 간단한 프로그램으로 요약하는 것을 코딩한다.

(1) 주요 패키지

  • 이번 포스트부터 gt 패키지를 사용하려고 한다.
    • gt: ggplot2와 같이 Table를 문법으로 컨트롤 할 수 있도록 구현된 패키지이다.
    • kableExtra: HTML로 출력할 수 있도록 도와주는 패키지이다.
library(readr)
library(dplyr)
library(gt)
library(gtsummary)

I. 데이터 가져오기

  • 우선 데이터를 불러온다.
data <- read_csv('data/thesis_mater.csv') %>% 
  distinct() %>% # 중복데이터 제거
  rename(Position = founder_employee, # 출력을 위한 변수명 정리
         Age = age_of_respondent, 
         Education = Education_Level)
glimpse(data %>% select(Firm_Age:Business_Area))
  • 전체 34개의 변수 중에서, 문자열 관련 데이터만 추출하였다.
  • 어떤 데이터를 표본의 특성으로 삼아야 할까?
    • 위 10개의 데이터에는 통제변수1가 들어가 있다.
    • 통제변수는 표본의 특징이 아니기 때문에 통제변인을 제외한 나머지 변수들을 추출한다.
## Rows: 103
## Columns: 10
## $ Firm_Age      <chr> "5 years above", "Less than 2 years", "5 years above", …
## $ Firm_Size     <chr> "Above 15 members", "Less than 5 members", "Less than 5…
## $ WE1           <chr> "No, I don't have experience", "No, I don't have experi…
## $ WE2           <chr> "Yes", "No", "No", "Yes", "No", "No", "No", "No", "No",…
## $ WE3           <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "No", "Yes", "No", "…
## $ gender        <chr> "Female", "Male", "Female", "Male", "Male", "Female", "…
## $ Position      <chr> "Employee", "Employee", "Founder of Company", "Employee…
## $ Age           <chr> "30-39", "Younger than 30", "Younger than 30", "Younger…
## $ Education     <chr> "Undergraduate School", "Undergraduate School", "Underg…
## $ Business_Area <chr> "Others", "Media and Entertainment", "Others", "Others"…
  • 표본의 특성을 기술하는 데이터는 아래와 같이 추출한다.
    • gender, founder_employee, age_of_respondent, educational_level, business_area
data2 <- data %>% 
  select(gender, Position, Age, Education, Business_Area)

glimpse(data2)
## Rows: 103
## Columns: 5
## $ gender        <chr> "Female", "Male", "Female", "Male", "Male", "Female", "…
## $ Position      <chr> "Employee", "Employee", "Founder of Company", "Employee…
## $ Age           <chr> "30-39", "Younger than 30", "Younger than 30", "Younger…
## $ Education     <chr> "Undergraduate School", "Undergraduate School", "Underg…
## $ Business_Area <chr> "Others", "Media and Entertainment", "Others", "Others"…

II. 표본 특성 표 출력

  • 보통 논문에 들어가는 표본의 특징은 Category, Frequency, and Percentage(%) 정도만 필요하다.
  • 이 때, Table을 가공해줄 수 있는 gtsummary 패키지를 활용한다.
set_gtsummary_theme(theme_gtsummary_journal(journal = "jama"))

data2 %>% 
  tbl_summary(by = gender) %>% 
  add_overall() %>% 
  add_n() %>% 
  modify_header(label = "**Variable**") %>% # update the column header
  bold_labels()
Variable N Overall, N = 103 Female, N = 621 Male, N = 411
Position 103
Employee 68 (66) 35 (56) 33 (80)
Founder of Company 35 (34) 27 (44) 8 (20)
Age 103
30-39 37 (36) 19 (31) 18 (44)
40-49 8 (7.8) 4 (6.5) 4 (9.8)
50 or above 2 (1.9) 2 (3.2) 0 (0)
Younger than 30 56 (54) 37 (60) 19 (46)
Education 103
Graduate School 25 (24) 15 (24) 10 (24)
High School 7 (6.8) 6 (9.7) 1 (2.4)
Undergraduate School 71 (69) 41 (66) 30 (73)
Business_Area 103
E-Commerce 16 (16) 11 (18) 5 (12)
Education 4 (3.9) 2 (3.2) 2 (4.9)
Energy 1 (1.0) 0 (0) 1 (2.4)
Enterprise Services 4 (3.9) 2 (3.2) 2 (4.9)
Fintech 9 (8.7) 6 (9.7) 3 (7.3)
Logistics 5 (4.9) 1 (1.6) 4 (9.8)
Manufacturing 3 (2.9) 2 (3.2) 1 (2.4)
Media and Entertainment 7 (6.8) 4 (6.5) 3 (7.3)
Medical and Healthcare 1 (1.0) 1 (1.6) 0 (0)
Online to Offline Commerce 2 (1.9) 1 (1.6) 1 (2.4)
Others 45 (44) 31 (50) 14 (34)
Real Estate and Household 1 (1.0) 0 (0) 1 (2.4)
Transportation/Automotive 4 (3.9) 0 (0) 4 (9.8)
Travel 1 (1.0) 1 (1.6) 0 (0)

1 Statistics presented: n (%)

ch 11 - Data Import & Label Encoding in R

Intro

  • 설문조사가 끝났으면 이제 정리를 해야 한다.
  • 일련의 과정은 보통 코딩이라 부른다.

(1) 주요 패키지

  • 이번 포스트부터 gt 패키지를 사용하려고 한다.
    • gt: ggplot2와 같이 Table를 문법으로 컨트롤 할 수 있도록 구현된 패키지이다.
    • kableExtra: HTML로 출력할 수 있도록 도와주는 패키지이다.

문제점

  • SmartPLS 프로그램을 쓴다 하더라도 기본적으로 모든 데이터의 entry는 수치형으로 일단 바뀌어 있어야 한다.
  • 우선 데이터를 불러와서 확인해보자.
library(tidyverse)
library(gt)
library(kableExtra)


# 데이터 불러오기
data <- read_csv("data/thesis_mater.csv")

data %>% 
  head() %>% 
  kable() %>% 
  kable_styling("striped") %>% 
  scroll_box(width = "100%")
EI_1 EI_2 EI_3 EP_1 EP_2 EP_3 ER_1 ER_2 ER_3 SS_1 SS_2 SS_3 SC_1 SC_2 SC_3 SR_1 SR_2 SR_3 F1 F2 F3 NF1 NF2 NF3 Firm_Age Firm_Size WE1 WE2 WE3 gender founder_employee age_of_respondent Education_Level Business_Area
2 3 4 3 3 4 3 2 4 1 1 3 3 3 3 2 2 1 2 2 3 3 1 3 5 years above Above 15 members No, I don't have experience Yes Yes Female Employee 30-39 Undergraduate School Others
5 5 2 3 5 3 4 4 4 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 Less than 2 years Less than 5 members No, I don't have experience No Yes Male Employee Younger than 30 Undergraduate School Media and Entertainment
1 2 2 1 1 2 1 2 1 2 2 1 1 2 2 1 2 1 2 1 1 1 1 1 5 years above Less than 5 members As founder or employee, I have startup experiences more than 3 times No Yes Female Founder of Company Younger than 30 Undergraduate School Others
3 3 2 1 2 1 2 1 3 2 1 3 1 1 1 2 3 3 3 3 2 3 2 2 Less than 2 years Less than 5 members No, I don't have experience Yes Yes Male Employee Younger than 30 Undergraduate School Others
5 3 5 2 5 4 4 4 4 4 5 4 5 5 5 5 5 5 4 5 4 4 5 5 3-4 years Less than 5 members As founder or employee, I have startup experiences more than 3 times No Yes Male Founder of Company 30-39 Undergraduate School Others
1 3 3 1 3 3 2 3 1 4 1 2 3 3 1 2 2 1 1 2 3 1 3 1 5 years above 5-9 members As founder or employee, I have startup experience, one time No No Female Employee Younger than 30 Undergraduate School Others
  • 위 데이터에서 보면 알 수 있듯이, WE1 ~ Business_Area 까지의 데이터는 모두 문자로 되어 있다.
  • Python에서는 LabelEncoder라는 것이 있다.
  • R에서는 기본 문법인 factor만 있어도 가능하다.

Factor의 활용

  • 이제 본격적으로 코딩을 시작한다.
  • 데이터 전처리에서 쉬운 방법은 없다.

(1) 기본

  • 가상의 데이터를 만든 후 factor를 활용하자.
temp <- data.frame(x = c(1, 1, 2), 
                   gender = c("Female", "Male", "Male"))
temp
##   x gender
## 1 1 Female
## 2 1   Male
## 3 2   Male
  • 이제 gender를 변환하자.
temp$gender <- as.numeric(factor(temp$gender))
temp
##   x gender
## 1 1      1
## 2 1      2
## 3 2      2
  • factor로 변환한 뒤, as.numeric으로 형 변환을 하면 쉽게 바꿀수는 있다.
  • 위와 같이 형변환을 하면 1이 무엇을 의미하는지, 2는 무엇을 의미하는지 알 수 없게 된다. (즉, 정보의 손실이 올 수 있다.)

(2) 응용

  • 이제 factor를 조금 더 활용해 본다.
  • 같은 가상의 데이터를 사용한다.
temp <- data.frame(x = c(1, 1, 2), 
                   gender = c("Female", "Male", "Male"))

temp$gender <- as.numeric(factor(temp$gender), 
                          levels = c("Female", "Male"), 
                          labels = c(1, 2))

temp
##   x gender
## 1 1      1
## 2 1      2
## 3 2      2
  • 결과는 똑같다. 그러나, 각 label에 대한 해석이 보다 명확해지기 때문에 추후 분석결과보고서를 작성할 때 보다 쉽게 작성할 수 있다.

(3) 적용

  • 이제 내 데이터에 적용해보자.
  • 적용해야 할 변수는 모두 Firm_Age ~ Business_Area 까지이다.
  • 여기에서 하나의 Rule을 만들어야 한다.
    • 각 범주마다 하나씩 맞추는 노가다는 지양해야 한다.
    • 따라서, 범주의 값은 모두 알파벳순으로 정렬한다.
    • 이를 프로그래밍으로 해결한다.
temp <- data.frame(x = c(1, 1, 2), 
                   gender = c("Female", "Male", "Male"),
                   grade = c("A", "B", "C"))

temp
##   x gender grade
## 1 1 Female     A
## 2 1   Male     B
## 3 2   Male     C
  • 데이터가 하나 더 생겼다. 다음과 같은 함수를 만든다.
factor2numeric <- function(x) {
  # input 변수가 문자형인지 확인하여 다음 코드를 실행한다. 
  # 만약 문자가 아니면 `else` 코드로 넘어간다. 
  if(is.character(x) == TRUE) { 
      # levels 함수를 사용하면 알파벳으로 자동 정렬해준다.
      char_levels <- levels(factor(x)) 
      
      # 변환되기 전 factor의 `level`를 확인한다.
      print("----변환 시작 전----")
      print(char_levels)   
      
      # factor형으로 변환했다. 
      x <- factor(x, 
                  levels = char_levels, 
                  labels = c(1:length(char_levels))) # 이 코드는 labels 숫자로 정의한다. 
     
       # 변환되기 전 factor의 `level`를 확인한다.
      print(levels(x)) 
      print("----변환 종료----")
  } else {
    # 에러 메시지를 출력한다
    print("This is not character!!")
  } 
  return(x)
}

# 전체 데이터에서 character인 데이터를 사용자 정의 함수를 적용하였다. 
data2 <- data %>% 
  mutate_if(is.character, factor2numeric)
## [1] "----변환 시작 전----"
## [1] "3-4 years"         "5 years above"     "Less than 2 years"
## [1] "1" "2" "3"
## [1] "----변환 종료----"
## [1] "----변환 시작 전----"
## [1] "10-14 members"       "5-9 members"         "Above 15 members"   
## [4] "Less than 5 members"
## [1] "1" "2" "3" "4"
## [1] "----변환 종료----"
## [1] "----변환 시작 전----"
## [1] "As founder or employee,  I have startup experience, one time"        
## [2] "As founder or employee, I have startup experiences more than 3 times"
## [3] "As founder or employee, I have startup experiences, two times"       
## [4] "No, I don't have experience"                                         
## [1] "1" "2" "3" "4"
## [1] "----변환 종료----"
## [1] "----변환 시작 전----"
## [1] "No"  "Yes"
## [1] "1" "2"
## [1] "----변환 종료----"
## [1] "----변환 시작 전----"
## [1] "No"  "Yes"
## [1] "1" "2"
## [1] "----변환 종료----"
## [1] "----변환 시작 전----"
## [1] "Female" "Male"  
## [1] "1" "2"
## [1] "----변환 종료----"
## [1] "----변환 시작 전----"
## [1] "Employee"           "Founder of Company"
## [1] "1" "2"
## [1] "----변환 종료----"
## [1] "----변환 시작 전----"
## [1] "30-39"           "40-49"           "50 or above"     "Younger than 30"
## [1] "1" "2" "3" "4"
## [1] "----변환 종료----"
## [1] "----변환 시작 전----"
## [1] "Graduate School"      "High School"          "Undergraduate School"
## [1] "1" "2" "3"
## [1] "----변환 종료----"
## [1] "----변환 시작 전----"
##  [1] "E-Commerce"                 "Education"                 
##  [3] "Energy"                     "Enterprise Services"       
##  [5] "Fintech"                    "Logistics"                 
##  [7] "Manufacturing"              "Media and Entertainment"   
##  [9] "Medical and Healthcare"     "Online to Offline Commerce"
## [11] "Others"                     "Real Estate and Household" 
## [13] "Transportation/Automotive"  "Travel"                    
##  [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13" "14"
## [1] "----변환 종료----"

(4) 중복치와 결측치 확인

  • 모든 것을 온라인으로 받다보니, 중복치와 결측치가 발생할 수 있다.
  • 따라서 해당 내용이 있는지 확인한다.
sum(duplicated(data2))
## [1] 5
  • 중복치가 5개 인것으로 확인이 된다.
  • 중복치를 제거하는 것은 간단하다.
data3 <- data2 %>% distinct()
  • 이번에는 결측치를 확인한다.
colSums(is.na(data3))
##              EI_1              EI_2              EI_3              EP_1 
##                 0                 0                 0                 0 
##              EP_2              EP_3              ER_1              ER_2 
##                 0                 0                 0                 0 
##              ER_3              SS_1              SS_2              SS_3 
##                 0                 0                 0                 0 
##              SC_1              SC_2              SC_3              SR_1 
##                 0                 0                 0                 0 
##              SR_2              SR_3                F1                F2 
##                 0                 0                 0                 0 
##                F3               NF1               NF2               NF3 
##                 0                 0                 0                 0 
##          Firm_Age         Firm_Size               WE1               WE2 
##                 0                 0                 0                 0 
##               WE3            gender  founder_employee age_of_respondent 
##                 0                 0                 0                 0 
##   Education_Level     Business_Area 
##                 0                 0
  • column마다 결측치를 확인한 결과 모두 0인 것을 확인할 수 있다.

(5) 결과 확인

  • 이제 변환된 결과를 확인한다.
# 결과값을 확인한다. 
data3 %>% 
  head() %>% 
  kable() %>% 
  kable_styling("striped") %>% 
  scroll_box(width = "100%")
EI_1 EI_2 EI_3 EP_1 EP_2 EP_3 ER_1 ER_2 ER_3 SS_1 SS_2 SS_3 SC_1 SC_2 SC_3 SR_1 SR_2 SR_3 F1 F2 F3 NF1 NF2 NF3 Firm_Age Firm_Size WE1 WE2 WE3 gender founder_employee age_of_respondent Education_Level Business_Area
2 3 4 3 3 4 3 2 4 1 1 3 3 3 3 2 2 1 2 2 3 3 1 3 2 3 4 2 2 1 1 1 3 11
5 5 2 3 5 3 4 4 4 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 3 4 4 1 2 2 1 4 3 8
1 2 2 1 1 2 1 2 1 2 2 1 1 2 2 1 2 1 2 1 1 1 1 1 2 4 2 1 2 1 2 4 3 11
3 3 2 1 2 1 2 1 3 2 1 3 1 1 1 2 3 3 3 3 2 3 2 2 3 4 4 2 2 2 1 4 3 11
5 3 5 2 5 4 4 4 4 4 5 4 5 5 5 5 5 5 4 5 4 4 5 5 1 4 2 1 2 2 2 1 3 11
1 3 3 1 3 3 2 3 1 4 1 2 3 3 1 2 2 1 1 2 3 1 3 1 2 2 1 1 1 1 1 4 3 11

파일 내보내기

  • 이제 SmartPLS에서 사용할 수 있도록 csv 파일 형태로 내보낸다.
write_csv(data3, "~/Desktop/thesis_master2.csv")

소결론

  • 데이터 전처리는 중요하다. 그러나, 시간이 조금 걸린다.
  • 설문조사에서 특히 문제가 되는 부분은 문자열 데이터를 수치형 데이터로 변환해주는 문제가 있다.
    • 이를 프로그래밍으로 해결하면 보다 쉽게 접근할 수 있다.
  • 이제 본격적으로 분석을 해보자.