텍스트 마이닝 감정 분석 넥플릭스 [지금 우리 학교는]

     지금 우리 학교는은  전세계  Netflilx에서 매우 Hot 하다.   나도 처음 나오자 마자 정주행 했던 것이다.    역시  한국 좀비 영화가 재미가 있다.  이유는 앞의 이야기를 예측을 할 수 없기 때문이다.   R에서 텍스트 마이닝에서 감정 분석 하는 것이 있다.      

Nexflix 화면

사람들의 반응은 어떠 하였는가 ?    나는 이것 보고 나서,  유튜브나, 넷플릿스 다른 것이 재미 없었다.    


만약에 R4.2버전에서  KoNLP가 설치 되어 있지 않았다면 아래의 패이지를 참고해서 설치 한다. 


https://rdmkyg.blogspot.com/2022/06/r-42-windows-rjava-kolnp.html

이버 댓글을 수집하는 패키지인 "N2H4" 패키지로  Naver News 의  댓글을 데이터화 시켰다.  여기에서 댓글 자체가 영화 평과 같다고 생각 하였다. 

일단,  필요한 패키지는 모두 불러오자.
# 라이브러리 불러오기 

## 불필요 객체 지우기  
  rm(list = ls())

## 패키지 불러오기
  pkgs <- c(
    # Data munipulate packages   
    "dplyr",
    "rio",
    "stringr",
    "httr",
    "ggwordcloud",
    "readr",
    "readxl",

    # crawl Packages
    "N2H4",
        
    # text mining packages
    "tidytext",
    "ggwordcloud",
    "KoNLP",
    "tidyr",
    "textclean",
    
    # graphic packages
    "ggplot2"
  )

  # 패키지 동시에 적용
  sapply(pkgs, require, character.only = T)

KoNLP의  사전을 불러온다. 

  useNIADic()
## Backup was just finished!
## 1213109 words dictionary was built.

아래는 네이버,  뉴스기사의 댓글을 가져온 것이다.
    
# 지금 우리 학교는 데이터 url   
  url  = "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=032&aid=0003126541"
  url1 = "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=052&aid=0001698211"
  url2 = "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=437&aid=0000288469"
  url3 = "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=020&aid=0003408896"
  url4 = "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=028&aid=0002577686"
  url5 = "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=052&aid=0001696962"
  url6 = "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=025&aid=0003170581"
  url7=  "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=079&aid=0003603645"
  url8 = "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=366&aid=0000791610"
  url9 = "https://news.naver.com/main/read.naver?mode=LSD&mid=shm&sid1=103&oid=214&aid=0001176617"
  
# 지금 우리 학교는 이라는 컨텐츠를 하나로 묶는다. 
  url_vt <-  c(url, url1, url2, url3, url4, url5, url6, url7, url8, url9)   
  rm(url, url1, url2, url3, url4, url5, url6, url7, url8, url9)
  
# 네이버 뉴스 댓글 가지고 오기   
  comment_get_fun <- function(x){

    comments <- getAllComment(url_vt[x]) %>% select(contents)
  
    return(list(comments))  
  }

  x = 1:length(url_vt)

# 뉴스 댓글 가져오는 함수 가져오기   
  mylist <- sapply(x, comment_get_fun)
raw_news_comment <-  tibble(do.call(rbind, mylist))

  head(raw_news_comment)
## # A tibble: 6 × 1
##   contents                                                                     
##   <chr>                                                                        
## 1 "우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재하기까지를 생각…
## 2 "짱개들은 좀비도이제 강시가 원조라고 우기겠군"                               
## 3 "도대체 댓글 24시간 내내 쓰는 놈들은 누구일까 ㅋㅋㅋㅋㅋㅋㅋㅋ"              
## 4 "하 🐕중국인들 넘쳐나네 ㅋㅋㅋㅋ 시진핑에 어머니 받칠 애들 ㅋㅋㅋㅋㅋㅋ"     
## 5 "짱OOO들 열심히 퍼다가 리뷰질 하드라.\n같쟎은 그 종자. 짱개"                 
## 6 "넷플 중국인 15억 뒤지는드라마 한편 만들면 전세계1위가능"

여기에서 뉴스기사를 가져올때,  가장 핵심인 함수는 "N2H4" 패키지의 getAllComment 라는 함수이며, 함수 안에 URL을 삽입하면, 댓글을 가져온다.

아래는 감정사전을 가져오는 것이다. 
  url = "https://drive.google.com/u/0/uc?id=14t6CqgzfNpJjU45W8HFmLBlHaI_FjOUw&export=download"
  GET(url, write_disk(tf <- tempfile(fileext = ".csv")))
  dic <- read_csv(tf) 
## Rows: 14854 Columns: 2
  rm(url,tf)

감정사전은  군산대학교 소프트웨어 융합 공학과에서 만든 'KNU 한국어 감성사전'을 이용하였다.  아래는 긍정 단어와,   부정단어가 있다.

# 긍정단어
  dic %>% 
    filter(polarity == 2) %>% 
    arrange(word)
## # A tibble: 2,602 × 2
##    word              polarity
##    <chr>                <dbl>
##  1 가능성이 늘어나다        2
##  2 가능성이 있다고          2
##  3 가능하다                 2
##  4 가볍고 상쾌하다          2
##  5 가볍고 상쾌한            2
##  6 가볍고 시원하게          2
##  7 가볍고 편안하게          2
##  8 가볍고 환하게            2
##  9 가운데에서 뛰어남        2
## 10 가장 거룩한              2
## # … with 2,592 more rows
# 부정 단어
  dic %>% 
    filter(polarity == -2) %>% 
    arrange(word) 
## # A tibble: 4,799 × 2
##    word            polarity
##    <chr>              <dbl>
##  1 가난                  -2
##  2 가난뱅이              -2
##  3 가난살이              -2
##  4 가난살이하다          -2
##  5 가난설음              -2
##  6 가난에                -2
##  7 가난에 쪼들려서       -2
##  8 가난하게              -2
##  9 가난하고              -2
## 10 가난하고 어렵다       -2
## # … with 4,789 more rows
댓글 데이터 베이스 전처리 한다.   일단 번호를 주고,  데이터 공백을 없애주고,  특수문자와 영문을 뺀 한글 2자리수 이상의 단어(word) 만 추려서,  감성 분석을 하였다. 


 댓글 데이터 전처리 하기   
  news_comment <- raw_news_comment %>% 
    mutate(id = row_number(),
           reply = str_squish(contents))
  
# 데이터 토콘화 
  word_comment <- news_comment %>% 
    unnest_tokens(input = reply,
                  output = word,
                  token = "words",
                  drop = F)%>% 
    filter(str_detect(word, "[가-힣]")) %>%   
    filter(str_count(word) >= 2)
  
  word_comment %>% 
    select(word, reply)
## # A tibble: 12,980 × 2
##    word           reply                                                        
##    <chr>          <chr>                                                        
##  1 우리네         우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
##  2 인생사가       우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
##  3 반영되어       우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
##  4 안타깝게       우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
##  5 봤네요         우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
##  6 현재의         우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
##  7 우리가         우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
##  8 존재하기까지를 우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
##  9 생각하여       우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
## 10 인류가         우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재…
## # … with 12,970 more rows

감정 점수를 부여는 댓글의 문장과 감성사전을 join(매칭) 하여,  점수를 부여 하였고,   2점 보다 높으면,  긍정인 글로 정리 하였고,  -2점 인 것은 부정으로 정리 하였다. 

 # 감정점수 부여
  word_comment <- word_comment %>% 
    left_join(dic, by ="word") %>% 
    mutate(polarity = ifelse(is.na(polarity), 0 , polarity))
  
  # 감정 분류 하기 
  word_comment <- word_comment %>% 
    mutate(sentiment = ifelse(polarity ==  2, "pos",
                              ifelse(polarity == -2, "neg", "neu")))

 word_comment %>% 
   count(sentiment)
## # A tibble: 3 × 2
##   sentiment     n
##   <chr>     <int>
## 1 neg         139
## 2 neu       12684
## 3 pos         157

 중립단어를 제외 하고,  가장 자주 사용되는 단어,  긍정 10개,  부정 10개 씩 정리하여 추출 하여, 그래프를 그렸다. 

 # 중립 단어를 제외하고, 긍정 단어와 부정 단어중 
 # 가장 자주 사용된 단어를 10개씩 추출
 top10_sentiment <- word_comment %>% 
   filter(sentiment != "neu") %>% 
   count(sentiment, word) %>% 
   group_by(sentiment) %>% 
   slice_max(n, n =10)
 
 
 top10_sentiment %>% 
   ggplot(aes(x = reorder(word,n),
              y = n, 
              fill = sentiment
   )) +
   geom_col() +
   coord_flip() +
   geom_text(aes(label = n), hjust = -0.3)+
   facet_wrap(~ sentiment, scales = "free") +
   scale_y_continuous(expand = expansion(mult = c(0.05, 0.15))) +
   labs(x = NULL) +
   theme(text = element_text(family="nanumgothic"))
지금 우리학교는 긍정인 단어와 부정인 단어


텍스트 마이닝에서 빠질수 없는 wordcloud를 그렸다.  너무, 솔직히, 진짜, 그냥 단어가 들어 갔다.   너무, 솔직히, 진짜의 부사어는 긍정이나, 부정어에서 충분히 같이 쓸 수 있는 단어이다. 


# 지금 우리 학교는의 wordcloud 그리기     
 word_comment %>% 
   count(word, sort = T) %>% 
 ggplot(aes(label = word,
            size =n,
            color = n)) +
   geom_text_wordcloud(seed = 1004) +
   scale_radius(limits = c(3, NA), # 최소, 최대 단어 빈다
                range = c(3, 30) #  최소 최대 글자 크기
   ) +
   scale_color_gradient(low = "#66aaf2",
                        high= "#004EA1") +
   theme_minimal()

지금 우리학교는 워드 클라우드


 주인공 이름 보다는  영화를 보고 느꼈던 이야기를 적었다,  오징어라는 글자도 간혹보인다.  이것과 오징어 게임과 일부 비교한 것이 보인다. 

# 댓글별 감정 점수 구하기 
  score_comment <- word_comment %>% 
   group_by(id, reply) %>% 
   summarise(score = sum(polarity)) %>% 
   ungroup()

 score_comment %>% 
   select(score, reply)
## # A tibble: 1,283 × 2
##    score reply                                                                  
##    <dbl> <chr>                                                                  
##  1    -1 우리네 인생사가 반영되어 안타깝게 봤네요. 현재의 우리가 존재하기까지를…
##  2     0 짱개들은 좀비도이제 강시가 원조라고 우기겠군                           
##  3     0 도대체 댓글 24시간 내내 쓰는 놈들은 누구일까 ㅋㅋㅋㅋㅋㅋㅋㅋ          
##  4     0 하 🐕중국인들 넘쳐나네 ㅋㅋㅋㅋ 시진핑에 어머니 받칠 애들 ㅋㅋㅋㅋㅋㅋ 
##  5     1 짱OOO들 열심히 퍼다가 리뷰질 하드라. 같쟎은 그 종자. 짱개              
##  6     0 넷플 중국인 15억 뒤지는드라마 한편 만들면 전세계1위가능                
##  7     0 엄청나게 뛰어제끼는 한국좀비. 너무정신사나워..                         
##  8     0 훠훠훠 역쉬 k방역 그리고 k학폭 입니다.                                 
##  9    -1 왜케 넷플릭스는 잔인한 장면과 피칠갑화면... 엽기적 내용, 싸이코 등장인…
## 10    -1 예고편 보는데 찬혁이 나와서 깜놀..                                     
## # … with 1,273 more rows

# 긍정 댓글 
  score_comment %>% 
    select(score, reply) %>% 
    arrange(-score)
## # A tibble: 1,283 × 2
##    score reply                                                                  
##    <dbl> <chr>                                                                  
##  1     6 "정말 재미있게 봤음. 오징어게임보다 더 재미 있게 봤다. 대한민국 드라마…
##  2     6 "이런거 그만봐요. 마인드컨트롤로 조종 되는거에요. 행복하고 유쾌하고 즐…
##  3     6 "김대중 대통령님 감사합니다 . . 수구꼴통들이 국가 부도 사태에 빠뜨려놓…
##  4     6 "DJ..창조적인 지식은 높은 부가가치를 창출한다. 우리나라는 높은 교육수… 
##  5     5 "탑스타 간판없이 무명으로 꾸린 영화가 대박난다는건 좋은 현상이다. 영화…
##  6     5 "예전에 보지않던 좀비물인데 코로나이후 새롭게 보임. 몰입도도 좋고 재미…
##  7     5 "좀비 영화의 새로운 연출은 좋다 . 일부 아닌것도 있지만 좋은것이 더 많… 
##  8     5 "재미있게 잘 봤어요 학생들 연기하느라 고생했어요 당신들 고생덕에 즐거… 
##  9     5 "한류 재도약의 근간은 민주주의 바탕이 있기때문에 가능하다.제작의 창의… 
## 10     4 "썩 재미있던데 웹툰 본 사람들이 일부 비교하면서 어쩌구저쩌구 많이 하더…
## # … with 1,273 more rows
# 부정 댓글
  score_comment %>% 
    select(score, reply) %>% 
    arrange(score)  
## # A tibble: 1,283 × 2
##    score reply                                                                  
##    <dbl> <chr>                                                                  
##  1    -8 좀비 너무보기에 혐오스럽다! ~~~학교라는곳을 좀비랑 연계해 굳이 이런 영…
##  2    -8 넷플 한국드라마 특징이 엽기적으로 잔인하고 더럽게 잔인하고 끝까지 잔인…
##  3    -5 좀비물은 인간형상을 극도로 왜곡하고 인간이 인간을 공격하고 죽이는 것이…
##  4    -5 이런 기사에 안좋은 글을쓰시는 분들은 스스로의 망상에 빠져있는 분들이거…
##  5    -5 ㅋㅋㅋㅋㅋ 오징어게임 지옥 우리학교는 전부 1위 하면서 어째 한국에 대한…
##  6    -5 공포감 확대와 강조는 이해하나 굳이 내장까지 먹고 아비규환속 살아 남은 …
##  7    -5 글쎄 ㅠㅠ 캐릭터들에 좀더 애정을 가지고 만들었으면 좋았겠다는 생각이들…
##  8    -5 반전이 없는게 반전임. 너무 많이, 너무 안타깝게 죽어서. 살아 남은 사람… 
##  9    -4 혐오스럽다 딱봐도 불건전                                               
## 10    -4 잼있지만 쓸데없는 로맨스나 신파는 좀 줄이고 8~10부작 정도가 적당. 그리…
## # … with 1,273 more rows

위에는 댓글별로 점수를 구하였고,  물론 점수가 높은 것은 긍정 댓글이고,  점수가 낮은 것은 부정 댓글이다.  

# 감정 점수 빈도 구하기 
  score_comment %>% 
    count(score) %>% 
    print(n =Inf)
## # A tibble: 13 × 2
##    score     n
##    <dbl> <int>
##  1    -8     2
##  2    -5     6
##  3    -4     5
##  4    -3    12
##  5    -2    81
##  6    -1    87
##  7     0   923
##  8     1    46
##  9     2    96
## 10     3     9
## 11     4     7
## 12     5     5
## 13     6     4
  # 감정 분류하고 막대 그래프 만들기 
  score_comment <- score_comment %>% 
    mutate(sentiment = ifelse(score >= 1, "pos",
                              ifelse(score <=-1, "neg", "neu") ))
  
  frequency_score <- score_comment %>% 
    count(sentiment) %>% 
    mutate(ratio = n/sum(n)*100)
  
  frequency_score  
## # A tibble: 3 × 3
##   sentiment     n ratio
##   <chr>     <int> <dbl>
## 1 neg         193  15.0
## 2 neu         923  71.9
## 3 pos         167  13.0
  # 감정분류 막대 그래프 그리기
  frequency_score %>% 
    ggplot(aes(x=sentiment, 
               y = n,
               fill = sentiment)) +
    geom_col() +
    geom_text(aes(label = n, vjust = -0.3)) +
    scale_x_discrete(limits = c("pos", "neu", "neg"))

지금 우리학교는 긍정, 부정, 중립의 비중


  위와 같이 긍정, 부정, 중립의 갯수를 막대 그래프로 볼 수 있다.   눈으로만 보았을 경우 중립의 비중이 높고,  긍정과, 부정의 비중은  적다.   구체적인 비중을 보고 싶으면 아래와 같이 그래프를 그리면 된다. 

 댓글 감정비율로 누적 막대 그래프 만들기 
  # frequency_score 에는 샘플 데이터 contry 처럼 x 축을 구성 할 변수가 없다
  # 이때는 임의의 값을 넣은 더비변수를 활용함 (dummy variable)
  frequency_score$dummy <- 0
  frequency_score
## # A tibble: 3 × 4
##   sentiment     n ratio dummy
##   <chr>     <int> <dbl> <dbl>
## 1 neg         193  15.0     0
## 2 neu         923  71.9     0
## 3 pos         167  13.0     0
  # 감정을 비율 막대 그래프로 그린다. 
  frequency_score %>% 
    ggplot(aes(x = dummy, 
               y =ratio,
               fill= sentiment)) +
    geom_col()+
    geom_text(aes(label = paste0(round(ratio,1), "%")),
              position = position_stack(vjust =0.5)) +
    theme(axis.text.x = element_blank(),
          axis.title = element_blank(),
          axis.ticks = element_blank())


지금 우리학교는 긍정, 중립, 부정 비중 [네이버 댓글 기준]


   데이터 점수를 보고 긍정적인 고빈도 단어와 부정적인 고빈도 단어를 보자  

 감정 변수별 주요 단어 보기 
# 로그 오즈비를 이용해 긍정 댓글 과 부정 댓글에 상대적으로 어떤
# 단어가 자주 사용되었는지 알아 보자. 
  
  comment <- score_comment %>% 
    unnest_tokens(input = reply,
                  output = word,
                  token =  "words",
                  drop = F) %>% 
    filter(str_detect(word, "[가-힣]")) %>%   
    filter(str_count(word) >=2)
  
# 감정 및 단어별 빈도 구하기ㅣ 
  frequency_word <- comment %>% 
    count(sentiment, word, sort = T)  

  
# 긍정 댓글 고빈도 단어
  frequency_word %>% 
    filter(sentiment == "pos")
## # A tibble: 1,988 × 3
##    sentiment word         n
##    <chr>     <chr>    <int>
##  1 pos       너무        23
##  2 pos       좀비        18
##  3 pos       재미        16
##  4 pos       드라마      11
##  5 pos       재미있게    11
##  6 pos       우리나라    10
##  7 pos       대한민국     9
##  8 pos       그냥         8
##  9 pos       좋고         8
## 10 pos       지금         8
## # … with 1,978 more rows
# 부정 댓글 고빈도 단어
  frequency_word %>% 
    filter(sentiment == "neg")
## # A tibble: 2,400 × 3
##    sentiment word       n
##    <chr>     <chr>  <int>
##  1 neg       너무      32
##  2 neg       그냥      23
##  3 neg       좀비      21
##  4 neg       진짜      14
##  5 neg       지옥      12
##  6 neg       노잼      10
##  7 neg       그리고     9
##  8 neg       연기       9
##  9 neg       지겹다     9
## 10 neg       하는       9
## # … with 2,390 more rows

부정적인 조건을 두고,  "너무"가 포함된 글을 보았다. 

score_comment %>% 
    filter(sentiment == "neg") %>% 
    filter(str_detect(reply,"너무"))
## # A tibble: 32 × 4
##       id reply                                                   score sentiment
##    <int> <chr>                                                   <dbl> <chr>    
##  1    63 밖에는 물고뜯고 피철철 난린데 좀비 피해온 교실에있는 …     -3 neg      
##  2    94 게임하는건 좋지만 학교에서 저리 위험한 행동은 자제해야…    -2 neg      
##  3   135 이게 왜 열풍인지 모르겠다 애들 연기 너무 못하고 특히 …     -3 neg      
##  4   170 오징어 만큼은 안됨. 극의 긴장감이 중간 중간 너무 떨어…     -3 neg      
##  5   196 배우들 연기 너무 못해서 아쉬움 처음에는 일반인 데리고 …    -1 neg      
##  6   253 쓸데없이 1818을 너무많이함 ㅜㅜ 연기가안됨                 -2 neg      
##  7   276 너무 지루하고 재미없어 빨리보기로 그냥 넘기고 말았다. …    -3 neg      
##  8   297 몰입도 떨어지는 지루한 스토리가 곳곳에 있던데 런닝타임…    -1 neg      
##  9   351 좀비 너무보기에 혐오스럽다! ~~~학교라는곳을 좀비랑 연…     -8 neg      
## 10   355 잔인하고 악함이 극에달한 저런 컨텐츠가 유행하는 현실이…    -1 neg      
## # … with 22 more rows

부정에는 잔인함과,  배우 연기 이야기, 그리고 지루한 스토리가 나왔다.   물론 나도 내용이 길어서 조금은 지루 하였다.   그래도 시청자 세계 1등을 현재(2022-02-06) 까지 기록 하고 있다.  

감정 변수별 주요 단어를  긍정이 부정 보다  강조를 많이 한것과,   부정이 긍정보다 많이 강조한  로그 오즈비를 계산하기 위해서,  단어별로,  wide form 으로 변환 하였다. 

# wid_form으로 변환 하여, 로그 오즈비를 구한다. 
  comment_wide <- frequency_word %>% 
    filter(sentiment != "neu") %>% 
    pivot_wider(names_from = sentiment,
                values_from = n,
                values_fill = list(n = 0) )
  
  comment_wide
## # A tibble: 4,016 × 3
##    word       neg   pos
##    <chr>    <int> <int>
##  1 너무        32    23
##  2 그냥        23     8
##  3 좀비        21    18
##  4 재미         2    16
##  5 진짜        14     7
##  6 지옥        12     0
##  7 드라마       7    11
##  8 재미있게     0    11
##  9 노잼        10     0
## 10 우리나라     2    10
## # … with 4,006 more rows

로그 오즈비를 구한 다음,  긍정 10개와  부정 10개를 추출 하여,  긍정과 부정을 비교 분석 하였다. 
지금 우리학교는 로그 오즈비 비교분석



"Do It 쉽게 배우는 R 텍스트 마이닝"의 코드를 많이 참조 하여, 작성 되었다.    
그런데  신문기사에 대한 평가는 아래와 같았다. 

이것은 댓글을 적지 않아도,  평을 낼 수 있다.   대부분이 좋다고 평하였다. 

영화 내용은 그렇다 치더라도,   기사 제목이 아래와 같아서 좋다고 평한 것 같다. 


'지금 우리 학교는', 미국 포함 전세계 1위···'오징어게임' 열풍 잇나




댓글 없음:

댓글 쓰기

[주식 클로드 자동매매 프로젝트] 제2편. 데이터 인프라 구축 — 모델보다 데이터가 먼저다

 머신러닝 모델을 이야기하기 전에, 그 모델이 먹는 데이터가 어디서 어떻게 왔는지부터 정직하게 밝혀야 한다. 이 프로젝트에서 가장 많은 시간이 들어간 부분은 화려한 모델 튜닝이 아니라, "내가 정말 그 데이터를 갖고 있는가"를 확인하...