JuliaCon 2021: DataFrames.jl 1.2 tutorial

  • Julia 1.6.1 기반

이 튜토리얼은 다음과 같은 통계개념을 사용하는데 미리 알고 가면 좋습니다.

  • confidence intervals - 신뢰구간
  • density estimators - 밀도추정
  • probit model - 프로빗모형
    • 선택확률 $\pi(x)$가 [0,1]의 구간에 놓이도록 하기 위해서는 설명변수와 선택확률 사이에 선형이 아닌 비선형의 관계가 요구 됩니다. 이러한 비선형 관계를 표준정규분포의 누적확률 분포함수를 이용하여 표현하는 경우 이를 프로빗 모형이라고 하며, 이 때 사용되는 링크함수를 프로빗 함수하고 합니다. 예를 들어 구매금액에 따라 재구매 고객의 여부를 분석할 때, 구매금액이 많으면 재구매를 한다는 가정을 한다면, 여기서 단순한 구매금액의 분포는 정규분포의 형태를 띠게 될 것입니다. 하지만 가정에 따르면 어느 정도 구매금액이면 재구매를 할 것이므로 재구매의 분포는 누적정규분포의 형태일 것입니다.
  • bootstrapping)

  • 여기서 사용하는 데이터는 경제활동참가(Labor force participation) 데이터 입니다.
  • 데이제공정보
    • 국가 : 스위스

경제활동참여 (Labor force participation)

  • 데이터 설명
컬럼명 영문 한글
lfp labour force participation ? 경제활동참여 여부
lnnlinc the log of nonlabour incom 비 노동소득의 로그값
age age in years devided by 10 나이를 10으로 나눈값
educ years of formal education 정규교육연수
nyc the number of young children (younger than 7) 7세 미만 자녀수
noc number of older children 7세 이상 자녀수
foreign foreigner ? 외국인 여부
In [1]:
using Bootstrap
using CSV
using CategoricalArrays
using Chain
using DataFrames
import Downloads
using GLM
using Plots
using Random
using StatsPlots
using Statistics

DataFrame 출력시 열(column)은 1000개, 행(row)은 20개 까지 표시 되도록 환경변수를 설정 한다.

In [2]:
ENV["LINES"] = 20
ENV["COLUMNS"] = 1000
Out[2]:
1000

Data preprocessing

  • 데이터 다운로드
In [8]:
data_file = "data/participation.csv"
Out[8]:
"data/participation.csv"
In [9]:
Downloads.download("https://vincentarelbundock.github.io/Rdatasets/csv/Ecdat/Participation.csv", data_file)
Out[9]:
"data/participation.csv"
In [10]:
readlines(data_file)
Out[10]:
873-element Vector{String}:
 "\"\",\"lfp\",\"lnnlinc\",\"age\",\"educ\",\"nyc\",\"noc\",\"foreign\""
 "\"1\",\"no\",10.787497,3,8,1,1,\"no\""
 "\"2\",\"yes\",10.524251,4.5,8,0,1,\"no\""
 "\"3\",\"no\",10.968578,4.6,9,0,0,\"no\""
 "\"4\",\"no\",11.104999,3.1,11,2,0,\"no\""
 "\"5\",\"no\",11.10847,4.4,12,0,2,\"no\""
 "\"6\",\"yes\",11.028254,4.2,12,0,1,\"no\""
 "\"7\",\"no\",11.454707,5.1,8,0,0,\"no\""
 ⋮
 "\"866\",\"yes\",10.69323,3.5,9,0,2,\"yes\""
 "\"867\",\"yes\",10.027595,4.2,14,0,0,\"yes\""
 "\"868\",\"no\",10.597393,2.4,4,2,1,\"yes\""
 "\"869\",\"yes\",10.377773,4.8,8,0,0,\"yes\""
 "\"870\",\"no\",10.13374,2.4,6,2,0,\"yes\""
 "\"871\",\"yes\",10.932351,4.1,10,0,1,\"yes\""
 "\"872\",\"no\",11.220691,5.1,10,0,0,\"yes\""

데이터 분석의 목적은 경제활동참여여부(lfp) 예측모델을 만드는 것입니다

In [13]:
df_raw = CSV.read(data_file, DataFrame)
Out[13]:

872 rows × 8 columns

Column1lfplnnlincageeducnycnocforeign
Int64StringFloat64Float64Int64Int64Int64String
11no10.78753.0811no
22yes10.52434.5801no
33no10.96864.6900no
44no11.1053.11120no
55no11.10854.41202no
66yes11.02834.21201no
77no11.45475.1800no
88yes10.49093.2802no
99no10.62473.91200no
1010no10.48644.31102no
1111no10.66064.51102no
1212no10.46766.01200no
1313no11.22963.31120no
1414no11.90655.61400no
1515no11.50165.61100no
1616no11.29354.71101no
1717no10.86135.0800no
1818yes11.8443.91200no
1919no11.04864.7801no
2020yes10.95785.31100no
In [14]:
describe(df_raw)
Out[14]:

8 rows × 7 columns

variablemeanminmedianmaxnmissingeltype
SymbolUnion…AnyUnion…AnyInt64DataType
1Column1436.51436.58720Int64
2lfpnoyes0String
3lnnlinc10.68567.186910.643112.37570Float64
4age3.995532.03.96.20Float64
5educ9.3073419.0210Int64
6nyc0.31192700.030Int64
7noc0.98279801.060Int64
8foreignnoyes0String
  • Data Transformation
    • select function을 사용하여 아래와 같은 변환을 합니다.
    • recode : lfp를 text에서 binary로 변환
    • add square of : age
    • change : foreign 컬럼을 categorical로 변환
    • 그외 다른 모든 컬럼들은 그대로 둔다.
    • 컬럼변환 옵션의 일반적인 syntax
    • source columns => transformation => target columns name
    • ByRaw wrapper는 row방향으로 select 를 수행하도록 합니다.(기본은 colum 방향으로 수행)
In [17]:
df = select(df_raw,
            :lfp => (x -> recode(x,"yes" => 1, "no" => 0)) => :lfp,
            :lnnlinc,
            :age,
            :age => ByRow(x -> x^2) => :age²,
            Between(:educ, :noc),
            :foreign => categorical => :foreign
  )
Out[17]:

872 rows × 8 columns

lfplnnlincageage²educnycnocforeign
Int64Float64Float64Float64Int64Int64Int64Cat…
1010.78753.09.0811no
2110.52434.520.25801no
3010.96864.621.16900no
4011.1053.19.611120no
5011.10854.419.361202no
6111.02834.217.641201no
7011.45475.126.01800no
8110.49093.210.24802no
9010.62473.915.211200no
10010.48644.318.491102no
11010.66064.520.251102no
12010.46766.036.01200no
13011.22963.310.891120no
14011.90655.631.361400no
15011.50165.631.361100no
16011.29354.722.091101no
17010.86135.025.0800no
18111.8443.915.211200no
19011.04864.722.09801no
20110.95785.328.091100no

