StringR 패키지 기초부터 실전까지

Author

waterfirst

1. stringr 패키지 소개

stringr 패키지는 문자열 처리를 위한 R 패키지로, 일관된 문법과 직관적인 함수명을 제공합니다. tidyverse의 한 부분으로, 데이터 분석 시 문자열 처리에 매우 유용합니다.

# 패키지 설치 및 로드
# install.packages("stringr")
library(stringr)
library(tidyverse)

2. 기본적인 문자열 처리

예제 1: 문자열 결합 (str_c)

가장 기본적인 문자열 처리 작업인 문자열 결합을 해보겠습니다.

# 데이터 준비
names <- c("김철수", "이영희", "박민수")
greetings <- c("안녕하세요", "반갑습니다", "좋은 하루입니다")

# 문자열 결합
str_c(names, "님, ", greetings)

실행 결과 해석: - 각 이름 뒤에 ’님’을 붙이고, 인사말을 결합했습니다. - str_c는 벡터화된 연산을 지원하므로, 각 요소별로 결합이 이루어집니다.

예제 2: 문자열 길이와 부분 추출 (str_length, str_sub)

# 데이터 프레임 생성
df <- tibble(
  제품명 = c("사과주스", "오렌지에이드", "딸기스무디", "바나나우유"),
  가격 = c(3000, 3500, 4000, 2500)
)

# 문자열 길이 확인
df %>%
  mutate(글자수 = str_length(제품명))

# 첫 두 글자만 추출
df %>%
  mutate(축약명 = str_sub(제품명, 1, 2))

실행 결과 해석: - str_length()로 각 제품명의 글자 수를 계산했습니다. - str_sub()로 제품명에서 첫 두 글자만 추출하여 축약명을 만들었습니다.

예제 3: 패턴 매칭 (str_detect, str_count)

# 메뉴 데이터 생성
menus <- c("아메리카노", "카페라떼", "카페모카", "녹차라떼", "초콜릿라떼")

# '라떼'가 포함된 메뉴 찾기
menus[str_detect(menus, "라떼")]

# 각 메뉴에서 '카페' 단어 개수 세기
str_count(menus, "카페")

실행 결과 해석: - str_detect()로 ’라떼’가 포함된 메뉴만 필터링했습니다. - str_count()로 각 메뉴에서 ’카페’라는 단어가 몇 번 나타나는지 계산했습니다.

예제 4: 문자열 대체 (str_replace, str_replace_all)

# 주소 데이터 생성
addresses <- c(
  "서울시 강남구 테헤란로",
  "서울시 서초구 반포대로",
  "서울시 종로구 종로"
)

# '시'를 '특별시'로 변경
str_replace(addresses, "시", "특별시")

# 띄어쓰기를 '_'로 모두 변경
str_replace_all(addresses, " ", "_")

실행 결과 해석: - str_replace()로 첫 번째 ’시’만 ’특별시’로 변경했습니다. - str_replace_all()로 모든 띄어쓰기를 밑줄로 변경했습니다.

예제 5: 고급 패턴 매칭 (정규표현식 활용)

# 이메일 데이터 생성
emails <- c(
  "user1@gmail.com",
  "user.2@naver.com",
  "user_3@company.co.kr",
  "invalid.email",
  "user4#company.com"
)

# 이메일 형식 검증
is_valid_email <- str_detect(
  emails,
  "^[[:alnum:]._%+-]+@[[:alnum:].-]+\\.[[:alpha:]]{2,}$"
)

# 결과 확인
tibble(
  이메일 = emails,
  유효성 = is_valid_email
)

# 도메인 추출
domains <- str_extract(emails, "@[[:alnum:].-]+\\.[[:alpha:]]{2,}$")
str_remove(domains, "@")

실행 결과 해석: - 정규표현식을 사용하여 이메일 주소의 유효성을 검사했습니다. - str_extract()로 이메일에서 도메인 부분을 추출했습니다. - str_remove()로 추출된 도메인에서 ‘@’ 기호를 제거했습니다.

6. 텍스트 빈도 분석과 시각화

텍스트 데이터를 분석하고 시각화하는 실전 예제를 살펴보겠습니다.

Code
# 필요한 패키지 로드
library(stringr)
library(tidyverse)
library(wordcloud2)
library(tidytext)

# 샘플 리뷰 데이터 생성
set.seed(123)
reviews <- tibble(
  id = 1:100,
  review = c(
    "커피가 너무 맛있어요. 특히 아메리카노가 좋아요",
    "분위기가 좋고 커피도 맛있어요",
    "직원분들이 친절하고 커피 맛도 좋네요",
    "케이크가 맛있어요. 커피와 잘 어울려요",
    "가격이 조금 비싸지만 맛있어서 자주 와요",
    "인테리어가 예쁘고 분위기가 좋아요",
    "아메리카노가 진하고 맛있어요",
    "디저트가 맛있고 직원분들도 친절해요",
    "커피 맛이 일정하고 좋아요",
    "매장이 청결하고 좋네요"
  ) %>% 
    sample(100, replace = TRUE)  # 100개의 리뷰로 복제
)

# 단어 빈도 분석
word_freq <- reviews %>%
  unnest_tokens(word, review) %>%  # 텍스트를 단어로 분리
  count(word, sort = TRUE) %>%     # 단어 빈도 계산
  filter(str_length(word) > 1)     # 한 글자 단어 제외

# 상위 15개 단어 빈도 시각화
word_freq %>%
  head(15) %>%
  ggplot(aes(x = reorder(word, n), y = n)) +
  geom_col(fill = "skyblue") +
  coord_flip() +
  labs(
    title = "리뷰 텍스트 내 단어 빈도",
    x = "단어",
    y = "빈도"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    axis.text = element_text(size = 12)
  )

Code
# 워드클라우드 생성
wordcloud2(word_freq, 
           size = 0.5,
           color = 'random-dark',
           backgroundColor = "white",
           rotateRatio = 0.3)

실행 결과 해석: 1. 텍스트 전처리: - unnest_tokens()를 사용하여 리뷰 텍스트를 개별 단어로 분리했습니다. - str_length()로 한 글자 단어를 필터링했습니다.

  1. 빈도 분석 그래프:
    • 막대 그래프로 상위 15개 단어의 출현 빈도를 시각화했습니다.
    • coord_flip()으로 가로 막대 그래프로 변환하여 가독성을 높였습니다.
  2. 워드클라우드:
    • 단어의 빈도에 따라 크기가 다르게 표시됩니다.
    • 랜덤한 색상과 회전을 적용하여 시각적 효과를 높였습니다.

추가적인 분석 아이디어: 1. str_detect()를 활용하여 특정 키워드가 포함된 리뷰만 필터링 2. str_extract_all()로 특정 패턴(예: 형용사)을 추출하여 감성 분석 3. str_replace_all()로 불필요한 문자나 기호를 전처리

정리

stringr 패키지의 주요 함수들:

  1. str_c(): 문자열 결합
  2. str_length(): 문자열 길이 계산
  3. str_sub(): 부분 문자열 추출
  4. str_detect(): 패턴 존재 여부 확인
  5. str_count(): 패턴 출현 횟수 계산
  6. str_replace(), str_replace_all(): 문자열 대체
  7. str_extract(): 패턴 추출

이러한 함수들을 활용하면 데이터 전처리와 텍스트 분석 작업을 효율적으로 수행할 수 있습니다.

한글 워드 클라우드 코드

# 필요한 패키지 설치 및 로드
library(tidytext)
library(wordcloud2)
library(tidyverse)
library(stringr)
library(RcppMeCab)

df <- c("여기에 복붙")
# 텍스트 데이터 준비
text <- df
# 형태소 분석 결과 처리 함수
process_text <- function(pos_result) {
  # 문자열을 분리하고 데이터프레임으로 변환
  words_df <- tibble(morpheme = unlist(pos_result)) %>%
    # 형태소와 품사 분리
    separate(morpheme, into = c("word", "pos"), sep = "/") %>%
    # 명사와 형용사만 선택
    filter(pos %in% c("NNG", "NNP", "VA")) %>%
    # 품사 유형 분류
    mutate(word_type = case_when(
      pos %in% c("NNG", "NNP") ~ "명사",
      pos == "VA" ~ "형용사",
      TRUE ~ "기타"
    ))
  
  return(words_df)
}

# 불용어 정의
stop_words <- c("것", "등", "들", "및", "을", "를", "이", "가", "은", "는", 
                "로", "으로", "에서", "에게", "여러분", "때문", "통해")

# 형태소 분석 실행 및 전처리
pos_result <- posParallel(text)
word_df <- process_text(pos_result)

# 단어 빈도수 계산
word_freq <- word_df %>%
  filter(!str_detect(word, "^[A-Za-z]+$")) %>%
  filter(!word %in% stop_words) %>%  # 불용어 제거
  filter(str_length(word) > 1) %>%   # 2글자 이상 단어만 선택
  filter(str_detect(word, "[가-힣]+")) %>% # 한글만 선택
  count(word, word_type, sort = TRUE) %>%
  filter(n >= 2)  # 최소 빈도 2 이상

# 품사별 상위 단어 출력
print("상위 명사:")
word_freq %>% 
  filter(word_type == "명사") %>% 
  head(10) %>%
  print()

print("\n상위 형용사:")
word_freq %>% 
  filter(word_type == "형용사") %>% 
  head(10) %>%
  print()

# 워드클라우드 생성
wordcloud2(word_freq %>% select(word, n), 
           size = 1,
           fontFamily = "NanumGothic",
           color = "random-dark",
           backgroundColor = "white",
           rotateRatio = 0.35,
           minRotation = -pi/6,
           maxRotation = pi/6)

# 결과 저장
library(htmlwidgets)
saveWidget(wordcloud2(word_freq %>% select(word, n)), 
           "korean_wordcloud.html", 
           selfcontained = TRUE)

영어 워드 클라우드 코드

ibrary(tidytext)
library(wordcloud2)
library(tidyverse)
library(stringr)
library(udpipe)

# 영어 모델 다운로드 (최초 1회만 실행)
udpipe_download_model(language = "english")
english_model <- udpipe_load_model(file = "english-ewt-ud-2.5-191206.udpipe")

df <- c("여기에 복붙")

# 텍스트 데이터 준비
text <- df

# 텍스트 품사 태깅
parsed_text <- udpipe_annotate(english_model, text)
parsed_df <- as.data.frame(parsed_text)

# 명사와 형용사만 추출
word_df <- parsed_df %>%
  # 명사(NOUN)와 형용사(ADJ)만 선택
  filter(upos %in% c("NOUN", "ADJ")) %>%
  # 소문자로 변환
  mutate(word = str_to_lower(token)) %>%
  # 기본 전처리
  filter(!str_detect(word, "^[0-9]+$")) %>%   # 숫자 제거
  filter(str_length(word) > 2) %>%            # 3글자 이상 단어만 선택
  filter(str_detect(word, "[a-z]")) %>%       # 알파벳이 포함된 단어만 선택
  # 불필요한 단어 추가 제거
  filter(!word %in% c("the", "and", "very", "more", "such", "etc"))

# 단어 빈도수 계산
word_freq <- word_df %>%
  count(word, upos, sort = TRUE) %>%
  filter(n >= 2)  # 최소 빈도 2 이상

# 품사별 상위 단어 출력
print("Top Nouns:")
word_freq %>% 
  filter(upos == "NOUN") %>% 
  head(10) %>%
  print()

print("\nTop Adjectives:")
word_freq %>% 
  filter(upos == "ADJ") %>% 
  head(10) %>%
  print()

# 워드클라우드 생성
wordcloud2(word_freq %>% select(word, n), 
           size = 1,
           fontFamily = "Arial",
           color = "random-dark",
           backgroundColor = "white",
           rotateRatio = 0.35,
           minRotation = -pi/6,
           maxRotation = pi/6)

# 품사별로 다른 색상을 적용한 워드클라우드 (선택사항)
word_freq_color <- word_freq %>%
  mutate(color = if_else(upos == "NOUN", "#1f77b4", "#ff7f0e"))  # 명사는 파란색, 형용사는 주황색

wordcloud2(word_freq_color %>% select(word, n),
           size = 1,
           fontFamily = "Arial",
           color = word_freq_color$color,
           backgroundColor = "white",
           rotateRatio = 0.35)

# 결과 저장
library(htmlwidgets)
saveWidget(wordcloud2(word_freq %>% select(word, n)), 
           "noun_adj_wordcloud.html", 
           selfcontained = TRUE)