스프레드쉬트에서 데이터베이스로

데이터 추출

학습목표

  • csv.reader를 왜 "랩퍼(wrapper)"로 부르는지 설명한다.
  • 짧은 파이썬 프로그램을 작성해서 CSV 파일에서 데이터를 추출한다.
  • 문자열 쪼개기를 사용해서 CSV같은 데이터 형식 자료를 파싱하는 위험을 설명한다.
  • 그럼에도 불구하고, 이 데이터에서 저자명을 추출하는데 문자열 쪼개기가 왜 수용가능한 접근법인지 설명한다.

첫번째 단계는 스프레드쉬트 행을 (키값, 저자명) 짝으로 변환하는 것이다. 파이썬이 올바르게 스프레드쉬트를 읽어들이도록 확인하고서 시작한다:

# count-lines.py
# 스프레드쉬트에 얼마나 많은 줄이 있는지 계수한다.
import sys

filename = sys.argv[1]
reader = open(filename, 'r')
count = 0
for line in reader:
    count += 1
reader.close()
print count

상기 코드가 이제는 칙숙해보여야 된다: 파일명이 첫번째 명령라인 인자(sys.argv[1])로 주어졌다. 따라서, 파일을 열고, for 루프를 사용해서 한줄씩 읽어들인다. 매번 루프가 실행될 때, 1을 count 변수에 더한다; 루프가 종류될 때, 파일을 닫고 계수 결과를 출력한다.

상기 프로그램을 다음과 같이 실행한다:

$ python code/count-lines.py data/bibliography.csv

물론, 결과는 다음과 같다:

2937

그래서, 파이썬이 모든 행을 읽어들인 것을 알게된다.

다음 단계는 각 줄을 필드로 쪼개서 각 항목에 대한 키값과 저자명을 얻게된다. 필드는 콤마로 구분된다. 그래서 str.split 사용해서 시도해볼 수 있다. 하지만, 동작하지는 않는데 이유는 저자명에도 콤마가 포함되어서 그렇다("성, 이름"같은 형식으로 되어 있어서 그렇다).

대신에 취할 수 있는 조치는 선호하는 검색엔진에 도움을 청한다. 물론, "python csv"에 대한 검색결과는 csv 라이브러리가 나오고, 표준 파이썬 배포판의 일부이기도 하다. 라이브러리 문서에 일부 예제가 포함되어 있다. 몇번 실험을 한 뒤에, 다음과 같은 결과가 나오게 된다:

# read-fields.py
# CSV 파일에서 필드값을 제대로 읽어 오는지 확인한다.

import sys
import csv

raw = open(sys.argv[1], 'r')
reader = csv.reader(raw);
for line in reader:
    print line
raw.close()

작성한 프로그램은 참고문헌 파일을 열어서 시작한다(다시 한번, 첫번째 명령-라인 인자로 파일명을 넘긴다) 그리고 나서, csv.reader 메쏘드를 호출해서 파일주위에 래퍼를 생성한다. open으로 생성된 기본 파일 객체가 한번에 한줄씩 읽어올 때, csv.reader에 의해서 생성된 래퍼가 해당 라인을 올바른 지점에서 필드로 쪼갠다. csv.reader는 해당 필드에 내장된 콤마, 특수문자, 신경쓰지 않아도 되는 다른 엄청난 것에 대해 어떻게 처리하는지 알고 있다.

올바르게 동작하는지 점검하려면, csv.reader에 의한 처리가 끝난 후에 각 줄을 출력하면 된다. 출력결과 중 첫 몇줄이 다음에 나와 있다:

$ python code/read-fields.py data/bibliography.csv | head -5
['8SW85SQM', 'journalArticle', '2013', 'McClelland, James L', 'Incorporating Rapid Neocortical Learning of New Schema-Consistent Information Into Complementary Learning Systems Theory.', 'J Exp Psychol Gen', '', '1939-2222', '', 'http://www.biomedsearch.com/nih/Incorporating-Rapid-Neocortical-Learning-New/23978185.html', '', '', '', '', '', '', '', '', '']
['85QV9X5F', 'journalArticle', '1995', "McClelland, J. L.; McNaughton, B. L.; O'Reilly, R. C.", 'Why There are Complementary Learning Systems in the Hippocampus and Neocortex: Insights from the Successes and Failures of Connectionist Models of Learning and Memory', 'Psychological Review', '', '', '', '', '', '', '', '', '', '', '', '', '']
['Z4X6DT6N', 'journalArticle', '1990', 'Ratcliff, R.', 'Connectionist models of recognition memory: constraints imposed by learning and forgetting functions.', 'Psychological review', '', '0033-295X', '', 'http://view.ncbi.nlm.nih.gov/pubmed/2186426', '', '', '', '', '', '', '', '', '']
['F5DGU3Q4', 'bookSection', '1989', 'McCloskey, M.; Cohen, N. J.', 'Catastrophic Interference in Connectionist Networks: The Sequential Learning Problem', 'The Psychology of Learning and Motivation, Vol. 24', '', '', '', '', '', '', '', '', '', '', '', '', '']
['PNGQMCP5', 'conferencePaper', '2006', 'Bucilu\xc7\x8e, Cristian; Caruana, Rich; Niculescu-Mizil, Alexandru', 'Model compression', 'Proceedings of the 12th ACM SIGKDD international conference on Knowledge discovery and data mining', '', '', '', '', '', '', '', '', '', '', '', '', '']

(프로그램 출력결과를 head 명령어로 실행해서 출력결과를 스크롤해서 다시 위로 올라가기 보다 첫 몇줄만 화면에 출력함에 주목한다.) 상기 결과는 정확하게 필요한 결과다: 키값이 각 리스트 첫번째 구성요소로 있고, 저자는 모두 네번째에 몰려있따. 프로그램을 변경해서, 단지 두 필드만 출력하게 변경하자:

# display-fields.py
# 키값과 저자 모두를 화면에 출력한다.

import sys
import csv

raw = open(sys.argv[1], 'r')
reader = csv.reader(raw);
for line in reader:
    print line[0], line[3]
raw.close()

출력결과는 다음과 같다:

8SW85SQM McClelland, James L
85QV9X5F McClelland, J. L.; McNaughton, B. L.; O'Reilly, R. C.
Z4X6DT6N Ratcliff, R.
F5DGU3Q4 McCloskey, M.; Cohen, N. J.
PNGQMCP5 Buciluǎ, Cristian; Caruana, Rich; Niculescu-Mizil, Alexandru

마지막 단계는 저자 다수를 갖는 행을 복수개 행으로 단일저자가 한줄에 나타나도록 변경한다. 이번이 str.split 메쏘드를 사용할 때다: 저자명이 세미콜론으로 구분되어 있어서, 저자 목록을 각 저자별로 나눌 수 있다. 또다른 루프를 사용해서 하나씩 결과를 화면에 출력한다:

$ python code/display-authors-1.py data/bibliography.csv | head -10
8SW85SQM McClelland, James L
85QV9X5F McClelland, J. L.
85QV9X5F  McNaughton, B. L.
85QV9X5F  O'Reilly, R. C.
Z4X6DT6N Ratcliff, R.
F5DGU3Q4 McCloskey, M.
F5DGU3Q4  Cohen, N. J.
PNGQMCP5 Buciluǎ, Cristian
PNGQMCP5  Caruana, Rich
PNGQMCP5  Niculescu-Mizil, Alexandru

이제 원하는 바에 가까워졌다. 하지만, 꼭 그렇지는 않다; 저자명은 실제로 세미콜론과 공백으로 구분되는데 세미콜론만으로 구분했기 때문에, 각 줄마다 두번째와 이어진 명칭에 원치않는 공백이 앞에 온다. 세미콜론과 공백으로 쪼개면 어떻게 될까?

# display-authors-2.py
# (키값, 저자명) 짝을 화면에 출력한다.

import sys
import csv

raw = open(sys.argv[1], 'r')
reader = csv.reader(raw);
for line in reader:
    key, authors = line[0], line[3]
    for auth in authors.split('; '): # 세미콜론 대신에, 세미콜론과 공백 사용
        print key, auth
raw.close()
8SW85SQM McClelland, James L
85QV9X5F McClelland, J. L.
85QV9X5F McNaughton, B. L.
85QV9X5F O'Reilly, R. C.
Z4X6DT6N Ratcliff, R.
F5DGU3Q4 McCloskey, M.
F5DGU3Q4 Cohen, N. J.
PNGQMCP5 Buciluǎ, Cristian
PNGQMCP5 Caruana, Rich
PNGQMCP5 Niculescu-Mizil, Alexandru

그리고나면 작업완료: 데이터 추출 첫번째 작업 완료. 뭔가 유용한 것을 얻었기 때문에, 후세를 위해서 저장한자:

$ git init .
Initialized empty Git repository in /Users/aturing/lessons/capstone-novice-spreadsheet-biblio/.git
$ git add -A
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

  new file:   code/count-lines.py
  new file:   code/display-authors-1.py
  new file:   code/display-authors-2.py
  new file:   code/display-fields.py
  new file:   code/read-fields.py
  new file:   data/bibliography.csv
$ git commit -m "Extracting (key, author) pairs from bibliography"
[master (root-commit) 9db78ed] Extracting (key, author) pairsfrom bibliography
 6 files changed, 2996 insertions(+)
 create mode 100644 code/count-lines.py
 create mode 100644 code/display-authors-1.py
 create mode 100644 code/display-authors-2.py
 create mode 100644 code/display-fields.py
 create mode 100644 code/read-fields.py
 create mode 100644 data/bibliography.csv

가정사항 점검

세미콜론과 공백이 아닌 세미콜론만으로 저자명 일부로 구분된다는 의구심이 있다. 이런 사항을 점검하는데 사용할 유닉스 쉘 명령어가 무얼까?

더 안전한 필드 쪼개기

저자명 일부가 세미콜론과 공백이 아닌 세미콜론만으로 구부된다는 것을 알게됐다고 가정하자. 프로그램을 변경해서, 세미콜론으로 저자명 필드를 쪼개고 나서, 출력하는 동안에 원치않는 공백을 개별 저자명에서 벗겨내도록 하시오.