컬럼명을 변경하지 않고 그대로 둔다면 renamecols=false를 인자로 준다.

In [19]:
df = select(df_raw,
            :lfp => x->recode(x,"yes"=>1,"no"=>0),
            :lnnlinc,
            :age,
            :age=>ByRow(x->x^2) => :age²,
            Between(:educ,:noc),
            :foreign=>categorical,
            renamecols=false
)
Out[19]:

872 rows × 8 columns

lfplnnlincageage²educnycnocforeign
Int64Float64Float64Float64Int64Int64Int64Cat…
1010.78753.09.0811no
2110.52434.520.25801no
3010.96864.621.16900no
4011.1053.19.611120no
5011.10854.419.361202no
6111.02834.217.641201no
7011.45475.126.01800no
8110.49093.210.24802no
9010.62473.915.211200no
10010.48644.318.491102no
11010.66064.520.251102no
12010.46766.036.01200no
13011.22963.310.891120no
14011.90655.631.361400no
15011.50165.631.361100no
16011.29354.722.091101no
17010.86135.025.0800no
18111.8443.915.211200no
19011.04864.722.09801no
20110.95785.328.091100no
In [20]:
describe(df)
Out[20]:

8 rows × 7 columns

variablemeanminmedianmaxnmissingeltype
SymbolUnion…AnyUnion…AnyInt64DataType
1lfp0.45986200.010Int64
2lnnlinc10.68567.186910.643112.37570Float64
3age3.995532.03.96.20Float64
4age²17.07634.015.2138.440Float64
5educ9.3073419.0210Int64
6nyc0.31192700.030Int64
7noc0.98279801.060Int64
8foreignnoyes0CategoricalValue{String, UInt32}

데이터 분석

Exploratory data analysis(탐색적 데이터 분석)

image.png

“ '탐색적 데이터 분석(EDA)’은 우리가 존재한다고 믿는 것들은 물론이고 존재하지 않는다고 믿는 것들을 발견하려는 태도, 유연성, 그리고 자발성이다. “ - 존 튜키 (도서 Doing Data Science 중)

탐색적 데이터 분석이란 벨 연구소의 수학자 존 튜키가 제안한 데이터 분석 방법으로 통계적 가설 검정 등에 의존한 기존 통계학으로는 새롭게 나오는 많은 양의 데이터의 핵심 의미를 파악하는 데 어려움이 있다고 생각하여 이를 보완한 탐색적 데이터 분석을 도입했다고 합니다. 데이터를 분석하고 결과를 내는 과정에서 원 데이터에 대한 탐색과 이해를 기본으로 가지는 것이 가장 중요합니다. 이에 따라 탐색적 데이터 분석은 데이터의 분포와 값을 다양한 각도에서 관찰하며 데이터가 표현하는 현상을 더 잘 이해할 수 있도록 도와주고 데이터를 다양한 기준에서 살펴보는 과정을 통해 문제 정의 단계에서 미처 발견하지 못한 다양한 패턴을 발견하고 이를 바탕으로 기존의 가설을 수정하거나 새로운 가설을 추가할 수 있도록 합니다. 데이터에 대한 관찰과 지식이 이후에 통계적 추론이나 예측 모델 구축 시에도 사용되므로 데이터 분석 단계 중 중요한 단계라고 할 수 있습니다. EDA의 목표는 관측된 현상의 원인에 대한 가설을 제시하고, 적절한 통계 도구 및 기법의 선택을 위한 가이드가 되며, 통계 분석의 기초가 될 가정을 평가하고 추가 자료수집을 위한 기반을 제공합니다.

  • 목표에 영향을 미치는 방향을 초기에 확인하기 위해 :lfp로 숫자 열의 평균을 계산하려고 합니다.
In [21]:
@chain df begin
  groupby(:lfp)
  combine([:lnnlinc, :age, :educ, :nyc, :noc] .=> mean)
end
Out[21]:

2 rows × 6 columns

lfplnnlinc_meanage_meaneduc_meannyc_meannoc_mean
Int64Float64Float64Float64Float64Float64
1010.75134.085359.594480.4097660.902335
2110.60833.890028.970070.1970071.07731
In [24]:
[:lnnlinc, :age, :educ, :nyc, :noc].=>mean
Out[24]:
5-element Vector{Pair{Symbol, typeof(mean)}}:
 :lnnlinc => Statistics.mean
     :age => Statistics.mean
    :educ => Statistics.mean
     :nyc => Statistics.mean
     :noc => Statistics.mean
  • 위와 같은 동작을 하는 또 다른 방법
    • 실수타입의 모든 컬럼을 추출 : names(df,Real)
In [25]:
@chain df begin
  groupby(:lfp)
  combine(names(df,Real) .=> mean)
end
Out[25]:

2 rows × 8 columns

lfplfp_meanlnnlinc_meanage_meanage²_meaneduc_meannyc_meannoc_mean
Int64Float64Float64Float64Float64Float64Float64Float64
100.010.75134.0853518.03579.594480.4097660.902335
211.010.60833.8900215.94968.970070.1970071.07731
  • Categorical .변수 : foreign 취급 하기
  • nrowcombine에 넘겨주어 각 group의 row의 갯수를 return하게 한다.
In [27]:
@chain df begin 
  groupby([:lfp, :foreign])
  combine(nrow)
end
Out[27]:

4 rows × 3 columns

lfpforeignnrow
Int64Cat…Int64
10no402
20yes69
31no254
41yes147
  • Cross-Tabulation (교차표)
    • 교차분석 : 보통은 크로스탭(crosstab) 분석이라 하고, 2개의 명목 혹은 서열형 척도를 변수로 분석에 활용합니다. 즉 한 변수의 범주를 다른 변수의 범주와 교차 시키고 각각 교차된 경우에 해당하는 셀의 빈도를 분석하는 방법입니다.
    • 아래 예는 외국인 여부(foreign)와 경제활동참여(lfp)여부의 빈도(nrow)를 교차표로 나타 냅니다.
    • 경제할동에 참여하는(1) 사람들중 외국인(yes)은 147명, 내국인(no)는 254명
    • 경제활동에 참여하지 않는(0) 사람들중 외국인(yes)은 69명, 내국인(no)는 402명
In [65]:
@chain df begin
  groupby([:lfp, :foreign])
  combine(nrow)
  unstack(:lfp,:foreign,:nrow)
end
Out[65]:

