귀퉁이 서재

DATA - 23. Data Wrangling (Gathering Data) 본문

데이터 분석

DATA - 23. Data Wrangling (Gathering Data)

Baek Kyun Shin 2019. 5. 10. 22:40

우선, 데이터 랭글링(Data Wrangling)이란 무엇인지 알아보겠습니다. 아래는 위키피디아 정의입니다.

Data wrangling, sometimes referred to as data munging, is the process of transforming and mapping data from one "raw" data form into another format with the intent of making it more appropriate and valuable for a variety of downstream purposes such as analytic.

쉽게 말하면, 원천 데이터(raw data)를 분석하기 좋은 데이터로 변환하는 작업을 뜻합니다.

데이터 랭글링은 총 5단계로 구분됩니다.

1. Gather
 - 데이터를 얻는 방법으로는 다운로드(Download), 웹 스크레이핑(Web scrapping), API(Application Programming Interface)가 있습니다.

2. Assess
 - 얻은 데이터를 읽고 데이터가 깨끗한지 아닌지 판단하는 단계입니다.

3. Clean
 - 데이터를 정제하는 방법으로는 Define, Code, Test가 있습니다. 2단계(Assess)에서 발견된 데이터의 문제점을 보고 어떤 부분을 정제할지 정의하고(Define), 정제하기 위한 코드를 짜고(Code), 잘 정제가 되었는지 테스트를 해보는(Test) 것입니다.

4. Reassess and Iterate
 - 다시 2단계로 돌아가 데이터가 잘 정제되었는지 판단을 합니다. 추가로 정제해야 할 부분이 있다면 다시 2-3-4 단계를 반복합니다.

5. Store (optional)
 - 나중에 다시 사용하기 위해 저장하는 단계입니다.

이번 챕터에서는 1단계인 Gathering Data에 대해 알아보겠습니다.

Gathering 단계에는 다운로드, 웹 스크레이핑, API를 활용하는 방법이 있다고 했습니다.

다운로드(Download)

다운받은 파일은 주로 플랫 파일(flat file) 형태입니다. (Reference1) 플랫 파일 형식은 주로 csv, tsv입니다. csv는 comma separated values, tsv는 tab separated values입니다. 아래와 같이 csv는 콤마로 분리가 되어 있는 데이터를 뜻하고, tsv는 tab으로 분리가 되어 있는 데이터를 뜻합니다.

출처: Udacity
출처: Udacity

csv든 tsv든 text editor로 읽으면 delimiter(콤마, tab 등)는 다르게 보이지만 Google 스프레드 sheet나 엑셀에서는 delimiter가 보이지 않기 때문에 형식이 똑같게 보입니다.

참고로, tsv를 읽는 방법은 아래와 같습니다.

import pandas as pd

# Import the Rotten Tomatoes bestofrt TSV file into a DataFrame
df = pd.read_csv('bestofrt.tsv', sep='\t')

플랫 파일의 장점은
1. 가볍다, 2. 사용자가 쉽게 읽을 수 있다, 3. 이해하기 쉽다, 4. 읽을 수 있는 프로그램이 다양하다, 5. 용량이 작은 데이터 셋에 적합하다.

반면, 단점은
1. 표준화가 안 되어 있다, 2. 불필요한 중복이 있다, 3. 데이터 공유가 번거롭다, 4. 용량이 큰 데이터 셋에 적합하지 않다.
입니다.

데이터의 크기가 커지면 플랫 파일 대신 관계형 데이터베이스를 사용하는 것이 좋습니다. (Reference2)

BeautifulSoup를 활용한 웹 스크레이핑 (Web Scrapping)

웹 스크레이핑에 사용되는 주요 라이브러리로는 Requests (Reference3)와 BeautifulSoup (Reference4)가 있습니다.

위 라이브러리를 활용해 웹 스크레이핑을 하는 실습을 해보겠습니다. 본 실습에서 사용하는 데이터는 rt_html 이 링크를 통해 다운로드할 수 있습니다. zip 파일이며 압축을 풀면 영화에 대한 관객평점과 랭킹을 담고 있는 웹 페이지(html)들이 있습니다. (원본 데이터는 영화에 대한 평점과 감상평 등을 제공해주는 Rotten Tomatoes 사이트에 있습니다.)

직접 압축을 풀지 않고 아래 코드로 압축을 풀 수 있습니다.

import zipfile

# Extract all contents from zip file
with zipfile.ZipFile('rt_html.zip', 'r') as myzip:
    myzip.extractall()

rt_html 폴더 안에 여러 html 파일이 있을 겁니다. 이제 BeautifulSoup를 통해 웹 스크레이핑을 하겠습니다. 이름이 예뻐서 그런지 개인적으로 좋아하는 라이브러리 중 하나입니다. ㅎㅎ

from bs4 import BeautifulSoup
import os
import pandas as pd

# List of dictionaries to build file by file and later convert to a DataFrame
df_list = []
folder = 'rt_html'
for movie_html in os.listdir(folder):
    with open(os.path.join(folder, movie_html)) as file:
        soup = BeautifulSoup(file, "lxml")
        title = soup.find('title').contents[0][0:-len(' - Rotten Tomatoes')].replace(u'\xa0', u' ')
        audience_score = soup.find_all('div', class_='audience-score meter')[0].find('span').text[:-1]
        num_audience_ratings = soup.find_all('div', class_='audience-info hidden-xs superPageFontColor')[0].find_all('div')[1].contents[2].strip().replace(',','')

        # Append to list of dictionaries
        df_list.append({'title': title,
                        'audience_score': int(audience_score),
                        'number_of_audience_ratings': int(num_audience_ratings)})
df = pd.DataFrame(df_list, columns = ['title', 'audience_score', 'number_of_audience_ratings'])

rt_html 폴더 안의 모든 html 파일을 돌며 해당 html을 엽니다. BeautifulSoup를 통해 'lxml' 방식으로 파싱한 뒤 title, 관객 평점, 랭킹 데이터를 딕셔너리 형태로 df_list에 저장합니다. BeautifulSoup애서 'lxml'모드는 default지만 시스템에 따라 다른 형식이 default로 지정되는 경우가 있습니다. 따라서 'lxml'로 지정해주는 것이 좋습니다.

BeautifulSoup는 html 태그를 기준으로 데이터를 추출할 수 있습니다. title, audience_score, num_audience_ratings를 추출하는 게 굉장히 복잡해 보이지만 실제 print 되는 결과물을 보면서 하면 쉽게 코드를 작성할 수 있습니다. 여기서는 find()와 find_all()을 사용했습니다. 마지막으로 df_list를 DataFrame로 변환했습니다. 

참고로, Python에서 파일을 열 때 권장하는 형식은 'with open(file루트) as file명:' 입니다. 링크를 참고하시기 바랍니다. (Reference5)

이제 잘 추출이 되었는지 보겠습니다.

df.head()

title과 관객평, 랭킹이 잘 추출되었습니다.

requests를 활용한 웹 스크레이핑 (Web Scrapping)

import requests
import os

# Make directory if it doesn't already exist
folder_name = 'ebert_reviews'
if not os.path.exists(folder_name):
    os.makedirs(folder_name)

현재 디렉토리에 ebert_reviews폴더가 없다면 하나 만들어주는 코드입니다. 아래는 url을 리스트에 하드 코딩해놓은 것입니다.

ebert_review_urls = ['https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9900_1-the-wizard-of-oz-1939-film/1-the-wizard-of-oz-1939-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9901_2-citizen-kane/2-citizen-kane.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9901_3-the-third-man/3-the-third-man.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9902_4-get-out-film/4-get-out-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9902_5-mad-max-fury-road/5-mad-max-fury-road.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9902_6-the-cabinet-of-dr.-caligari/6-the-cabinet-of-dr.-caligari.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9903_7-all-about-eve/7-all-about-eve.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9903_8-inside-out-2015-film/8-inside-out-2015-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9903_9-the-godfather/9-the-godfather.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9904_10-metropolis-1927-film/10-metropolis-1927-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9904_11-e.t.-the-extra-terrestrial/11-e.t.-the-extra-terrestrial.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9904_12-modern-times-film/12-modern-times-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9904_14-singin-in-the-rain/14-singin-in-the-rain.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9905_15-boyhood-film/15-boyhood-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9905_16-casablanca-film/16-casablanca-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9905_17-moonlight-2016-film/17-moonlight-2016-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9906_18-psycho-1960-film/18-psycho-1960-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9906_19-laura-1944-film/19-laura-1944-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9906_20-nosferatu/20-nosferatu.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9907_21-snow-white-and-the-seven-dwarfs-1937-film/21-snow-white-and-the-seven-dwarfs-1937-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9907_22-a-hard-day27s-night-film/22-a-hard-day27s-night-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9907_23-la-grande-illusion/23-la-grande-illusion.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9908_25-the-battle-of-algiers/25-the-battle-of-algiers.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9908_26-dunkirk-2017-film/26-dunkirk-2017-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9908_27-the-maltese-falcon-1941-film/27-the-maltese-falcon-1941-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9909_29-12-years-a-slave-film/29-12-years-a-slave-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9909_30-gravity-2013-film/30-gravity-2013-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9909_31-sunset-boulevard-film/31-sunset-boulevard-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990a_32-king-kong-1933-film/32-king-kong-1933-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990a_33-spotlight-film/33-spotlight-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990a_34-the-adventures-of-robin-hood/34-the-adventures-of-robin-hood.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990b_35-rashomon/35-rashomon.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990b_36-rear-window/36-rear-window.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990b_37-selma-film/37-selma-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990c_38-taxi-driver/38-taxi-driver.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990c_39-toy-story-3/39-toy-story-3.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990c_40-argo-2012-film/40-argo-2012-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990d_41-toy-story-2/41-toy-story-2.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990d_42-the-big-sick/42-the-big-sick.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990d_43-bride-of-frankenstein/43-bride-of-frankenstein.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990d_44-zootopia/44-zootopia.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990e_45-m-1931-film/45-m-1931-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990e_46-wonder-woman-2017-film/46-wonder-woman-2017-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990e_48-alien-film/48-alien-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990f_49-bicycle-thieves/49-bicycle-thieves.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990f_50-seven-samurai/50-seven-samurai.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad990f_51-the-treasure-of-the-sierra-madre-film/51-the-treasure-of-the-sierra-madre-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9910_52-up-2009-film/52-up-2009-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9910_53-12-angry-men-1957-film/53-12-angry-men-1957-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9910_54-the-400-blows/54-the-400-blows.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9911_55-logan-film/55-logan-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9911_57-army-of-shadows/57-army-of-shadows.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9912_58-arrival-film/58-arrival-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9912_59-baby-driver/59-baby-driver.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9913_60-a-streetcar-named-desire-1951-film/60-a-streetcar-named-desire-1951-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9913_61-the-night-of-the-hunter-film/61-the-night-of-the-hunter-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9913_62-star-wars-the-force-awakens/62-star-wars-the-force-awakens.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9913_63-manchester-by-the-sea-film/63-manchester-by-the-sea-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9914_64-dr.-strangelove/64-dr.-strangelove.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9914_66-vertigo-film/66-vertigo-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9914_67-the-dark-knight-film/67-the-dark-knight-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9915_68-touch-of-evil/68-touch-of-evil.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9915_69-the-babadook/69-the-babadook.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9915_72-rosemary27s-baby-film/72-rosemary27s-baby-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9916_73-finding-nemo/73-finding-nemo.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9916_74-brooklyn-film/74-brooklyn-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9917_75-the-wrestler-2008-film/75-the-wrestler-2008-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9917_77-l.a.-confidential-film/77-l.a.-confidential-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9918_78-gone-with-the-wind-film/78-gone-with-the-wind-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9918_79-the-good-the-bad-and-the-ugly/79-the-good-the-bad-and-the-ugly.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9918_80-skyfall/80-skyfall.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9919_82-tokyo-story/82-tokyo-story.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9919_83-hell-or-high-water-film/83-hell-or-high-water-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9919_84-pinocchio-1940-film/84-pinocchio-1940-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad9919_85-the-jungle-book-2016-film/85-the-jungle-book-2016-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991a_86-la-la-land-film/86-la-la-land-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991b_87-star-trek-film/87-star-trek-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991b_89-apocalypse-now/89-apocalypse-now.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991c_90-on-the-waterfront/90-on-the-waterfront.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991c_91-the-wages-of-fear/91-the-wages-of-fear.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991c_92-the-last-picture-show/92-the-last-picture-show.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991d_93-harry-potter-and-the-deathly-hallows-part-2/93-harry-potter-and-the-deathly-hallows-part-2.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991d_94-the-grapes-of-wrath-film/94-the-grapes-of-wrath-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991d_96-man-on-wire/96-man-on-wire.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991e_97-jaws-film/97-jaws-film.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991e_98-toy-story/98-toy-story.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991e_99-the-godfather-part-ii/99-the-godfather-part-ii.txt',
                     'https://d17h27t6h515a5.cloudfront.net/topher/2017/September/59ad991e_100-battleship-potemkin/100-battleship-potemkin.txt']
# Implement the code in the video above in a for loop for all Ebert reviews
for url in ebert_review_urls:
    response = requests.get(url)
    file_name = url.split('/')[-1]
    with open(os.path.join(folder_name, file_name), mode='wb') as file:
        file.write(response.content)

ebert_review_urls를 돌면서 requests 라이브러리를 통해 get요청을 합니다. get요청의 결과를 response에 담습니다. response의 내용을 file에 저장하는 코드입니다. 여기서 mode='wb'는 write + binary의 약자입니다. text면 mode='r', 'w'로 써도 되고 그렇지 않고 binary형식으로 읽고 쓸 거면 'rb', 'wb'로 써야 합니다. (Reference6)

잘 되었는지 테스트해보겠습니다.

import filecmp
dc = filecmp.dircmp('ebert_reviews', 'ebert_reviews_solution')
assert len(dc.common) == 88

ebert_reviews_solution은 udacity에서 제공한 정답입니다. 지금까지 만든 ebert_reviews 폴더 안의 파일들과 ebert_reviews_solution의 파일들을 서로 비교해서 같은 것이 88개이면 아무 메시지를 출력 안합니다. assert는 참이면 아무 메시지를 출력 안 하고, 거짓일 때만 에러 메시지를 띄웁니다.

모든 파일의 내용을 DataFrame에 담기

glob라이브러리를 사용해서 방금까지 만든 모든 파일의 내용을 DataFrame에 담아보겠습니다. 

import glob
import pandas as pd

# List of dictionaries to build file by file and later convert to a DataFrame
df_list = []
for ebert_review in glob.glob('ebert_reviews/*.txt'):
    with open(ebert_review, encoding='utf-8') as file:
        title = file.readline()[:-1]
        review_url = file.readline()[:-1]
        review_text = file.read()

        # Append to list of dictionaries
        df_list.append({'title': title,
                        'review_url': review_url,
                        'review_text': review_text})
df = pd.DataFrame(df_list, columns = ['title', 'review_url', 'review_text'])

참고로, UTF-8은 binary data를 숫자로 바꾸어주는 인코딩 방식을 의미하고, Unicode는 그 숫자를 문자로 대응시킬 때 사용하는 문자 매핑 표준입니다. (Reference7)

file object는 iterator이며, 루프를 한번 돌면 끝입니다. 무슨 말인지 이해가 안 가면 (Reference8)을 참고하시기 바랍니다.예제와 함께 자세히 설명되어 있습니다.

API를 사용해서 데이터를 받아오는 방법은 다음 챕터부터 다루겠습니다.

References

Reference 1: flat file; 플랫 파일

Reference 2: Relational Databases Not your Father’s Flat Files

Reference 3: Requests: HTTP for Humans

Reference 4: BeautifulSoup

Reference 5: How to read a large file, line by line, in Python

Reference 6: What does 'wb' mean in this code, using Python?

Reference 7: The difference between UTF-8 and Unicode

Reference 8: Is file object in python an iterable

 

Comments