โปโป ๋ํ์ถ 1ํ ๊ด๋ จ๋ ์คํฌ์ผ๋ฌ๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. โปโป
๋ํ์ถ ์์ฆ4 ์ฒซํ๋ ๋งํ๋ค.
๋๋ ์ฌ๋ฅ์๋ PD๊ฐ ์ฌํ์ ๊ธฐ์ธ์ฌ ๋ง๋ ํ์๊ฐ ๋ฐ์ง๋ฆฌ ๋ ธ๋ํต๋ญ ๊ด๊ณ ๋ฅผ ๋ดค๋ค.
์์ฆ3 ์ข ์ ์ดํ ๋ฌด๋ ค 1๋ ์ด๋ ๊ธฐ๋ค๋ ธ๋๋ฐ...
๋ค๋ฅธ์ฌ๋๋ค์ ์ด๋ป๊ฒ ์๊ฐํ ๊น, ์ผ๋จ ๋ฐ์ดํฐ ์์ง์ด ๋ง๋งํ ํธ์ํฐ ๋ฐ์๋ถํฐ ์ดํด๋ณด๊ธฐ๋ก ํ๋ค.
R ํจํค์ง rtweet ์ ์ฌ์ฉํ๋ฉด ํค์๋๋ฅผ ์ด์ฉํด ์ฝ๊ฒ ํธ์์ ์์งํ ์ ์๋ค.
search_tweets์ ์ด์ฉํ๋ฉด ํค์๋๋ฅผ ์ด์ฉํด ๊ฐํธํ ์์ง ๊ฐ๋ฅํ๋ค.
library("rtweet")
TWEET_N <- 18000
HASHTAG <- "๋ํ์ถ"
rt <- search_tweets(HASHTAG,
n=TWEET_N, # ์์งํ tweet ์ (max 18000)
include_rts=FALSE) #๋ฆฌํธ์ ํฌํจ์ฌ๋ถ FALSE ๋ฏธํฌํจ, TRUE ํฌํจ
์ ์ฝ๋๋ฅผ ์คํํ๋ฉด ์๋์ ๊ฐ์ด ๊ณ์ ์ธ์ฆ์ ์๊ตฌํ๋ค.
๋งค๋ฒ ๋ธ๋ผ์ฐ์ ์์ ๋ก๊ทธ์ธํ๊ธฐ๋ ๊ท์ฐฎ์ผ๋ฏ๋ก https://developer.twitter.com/en ์์ API ์ฌ์ฉ์ ์ฒญ์ ํ ํ token์ ์์ฑํด ์ ์ฅํด ๋๋๋ค. (github์ 1. create_token.R)
TOKEN_NAME <- "./token/twitter_token.rds"
TWEET_N <- 18000
HASHTAG <- "๋ํ์ถ"
twitter_token <- readRDS(TOKEN_NAME)
rt <- search_tweets(HASHTAG,
n=TWEET_N,
include_rts=FALSE,
token = twitter_token)
์ด๋ ๊ฒ ํ๋ฉด ๋งค๋ฒ ์ธ์ฆํ ํ์๊ฐ ์๋ค.
์ฒ์์๋ "#๋ํ์ถ"์ ์ด์ฉํด ๊ฒ์์ ํ์ผ๋ ์์ง๋๋ ํธ์ ์์ด ๋๋ฌด ์ ์ด ํด์ํ๊ทธ๋ฅผ ๋ผ๊ณ "๋ํ์ถ" ํค์๋๋ก ์์งํ๋ค.
์์ง๋ text๋ฅผ ํ์ธํด๋ณด๋ฉด ๊ต์ฅํ ๋ง์ ์ปฌ๋ผ๋ค์ด ํฌํจ๋์ด์๋๋ฐ ์ฌ๊ธฐ์๋ ๋ณธ๋ฌธ text๋ง ํ์ํ๋ฏ๋ก text์ปฌ๋ผ๋ง ๊ฐ์ ธ์ค๊ณ , ๋ค์์ ์๋ฏธ์ฐ๊ฒฐ๋ง์ ๊ทธ๋ฆฌ๊ธฐ ์ํด ํธ์๋ณ id๋ฅผ ๋ถ์ฌํด์ค๋ค.
์ฑ๊ณต์ ์ผ๋ก ๋ํ์ถ ๊ด๋ จ tweet์ ์์งํ์ง๋ง ์ด๋ชจ์ง, ๋งํฌ, ํน์๋ฌธ์ ๋ฑ ๋ถํ์ํ ์ฐ๊บผ๊ธฐ๋ค์ด ๋ง์ด ํฌํจ๋์ด ์๋ค.
์ด์ ์ด๊ฒ๋ค์ ์ฒ๋ฆฌํด๋ณด์
์ถ์ถํ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ฉด ๋ถํ์ํ ํ ์คํธ๊ฐ ๋ง์ด ํฌํจ๋์ด ์์ด ๋ชจ๋ ์ ๊ฑฐํด ์ฃผ์๋ค.
์ ๊ฑฐํ ๊ฒ๋ค
์ถ๊ฐ์ฒ๋ฆฌ
rmURLs <- function(x) { gsub("(f|ht)tp(s?)://\\S+", "", x, perl=T) }
rmTag <- function(x) { gsub("(@[A-Za-z๊ฐ-ํฃ0-9_]+)", "", x, perl=T) }
rmEmoji <- function(x) { gsub("[\U00010000-\U0010FFFF]+", "", x, perl=T) }
toSpace <- function(x, pattern) { gsub(pattern, " ", x) }
preprocess_text <- function(text_df) {
text_df %>%
mutate(text=rmURLs(text),
text=rmTag(text),
text=rmEmoji(text),
text=toSpace(text, "\n"),
text=toSpace(text, "[^๊ฐ-ํฃA-Za-z]"),
text=gsub(" +", " ", text),
text=trimws(text),
text=toupper(text))
}
texts <- preprocess_text(texts)
๊น๋ํด์ก๋ค!
์์ ๊ฐ์ด ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ ๋ฐ๋ก ํํ์ ๋ถ์์ ์๋ํ๋ ๋ฌธ์ ๊ฐ ์๊ฒผ๋ค.
๋ํ์ถ, ์ฌ๊ณ ์ถ๋ฆฌ๋ฐ, ๋ ธ๋ํต๋ญ, PPL ๋ฑ ์ฌ์ ์ ์๋ ๋จ์ด๋ค์ด ์ ๋๋ก ํํ์ ๋ถ์์ด ๋์ง ์์๋ค.
์ด๋ฐ๊ฒฝ์ฐ ํํ์๋ถ์๊ธฐ์ ๋ํ์ถ ๊ด๋ จ ์ฉ์ด๋ค์ ์ฌ์ฉ์ ์ฌ์ ์ ๋ฑ๋กํด์ฃผ๋ฉด ๋์ง๋ง ๊ต์ฅํ ๊ท์ฐฎ์ ์ผ์ด๋ค... ์ฌ๋ฏธ๋ก ํ๋์ผ์ ๋๋ฌด ๋ง์ ๊ณต์๋ฅผ ๋ค์ด๊ธฐ๋ ์ซ์ด์ ๊ฒ์์ ํด๋ณด๋ ์ค ํฅ๋ฏธ๋ก์ด python ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ด ์๋ํด ๋ณด์๋ค.
์ฃผ์ด์ง ๋ฌธ์๋ฅผ ์ฝ์ด์ ํด๋น ๋ฌธ์ ์์์ ์ฌ์ฉ๋๋ ๋ช ์ฌ๋ก ์ถ์ ๋๋ ๋จ์ด๋ฅผ ์ถ์ถํด์ฃผ๋ Python๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค.
์ด ๊ฒฝ์ฐ์๋ ๋ํ์ถ ๋ฐฉ์ก๊ณผ ๊ด๋ จ๋ ๋จ์ด๋ค์ ๊ฝค๋ ์ถ์ถํด๋ผ ์ ์์๋ค.
์๋ฒฝํ ์ฌ์ฉ์ ์ฌ์ ์ด๋ผ๊ณ ํ ์ ์์ง๋ง ๋๋ฆ ์ธ๋งํ ๊ฒฐ๊ณผ๋ฅผ ์ป์๊ณ ์ฌ๋ฏธ๋ก ํ๋๊ฑฐ๋ผ ์ด์ ๋๋ง ํ๊ธฐ๋ก ํ๋ค.
์ด๋ถ๋ถ๋ง python์ผ๋ก ๊ตฌํํ๊ณ ์ถ์ถ๋ ๋จ์ด๋ค์์ ํ์ํด๋ณด์ด๋ ๊ฒ๋ค๋ง ์๋์ผ๋ก ๊ณจ๋ผ๋ด๊ณ ์ถ์ฐ์ง ์ด๋ฆ์ ๋๋ง ์ถ๊ฐํ ํ user_dict.txt์ ์ ์ฅํด ํํ์ ๋ถ์๊ธฐ์ ์ ์ฉํ๋ค. (word_extraction.ipynb)
์ด๋ฐ๊ฒ ์๋์ค ์์์ผ๋ฉด ์ฒจ๋ถํฐ ์ ๋ถ Python์ผ๋ก ํ ๊ฑธ
corpus_fname = 'twitter_text.txt'
with open(corpus_fname, 'r', encoding="cp949") as f:
lines = f.readlines()
lines = [s.replace("\n", "").replace('"', "") for s in lines][1:]
lines = [s.upper() for s in lines][1:]
from soynlp.word import WordExtractor
word_extractor = WordExtractor(
max_left_length=6,
min_frequency=10,
min_cohesion_forward=0.02,
min_right_branching_entropy=0.0
)
word_extractor.train(lines)
words = word_extractor.extract()
import math
def word_score(score):
return (score.cohesion_forward * math.exp(score.right_branching_entropy))
print('๋จ์ด (๋น๋์, cohesion, branching entropy)\n')
for word, score in sorted(words.items(), key=lambda x:word_score(x[1]), reverse=True)[:100]:
print('%s (%d, %.3f, %.3f)' % (
word,
score.leftside_frequency,
score.cohesion_forward,
score.right_branching_entropy
)
)
๋ํ์ถ, ์ค์ผ์ผ, ๋ ธ๋ํต๋ญ, ํ์๋จธ์ , ํผํผ์, ์นํจ, ์์ฆ ๋ฑ ์ฌ์ ์ ์๋ ๋จ์ด๊ฐ ์ถ์ถ๋๋ค.
์ดํ ํํ์ ๋ถ์ ๊ฒฐ๊ณผ์์๋ ๊ด์ฐฎ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์๋ค.
ํํ์ ๋ถ์๊ธฐ๋ tidytext ์ ํจ๊ป ์ฌ์ฉํ๊ธฐ๊ฐ ์ฝ๊ณ ์ค์น๋ ๊ฐ๋จํ Elbird ํจํค์ง๋ฅผ ์ฌ์ฉํ๋ค.
library("tidytext")
library("Elbird")
tokenize_text <- function(text_df) {
text_df %>%
unnest_tokens(
input = text,
output = word,
token = analyze_tidy
) %>%
separate(word, sep="/", into=c("word", "morph"))
}
read_user_dict("./user_dict.txt")
words <- tokenize_text(texts)
ํํ์๋ณ๋ก ๋ถ๋ฆฌ๋ ๋จ์ด ๋ฐ์ดํฐ ํ๋ ์์ ์ป์๋ค!!
ใด๊น, ใน๊น, ์ ๊ฐ์ ํํ์๋ค์ ํ์ ์์ผ๋ฏ๋ก ๊ด์ฌ์๋ ํ์ฌ๋ง ๋จ๊ธฐ๊ณ ์ ์ธํ๊ณ ์ ๋ถ ์ ๊ฑฐํด์ค๋ค.
๊ทธ๋ฆฌ๊ณ ๋จ์ ๋จ์ด๋ค์ ๊ฐฏ์๋ฅผ ์ธ์ ๋น๋์ ๋ด๋ฆผ์ฐจ์์ผ๋ก ์ ๋ ฌํด์ค๋ค.
target_morph <- c("nng", "nnp", "va", "xr", "sl", "@")
synonym <- data.table::fread("synonym.csv", encoding = "UTF-8")
synonym_dict <- NULL
for (i in 1:dim(synonym)[1]){
# print(i)
synonym_dict[synonym$og[i]] <- synonym$synonym[i]
}
processed_word <- words %>%
filter(morph %in% target_morph,
word!="๊ฐ") %>%
mutate(word=ifelse(morph=="va", paste0(word,"๋ค"), word),
word=ifelse(word %in% names(synonym_dict),
synonym_dict[word],
word),
word=toupper(word)) %>%
count(word) %>%
filter(nchar(word)>1,
n>10) %>%
rename(freq=n) %>%
arrange(desc(freq))
์ด์ wordcloud ๊ทธ๋ฆฌ๊ธฐ๋ ์์ฃผ ๊ฐ๋จํ๋ค.
library(wordcloud2)
SOURCE_NAME <- "twitter"
# preprocess.R ๋จผ์ ์คํ
processed_word$freq[1] <- min(processed_word$freq[1],
processed_word$freq[2]*2)
wc <- wordcloud2(
processed_word,
size=1.5,
color = c("black",
sample(
rep_len(gray.colors(20, start = 0, end = .4),
nrow(processed_word) - 1),
nrow(processed_word) - 1
)),
backgroundColor = "#FFE400",
rotateRatio = .4,
shape = "diamond",
gridSize = 7,
ellipticity = .6,
shuffle=FALSE
)
wc
์๋ํด๋ผ์ฐ๋์์๋ ๋น๋์ = ๊ธ์ํฌ๊ธฐ ์ธ๋ฐ ๋ํ์ถ ์ด๋ผ๋ ๋จ์ด๊ฐ ๊ฒ์ ํค์๋๋ผ ๊ทธ๋ฐ์ง ๋ชจ๋ ํธ์์ ํฌํจ๋์ ๋๋ฌด ํฌ๊ฒ ๋์๋ค. ์ ๋นํ ํฌ๊ธฐ๋ฅผ ์กฐ์ ํด์ค์ ๋ณด๊ธฐ ์์๊ฒ ํ๋ค.
๋ํ์ถ ํฌ์คํฐ๋ฅผ ๋ณด๋ฉด ๋ ธ๋ ๋ฐํ์ ๊ฒ์์ ๊ธ์จ๋ง์ฌ์ฉํ๋๊น ๋ง์ถฐ์ ๊ทธ๋ ค๋ณด์.
๋ ธ๋ ๋ฐํ์์ ๋ํ์ถ ํฌ์คํฐ๋ฅผ ๊ฒ์ํด์ color picker๋ก ์ฐ์ด์๋ค.
๋ํ์ถ ํ
๋ง ์์ผ๋ก ๋ง์ถฐ์ ๊ทธ๋ ธ๋๋ฐ, ๋
ธ๋ํต๋ญ ์๊น์ด๋ค ํ๊ฐ๋๋ค.
๊ทธ๋๋ ์์๊ฒ ์ ๋์๋ค. ํ๋ฒ ์ดํด๋ณด์
ํ๋๊น์ ์๋ฏธ์ฐ๊ฒฐ๋ง๋ ๊ทธ๋ ค๋ณด์
library("widyr")
library("tidygraph")
library("ggraph")
library("showtext")
SOURCE_NAME <- "twitter"
texts <- readRDS(get_latest_data(SOURCE_NAME)) %>%
get_text(SOURCE_NAME)
texts <- preprocess_text(texts)
words <- tokenize_text(texts)
# -------------------------------------------------------------------------
target_morph <- c("nng", "nnp", "va", "xr", "sl", "@")
pair <- words %>%
mutate(word=ifelse(morph=="va", paste0(word,"๋ค"), word),
word=ifelse(word %in% names(synonym_dict),
synonym_dict[word],
word),
word=toupper(word)) %>%
filter(word!="๋ํ์ถ",
word!="๊ฐ๋ค",
nchar(word)>1,
morph %in% target_morph) %>%
pairwise_count(item=word,
feature=id,
sort=T)
# ๊ด๋ จ์๋ ํค์๋ ์ญ์
trash <- c("LT", "GT",
"๊ฒฐ์ ", "ํฐ๋น",
"์ ๋", "๋ฒ์ค",
"ํผ์ค", "๋ธ๋ฝ๋น", "BLOCKB",
"๊ฐ๋ค",
"์ธ์ฑ", "์ต๊ณ ", "์ ์ฌ", "์ถํ", "์์ผ", "HAPPYINSEONGDAY")
set.seed(1)
graph_component <- pair %>%
filter(n>7,
!((item1 %in% trash) & (item1 %in% trash))) %>%
as_tbl_graph(directed=FALSE) %>%
mutate(centrality=centrality_degree(),
group=as.factor(group_infomap()))
ggraph(graph_component,
layout="nicely") +
geom_edge_link(color="gray50",
alpha=.5) +
geom_node_point(aes(color=group,
size=centrality),
show.legend = FALSE) +
scale_size(range=c(5, 15)) +
geom_node_text(aes(label=name),
repel=TRUE,
size=5,
family="naumgothic") +
theme_graph()
์๋ฏธ์ฐ๊ฒฐ๋ง์ ๊ทธ๋ฆฌ๊ณ ๋๋ ์ถ์ฐ์ง์ค ํ๋ช ์ธ ํผ์ค์ ์์๊ทธ๋ฃน BLOCKB์ ๋ํ ์ธ๊ธ์ด ๋๋ฌด ๋ง์์ ์ ์ธํด์ฃผ์๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ์ดํด๋ณด์
๋๋ฆ ์ฌ๋ฏธ์์๋ค.
์ฒซ๋ฐฉ์ดํ ์ฐ๋์ฐ๋ ํฌ์คํ
ํ๋ค๋ณด๋ ์ด๋์ 2ํ๋ ๋ฐฉ์ํ๋ค.
๋ฌผ๋ก 2ํ ์ดํ์๋ ๋์ผํ ์ฝ๋๋ฅผ ๋๋ ค์ ๋ถ์ํด๋ณด์๋ค.
์ด๊ฒ๋ ๋์ค์ ๊ฒฐ๊ณผ๋ง ํฌ์คํ
ํด์ผ์ง...
์ฐธ, ์ ํ๋ธ ๋ค์ํ ์๊ณ ํธ์์ ๋๊ธ ์์งํด์ ๋ง๋ ์ ํ๋ธ ๋ฒ์ ๋ ์๋๋ฐ ์ด์ชฝ์ ๋นํ์ ์ธ ์๊ฒฌ์ด ํจ์ฌ ๋ง์ ๊ฒ ๊ฐ๋ค.
์ ํ๋ธ ๋๊ธ ์์งํ ๊ฒฐ๊ณผ๋ ์ธ์ ๊ฐ ํฌ์คํ
์์ ...
์๊ฐ์ด ๋ ๋, ์ฝ๋๋ฅผ ์ข ๋ ์ ๋ฆฌํด์ ๋งค์ฃผ ์๋์ผ๋ก ๋์๊ฐ๊ฒ ๋ง๋ค์ด ๋ด์ผ๊ฒ ๋ค.
๋!