2 rows × 3 columns

lfpnoyes
Int64Int64?Int64?
1040269
21254147
  • 경제활동참여 여부에 따른 외국인의 비율 조사
In [69]:
@chain df begin
  groupby([:lfp, :foreign])
  combine(nrow)
  unstack(:lfp,:foreign,:nrow)
  select(:lfp,[:no,:yes] => ByRow((x,y)->y/(x+y))=>:foreign_yes)
end
Out[69]:

2 rows × 2 columns

lfpforeign_yes
Int64Float64
100.146497
210.366584

위의 방법을 아래와 같이 간단하세 할 수 있다.

In [70]:
@chain df begin
  groupby(:lfp)
  combine(:foreign => (x -> mean(x .== "yes")) => :foreign_yes)
end
Out[70]:

2 rows × 2 columns

lfpforeign_yes
Int64Float64
100.146497
210.366584

groupby 함수에 의해 생성된 GroupedDataFrame은 그 자체로 작업하기에 유용한 객체가 될 수 있습니다.

In [71]:
gd = groupby(df,:lfp)
Out[71]:

GroupedDataFrame with 2 groups based on key: lfp

First Group (471 rows): lfp = 0

lfplnnlincageage²educnycnocforeign
Int64Float64Float64Float64Int64Int64Int64Cat…
1010.78753.09.0811no
2010.96864.621.16900no
3011.1053.19.611120no
4011.10854.419.361202no
5011.45475.126.01800no
6010.62473.915.211200no
7010.48644.318.491102no
8010.66064.520.251102no
9010.46766.036.01200no
10011.22963.310.891120no
11011.90655.631.361400no
12011.50165.631.361100no
13011.29354.722.091101no
14010.86135.025.0800no
15011.04864.722.09801no
16010.86012.98.411900no
17010.84864.419.36802no
18010.48242.04.01210no
19010.54062.87.84920no
20011.22434.722.09801no

Last Group (401 rows): lfp = 1

lfplnnlincageage²educnycnocforeign
Int64Float64Float64Float64Int64Int64Int64Cat…
1110.52434.520.25801no
2111.02834.217.641201no
3110.49093.210.24802no
4111.8443.915.211200no
5110.95785.328.091100no
6111.10814.621.161101no
7110.4845.429.16800no
8110.60124.722.09800no
9110.84114.924.011200no
10111.15663.612.961302no
11111.10634.116.811202no
12110.64464.217.641002no
13110.4673.210.24801no
14110.47823.411.56902no
15110.43554.419.36901no
16110.42735.833.641200no
17110.47184.419.36902no
18111.41124.016.01302no
19110.49333.512.25402no
20110.71863.19.611011no

gd를 인덱싱하는 여러 방법을 아래에 보여 준다

In [72]:
gd[1]
Out[72]:

471 rows × 8 columns

lfplnnlincageage²educnycnocforeign
Int64Float64Float64Float64Int64Int64Int64Cat…
1010.78753.09.0811no
2010.96864.621.16900no
3011.1053.19.611120no
4011.10854.419.361202no
5011.45475.126.01800no
6010.62473.915.211200no
7010.48644.318.491102no
8010.66064.520.251102no
9010.46766.036.01200no
10011.22963.310.891120no
11011.90655.631.361400no
12011.50165.631.361100no
13011.29354.722.091101no
14010.86135.025.0800no
15011.04864.722.09801no
16010.86012.98.411900no
17010.84864.419.36802no
18010.48242.04.01210no
19010.54062.87.84920no
20011.22434.722.09801no
In [80]:
gd[(lfp=0,)]
Out[80]:

471 rows × 8 columns

lfplnnlincageage²educnycnocforeign
Int64Float64Float64Float64Int64Int64Int64Cat…
1010.78753.09.0811no
2010.96864.621.16900no
3011.1053.19.611120no
4011.10854.419.361202no
5011.45475.126.01800no
6010.62473.915.211200no
7010.48644.318.491102no
8010.66064.520.251102no
9010.46766.036.01200no
10011.22963.310.891120no
11011.90655.631.361400no
12011.50165.631.361100no
13011.29354.722.091101no
14010.86135.025.0800no
15011.04864.722.09801no
16010.86012.98.411900no
17010.84864.419.36802no
18010.48242.04.01210no
19010.54062.87.84920no
20011.22434.722.09801no
In [81]:
gd[Dict(:lfp => 0)]
Out[81]:

471 rows × 8 columns

lfplnnlincageage²educnycnocforeign
Int64Float64Float64Float64Int64Int64Int64Cat…
1010.78753.09.0811no
2010.96864.621.16900no
3011.1053.19.611120no
4011.10854.419.361202no
5011.45475.126.01800no
6010.62473.915.211200no
7010.48644.318.491102no
8010.66064.520.251102no
9010.46766.036.01200no
10011.22963.310.891120no
11011.90655.631.361400no
12011.50165.631.361100no
13011.29354.722.091101no
14010.86135.025.0800no
15011.04864.722.09801no
16010.86012.98.411900no
17010.84864.419.36802no
18010.48242.04.01210no
19010.54062.87.84920no
20011.22434.722.09801no
In [91]:
gd[(0,)]
Out[91]:

471 rows × 8 columns

lfplnnlincageage²educnycnocforeign
Int64Float64Float64Float64Int64Int64Int64Cat…
1010.78753.09.0811no
2010.96864.621.16900no
3011.1053.19.611120no
4011.10854.419.361202no
5011.45475.126.01800no
6010.62473.915.211200no
7010.48644.318.491102no
8010.66064.520.251102no
9010.46766.036.01200no
10011.22963.310.891120no
11011.90655.631.361400no
12011.50165.631.361100no
13011.29354.722.091101no
14010.86135.025.0800no
15011.04864.722.09801no
16010.86012.98.411900no
17010.84864.419.36802no
18010.48242.04.01210no
19010.54062.87.84920no
20011.22434.722.09801no
  • 경제할동참여 여부에 따른 나이별 확률 밀도를 그래프로 나타 냅니다.
  • 아래 그래프에서 age는 10으로 나눈값을 사용하고 있으므로 실제는 10대 부터 70대 까지 경제활동에 참여 하는 나이별 확률 밀도를 나타 낸다.
    • 아래 그래프에서 경제활동에 참여하는(빨간색 그래프) 가장 많은 나이대는 30대 후반
    • 경제활동에 참여하지 않는 가장 많은 나이대는 30대 초반
In [92]:
@df df density(:age, group=:lfp)
Out[92]:

예측모델 생성

GLM.jl 패키지를 사용한 probit model - 프로빗모형 생성

참조: R에서 프로빗 회귀분석(Probit Regression) 실시하기

프로빗 회귀분석(Probit Regression)은 종속변수가 이항형문제(즉, 유효한 범주의 개수가 두개인 경우)를 분류하는 모델로 일반화 선형 회귀모형(Generalized Linear Regression, GLM)중 하나입니다.

프로빗 회귀분석이 일반적인 회귀분석과 가장 크게 차이나는 부분은 종속변수를 단순히 Y로 두는 대신에 정규 누적함수($\Phi$)를 이용합니다. 정규누적함수를 이용하는 이유는 p(0 ~ 1)를 단순하게 종속변수로 둔다면 선형회귀모형을 만들면 다음과 같은 식이 만들어 집니다. $$ p = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + ... + \beta_q x_q $$ 하지만 위의 식은 p가 0 ~ 1사이의 값을 갖는다는 것을 보장할 수 없게 되고, 따라서 다음과 같은 비선형함수를 사용합니다. $$ p = \Phi\left(\beta_0 + \beta_1 x_1 + \beta_2 x_2 + ... + \beta_q x_q\right) $$ 여기서 $\Phi$는 표준정규분포의 누적분포함수를 의미하고 식은 아래와 같습니다. $$ \Phi(z) = \frac{1}{\sqrt{2\pi}}\int^z_{-\infty}e^{-\frac{t^2}{2}}dt $$ 두번째 식에서 $\Phi(x)$함수의 특성상 $x_1,x_2,...,x_q$가 어떤값을 가지더라도 우변은 항상 0과 1의 값을 가지게 됩니다.. 프로빗 회귀모형은 아래와 같이 데이터 갯수가 n개에 대한 log likelihood 함수를 최대화 시키는 방향으로 추정모수 $\hat{\beta}$를 구하게 됩니다. Probit model 참조

  • 다음은 비노동수익(lnnlinc),나이,교육정도(educ)등의 데이터가 주어 졌을 때 경제활동에 참여(lfp=1) 할 확률$Pr(lfp=1)$을 예측하는 것입니다
In [93]:
probit = glm(@formula(lfp ~ lnnlinc + age + age²+ educ + nyc + noc + foreign),
            df, Binomial(), ProbitLink())
Out[93]:
StatsModels.TableRegressionModel{GeneralizedLinearModel{GLM.GlmResp{Vector{Float64}, Binomial{Float64}, ProbitLink}, GLM.DensePredChol{Float64, LinearAlgebra.Cholesky{Float64, Matrix{Float64}}}}, Matrix{Float64}}

lfp ~ 1 + lnnlinc + age + age² + educ + nyc + noc + foreign

Coefficients:
────────────────────────────────────────────────────────────────────────────
                   Coef.  Std. Error      z  Pr(>|z|)  Lower 95%   Upper 95%
────────────────────────────────────────────────────────────────────────────
(Intercept)    3.74896     1.40663     2.67    0.0077   0.992006   6.50591
lnnlinc       -0.666932    0.13192    -5.06    <1e-06  -0.92549   -0.408374
age            2.07531     0.405407    5.12    <1e-06   1.28073    2.86989
age²          -0.294345    0.0499446  -5.89    <1e-08  -0.392235  -0.196456
educ           0.0191963   0.0179255   1.07    0.2842  -0.015937   0.0543295
nyc           -0.714465    0.100397   -7.12    <1e-11  -0.911238  -0.517691
noc           -0.146985    0.0508854  -2.89    0.0039  -0.246718  -0.0472509
foreign: yes   0.71438     0.121324    5.89    <1e-08   0.476589   0.95217
────────────────────────────────────────────────────────────────────────────

위 예에서 @formula를 손으로 넣었는데 아래와 같이 프로그램적으로 생성 할 수 있다.

In [94]:
probit = glm(Term(:lfp) ~ sum(Term.(propertynames(df)[2:end])),
          df, Binomial(), ProbitLink() )
Out[94]:
StatsModels.TableRegressionModel{GeneralizedLinearModel{GLM.GlmResp{Vector{Float64}, Binomial{Float64}, ProbitLink}, GLM.DensePredChol{Float64, LinearAlgebra.Cholesky{Float64, Matrix{Float64}}}}, Matrix{Float64}}

lfp ~ 1 + lnnlinc + age + age² + educ + nyc + noc + foreign

Coefficients:
────────────────────────────────────────────────────────────────────────────
                   Coef.  Std. Error      z  Pr(>|z|)  Lower 95%   Upper 95%
────────────────────────────────────────────────────────────────────────────
(Intercept)    3.74896     1.40663     2.67    0.0077   0.992006   6.50591
lnnlinc       -0.666932    0.13192    -5.06    <1e-06  -0.92549   -0.408374
age            2.07531     0.405407    5.12    <1e-06   1.28073    2.86989
age²          -0.294345    0.0499446  -5.89    <1e-08  -0.392235  -0.196456
educ           0.0191963   0.0179255   1.07    0.2842  -0.015937   0.0543295
nyc           -0.714465    0.100397   -7.12    <1e-11  -0.911238  -0.517691
noc           -0.146985    0.0508854  -2.89    0.0039  -0.246718  -0.0472509
foreign: yes   0.71438     0.121324    5.89    <1e-08   0.476589   0.95217
────────────────────────────────────────────────────────────────────────────

Note the following:

In [95]:
Term(:lfp) ~ sum(Term.(propertynames(df)[2:end]))
Out[95]:
FormulaTerm
Response:
  lfp(unknown)
Predictors:
  lnnlinc(unknown)
  age(unknown)
  age²(unknown)
  educ(unknown)
  nyc(unknown)
  noc(unknown)
  foreign(unknown)

vs.

In [96]:
@formula(lfp ~ lnnlinc + age + age²+educ + nyc + noc + foreign)
Out[96]:
FormulaTerm
Response:
  lfp(unknown)
Predictors:
  lnnlinc(unknown)
  age(unknown)
  age²(unknown)
  educ(unknown)
  nyc(unknown)
  noc(unknown)
  foreign(unknown)

마지막으로 @formula가 :age의 제곱을 자동으로 계산할 만큼 충분히 강력하다는 것을 봅시다.

In [97]:
probit = glm(@formula(lfp ~ lnnlinc + age + age^2 + educ + nyc + noc + foreign),
            df, Binomial(), ProbitLink())
Out[97]:
StatsModels.TableRegressionModel{GeneralizedLinearModel{GLM.GlmResp{Vector{Float64}, Binomial{Float64}, ProbitLink}, GLM.DensePredChol{Float64, LinearAlgebra.Cholesky{Float64, Matrix{Float64}}}}, Matrix{Float64}}

lfp ~ 1 + lnnlinc + age + :(age ^ 2) + educ + nyc + noc + foreign

Coefficients:
────────────────────────────────────────────────────────────────────────────
                   Coef.  Std. Error      z  Pr(>|z|)  Lower 95%   Upper 95%
────────────────────────────────────────────────────────────────────────────
(Intercept)    3.74896     1.40663     2.67    0.0077   0.992006   6.50591
lnnlinc       -0.666932    0.13192    -5.06    <1e-06  -0.92549   -0.408374
age            2.07531     0.405407    5.12    <1e-06   1.28073    2.86989
age ^ 2       -0.294345    0.0499446  -5.89    <1e-08  -0.392235  -0.196456
educ           0.0191963   0.0179255   1.07    0.2842  -0.015937   0.0543295
nyc           -0.714465    0.100397   -7.12    <1e-11  -0.911238  -0.517691
noc           -0.146985    0.0508854  -2.89    0.0039  -0.246718  -0.0472509
foreign: yes   0.71438     0.121324    5.89    <1e-08   0.476589   0.95217
────────────────────────────────────────────────────────────────────────────

다시 한번 formula를 체크 하면 아래와 같습니다.

In [98]:
@formula(lfp ~ lnnlinc + age + age^2 + educ + nyc + noc + foreign)
Out[98]:
FormulaTerm
Response:
  lfp(unknown)
Predictors:
  lnnlinc(unknown)
  age(unknown)
  (age)->age ^ 2
  educ(unknown)
  nyc(unknown)
  noc(unknown)
  foreign(unknown)

다음으로 우리는 다른 모든 변수를 고정하고 :age를 수정함에 따라 모델의 예측이 어떻게 변경되는지 확인하기위해 새 데이터 프레임을 준비합니다.

예) 비노동소득이 22,026 ($log(22026) \approx 10.0$)이고 연령대가 20 ~ 62, 정규교육을 9년 정도 받고 7세 이상의 자녀를 1명둔 외국인 노동자들의 데이터

In [114]:
df_pred = DataFrame(lnnlinc=10.0, age=2.0:0.01:6.2, educ =9, nyc = 0, noc=1,
                    foreign="yes")
Out[114]:

421 rows × 6 columns

lnnlincageeducnycnocforeign
Float64Float64Int64Int64Int64String
110.02.0901yes
210.02.01901yes
310.02.02901yes
410.02.03901yes
510.02.04901yes
610.02.05901yes
710.02.06901yes
810.02.07901yes
910.02.08901yes
1010.02.09901yes
1110.02.1901yes
1210.02.11901yes
1310.02.12901yes
1410.02.13901yes
1510.02.14901yes
1610.02.15901yes
1710.02.16901yes
1810.02.17901yes
1910.02.18901yes
2010.02.19901yes

신뢰 구간과 함께 예측을 수행합니다.

In [115]:
probit_pred = predict(probit,df_pred,interval=:confidence)
Out[115]:

421 rows × 3 columns

predictionlowerupper
Float64?Float64?Float64?
10.7861220.6367120.891844
20.788720.6411940.892941
30.7912820.6456230.894026
40.793810.6500010.895097
50.7963020.6543250.896155
60.798760.6585970.8972
70.8011830.6628170.898233
80.8035720.6669830.899252
90.8059280.6710970.90026
100.8082510.6751590.901254
110.810540.6791670.902237
120.8127970.6831240.903207
130.8150210.6870280.904166
140.8172130.6908790.905113
150.8193730.6946780.906048
160.8215020.6984260.906971
170.82360.7021210.907883
180.8256670.7057650.908784
190.8277030.7093580.909674
200.829710.7128990.910552

데이터가 주어졌을 때 나이에 따른 경제활동에 참여할 확율을 plot으로 나타냅니다.

데이터 프레임에서 행렬(matrix)을 쉽게 만들기 위해 Matrix를 사용합니다.

In [116]:
plot(df_pred.age, Matrix(probit_pred),labels=["lfp" "lower" "upper"],
    xlabel="age", ylabel="Pr(lfp=1)")
Out[116]:

Advanced DataFrames.jl functionalities: bootstrapping example

프로빗 객체를 다시 조사해 보겠습니다.

In [117]:
probit
Out[117]:
StatsModels.TableRegressionModel{GeneralizedLinearModel{GLM.GlmResp{Vector{Float64}, Binomial{Float64}, ProbitLink}, GLM.DensePredChol{Float64, LinearAlgebra.Cholesky{Float64, Matrix{Float64}}}}, Matrix{Float64}}

lfp ~ 1 + lnnlinc + age + :(age ^ 2) + educ + nyc + noc + foreign

Coefficients:
────────────────────────────────────────────────────────────────────────────
                   Coef.  Std. Error      z  Pr(>|z|)  Lower 95%   Upper 95%
────────────────────────────────────────────────────────────────────────────
(Intercept)    3.74896     1.40663     2.67    0.0077   0.992006   6.50591
lnnlinc       -0.666932    0.13192    -5.06    <1e-06  -0.92549   -0.408374
age            2.07531     0.405407    5.12    <1e-06   1.28073    2.86989
age ^ 2       -0.294345    0.0499446  -5.89    <1e-08  -0.392235  -0.196456
educ           0.0191963   0.0179255   1.07    0.2842  -0.015937   0.0543295
nyc           -0.714465    0.100397   -7.12    <1e-11  -0.911238  -0.517691
noc           -0.146985    0.0508854  -2.89    0.0039  -0.246718  -0.0472509
foreign: yes   0.71438     0.121324    5.89    <1e-08   0.476589   0.95217
────────────────────────────────────────────────────────────────────────────

매개변수에 대한 매개변수 신뢰구간을 얻었음을 알 수 있습니다. 그러나 샘플이 그리 크지 않았기 때문에 부트스트랩을 사용하여 검증하고자 합니다.

먼저 수동으로 부트스트랩을 수행하고 다음으로 결과를 Bootstrap.jl 패키지가 생성하는 것과 비교할 것입니다.

통상 DataFrames.jl의 몇 가지 새로운 기능을 배우려고 할 것입니다. 데이터 프레임을 취하는 함수로 시작하고

1. 콘텐츠의 부트스트랩 샘플 하나를 생성합니다.
2. 프로빗 모델을 부트스트랩된 데이터에 적용 합니다.
3. 계산된 계수와 함께 NamedTuple을 반환합니다.    
In [119]:
function boot_sample(df)
  # df에서 샘플을 df의 크기만큼 무작위로 뽑는다. (복원추출)
  df_boot = df[rand(1:nrow(df),nrow(df)),:]
  probit_boot = glm(@formula(lfp ~ lnnlinc + age + age^2 + educ + nyc + noc + foreign),
                    df_boot, Binomial(), ProbitLink())
  return (;(Symbol.(coefnames(probit_boot)) .=> coef(probit_boot))...)
    
end
Out[119]:
boot_sample (generic function with 1 method)

boot_sample 함수를 여러 번 실행해야 합니다. 결과를 coef_boot 데이터 프레임에 저장합니다.

In [120]:
function run_boot(df,reps)
  coef_boot = DataFrame()
  for _ in 1:reps
    push!(coef_boot, boot_sample(df))
  end
  return coef_boot
end
Out[120]:
run_boot (generic function with 1 method)

Bootstrap.jl이 생성하는 것과 유사한 결과를 원하기 때문에 난수 생성기에 시드를 사용합니다(수동 코드에서 동일한 방식으로 부트스트랩을 위한 샘플 행을 확인했습니다).

In [121]:
Random.seed!(1234)
@time coef_boot = run_boot(df,1000)
  2.895386 seconds (5.98 M allocations: 924.387 MiB, 5.50% gc time, 72.90% compilation time)
Out[121]:

1,000 rows × 8 columns

(Intercept)lnnlincageage ^ 2educnycnocforeign: yes
Float64Float64Float64Float64Float64Float64Float64Float64
13.27668-0.6850182.43103-0.3462420.0358374-0.656125-0.2047080.897463
23.46174-0.6042961.80522-0.2711830.0344276-0.830026-0.1126850.849524
35.58553-0.9312022.56893-0.3516960.0088754-0.661503-0.1325130.751413
45.36834-0.8699492.47276-0.3488790.0102268-0.680439-0.1179420.460296
55.02884-0.8153142.23525-0.3093160.0122217-0.679034-0.1569540.627065
63.18287-0.660762.31433-0.3213790.019614-0.716859-0.1782110.786647
75.90148-0.7811291.54387-0.2420730.0365077-0.771876-0.09742620.784947
82.91324-0.5025561.47766-0.214310.0222138-0.638783-0.1305590.785572
92.26184-0.585122.32937-0.3257950.0320947-0.695273-0.1093180.726193
105.07018-0.7606312.0212-0.2917940.0120951-0.811062-0.1819920.732723
114.495-0.684711.75418-0.2629630.0219971-0.761671-0.1027040.875118
124.2758-0.7766292.25114-0.3177440.0538256-0.786446-0.1917250.814185
135.25119-0.9188242.50674-0.3456740.0613745-0.832087-0.2065570.972061
142.7135-0.621882.42928-0.3363730.00439032-0.737164-0.1019110.751079
152.33295-0.4967641.96116-0.2825210.00808349-1.00032-0.1744520.792722
162.4115-0.3587991.30906-0.209269-0.0100682-0.908253-0.2034870.774428
175.98789-0.8154251.5742-0.2249370.0249402-0.532131-0.03890380.489873
181.06655-0.5229292.64387-0.3585020.020522-0.603649-0.192750.665522
193.71053-0.7267752.54058-0.3613340.0329529-0.972846-0.2302560.653925
203.13616-0.6484332.3537-0.3320970.0257304-0.766528-0.187740.726401

이 데이터를 사용하여 백분위수 부트스트랩을 사용하여 95% 신뢰 구간을 계산합니다.

In [122]:
conf_boot = mapcols(x->quantile(x,[0.025,0.975]),coef_boot)
Out[122]:

2 rows × 8 columns

(Intercept)lnnlincageage ^ 2educnycnocforeign: yes
Float64Float64Float64Float64Float64Float64Float64Float64
11.06625-0.943111.24434-0.397132-0.0154263-0.937041-0.2519840.469018
26.59637-0.4246062.91247-0.1914570.0543084-0.525028-0.05038980.972134

다음은 GLM.jl에 의해 계산된 매개변수 신뢰구간입니다. 부트스트랩 결과와 비교하고 싶습니다.

In [123]:
confint(probit)
Out[123]:
8×2 Matrix{Float64}:
  0.992006   6.50591
 -0.92549   -0.408374
  1.28073    2.86989
 -0.392235  -0.196456
 -0.015937   0.0543295
 -0.911238  -0.517691
 -0.246718  -0.0472509
  0.476589   0.95217
In [124]:
conf_param = DataFrame(permutedims(confint(probit)),names(conf_boot))
Out[124]:

2 rows × 8 columns

(Intercept)lnnlincageage ^ 2educnycnocforeign: yes
Float64Float64Float64Float64Float64Float64Float64Float64
10.992006-0.925491.28073-0.392235-0.015937-0.911238-0.2467180.476589
26.50591-0.4083742.86989-0.1964560.0543295-0.517691-0.04725090.95217

그리고 conf_boot 데이터 프레임에 'append!' 합니다.

In [125]:
append!(conf_boot,conf_param)
Out[125]:

4 rows × 8 columns

(Intercept)lnnlincageage ^ 2educnycnocforeign: yes
Float64Float64Float64Float64Float64Float64Float64Float64
11.06625-0.943111.24434-0.397132-0.0154263-0.937041-0.2519840.469018
26.59637-0.4246062.91247-0.1914570.0543084-0.525028-0.05038980.972134
30.992006-0.925491.28073-0.392235-0.015937-0.911238-0.2467180.476589
46.50591-0.4083742.86989-0.1964560.0543295-0.517691-0.04725090.95217

데이터의 각 행이 보유하고 있는 내용을 추적하는 것은 좋습니다. 따라서 데이터 프레임에 새 열을을 삽입합니다. 앞에 놓고 싶을 때 insertcols! 함수를 사용합니다.

In [126]:
insertcols!(conf_boot,1,:statistic=>["boot lo","boot hi","parametric lo", "parametric hi"])
Out[126]:

4 rows × 9 columns

statistic(Intercept)lnnlincageage ^ 2educnycnocforeign: yes
StringFloat64Float64Float64Float64Float64Float64Float64Float64
1boot lo1.06625-0.943111.24434-0.397132-0.0154263-0.937041-0.2519840.469018
2boot hi6.59637-0.4246062.91247-0.1914570.0543084-0.525028-0.05038980.972134
3parametric lo0.992006-0.925491.28073-0.392235-0.015937-0.911238-0.2467180.476589
4parametric hi6.50591-0.4083742.86989-0.1964560.0543295-0.517691-0.04725090.95217

데이터 프레임도 전치될 수 있습니다. 그러나 대상 데이터 프레임에서 열 이름으로 사용할 열을 제공해야 합니다(데이터 프레임 개체에는 열 이름이 있어야 함).

In [127]:
conf_boot_t = permutedims(conf_boot, :statistic)
Out[127]:

8 rows × 5 columns

