[AI SCHOOL 5기] 텍스트 분석 실습 - 워드클라우드

Okt Library #

  • 한국어 형태소 분석기 KoNLPy 패키지에 속한 라이브러리

KoNLPy 테스트 #

python
from konlpy.tag import Okt

tokenizer = Okt()
tokens = tokenizer.pos("아버지 가방에 들어가신다.", norm=True, stem=True)
print(tokens)
  • norm: 정규화(Normalization), ‘안녕하세욯’ -> ‘안녕하세요’
  • stem: 어근화(Stemming, Lemmatization), (‘한국어’, ‘Noun’)

Pickle Library (Extra) #

  • 파이썬 변수를 pickle 파일로 저장/불러오기
python
with open('raw_pos_tagged.pkl', 'wb') as f:
    pickle.dump(raw_pos_tagged, f) 

with open('raw_pos_tagged.pkl','rb') as f:
    data = pickle.load(f)

크롤링 데이터 전처리 #

크롤링 데이터 불러오기 #

python
df = pd.read_excel('result_220328_1314.xlsx')
articles = df['Article'].tolist()
articles = ''.join(articles)
  • Article 데이터를 불러와서 리스트화 시키고 다시 하나의 문자열로 변환

형태소 단위 분해 #

python
from konlpy.tag import Okt

tokenizer = Okt()
raw_pos_tagged = tokenizer.pos(articles, norm=True, stem=True)

단어 등장 빈도 시각화 #

python
word_cleaned = ['불용어가 제거된 단어 목록']

# NLTK의 Text() 클래스에서 matplotlib의 plot 기능 제공
word_counted = nltk.Text(word_cleaned)
plt.figure(figsize=(15, 7))
word_counted.plot(50)

count-graph

[AI SCHOOL 5기] 웹 크롤링 실습 - 웹 스크래핑 심화

Import Libraries #

python
import requests
from bs4 import BeautifulSoup

import pandas as pd
from datetime import datetime
import time # time.sleep()
import re

뉴스 검색 결과에서 네이버 뉴스 추출 #

네이버 뉴스 검색 결과 URL 분석 #

html
https://search.naver.com/search.naver?
    where=news&
    sm=tab_jum& <!-- 불필요 -->
    query=데이터분석

네이버 뉴스 검색 URL 불러오기 #

python
query = input() # 데이터분석

url = f'https://search.naver.com/search.naver?where=news&query={query}'
web = requests.get(url).content
source = BeautifulSoup(web, 'html.parser')

네이버 뉴스 기사 주제 가져오기 #

python
news_subjects = source.find_all('a', {'class' : 'news_tit'})

subject_list = []

for subject in news_subjects:
    subject_list.append(subject.get_text())

네이버 뉴스 기사 링크 가져오기 #

python
urls_list = []

for urls in source.find_all('a', {'class' : 'info'}):
    if urls.attrs['href'].startswith('https://news.naver.com'):
        urls_list.append(urls.attrs['href'])

단일 뉴스 페이지 분석 #

ConnectionError #

python
web_news = requests.get(urls_list[0]).content
source_news = BeautifulSoup(web_news, 'html.parser')
bash
ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
  • 브라우저를 거치지 않고 HTML 코드를 요청하면 ConnectionError 발생
  • 사용자임을 알리는 헤더 추가
python
headers = {'User-Agent':'Mozilla/5.0
           (Windows NT 6.3; Win64; x64) 
           AppleWebKit/537.36 (KHTML, like Gecko)
           Chrome/63.0.3239.132 Safari/537.36'}

web_news = requests.get(urls_list[0], headers=headers).content
source_news = BeautifulSoup(web_news, 'html.parser')

기사 제목 / 발행 날짜 추출 #

python
title = source_news.find('h3', {'id' : 'articleTitle'}).get_text()
date = source_news.find('span', {'class' : 't11'}).get_text()

Pandas Timestamp #

python
# 2022.03.25. 오전 10:18
date = source_news.find('span', {'class' : 't11'}).get_text()

# 2022.03.25.10:18am
pd_date = pd.Timestamp(reformatted_date)

기사 본문 추출 #

python
article = source_news.find('div', {'id' : 'articleBodyContents'}).get_text()

article = article.replace("\n", "")
article = article.replace("// flash 오류를 우회하기 위한 함수 추가function _flash_removeCallback() {}", "")
article = article.replace("동영상 뉴스       ", "")
article = article.replace("동영상 뉴스", "")
article = article.strip()

기사 발행 언론사 추출 #

python
press_company = source_news.find('address', {'class' : 'address_cp'}).find('a').get_text()
print(press_company)

여러 뉴스 데이터 수집 #

각 기사들의 데이터를 수집해 리스트에 추가 #

python
for url in urls_list:
    ...

    titles.append(title)
    dates.append(date)
    articles.append(article)
    article_urls.append(url)
    press_companies.append(press_company)

데이터에 대한 DataFrame 생성 #

python
article_df = pd.DataFrame({'Title':titles, 
                           'Date':dates, 
                           'Article':articles, 
                           'URL':article_urls, 
                           'PressCompany':press_companies})

article-df

[AI SCHOOL 5기] 텍스트 분석 실습 - 텍스트 분석

Scikit-learn Library #

  • Traditional Machine Learning (vs DL, 인공신경을 썼는지의 여부)
python
from sklearn import datasets, linear_model, model_selection, metrics

data_total = datasets.load_boston()

x = data_total.data
y = data_total.target

train_x, test_x, train_y, test_y =
    model_selection.train_test_split(x, y, test_size=0.3)

# 학습 전의 모델 생성
model = linear_model.LinearRegression()
# 모델에 학습 데이터를 넣으면서 학습 진행
model.fit(train_x, train_y)

# 모델에게 새로운 데이터를 주면서 예측 요구
predictions = model.predict(test_x)
# 예측 결과를 바탕으로 성능 점수 확인
metrics.mean_squared_error(predictions, test_y)
  • 실습에서 사용할 패키지
python
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

Vectorizer #

python
corpus = [doc1, doc2, doc3]

vectorizer = TfidfVectorizer()

X = vectorizer.fit_transform(corpus).todense()
# vertorizer.fit(corpus)
# X = vectorizer.trasnform(corpus)
  • Vectorizer는 리스트로 묶인 다수의 데이터에 대한 벡터 생성
  • fit_transform()은 기본적으로 메모리를 차지하는 ‘0’이라는 값들을 제외하고
    좌표에 대한 값의 형태로 표시
  • .todense(): ‘0’이라는 값들을 포함하여 행렬의 형태로 표시
  • X.shape: Vectorizer의 모양 확인 (row, column)
  • CountVectorizer(): 단어의 출연 횟수만으로 벡터 생성
python
[[0.0071001  0.00332632 0.         ... 0.         0.00166316 0.        ]
 [0.00889703 0.         0.00138938 ... 0.00138938 0.         0.00138938]]

Pandas Output #

vectorizer-pandas

[AI SCHOOL 5기] 텍스트 분석 실습 - 텍스트 데이터 분석

Tokenizing Text Data #

Import Libraries #

python
import nltk
from nltk.corpus import stopwords
from collections import Counter

Set Stopwords #

python
stop_words = stopwords.words("english")
stop_words.append(',')
stop_words.append('.')
stop_words.append('’')
stop_words.append('”')
stop_words.append('—')

Open Text Data #

python
file = open('movie_review.txt', 'r', encoding="utf-8")
lines = file.readlines()

Tokenize #

python
tokens = []
for line in lines:
    tokenized = nltk.word_tokenize(line)
    for token in tokenized:
        if token.lower() not in stop_words:
            tokens.append(token)

Counting Nouns #

POS Tagging #

python
tags = nltk.pos_tag(tokens)

word_list = []
for word, tag in tags:
    if tag.startswith('N'):
        word_list.append(word.lower())

Counting Nouns #

python
counts = Counter(word_list)
print(counts.most_common(10))

Output

[AI SCHOOL 5기] 텍스트 분석 실습 - 텍스트 분석

NLTK Library #

  • NLTK(Natural Language Toolkit)은 자연어 처리를 위한 라이브러리
python
import nltk

nltk.download()

문장을 단어 수준에서 토큰화 #

python
sentence = 'NLTK is a leading platform for building Python programs to work with human language data. It provides easy-to-use interfaces to over 50 corpora and lexical resources such as WordNet, along with a suite of text processing libraries for classification, tokenization, stemming, tagging, parsing, and semantic reasoning, wrappers for industrial-strength NLP libraries, and an active discussion forum.'

nltk.word_tokenize(sentence)

Output

[AI SCHOOL 5기] 웹 크롤링 실습 - 웹 스크래핑 기본

BeautifulSoup Library #

python
from bs4 import BeautifulSoup 
from urllib.request import urlopen 

단어의 검색 결과 출력 #

다음 어학사전 URL 불러오기 #

python
# 찾는 단어 입력
word = 'happiness'

url = f'https://alldic.daum.net/search.do?q={word}'
web = urlopen(url)
web_page = BeautifulSoup(web, 'html.parser')

찾는 단어 출력 #

python
text_search = web_page.find('span', {'class': 'txt_emph1'})
print(f'찾는 단어: {text_search.get_text()}')

단어의 뜻 출력 #

python
list_search = web_page.find('ul', {'class': 'list_search'})
list_text = list_search.find_all('span', {'class': 'txt_search'})
definitions = [definition.get_text() in list_text]
print(f'단어의 뜻: {definitions}')

Output

vim
찾는 단어: happiness
단어의 : ['행복', '만족', '기쁨', '행운']

영화 정보 출력 #

네이버 영화 URL 불러오기 #

python
# 찾는 영화 번호 입력 (향후 영화 제목으로 검색 구현)
movie = 208077

url = f'https://movie.naver.com/movie/search/result.naver?query={movie}'
web = urlopen(url)
web_page = BeautifulSoup(web, 'html.parser')

영화 제목 출력 #

python
title = web_page.find('h3', {'class':'h_movie'}).find('a')
print(f'Movie Title: {title.get_text()}')

네이버 영화 배우/제작진 URL 불러오기 #

python
url = f'https://movie.naver.com/movie/bi/mi/detail.naver?query={movie}'
web = urlopen(url)
web_page = BeautifulSoup(web, 'html.parser')

감독 이름 출력 #

python
director = web_page.find('div', {'class':'dir_product'}).find('a')
print(f'Director: {director.get_text()}')

출연 배우들 이름 출력 #

python
actor_list = web_page.find('ul', {'class':'lst_people'})
actor_names = actor_list.find_all('a', {'class':'k_name'})
actors = [actor.get_text() for actor in actor_names]
print(f'Actors: {actors}')

Output

[AI SCHOOL 5기] 웹 크롤링

Web Crawling vs Web Scraping #

  • Web Crawling: Bot이 web을 link를 통해 돌아다니는 것
  • Web Scraping: Webpage에서 원하는 자료를 긇어오는 것

HTML Tags #

  • Tag’s Name: html, head, body, p, span, li, ol, ul, div
  • Tag’s Attribute: class, id, style, href, src

The Process of Web Scraping #

  1. URL 분석 (query 종류 등)
  2. URL 구성
  3. HTTP Response 얻기 (urlopen(URL) or request.get(URL).content)
  4. HTTP source 얻기 (BeautifulSoup(HTTP Response, 'html.parser'))
  5. HTML Tag 꺼내기 (.find('tag_name', {'attr_name':'attr_value'}))
  6. Tag로부터 텍스트 혹은 Attribute values 꺼내기 (Tag.get_text() or Tag.attrs)

The Process of Data Analysis for Text Data #

  1. 텍스트 데이터를 str 자료형으로 준비
  2. Tokenize (형태소 분석)
  3. POS Tagging (Part-of-speech, 품사 표시)
  4. Stopwords 제거 (불용어 제거)
  5. 단어 갯수 카운팅 & 단어 사전 생성
  6. 단어 사전 기반 데이터 시각화
  7. (+ 머신러닝/딥러닝 모델 적용)

TF-IDF #

  • Term Frequency - Inverse Document Frequency
  • 특정 단어가 문서에서 어떤 중요도를 가지는지를 나타내는 지표
  • 많은 문서에 공통적으로 들어있는 단어는 문서 구별 능력이 떨어진다 판단하여 가중치 축소

Count Vectorizer #

  • 단어의 빈도수만을 사용해서 벡터 생성
DocumentThatNiceCarJohnHasRed
A111000
B101111

TF-IDF Vectorizer #

  • 단어의 빈도수(TF)를 TF-IDF 값으로 변경하여 가중치가 조정된 벡터 생성

tf-idf-fomular

[AI SCHOOL 5기] 데이터 분석 실습 - 데이터 시각화

Visualization Libraries #


Data Chart Types #


GeoJSON Data #

python
import json

# 한국의 지도 데이터 참조
# @ https://github.com/southkorea/southkorea-maps
geo_path = 'skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))
  • JSON(Javascript Object Notation): 데이터 교환을 위한 표준 포맷
  • GeoJSON: 지도 데이터 포맷
  • json.load: JSON 파일 불러오기
  • json.dump: JSON 파일 저장하기

PyPrnt Library #

python
from pyprnt import prnt

prnt(geo_str, truncate=True, width=80)

pyprnt

[AI SCHOOL 5기] 데이터 분석 실습 - 데이터 탐색

Visualization Library #

python
import seaborn as sns

sns.heatmap(gu_df[])

original


Visualization Issues #

  1. 한글 데이터 표시 오류
  2. 서로 다른 자릿수로 구성된 열에 동일한 스케일 적용
  3. 시각화된 테이블 형태의 비직관성 문제
  4. 인구수가 고려되지 않은 부정확한 데이터

한글 데이터 시각화 #

python
matplotlib inline

# Windows
font_name = font_manager.FontProperties(fname="C:/~/malgun.ttf").get_name()
rc('font', family=font_name)

# Mac
rc('font', family='AppleGothic')

Feature Scaling/Normalization #

  1. Min-Max Algorithm
    • 열에 대한 최솟값(min)을 0, 열에 대한 최댓값(max)를 1로 맞춤
    • 기존 열을 old_x, 새로운 열을 new_x라 할 때,
      new_x = ( old_x - min(column) ) / ( max(column) - min(column) )
  2. Standardization
    • 열에 대한 평균값(mean)을 0, 열에 대한 표준편차 값(std)를 1로 맞춤
    • 기존 열을 old_x, 새로운 열을 new_x라 할 때,
      new_x = ( old_x - mean(column) ) / std(column)
    • 표준 점수 (Z-score)와 동일

시각화 개선 #

  1. 전체 테이블의 사이즈 조정
    pit.figure(figsize = (x, y))
  2. 셀 형식 및 색상 등 변경
    sns.heatmap(norm, annot=, fmt=, linewidths=, cmap=)
    • annot: 셀 내에 수치 입력 여부 (defualt False)
    • fmt: 셀 내에 입력될 수치의 format ('f' == float)
    • linewidths: 셀 간 거리 (내부 테두리)
    • cmap: matplotlib colormap @https://goo.gl/YWpBES
  3. 테이블 제목 설정
    plt.tile()
  4. 시각화 설정된 테이블 표시
    plt.show()

데이터 정확성 개선 #

  1. 범죄 발생 횟수에 인구수 반영
python
# 시각화된 데이터에서 열방향(axis=0)을 기준으로 인구수 데이터를 나눔 (인구 10만 단위)
crime_ratio = crime_count_norm.div(gu_df['인구수'], axis=0) * 100000
  1. 구별 5대 범죄 발생 수치 평균 계산
python
crime_ratio['전체발생비율'] = crime_ratio.mean(axis=1)
  • 각 사건들의 중형도를 고려하지 못할 수 있음
  • ‘살인’ 열에 스케일 적용 중 이미 큰 값이 곱해졌기 때문에 가중치가 적용되었다고도 판단 가능

Improved Visualization #

python
plt.figure(figsize = (10,10))

sns.heatmap(crime_ratio.sort_values(by='전체발생비율', ascending=False),
            annot=True, fmt='f', linewidths=.5, cmap='Reds')
plt.title('범죄 발생(전체발생비율로 정렬) - 각 항목을 정규화한 후 인구로 나눔')
plt.show()

improved-visualization

[AI SCHOOL 5기] 데이터 분석 실습 - 데이터 분석

Practice Data #

  • 서울시 범죄현황 통계자료

original


범죄별로 검거율 계산 #

python
# gu_df는 실습 자료에 서울시 경찰청의 소속 구 데이터를 추가한 DataFrame
gu_df['강간검거율'] = gu_df['강간(검거)']/gu_df['강간(발생)']*100
gu_df['강도검거율'] = gu_df['강도(검거)']/gu_df['강도(발생)']*100
gu_df['살인검거율'] = gu_df['살인(검거)']/gu_df['살인(발생)']*100
gu_df['절도검거율'] = gu_df['절도(검거)']/gu_df['절도(발생)']*100
gu_df['폭력검거율'] = gu_df['폭력(검거)']/gu_df['폭력(발생)']*100
gu_df['검거율'] = gu_df['소계(검거)']/gu_df['소계(발생)']*100

gu-df

해당 계산법의 문제:

  • 이전 연도에 발생한 사건이 많이 검거될 경우 검거율이 100%를 초과
  • 발생 건수가 0인 경우 검거율에 결측치(N/A)가 발생

초과된 검거율을 최댓값으로 조정:

python
# 검거율에 해당되는 열의 집합 columns
columns = ['강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율']
  1. 모든 행에 대해 반복문 실행
python
for row_index, row in gu_df_rate.iterrows():
    for column in columns:
        if row[column] > 100:
            gu_df.at[row_index, column] = 100
  1. Masking 기법 활용
python
gu_df[ gu_df[columns] > 100 ] = 100
  • gu_df[columns] > 100은 True와 False로 이루어진 행렬을 반환
  • 해당 행렬을 gu_df의 Key로 사용하면 True에 해당하는 값이 채로 친듯 솎아 걸러짐
  • 조건이 두 개 이상일 경우 괄호로 감싸주어야 함
  • Pandas에서는 and, or, not이 동작하지 않기 때문에 &, |, ~ 사용
  • gu_df[ (gu_df['살인(발생)'] > 7) & (gu_df['폭력(발생)'] > 2000) ]

결측치를 의미있는 값으로 변경: