1) 연관규칙을 위해 lotto
데이터셋을 transaction
데이터로 변환하시오. (단, 로또번호가 추첨된 순서는 고려하지 않음) 그리고 변환된 데이터에서 가장 많이 등장한 10개의 번호를 순서대로 출력하고 이를 설명하시오.
transaction
데이터로 변환하기 위해서는 as()
를 사용하여 변환할 수 있음. 하지만 이것만 알면 안되고 현재 lott
o라는 데이터 셋이 어떻게 되어 있는지 알아야함. lotto
는 time_id
로또회차, numM
해당 회의 N번 째 당첨번호 6개로 총 7개의 열과 859개의 행으로 이루어져있음. 따라서 각 해당 회차에는 6개의 열로 번호가 표현되어 있음. 이를 하나로 통합한 후 집합처럼 묶어줄것임 이를 위해 reshape2
패키지의 melt()
함수와 split()
함수를 사용
lotto = read.table('clipboard', sep='\t', header=T)
head(lotto)
time_id num1 num2 num3 num4 num5 num6
1 859 8 22 35 38 39 41
2 858 9 13 32 38 39 43
3 857 6 10 16 28 34 38
4 856 10 24 40 41 43 44
5 855 8 15 17 19 43 44
6 854 20 25 31 32 36 43
str(lotto)
'data.frame': 859 obs. of 7 variables:
$ time_id: int 859 858 857 856 855 854 853 852 851 850 ...
$ num1 : int 8 9 6 10 8 20 2 11 14 16 ...
$ num2 : int 22 13 10 24 15 25 8 17 18 20 ...
$ num3 : int 35 32 16 40 17 31 23 28 22 24 ...
$ num4 : int 38 38 28 41 19 32 26 30 26 28 ...
$ num5 : int 39 39 34 43 43 36 27 33 31 36 ...
$ num6 : int 41 43 38 44 44 43 44 35 44 39 ...
# 연관분석을 위한 transaction 데이터 만들기
ibrary(reshape2) # 업로드
lot_melt = melt(lotto, id.vars=1) # 1열을 기준으로 나머지 열을 전부 하나의 열로 맞추기
lot_melt2 = lot_melt[,-2] # 2번째 열은 variable이므로 의미가 없음
str(lot_melt2) # 구조 확인
'data.frame': 5154 obs. of 2 variables:
$ time_id: int 859 858 857 856 855 854 853 852 851 850 ...
$ value : int 8 9 6 10 8 20 2 11 14 16 ...
library(arules) # as함수를 사용하기 위해 라이브러리 업로드
lot_sp = split(lot_melt2$value, lot_melt2$time_id) # id를 기준으로 구분 짓고 하나로 통합
lot_ts = as(lot_sp, 'transactions') # transactions 데이터로 전환
inspect(lot_ts) # transactions 데이터를 출력하기 위해서 inspect 함수를 사용
items transactionID
[1] {10,23,29,33,37,40} 1
[2] {9,13,21,25,32,42} 2
[3] {11,16,19,21,27,31} 3
[4] {14,27,30,31,40,42} 4
[5] {16,24,29,40,41,42} 5
[6] {14,15,26,27,40,42} 6
[7] {2,9,16,25,26,40} 7
[8] {8,19,25,34,37,39} 8
[9] {2,4,16,17,36,39} 9
[10] {9,25,30,33,41,44} 10
# 상위 10개의 로또 번호를 확인
itemFrequencyPlot(lot_ts, topN=10, type='absolute') # 절대도수를 기준으로 파악
itemFrequencyPlot(lot_ts, topN=10) # 상대도수를 기준으로 파악
유독 34라는 수가 높은 비율로 등장한 것을 확인할 수 있으며 나머지 수에서 큰 비율의 차이가 있지는 않음
2) 변환한 데이터에 대해 apriori()
함수를 사용하여 다음 괄호 안의 조건을 반영하여 연관규칙을 생성하고, 이를 'rules_1'
라는 변수에 저장하여 결과를 해석하시오. (최소지지도:0.002, 최소신뢰도:0.8, 최소조합 항목 수:2, 최대조합 항목 수:6)그리고 도출된 연관규칙들을 향상도를 기준으로 내림차순 정렬하여 상위 30개의 규칙을 확인하고 이를 데이터 프레임 형태로 반환하여 CSV파일로 출력하시오
# 연관분석 수행
metric_params = list(supp=0.002, conf=0.8, minlen=2, maxlen=6) # parameter list생성
rule_1 = apriori(lot_ts, parameter = metric_params) # apriori()를 이용해서 결과 생성, -> 총 679개의 데이터가 생성되었다.
inspect(rule_1[1:20,]) # 해당 규칙 20개만 확인
lhs rhs support confidence lift count
[1] {9,32,43} => {38} 0.002328289 1 7.601770 2
[2] {9,38,43} => {32} 0.002328289 1 8.855670 2
[3] {32,38,43} => {9} 0.002328289 1 9.651685 2
[4] {9,23,28} => {7} 0.002328289 1 7.535088 2
[5] {7,9,23} => {28} 0.002328289 1 8.180952 2
[6] {7,9,28} => {23} 0.002328289 1 8.676768 2
[7] {7,23,28} => {9} 0.002328289 1 9.651685 2
[8] {9,23,35} => {18} 0.002328289 1 7.099174 2
[9] {9,18,23} => {35} 0.002328289 1 8.103774 2
[10] {9,18,35} => {23} 0.002328289 1 8.676768 2
[11] {18,23,35} => {9} 0.002328289 1 9.651685 2
[12] {1,9,23} => {12} 0.002328289 1 6.983740 2
[13] {5,9,30} => {43} 0.002328289 1 6.872000 2
[14] {9,30,43} => {5} 0.002328289 1 7.218487 2
[15] {6,9,28} => {1} 0.002328289 1 7.040984 2
[16] {9,21,29} => {1} 0.002328289 1 7.040984 2
[17] {9,17,29} => {1} 0.002328289 1 7.040984 2
[18] {9,27,29} => {40} 0.002328289 1 6.817460 2
[19] {9,29,40} => {27} 0.002328289 1 6.817460 2
[20] {9,27,40} => {29} 0.002328289 1 8.103774 2
# lift를 기준으로 규칙을 내림차순 정렬한 후, 30개의 규칙을 확인
rule_2 = sort(rule_1, by = 'lift', decreasing = T)
inspect(rule_2[1:30,])
lhs rhs support confidence lift count
[1] {32,38,43} => {9} 0.002328289 1 9.651685 2
[2] {7,23,28} => {9} 0.002328289 1 9.651685 2
[3] {18,23,35} => {9} 0.002328289 1 9.651685 2
[4] {14,17,33} => {9} 0.002328289 1 9.651685 2
[5] {7,23,29} => {22} 0.002328289 1 9.336957 2
[6] {10,27,42} => {22} 0.002328289 1 9.336957 2
[7] {25,31,45} => {22} 0.002328289 1 9.336957 2
[8] {21,26,37} => {22} 0.002328289 1 9.336957 2
[9] {24,36,38} => {22} 0.002328289 1 9.336957 2
[10] {7,24,31} => {22} 0.002328289 1 9.336957 2
[11] {7,31,34} => {22} 0.002328289 1 9.336957 2
[12] {33,36,37} => {22} 0.002328289 1 9.336957 2
[13] {10,34,36} => {22} 0.002328289 1 9.336957 2
[14] {10,34,36,44} => {22} 0.002328289 1 9.336957 2
[15] {7,24,31,34} => {22} 0.002328289 1 9.336957 2
[16] {9,38,43} => {32} 0.002328289 1 8.855670 2
[17] {14,29,33} => {32} 0.002328289 1 8.855670 2
[18] {14,36,42} => {32} 0.002328289 1 8.855670 2
[19] {3,24,45} => {32} 0.002328289 1 8.855670 2
[20] {3,13,31} => {32} 0.002328289 1 8.855670 2
[21] {3,12,18} => {32} 0.002328289 1 8.855670 2
[22] {3,18,40} => {32} 0.002328289 1 8.855670 2
[23] {10,18,31} => {32} 0.002328289 1 8.855670 2
[24] {17,18,31} => {32} 0.002328289 1 8.855670 2
[25] {17,33,34} => {32} 0.003492433 1 8.855670 3
[26] {7,9,28} => {23} 0.002328289 1 8.676768 2
[27] {9,18,35} => {23} 0.002328289 1 8.676768 2
[28] {28,30,44} => {23} 0.002328289 1 8.676768 2
[29] {25,38,42} => {23} 0.002328289 1 8.676768 2
[30] {18,21,39} => {23} 0.002328289 1 8.676768 2
rule_2 = as(rule_2, 'data.frame') # data.frame으로 만들기
# 문제의 조건처럼 csv파일로 저장
write.csv(rule_2, file='___')
우선 로또의 경우 대표적인 독립적인 확률이기 때문에 아무런 연관성이 없을 것으로 생각이 크지만, 연관분석 결과 lift값이 생각보다 높게 측정되었음을 알 수 있다. 하지만, 지지도의 값이 0.002(0.2%)보다 약간 큰 값으로 역시 매우 연관성이 없을 알 수 있다.
3) 생성된 연관규칙 'rules_1'
에 대한 정보를 해석하고 , 1)번 문제를 통해 확인했을 때 가장 많이 추첨된 번호가 우측항에 존재하는 규칙들만을 'rules_most_freq'라는 변수에 저장하고 해당 규칙들을 해석하여 인사이트를 도출하시오.
# rule_1의 정보를 summary()함수로 요약
summary(rule_1)
set of 679 rules
rule length distribution (lhs + rhs):sizes
4 5
632 47
Min. 1st Qu. Median Mean 3rd Qu. Max.
4.000 4.000 4.000 4.069 4.000 5.000
summary of quality measures:
support confidence lift count
Min. :0.002328 Min. :1 Min. :6.410 Min. :2.000
1st Qu.:0.002328 1st Qu.:1 1st Qu.:7.041 1st Qu.:2.000
Median :0.002328 Median :1 Median :7.280 Median :2.000
Mean :0.002364 Mean :1 Mean :7.434 Mean :2.031
3rd Qu.:0.002328 3rd Qu.:1 3rd Qu.:7.670 3rd Qu.:2.000
Max. :0.003492 Max. :1 Max. :9.652 Max. :3.000
mining info:
data ntransactions support confidence
lot_ts 859 0.002 0.8
# 679개의 연관규칙분석이 도출되었으며, 그 중 632개의 규칙은 4개의 로또번호로 구성
# 47개의 규칙은 5개의 번호로 구성되어 있음
# 향상도의 최소값은 6.410으로 꽤 높으며
# 지지도의 평균값은 0.002364로 같이포함될 확률이 1%도 되지 않음
rule_most_freq = subset(rule_1,rhs %in% '34')
inspect(rule_most_freq)
# 19개의 규칙이 도출
# 1번의 규칙 살펴보면 7, 22, 31이 뽑힌난 뒤에 34개 뽑힐 지지도는 0.002328289로 0.2%를 이야기하고 있다.
# 여기서 향상도 값이 6.4 이상으로 높은 값을 유지하고 있으나,
# 본 연관 분석의 문제는 단순히 조합에 의한 연관성을 분석했다는 것에 의해
# 분 분석에서 높은 확률로 나타날 조합이 꼭 로또 번호가 되는 것은 아니다.
FIFA = read.csv('FIFA.csv')
head(FIFA)
str(FIFA) # 구조 확인
sum(is.na(FIFA)) # 결측치 없음
# 1. height 변수의 피트, 인치 단위로 저장된 키에 대한 데이터를 cm 단위의 값으로 변환하고, Height_cm에 저장한다.
# Height는 factor형 자료임 바로 수치로 바꿀 수 없음
FIFA$Height = as.character(FIFA$Height) # 문자형으로 바꾸었다가 수치형으로 바꾸어야함.
# Height 변수를 보면 ' 가 껴있음... 앞의 숫자는 피트 뒤의 숫자는 인치를 뜻함.
# 따라서 앞의 숫자는 30을, 뒤의 숫자는 2.5를 곱하고 더해야 cm가 나옴.
# substr()은 문자열의 부분을 의미함. 피트는 두 자리 수일수 없으므로 1번째만 짜르고 3번째부터 끝까지가 숫자임
FIFA$Height_cm = as.numeric(substr(FIFA$Height, 1, regexpr("'",FIFA$Height)-1)) * 30 +
as.numeric(substr(FIFA$Height, regexpr("'",FIFA$Height)+1, nchar(FIFA$Height))) * 2.5
# regexpr는 패턴이 데이터에서 등장하는 index를 반환해줌
2) 포지션을 의미하는 Position변수를 'Forward', 'Mildfielder', 'Defender', 'GoalKeeper'로 재범주화화고 Factor형으로 변환 Position_class라는 변수를 생성하고 저장하시오
# within()이라는 함수를 이용하여 선수의 포지션을 의미하는 position변수를 재범주화! -> Position_Class라는 변수에 저장
FIFA = within(FIFA,{
position_Class = character(0)
position_Class[Position %in% c('LS', 'ST', 'RS', 'LW', 'LF', 'CF', 'RF', 'RW')] = 'Forward'
position_Class[Position %in% c('LAM', 'CAM', 'RAM', 'LM', 'LCM', 'CM', 'RCM', 'RM')] = 'Midfielder'
position_Class[Position %in% c('LWB', 'LDM', 'CDM', 'RDM', 'RWB', 'LB', 'LCB', 'CB', 'RCB', 'RB')] = 'Defender'
position_Class[Position == 'GK'] = 'GoalKeeper'
})
FIFA$position_Class = factor(FIFA$position_Class,
levels=c('Forward', 'Midfielder', 'Defender','GoalKeeper'),
labels = c('Forward', 'Midfielder', 'Defender', 'GoalKeeper'))
str(FIFA)
FIFA_aov = aov(Value~position_Class, data = FIFA)
summary(FIFA_aov)
Df Sum Sq Mean Sq F value Pr(>F)
position_Class 3 4.081e+09 1.360e+09 41.87 <2e-16 ***
Residuals 16638 5.405e+11 3.249e+07
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
- 유의확률이 2e-16보다 작으므로 포지션에 따른 연봉의 차이는 유의미함. 단지 일원배치 분산분석은 집단 간의 평균에 차이가 있는지에 대한 분석이므로 어떤 집단이 더 큰 연봉을 갖고 있는지는 알 수 없음. 이를 위해 TukeyHSD분석을 실시 해야함.
# R의 TukeyHSD함수를 이용하여 4가지 포지션들 중 특히나 어떠한 포지션들 간에 선수의 시장가치에 차이가 있는지를 파악하기 위한 사후검정!
# 분산분석의 사후분석은 TukeyHSD()함수
TukeyHSD(FIFA_aov)
Tukey multiple comparisons of means
95% family-wise confidence level
Fit: aov(formula = Value ~ position_Class, data = FIFA)
$position_Class
diff lwr upr p adj
Midfielder-Forward -169.4944 -507.0010 168.0122 0.5691282
Defender-Forward -930.3730 -1250.0048 -610.7412 0.0000000
GoalKeeper-Forward -1437.7579 -1865.9257 -1009.5900 0.0000000
Defender-Midfielder -760.8787 -1035.0465 -486.7108 0.0000000
GoalKeeper-Midfielder -1268.2635 -1663.6509 -872.8761 0.0000000
GoalKeeper-Defender -507.3848 -887.6282 -127.1415 0.0034079
- 사후분석에서는 귀무가설:
집단들 사이의 평균은 같다.
대립가설:집단들 사이의 평균은 같지않다.
로 두고, 모든 집단 수준에 대해서 두 집단씩 짝을 지어 각각 다중비교한다. 사후분석 결과 1개의 두 집단인Midfielder-Forward
를 제외한 나머지 모든 집단에서 연봉의 차이가 통계적으로 유의미하다고 도출되었다. 따라서Midfielder
와Forward
둘 중 연봉이 어느 집단이 높은지는 말할 수 없다. 하지만 다른 결과들을 보았을 때, 그 차이가 명확한 점을 알아야한다.
4) Preferred_Foot(주로 사용하는 발)과 Position_Class(재범주화 된 포지션)변수에 따라 Value(연봉)의 차이가 있는지를 알아보기 위해 이원배치분산분석을 수행하고 결과를 해석 하시오.
귀무가설
- 선수의 주발에 따른 선수의 연봉에는 차이가 없다.
- 선수의 포지션에 따른 선수의 연봉에는 차이가 없다.
- 주발과 포지션 간 상호작용 효과는 없다.
대립가설
- 선수의 주발에 따른 선수의 연봉에는 차이가 있다.
- 선수의 포지션에 따른 선수의 연봉에는 차이가 있다.
- 주발과 포지션 간 상호작용 효과는 있다.
FIFA_aov2 = aov(Value~Preferred_Foot+
position_Class+
Preferred_Foot:position_Class,
data = FIFA)
summary(FIFA_aov2)
Df Sum Sq Mean Sq F value Pr(>F)
Preferred_Foot 1 1.461e+08 1.461e+08 4.501 0.03390
position_Class 3 4.087e+09 1.362e+09 41.976 < 2e-16
Preferred_Foot:position_Class 3 4.736e+08 1.579e+08 4.864 0.00221
Residuals 16634 5.399e+11 3.246e+07
Preferred_Foot *
position_Class ***
Preferred_Foot:position_Class **
Residuals
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
- 이원배치 분산분석 결과
5%
유의수준 하에서 주발의 유의확률은0.03390
, 포지션의 유의확률은2e-16
로 귀무가설을 기각하여 대립가설을 선택한다. 이를 통해 주발과 포지션은 선수의 연봉의 차이에 영향을 미치고 있었으며, 주발과 포지션은 상호작용효과를 갖고 있었다. 단 이원배치 분산분석을 통해 연봉 변동의 원인이 주발과 포지션이라는 것은 알 수 있으나, 이 요소가+
방향으로 영향을 주는지-
방향으로 영향을 주는지 알 수 없다.
바로 회귀분석을 돌려보자
step(lm(Value~1, # 처음 회귀분석을 돌릴때의 모습
data=FIFA),
scope=list(lower=~1, # 상수 회귀에서
upper=~Age+Overall+Wage+Height_cm+Weight_lb), # 문제에서 이야기한 모든 변수를 넣은 회귀
direction = 'both') # 단계적 선택법
Start: AIC=287969.5
Value ~ 1
Df Sum of Sq RSS AIC
+ Wage 1 4.0424e+11 1.4037e+11 265409
+ Overall 1 2.1561e+11 3.2900e+11 279584
+ Age 1 3.1824e+09 5.4143e+11 287874
+ Weight_lb 1 1.0611e+09 5.4355e+11 287939
<none> 5.4461e+11 287969
+ Height_cm 1 1.9447e+06 5.4461e+11 287971
Step: AIC=265408.8
Value ~ Wage
Df Sum of Sq RSS AIC
+ Overall 1 1.4741e+10 1.2563e+11 263564
+ Age 1 1.4800e+09 1.3889e+11 265234
+ Height_cm 1 1.3065e+08 1.4024e+11 265395
+ Weight_lb 1 7.9650e+07 1.4029e+11 265401
<none> 1.4037e+11 265409
- Wage 1 4.0424e+11 5.4461e+11 287969
Step: AIC=263564.5
Value ~ Wage + Overall
Df Sum of Sq RSS AIC
+ Age 1 1.1662e+10 1.1397e+11 261945
+ Weight_lb 1 7.0549e+08 1.2493e+11 263473
+ Height_cm 1 2.3851e+08 1.2539e+11 263535
<none> 1.2563e+11 263564
- Overall 1 1.4741e+10 1.4037e+11 265409
- Wage 1 2.0337e+11 3.2900e+11 279584
Step: AIC=261945.2
Value ~ Wage + Overall + Age
Df Sum of Sq RSS AIC
+ Height_cm 1 5.1404e+07 1.1392e+11 261940
+ Weight_lb 1 4.9934e+07 1.1392e+11 261940
<none> 1.1397e+11 261945
- Age 1 1.1662e+10 1.2563e+11 263564
- Overall 1 2.4922e+10 1.3889e+11 265234
- Wage 1 1.8259e+11 2.9656e+11 277858
Step: AIC=261939.7
Value ~ Wage + Overall + Age + Height_cm
Df Sum of Sq RSS AIC
<none> 1.1392e+11 261940
+ Weight_lb 1 6.1905e+06 1.1391e+11 261941
- Height_cm 1 5.1404e+07 1.1397e+11 261945
- Age 1 1.1475e+10 1.2539e+11 263535
- Overall 1 2.4906e+10 1.3883e+11 265228
- Wage 1 1.8264e+11 2.9656e+11 277860
Call:
lm(formula = Value ~ Wage + Overall + Age + Height_cm, data = FIFA)
Coefficients:
(Intercept) Wage Overall Age Height_cm
-8690.818 184.184 241.345 -202.160 -8.445
- 분석 결과 문제에서 이야기한 모든 변수를 회귀모형의 독립변수로 대입하였을 때,
AIC
값이 가장 낮았으며, 최적의 모형이라 할 수 있다. 따라서 문제에서 이야기한 단계적 선택법을 활용한 최적의 회귀모형은 다음과 같다.
y = -8690.818 + 184.184*Wage + 241.345*Overall - 202.160*Age - 8.445*Height_cm
- 이 변수들을 선택하여 회귀분석을 실시한다.
FIFA_lm = lm(formula = Value ~ Wage + Overall + Age + Height_cm, data = FIFA)
summary(FIFA_lm)
Call:
lm(formula = Value ~ Wage + Overall + Age + Height_cm, data = FIFA)
Residuals:
Min 1Q Median 3Q Max
-24272 -837 -120 668 58287
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -8690.818 588.280 -14.77 < 2e-16 *** # 유의
Wage 184.184 1.128 163.32 < 2e-16 *** # 유의
Overall 241.345 4.002 60.31 < 2e-16 *** # 유의
Age -202.160 4.938 -40.94 < 2e-16 *** # 유의
Height_cm -8.445 3.082 -2.74 0.00615 ** # 유의
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2617 on 16637 degrees of freedom
Multiple R-squared: 0.7908, Adjusted R-squared: 0.7908
F-statistic: 1.572e+04 on 4 and 16637 DF, p-value: < 2.2e-16
- 회귀분석 결과 모든 독립변수가
5%
유의수준 하에서 통계적으로 유의미함을 알 수 있으며, 주급과 선수의 능력치는 연봉에+
영향을 나이와 키는-
영향을 주는 것을 알 수 있다. 특히나 축구선수의 빠른 은퇴의 원인으로 빠르게 감소하는 연봉이 어느 정도 영향을 미쳤을 것이라는 인사이트를 이야기할 수 있다. 모형의R_squared
값도0.7908
로 전체 변동의 약79%
설명할 만큼 좋은 결과를 내놓은 것을 확인할 수 있다. 모형의 유의성 역시 유의확률이2.2e-16
보다 작게 나와0.01%
에서도 유의함을 알 수 있다.
- 데이터부터 로드해보자.
# 필요 패키지 로드와 데이터 불러오기 시작
library(tm) # TermMartix
library(rJava) # KoNLP 패키지를 위한 rjava 불러오기
library(KoNLP) # KoNLP 불러오기
library(wordcloud)
library(plyr) # 데이터 전처리를 위한 plyr
useNIADic() # 사전 로드
useSejongDic()
setwd('C:/Users/junkyu/Desktop/DATA ANALYSIS') # 워킹디렉토리 설정
movie = readLines('영화 기생충_review.txt') # 영화 리뷰 데이터 불러오기
dic = readLines('영화 기생충_사전.txt') # 영화 관련 사전 데이터 불러오기
buildDictionary(ext_dic = 'woorimalsam',
user_dic = data.frame(readLines('영화 기생충_사전.txt'),
'ncn'),
replace_usr_dic = T) # 사전 등록
movie[1:10] # 데이터 확인
[1] "별1개 준 사람들은 나베당임"
[2] "역쉬"
[3] "영화가 끝나고 가슴이 먹먹하고 답답햇습니다 너무나 충격적이었습니다.."
[4] "지금까지 나온 감독의 모든 작품이 압축되어있다는 느낌을 받음. Bomb!!!"
[5] "대단한 영화. 몰입력 장난아님. 후아"
[6] "그닥"
[7] "칸하고 안맞나봄."
[8] "봉준호식의 코메디와 사회비판 페이소스"
[9] "좋았습니다"
[10] "군더더기 없이 깔끔한 영화, 지금도 영화가 주는 메세지를 생각하는 중입니다"
# 텍스트 전처리를 위한 함수 작성
clean_txt = function(txt) {
txt = tolower(txt) # 소문자로 변환
txt = removePunctuation(txt) # 구두점 제거
txt = removeNumbers(txt) # 숫자 제거
txt = stripWhitespace(txt) # 공백제거
return(txt)
}
movie_clean = clean_txt(movie) # 전처리 시행
movie_clean[1:10] # 데이터 확인
[1] "별개 준 사람들은 나베당임"
[2] "역쉬"
[3] "영화가 끝나고 가슴이 먹먹하고 답답햇습니다 너무나 충격적이었습니다"
[4] "지금까지 나온 감독의 모든 작품이 압축되어있다는 느낌을 받음 bomb"
[5] "대단한 영화 몰입력 장난아님 후아"
[6] "그닥"
[7] "칸하고 안맞나봄"
[8] "봉준호식의 코메디와 사회비판 페이소스"
[9] "좋았습니다"
[10] "군더더기 없이 깔끔한 영화 지금도 영화가 주는 메세지를 생각하는 중입니다"
- 전처리 시행 후 쓸모없는 빈칸이나 대문자, 구두점, 숫자, 공백이 제거 되었음을 확인할 수 있다. 그리고 이를
movie_clean
이라는 객체에 저장하였다.
tm
을 사용하기 위해서는VCorpus()
함수의 사용이 필수불가결이다. 따라서 이를 먼저 시행한 후에 tm을 사용하여TDM
을 구축해야한다.
# tm함수를 통해 데이터를 추가로 전처리 텍스트 데이터를 tm으로 만들려면
# 무조건 Corpus 형태의 데이터여야 함.
movie_C = VCorpus(VectorSource(movie))
clean_corpus= function(corpus) {
corpus = tm_map(corpus, stripWhitespace)
corpus = tm_map(corpus, removePunctuation)
corpus = tm_map(corpus, removeNumbers)
corpus = tm_map(corpus, content_transformer(tolower)) # content_transformer 이거 사용할 때 조심해야 함
return(corpus)
}
mov_clean = clean_corpus(movie_C)
dtm = TermDocumentMatrix(mov_clean, control=list(dictionary=dic))
m = as.matrix(dtm)
v = sort(rowSums(m), decreasing = T)
d = data.frame(word = names(v), freq = v)
head(d, 10)
word freq
봉준호 봉준호 78
송강호 송강호 29
기생충 기생충 17
이선균 이선균 10
조여정 조여정 10
최우식 최우식 4
이정은 이정은 3
박소담 박소담 2
박사장 박사장 1
장혜진 장혜진 1
- 즉
tm
패키지의Corpus()
함수와tm_map()
함수를 활용해 전처리를 하고TermDocumentMatrix()
를Dictionary
를 사전에 저장한dic
으로 하여dtm
에 저장했다. 그리고matrix
화하여 빈도를 내림차순으로 정렬하여v
에 저장하고d
라는data.frame
로 변환하여 단어 빈도를 체크했다. - 이후 단어 빈도를 바탕으로 막대그래프를 작성한다.
colors = rainbow(nrow(d)) # 색이 여러 개니까. rainbow로 다채롭게!
barplot(v[1:10], main='기생충 review 빈출 명사', col=colors)
legend('topright', names(v[1:10]), fill=colors, cex=0.7)

- barplot을 그린 결과 봉준호 감독에 대한 언급이 가장 많았고 그 다음 송강호, 기생충, 이선균 순이었다.
extracNoun()
함수를 활용해 명사를 추출한 후, 2음절 이상이고 최소 30번 이상 언급된 명사만 추출하여 워드클라우드를 작성한다.
movie_exN = sapply(movie_clean, extractNoun)
Noun = as.vector(unlist(movie_exN))
Noun_2 = Noun[nchar(Noun) >= 2]
result = data.frame(sort(table(Noun_2),
decreasing = T))
t = wordcloud(result$Noun_2,
result$Freq,
color=brewer.pal(8, 'Dark2'),
min.freq=30)

extractNoun()
함수를 활용해 명사를 추출하고, 단어가 2음절 이상이고 최소 30번 이상 나타났던 명사만 추출하여 워드 클라우드를 시각화 했다. 봉준호 감독에 대한 명사 등 영화 전체적인 관심이 동시에 감독에 대한 관심으로 표현되고 있음을 알 수 있으나, 불편과 같은 부정적인 감정에 대한 언급도 적지 않음을 알 수 있다.