statisticboot loboot hiparametric loparametric hi
StringFloat64Float64Float64Float64
1(Intercept)1.066256.596370.9920066.50591
2lnnlinc-0.94311-0.424606-0.92549-0.408374
3age1.244342.912471.280732.86989
4age ^ 2-0.397132-0.191457-0.392235-0.196456
5educ-0.01542630.0543084-0.0159370.0543295
6nyc-0.937041-0.525028-0.911238-0.517691
7noc-0.251984-0.0503898-0.246718-0.0472509
8foreign: yes0.4690180.9721340.4765890.95217

계수의 추정치를 표에 추가해 보겠습니다.

In [128]:
coef(probit)
Out[128]:
8-element Vector{Float64}:
  3.7489556492271108
 -0.6669318467301562
  2.075310025309943
 -0.2943452031363773
  0.01919626008510499
 -0.7144647481686731
 -0.1469845045078344
  0.7143798526957524
In [129]:
insertcols!(conf_boot_t,2,:estimate => coef(probit))
Out[129]:

8 rows × 6 columns

statisticestimateboot loboot hiparametric loparametric hi
StringFloat64Float64Float64Float64Float64
1(Intercept)3.748961.066256.596370.9920066.50591
2lnnlinc-0.666932-0.94311-0.424606-0.92549-0.408374
3age2.075311.244342.912471.280732.86989
4age ^ 2-0.294345-0.397132-0.191457-0.392235-0.196456
5educ0.0191963-0.01542630.0543084-0.0159370.0543295
6nyc-0.714465-0.937041-0.525028-0.911238-0.517691
7noc-0.146985-0.251984-0.0503898-0.246718-0.0472509
8foreign: yes0.714380.4690180.9721340.4765890.95217

이제 좀 더 발전된 것들을 위한 시간입니다. 신뢰 구간의 끝을 유지하는 열(3에서 6까지의 열)을 추정치의 절대 편차로 변환하려고 합니다. 이러한 변환은 플로팅에 유용합니다.

In [130]:
select!(conf_boot_t,:statistic, :estimate,3:6 .=> x->abs.(x .- conf_boot_t.estimate),
        renamecols=false)
Out[130]:

8 rows × 6 columns

statisticestimateboot loboot hiparametric loparametric hi
StringFloat64Float64Float64Float64Float64
1(Intercept)3.748962.682712.847412.756952.75695
2lnnlinc-0.6669320.2761780.2423260.2585580.258558
3age2.075310.8309660.8371630.7945840.794584
4age ^ 2-0.2943450.1027870.1028890.09788950.0978895
5educ0.01919630.03462260.03511210.03513330.0351333
6nyc-0.7144650.2225760.1894370.1967740.196774
7noc-0.1469850.1049990.09659470.09973360.0997336
8foreign: yes0.714380.2453620.2577540.237790.23779
In [138]:
scatter(0.05 .+ (1:8),conf_boot_t.estimate,
        yerror=(conf_boot_t."boot lo",conf_boot_t."boot hi"),
        label="bootstrap",
        xticks=(1:8,conf_boot_t.statistic), xrotation=45)
scatter!(-0.05 .+ (1:8), conf_boot_t.estimate,
        yerror=(conf_boot_t."parametric lo",conf_boot_t."parametric hi"),
        label="parametric")
Out[138]:
  • 보시다시피 이 경우 두 유형의 간격이 매우 가깝습니다. 마치기 전에 Bootstrap.jl 패키지를 사용하여 동일한 작업을 수행할 수 있었던 방법에 대한 샘플을 살펴보겠습니다. 이번에는 통계를 컴파일하는 함수가 Bootstrap.jl 패키지에 의해 처리되므로 샘플링을 수행할 필요가 없습니다.
In [139]:
function boot_probit(df_boot)
  probit_boot = glm(@formula(lfp ~ lnnlinc + age + age^2 + educ + nyc + noc + foreign),
                    df_boot, Binomial(), ProbitLink())
  return (;(Symbol.(coefnames(probit_boot)) .=> coef(probit_boot))...)
end
Out[139]:
boot_probit (generic function with 1 method)
In [156]:
Random.seed!(1234)
@time bs = bootstrap(boot_probit,df,BasicSampling(1000))
  2.383596 seconds (3.89 M allocations: 787.187 MiB, 5.19% gc time, 66.83% compilation time)
Out[156]:
Bootstrap Sampling
  Estimates:
     Var │ Estimate    Bias          StdError
         │ Float64     Float64       Float64
    ─────┼─────────────────────────────────────
       1 │  3.74896     0.0479617    1.36775
       2 │ -0.666932   -0.0106807    0.130741
       3 │  2.07531     0.0370709    0.422754
       4 │ -0.294345   -0.00502473   0.0521531
       5 │  0.0191963   0.000703102  0.0181475
       6 │ -0.714465   -0.0106769    0.108667
       7 │ -0.146985   -0.00404358   0.051472
       8 │  0.71438     0.0094247    0.123851
  Sampling: BasicSampling
  Samples:  1000
  Data:     DataFrame: { 872 × 8 }

다음으로 95% 백분위수 신뢰 구간을 계산합니다.

In [161]:
bs_ci = confint(bs, PercentileConfInt(0.95))
Out[161]:
((3.7489556492271108, 1.066245983435171, 6.596366029686565), (-0.6669318467301562, -0.9431096226752889, -0.42460580882388094), (2.075310025309943, 1.2443435825374765, 2.9124734945385455), (-0.2943452031363773, -0.3971318919150751, -0.19145657341949654), (0.01919626008510499, -0.01542631836583384, 0.05430837906449797), (-0.7144647481686731, -0.9370411722162185, -0.5250276743069752), (-0.1469845045078344, -0.25198378115277537, -0.05038976115323891), (0.7143798526957524, 0.4690178809315581, 0.972134322270289))

결과가 수동 간격과 일치하는지 확인하겠습니다. 먼저 튜플이 포함된 데이터 프레임에 새 열을 만듭니다. 이것은 DataFrames.jl에서 문제 없이 처리되는 중첩 데이터 구조의 일반적인 예입니다.

이전 계산과 일치하도록 추정치에서 신뢰 구간의 하한 및 상한 편차를 계산합니다.

In [163]:
conf_boot_t.bootstrap = [(ci[1],ci[1]-ci[2],ci[3]-ci[1]) for ci in bs_ci]
Out[163]:
8-element Vector{Tuple{Float64, Float64, Float64}}:
 (3.7489556492271108, 2.68270966579194, 2.847410380459454)
 (-0.6669318467301562, 0.2761777759451327, 0.24232603790627522)
 (2.075310025309943, 0.8309664427724666, 0.8371634692286025)
 (-0.2943452031363773, 0.10278668877869779, 0.10288862971688076)
 (0.01919626008510499, 0.03462257845093883, 0.03511211897939298)
 (-0.7144647481686731, 0.2225764240475454, 0.18943707386169795)
 (-0.1469845045078344, 0.10499927664494096, 0.09659474335459549)
 (0.7143798526957524, 0.24536197176419433, 0.2577544695745365)
In [165]:
conf_boot_t
Out[165]:

8 rows × 7 columns

statisticestimateboot loboot hiparametric loparametric hibootstrap
StringFloat64Float64Float64Float64Float64Tuple…
1(Intercept)3.748962.682712.847412.756952.75695(3.74896, 2.68271, 2.84741)
2lnnlinc-0.6669320.2761780.2423260.2585580.258558(-0.666932, 0.276178, 0.242326)
3age2.075310.8309660.8371630.7945840.794584(2.07531, 0.830966, 0.837163)
4age ^ 2-0.2943450.1027870.1028890.09788950.0978895(-0.294345, 0.102787, 0.102889)
5educ0.01919630.03462260.03511210.03513330.0351333(0.0191963, 0.0346226, 0.0351121)
6nyc-0.7144650.2225760.1894370.1967740.196774(-0.714465, 0.222576, 0.189437)
7noc-0.1469850.1049990.09659470.09973360.0997336(-0.146985, 0.104999, 0.0965947)
8foreign: yes0.714380.2453620.2577540.237790.23779(0.71438, 0.245362, 0.257754)

하지만 조금 불편합니다.

먼저 :bootstrap 열을 3개의 열로 중첩 해제합니다.

In [166]:
select!(conf_boot_t, Not(:bootstrap), :bootstrap=>["estimate 2","boot lo 2","boot hi 2"])
Out[166]:

8 rows × 9 columns

statisticestimateboot loboot hiparametric loparametric hiestimate 2boot lo 2boot hi 2
StringFloat64Float64Float64Float64Float64Float64Float64Float64
1(Intercept)3.748962.682712.847412.756952.756953.748962.682712.84741
2lnnlinc-0.6669320.2761780.2423260.2585580.258558-0.6669320.2761780.242326
3age2.075310.8309660.8371630.7945840.7945842.075310.8309660.837163
4age ^ 2-0.2943450.1027870.1028890.09788950.0978895-0.2943450.1027870.102889
5educ0.01919630.03462260.03511210.03513330.03513330.01919630.03462260.0351121
6nyc-0.7144650.2225760.1894370.1967740.196774-0.7144650.2225760.189437
7noc-0.1469850.1049990.09659470.09973360.0997336-0.1469850.1049990.0965947
8foreign: yes0.714380.2453620.2577540.237790.237790.714380.2453620.257754

Next reorder the columns using regular expressions.

In [168]:
select(conf_boot_t,:statistic,r"estimate",r"lo",r"hi")
Out[168]:

8 rows × 9 columns

statisticestimateestimate 2boot loparametric loboot lo 2boot hiparametric hiboot hi 2
StringFloat64Float64Float64Float64Float64Float64Float64Float64
1(Intercept)3.748963.748962.682712.756952.682712.847412.756952.84741
2lnnlinc-0.666932-0.6669320.2761780.2585580.2761780.2423260.2585580.242326
3age2.075312.075310.8309660.7945840.8309660.8371630.7945840.837163
4age ^ 2-0.294345-0.2943450.1027870.09788950.1027870.1028890.09788950.102889
5educ0.01919630.01919630.03462260.03513330.03462260.03511210.03513330.0351121
6nyc-0.714465-0.7144650.2225760.1967740.2225760.1894370.1967740.189437
7noc-0.146985-0.1469850.1049990.09973360.1049990.09659470.09973360.0965947
8foreign: yes0.714380.714380.2453620.237790.2453620.2577540.237790.257754

지원되는 다양한 colummn selector에 대한 자세한 설명은 여기에서 찾을 수 있습니다.

이제 수동 계산이 Bootstrap.jl 패키지와 정확히 동일한 결과를 생성한다는 것을 더 쉽게 알 수 있습니다.

완료하기 전에 :estimate로 데이터 프레임을 정렬합니다.

In [169]:
sort(conf_boot_t,:estimate)
Out[169]:

8 rows × 9 columns

statisticestimateboot loboot hiparametric loparametric hiestimate 2boot lo 2boot hi 2
StringFloat64Float64Float64Float64Float64Float64Float64Float64
1nyc-0.7144650.2225760.1894370.1967740.196774-0.7144650.2225760.189437
2lnnlinc-0.6669320.2761780.2423260.2585580.258558-0.6669320.2761780.242326
3age ^ 2-0.2943450.1027870.1028890.09788950.0978895-0.2943450.1027870.102889
4noc-0.1469850.1049990.09659470.09973360.0997336-0.1469850.1049990.0965947
5educ0.01919630.03462260.03511210.03513330.03513330.01919630.03462260.0351121
6foreign: yes0.714380.2453620.2577540.237790.237790.714380.2453620.257754
7age2.075310.8309660.8371630.7945840.7945842.075310.8309660.837163
8(Intercept)3.748962.682712.847412.756952.756953.748962.682712.84741

다음은 신뢰 구간의 너비를 기준으로 행을 정렬하는 고급 예입니다.

In [170]:
conf_boot_t[sortperm(conf_boot_t."boot hi" + conf_boot_t."boot lo"),:]
Out[170]:

8 rows × 9 columns

statisticestimateboot loboot hiparametric loparametric hiestimate 2boot lo 2boot hi 2
StringFloat64Float64Float64Float64Float64Float64Float64Float64
1educ0.01919630.03462260.03511210.03513330.03513330.01919630.03462260.0351121
2noc-0.1469850.1049990.09659470.09973360.0997336-0.1469850.1049990.0965947
3age ^ 2-0.2943450.1027870.1028890.09788950.0978895-0.2943450.1027870.102889
4nyc-0.7144650.2225760.1894370.1967740.196774-0.7144650.2225760.189437
5foreign: yes0.714380.2453620.2577540.237790.237790.714380.2453620.257754
6lnnlinc-0.6669320.2761780.2423260.2585580.258558-0.6669320.2761780.242326
7age2.075310.8309660.8371630.7945840.7945842.075310.8309660.837163
8(Intercept)3.748962.682712.847412.756952.756953.748962.682712.84741

데이터 프레임 정렬의 더 많은 예는 여기에서 찾을 수 있습니다.

오늘은 여기까지입니다

튜토리얼을 즐겼기를 바랍니다.

DataFrames.jl에 대해 자세히 알아보려면 주저하지 말고 여기 문서를 방문하십시오.

In [ ]: