단원 홈

Ⅰ. 데이터 과학의 이해3차시

같은 정보가
세 가지 모양으로

틀은 어디에 적혀 있나

망원역 1번출구 앞 따릉이 대여소 한 곳이 파일 · 응답 · 공지문 세 모양으로 옵니다. 담긴 내용은 같은데, 컴퓨터가 읽어 내는 값은 5 · 5 · 3 으로 갈립니다. 무엇이 이 차이를 만드는지 — 정형 · 반정형 · 비정형을 가르는 기준을 코드가 부딪히는 자리에서 찾습니다.

  • [12데과01-02]
  • 50분네 정거장
  • 새 도구csv · json · re
  • 자료서울 공공자전거 대여소 2,789곳 · 실시간 API 응답 5행
Ⅰ 단원 지도3 / 9차시
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9

학습 목표

  1. 개념에서

    정형·반정형·비정형을 가르는 기준이 틀이 적힌 곳(형태)임을 세 자료를 견주어 말할 수 있다.

  2. 손으로

    같은 내용을 csv · json · 정규식으로 각각 읽어 내고, 읽지 못한 값과 그 까닭을 찾을 수 있다.

  3. 확인에서

    자료의 모양이 바뀔 때 어느 읽는 법이 견디고 어느 것이 조용히 틀리는지 근거를 들어 적을 수 있다.

1
여는 장면 · 5분

앱 화면 한 칸 — "자전거 6대 · 거치대 15개"

따릉이 앱을 열면 대여소마다 칸이 하나씩 뜹니다. 망원역 1번출구 앞에는 지금 자전거가 6대 있고 거치대는 15개랍니다. 우리가 보는 것은 다 만들어진 화면이지만, 그 화면을 만들려고 앱은 어딘가에서 글자 뭉치를 받아 왔습니다.

그 글자 뭉치는 한 가지가 아닙니다. 같은 대여소가 서울시 오픈 API 에서는 JSON 한 덩어리로, 내려받은 대여소 정보 파일에서는 CSV 한 줄로, 게시판 공지에서는 문장 한 줄로 옵니다. 셋 다 사람이 읽으면 같은 말입니다.

그런데 컴퓨터가 읽으면 같지 않습니다. 오늘 우리가 셀 것은 하나입니다 — 다섯 줄 가운데 몇 줄을 읽어 냈나.

앱 화면받은 시각 21:05
서울자전거 따릉이 · 실시간 망원역 1번출구 앞 마포구 · 대여소 102번 6 대여 가능 자전거 15 거치대 2026-09-22 21:05 에 받은 값
자전거 6대거치대 15개

화면은 서울시 공공자전거 실시간 대여정보 오픈 API 응답(2026-09-22 21:05:41 KST)의 값을 그대로 옮겨 그린 것이다. 지금 다시 받으면 자전거 수는 다르다.

표 1같은 대여소 한 곳이 보낸 세 통의 글자102번 망원역 1번출구 앞 — 받은 그대로
어디서 온 글자받은 그대로'거치대 15개'는 어디에 적혀 있나'지금 자전거'가 있나
① 내려받은 파일
대여소 정보 CSV
대여소번호,대여소명,자치구,거치대수,…
00102,망원역 1번출구 앞,마포구,15,…
첫 줄에 한 번. 그 아래 모든 줄은 같은 차례로 값만 적는다 없다 — 이 파일에는 그 칸이 아예 없다
② 오픈 API 응답
실시간 대여정보 JSON
{"rackTotCnt":"15",
 "stationName":"102. 망원역 1번출구 앞",
 "parkingBikeTotCnt":"6", …}
값마다 이름표가 붙어 온다 — rackTotCnt 있다 — parkingBikeTotCnt
③ 게시판 공지
안내문 한 문장
망원역 1번출구 앞 대여소(102번)에는
지금 자전거 6대가 있고 거치대는 15개입니다.
글 안에는 없다. "거치대는 …개"라는 말버릇을 우리가 규칙으로 적어야 한다 있다 — 다만 "없습니다" · "한 대"처럼 말로 올 때가 있다

세 통 모두 '거치대 15개'를 담고 있습니다. 다른 것은 그 15가 무엇인지 알려 주는 글이 어디에 있느냐입니다 — 첫 줄에, 값 옆에, 아니면 어디에도 없이. 자료: ① 서울 열린데이터광장 공공자전거 대여소 정보(2026-06 기준) 2,789곳 · ② 실시간 대여정보 오픈 API 응답 원문(1,062바이트 한 줄, 2026-09-22 21:05:41 KST) · ③ ②의 값으로 수업을 위해 지어낸 문장

먼저 예측

세 통의 글자를 컴퓨터에게 주고 "대여소 다섯 곳의 번호 · 이름 · 거치대 · 자전거를 표로 만들어라"고 시킨다면, 가장 잘 읽는 것과 가장 못 읽는 것은 무엇일까요? 그렇게 생각한 까닭을 한 문장으로 적어 보세요. 3정거장 실험실에서 여러분이 직접 그어 확인합니다.

체크포인트 1

같은 대여소가 세 모양으로 온다는 것을 보았고, 무엇이 가장 잘 읽힐지 한 문장으로 적어 두었다.

다음 정거장 · 개념 15분 →
2
개념 · 15분

가르는 것은 내용이 아니라 틀이 적힌 곳

2-1틀 — 무엇이 어느 값인지 적어 둔 글

표에서 15 라는 글자만 떼어 내면 아무 뜻도 없습니다. 그것이 거치대 수라는 것을 알려면 "이 자리는 거치대수"라고 적어 둔 다른 글이 있어야 합니다. 이 글을 틀이라고 부릅시다 (데이터를 다루는 자리에서는 스키마라고 합니다).

틀이 어디에 적혀 있느냐로 자료의 종류가 갈립니다.

  • 정형 — 틀이 표의 첫 줄에 한 번 적힌다. 모든 행이 같은 칸을 같은 차례로 가진다.
  • 반정형 — 틀이 값마다 이름표로 붙어 온다. 행마다 칸이 달라도 되고, 값 안에 또 값이 들어갈 수 있다.
  • 비정형 — 틀이 글 안에 없다. 사람의 문법과 말버릇 속에만 있어서, 읽으려면 우리가 규칙을 새로 적어야 한다.

2022 개정 교육과정 해설이 이 셋을 "데이터의 형태를 바탕으로" 나눈다고 못박은 것이 바로 이 뜻입니다. 내용이 아니라 형태 — 곧 틀이 적힌 자리 —— 가 기준입니다.

규칙자료를 받으면 먼저 묻는다 — 무엇이 어느 값인지 알려 주는 글은 어디에 있나?

머리글이 틀이라는 말은 "이름으로 읽을 수 있다"는 뜻입니다. 이름으로 읽은 코드와 몇 번째 칸인지로 읽은 코드는 지금은 똑같은 답을 냅니다. 갈라지는 것은 파일이 바뀔 때입니다.

그림 1같은 줄을 두 가지로 읽으면 — 지금, 그리고 칸 자리가 바뀐 뒤왼쪽 지금 파일 · 오른쪽 대여소번호와 거치대수의 자리를 맞바꾼 가상 개편판
지금 파일 둘 다 맞다 대여소번호 대여소명 자치구 거치대수 첫 줄 = 틀 00102 망원역 1번출구 앞 마포구 15 칸 번호 0 1 2 3 r['거치대수'] → 15 이름으로 읽기 row[3] → 15 자리로 읽기 ✓ 두 방법이 같은 답 — 여기서는 갈라지지 않는다 가상 개편판 한쪽만 조용히 틀린다 거치대수 대여소명 자치구 대여소번호 머리글도 함께 옮겨 갔다 15 망원역 1번출구 앞 마포구 00102 칸 번호 0 1 2 3 r['거치대수'] → 15 이름으로 읽기 — 그대로 row[3] → 102 오류 한 줄 없이 ✗ 파일에서 가장 큰 대여소도 거치대 62개다 지금 파일 둘 다 맞다 대여소번호 대여소명 · 자치구 거치대수 00102 망원역 1번출구 앞 15 칸 0 칸 1 · 2 칸 3 r['거치대수'] → 15 · 이름으로 읽기 row[3] → 15 · 자리로 읽기 ✓ 두 방법이 같은 답 — 갈라지지 않는다 가상 개편판 한쪽만 조용히 틀린다 거치대수 대여소명 · 자치구 대여소번호 15 망원역 1번출구 앞 00102 칸 0 칸 1 · 2 칸 3 r['거치대수'] → 15 · 그대로 row[3] → 102 · 오류 한 줄 없이 ✗ 파일 최대 대여소도 거치대 62개다

개편판은 수업을 위해 지어낸 파일입니다. 실제 파일이 이렇게 바뀐 적은 없습니다 — 다만 공공데이터의 칸 차례가 바뀌는 일은 흔합니다. 그때 자리로 읽은 코드는 멈추지 않고 102 를 답으로 내놓습니다. 자료: 대여소 정보 파일 2,789곳(원장 계산 — 코드 ①이 찍는 15 · 102 · 62 와 같은 값)

2-2모양이 곧 읽어 낼 수 있는 행의 수

세 통에서 다섯 줄을 읽어 표로 옮겨 봅니다. 읽어 낸 행의 수가 달라지고, 읽었다고 해서 값이 쓸 만한 것도 아닙니다. 같아야 할 두 숫자를 나란히 놓으면 무엇이 걸리는지 보입니다.

CSV · 읽은 행과 빈 칸
5/5번호·이름·거치대
인데
0/5'지금 자전거'
번호 이름 거치대 자전거 칸 없음 파일마다 담는 칸이 다르다

같은 대여소라도 파일마다 담는 칸이 다르다.

JSON · 거치대와 자전거를 더하면
21사람이 더한 값
vs
156파이썬이 낸 값
'15' + '6' = '156' 첫 행의 값 일곱 개가 모두 글자(str)다

이름표는 이름만 말한다 — 자료형은 말하지 않는다.

안내문 · 규칙 하나로 읽은 행
5받은 줄
→
3읽어 낸 줄
없습니다 한 대 읽음

숫자를 말로 쓴 두 줄에서 규칙이 헛돈다.

JSON · shared 라는 이름표의 뜻
8응답이 준 shared
vs
8.33자전거÷거치대×100
8.33 을 어떻게 8 로 만들었나? 반올림 → 8 버림 → 8 다섯 줄로는 가를 수 없다 — 설명서를 읽어야 한다

이름표가 있어도 값의 뜻은 따로 적혀 있어야 한다.

자료: 실시간 대여정보 오픈 API 응답 5행 · 대여소 정보 파일 · 안내문 — 위 네 쌍의 숫자는 모두 코드 ① · ② · ③ 이 찍는 값이다.

2-3반정형은 왜 생겼나 — 그리고 무엇을 말해 주지 않나

표는 편리하지만 모든 행이 같은 칸을 가져야 합니다. 어떤 대여소에만 있는 정보를 넣으려면 모든 행에 빈 칸을 하나씩 더해야 하지요. 반정형은 이 제약을 풉니다 — 값마다 이름표가 붙으니 행마다 칸이 달라도 됩니다.

게다가 값 안에 또 값이 들어갑니다. 오늘의 응답도 나무처럼 겹쳐 있습니다 — 맨 위에 rentBikeStatus 하나, 그 아래에 list_total_count · RESULT(처리 결과) · row(행 목록)가 나란히 있고, 행 목록 안에 다섯 대여소가 들어 있습니다. 한 응답에 결과 코드와 자료를 함께 담을 수 있다는 뜻이고, 그래서 서버끼리 주고받기 좋습니다. 공공 API 가 대개 JSON 인 까닭입니다.

다만 이름표는 이름만 말합니다. shared 는 다섯 곳 모두 자전거 ÷ 거치대 × 100 의 정수와 같았지만, 반올림인지 버림인지는 8.33 → 8 하나로는 가릴 수 없고, 자전거가 거치대보다 많으면 100 을 넘는지도 이 다섯 줄은 말하지 않습니다. 설명서(메타데이터)를 읽어야 압니다.

이름표가 말해 주지 않는 것이 하나 더 있습니다. 같은 104번 대여소의 위도가 대여소 정보 파일에서는 37.55062866, 실시간 응답에서는 37.55073929 입니다. 약 0.0001° — 12m 쯤 되는 차이인데, 어느 쪽이 맞는지는 두 파일 어디에도 적혀 있지 않습니다.

규칙값을 쓰기 전에 그 값의 정의를 설명서에서 확인한다. 이름표는 이름일 뿐이다.

응답 한 덩어리나무처럼 겹친다
rentBikeStatus list_total_count : 5 RESULT : { CODE, MESSAGE } row : [ … ] 102번 · 거치대 15 · 자전거 6 103번 · 거치대 14 · 자전거 7 … 106번까지 다섯 행
깊이 3단행 5개

도해: 코드 ②가 찍는 이름표 목록 그대로. 맨 위 이름표 하나, 그 아래 셋, row 안에 다섯 행.

2-4정형 속의 비정형 — 칸은 있는데 틀이 없다

정형이라고 해서 마음 놓을 일은 아닙니다. 대여소 정보 파일의 상세주소 칸을 보세요. 칸은 정해져 있지만 그 안의 글은 사람이 쓴 문장입니다 — 당산로31길 32 서측(구청별관 주변) 처럼 괄호 설명이 붙은 곳이 69곳, 아예 글자가 깨져 물음표가 섞인 곳도 두 곳(182번 · 457번) 있습니다. 칸은 정형, 칸 안은 비정형입니다.

읽는 규칙도 있습니다. 쉼표로 칸을 나누는 파일이라면서, 칸 안에 쉼표가 들어 있는 줄이 있습니다 — 염창역 3, 4번 출구 사이 같은 대여소 이름이지요. 그래서 이 파일은 그런 칸을 따옴표로 묶어 보냅니다. 따옴표도 틀입니다.

쉼표로만 잘라 보면 2,789줄 가운데 26줄이 9칸이 아니게 됩니다. csv 모듈로 읽으면 0줄입니다. 손으로 자르는 순간 26줄이 조용히 망가지는 것이지요.

표 2쉼표로만 잘랐을 때 어긋나는 26줄의 속내한 줄에 두 칸이 걸린 곳이 있어 합이 26을 넘는다
쉼표가 든 칸줄 수예
대여소명15염창역 3, 4번 출구 사이
상세주소9(주소 안의 쉼표)
운영방식6LCD,QR — 한 대여소에 두 방식
쉼표로만 자르면 9칸이 아닌 줄26겹친 줄을 한 번만 센 수
csv 모듈로 읽으면0따옴표 규칙까지 함께 읽는다

'운영방식 LCD,QR' 여섯 곳은 뒤에서 한 번 더 만납니다 — 이 파일의 거치대수는 원래 LCD 거치대수 · QR 거치대수 두 칸이었고, 그 여섯 곳만 두 칸이 다 차 있습니다(예: 1053번 8 + 3 = 11). 자료: 대여소 정보 파일 2,789곳(원장 계산 — 코드 ④가 찍는 26 · 0 · 69 와 같은 값)

자료를 받으면 묻는 네 가지

  1. 틀무엇이 어느 값인지 어디에 적혀 있나 — 첫 줄에, 값 옆에, 아니면 없나
  2. 칸모든 행이 같은 칸을 가지나 — 이 파일에 없는 칸은 무엇인가
  3. 뜻값의 정의가 적힌 설명서가 있나 — 단위 · 반올림 · 한계
  4. 변화파일이 바뀌면 내 코드는 멈추나, 아니면 조용히 틀린 값을 내나

넷 가운데 가장 위험한 것은 네 번째입니다. 오류가 나면 우리가 알아채지만, 조용히 틀리면 보고서가 나갈 때까지 아무도 모릅니다.

체크포인트 2

세 종류를 가르는 기준이 '틀이 적힌 곳'임을 말할 수 있고, 정형 속에도 비정형이 있다는 것을 보았다. 이제 직접 읽어 본다.

다음 정거장 · 손으로 20분 →
3
손으로 · 20분

세 통의 글자를 하나의 표로 읽어 낸다

오늘의 장비

csv · json · re — 파이썬에 처음부터 들어 있는 세 도구

오늘은 내려받을 것이 없습니다. 세 도구 모두 파이썬 표준 라이브러리라 import 한 줄이면 됩니다. 세 도구가 갈리는 자리가 곧 세 자료가 갈리는 자리예요 — 어떤 틀을 읽어 주느냐가 다릅니다.

부르는 모양

import csv, json, re

표 = csv.DictReader(글.splitlines())
값 = json.loads(응답)
m  = re.search(r"자전거 (\d+)대", 문장)

도구마다 읽어 주는 틀

csv.DictReader
첫 줄을 틀로 읽어 준다 — 그 뒤로 r['거치대수']
csv.reader
따옴표 규칙까지 읽어 준다 — 칸 이름은 없고 row[3]
json.loads
값마다 붙은 이름표를 그대로 사전과 목록으로
re.search
틀이 없으니 우리가 쓴 규칙을 글에 대어 본다

정규식은 오늘 한 조각만

  1. (\d+)이어진 숫자 한 덩어리를 잡아 따로 꺼낸다
  2. (.+?)아무 글자나 되도록 짧게 잡는다
  3. 그 밖의 글자적은 그대로 있어야 한다 — 하나라도 다르면 못 읽는다

대가 — 정규식은 말투 하나에만 맞습니다. 이 책에서 정규식은 이 차시에만 나옵니다.

1

CSV — 이름으로 읽기와 자리로 읽기, 그리고 틀 긋기판

9분

여는 장면에 적어 둔 내 예측입니다. 코드 ①은 대여소 정보 파일에서 다섯 줄을 읽고, 이어서 칸 자리를 맞바꾼 가상 개편판을 두 가지 방법으로 읽습니다. 자리로 읽은 쪽은 오류를 낼까요, 아니면 아무 일 없이 숫자를 내놓을까요?

기본 빈칸 ①에 머리글 이름으로 거치대 수를 읽는 코드를 채우고 ▶ 실행하세요. 채우지 않고 눌러도 됩니다 — 무슨 일이 나는지 보는 것도 배움입니다.

도전 개편판을 만드는 줄(개편 = …)을 지우고 개편 = 원판 으로 바꾸면 두 방법이 다시 같아집니다. 그런데 머리글 줄 하나를 통째로 지우면(글.splitlines()[1:]) 어떤 방법이 먼저 멈출까요?

탐구 가장 큰 대여소가 62개라는 것을 알면 102 를 의심할 수 있습니다. 그렇다면 읽은 값이 말이 되는지를 코드가 스스로 살피게 하려면 무엇을 함께 찍어 두어야 할까요?

코드가 한 일을 이번에는 손으로 해 봅니다. 실험실에는 봉투가 셋 놓여 있습니다 — 봉투란 같은 대여소 다섯 곳이 담겨 온 글자 뭉치 한 통(① 파일 · ② 응답 · ③ 공지문)입니다. 날것 글자 그대로예요.

색연필을 골라 글자 한 조각 위를 누르면 그 조각이 칠해집니다. 기계는 그 한 번만 보고 읽는 법(칠한 자리를 어떻게 찾을지 적은 한 줄)을 만들어 나머지 네 줄을 같은 자리에서 읽어 목표 표를 채웁니다. 한 번 더 누르면 지워집니다. 한 회차 안에서 칠하거나 지운 횟수가 손질이에요.

그리고 회차를 넷 지나갑니다 — 회차는 날짜가 아니라 자료가 바뀌는 사건의 차례입니다. 회차를 열기 전에 세 봉투가 어떻게 될지 먼저 잠그고, 열어서 내가 그은 읽는 법이 버티는지 봅니다. 도전 셋을 풀면 카드가 스스로 닫힙니다.

시뮬레이터

틀 긋기판

기계에게 읽는 자리를 그어 주고, 바뀌는 자료를 버텨라

  1. 1색연필을 고르고 봉투의 글자 위를 눌러 칠한다 — 한 번 더 누르면 지워진다
  2. 2다음 회차에 세 봉투가 어떻게 될지 먼저 잠근다
  3. 3회차를 열어 내가 그은 읽는 법이 버티는지 본다
색연필 — 무엇을 칠할까
다음 회차에 이 봉투는 — 예측 잠금
회차 — 자료가 바뀐다
1회차 · 지금 받은 그대로색연필을 고르고 글자 위를 눌러 칠하세요

읽어 낸 칸 = 대여소 다섯 곳 × 값 넷 = 20칸 가운데 값이 나온 칸 · 맞은 칸 = 그 값이 참값과 같은 칸 · 조용한 오류 = 값은 나왔는데 참값과 다른 칸 · 손질 = 그 회차에 칠하거나 지운 횟수

판정 — 회차가 바뀐 뒤 다시 긋지 않고도 맞은 칸이 그대로면 그대로, 틀린 칸이 생기면 조용히 틀림, 읽어 낸 칸이 줄면 다시 그어야 함

맞은 칸0/60
조용한 오류0
이번 회차 손질0
누적 손질0
예측 적중—
회차1/4
도전 1

1회차에서 세 봉투를 모두 칠해 목표 표를 채워라 — 세 봉투가 읽어 낸 칸은 각각 몇 칸인가? 끝내 채울 수 없는 칸은 무엇인가?

도전 2

CSV 의 거치대를 값 쪽에 칠해(자리로 읽기) 두고 2회차를 열어라 — 오류가 나는가, 숫자가 나오는가?

도전 3

4회차까지 가라 — 1회차 뒤로 한 번도 다시 긋지 않아도 되었던 봉투는 어느 것인가?

자료: ① 서울 열린데이터광장 공공자전거 대여소 정보(2026-06 기준) 2,789곳 가운데 102~106번 다섯 줄과 1053번 한 줄 · ② 실시간 대여정보 오픈 API 응답 원문 5행(2026-09-22 21:05:41 KST) · ③ ②의 값으로 수업을 위해 지어낸 문장. 2회차(칸 자리 바꿈)와 3회차(공지 말투 교체)는 지어낸 사건이고, 4회차의 두 칸(LCD · QR)은 원본 엑셀 파일의 실제 모양이다. 읽어 낸 칸·손질·26곳은 코드 ① · ④ 가 찍는 값과 같은 규칙으로 센다.

머리글은 를 위해 있다. 자리로 읽은 코드가 개편판에서 멈추지 않은 것은 때문이고, 그래서 102 라는 값이 로 남는다.

확인 문제 6에 적어 제출 →

세 봉투를 나란히 견주는 표는 확인 문제 1에 있습니다 — 미션 ④를 마친 뒤 한 번에 채웁니다.

2

JSON — 이름표를 따라 나무를 내려간다

4분

응답에서 첫 행의 거치대 '15'와 자전거 '6'을 + 로 더하면 무엇이 나올까요? 21 이라고 답하기 전에, 코드가 찍어 줄 일곱 값의 자료형을 먼저 보세요.

기본 빈칸 ②에 행 목록이 있는 자리까지 내려가는 길을 채우세요. 바로 위 두 줄이 이름표를 한 단씩 찍어 줍니다 — 그 길을 그대로 이어 쓰면 됩니다.

도전 stationId 가 대여소번호일 것 같지만 ST-4 는 102번입니다. 번호를 이름 글자에서 꺼내는 줄(split("."))을 stationId 로 바꿔 돌려 보고, 무엇이 달라지는지 보세요.

탐구 이 응답을 표로 옮긴다면 어느 값을 int() 로 바꾸겠습니까? 바꾸기 전에 확인해야 할 것은 무엇일까요(빈 값 · 기호가 섞인 값 · 이름 안에 박힌 값).

이름표는 는 말해 주지만 는 말해 주지 않는다. shared 의 뜻을 확정하려면 를 읽어야 한다.

확인 문제 3에 적어 제출 →

'156'이 된 까닭은 확인 문제 2에 적습니다.

3

안내문 — 문장의 규칙을 우리가 적는다

4분

실험실의 공지문 봉투에서 칠한 조각만 빈칸으로 남기고 나머지 글자는 그대로 둔 문장을 틀 문장이라고 불렀지요. 코드에서는 그 빈칸 넷이 정규식 네 조각이 됩니다. 다섯 줄 가운데 몇 줄을 읽어 낼까요? 못 읽는 줄이 있다면 어느 줄이고, 왜일까요?

기본 빈칸 ③은 셋째 조각 — 자전거 대수입니다. 바로 위 둘째 조각이 번호를 어떻게 잡는지 보고 같은 모양으로 적으세요.

도전 놓친 두 줄을 읽는 규칙을 하나 더 적어 5/5 를 만들어 보세요. re.search(PATTERN, 줄) or re.search(PATTERN2, 줄) 처럼 두 규칙을 차례로 대어 보고, '없습니다' → 0 · '한 대가' → 1 로 바꿔 주는 낱말 사전을 하나 두면 됩니다(원장 실측: 이렇게 하면 5/5).

탐구 5/5 를 만든 뒤, NOTICE 목록 맨 뒤에 위 주석의 새 말투 한 줄을 붙여 다시 돌려 보세요. 두 규칙 모두 이 줄을 읽지 못합니다 — 규칙을 몇 개까지 늘려야 끝날까요?

(\d+) 는 를 잡는다. 두 줄을 놓친 것은 때문이고, 규칙을 늘려 5/5 를 만들어도 가 남는다.

확인 문제 4에 적어 제출 →
4

정형 속의 틀 — 따옴표, 그리고 주소 칸

3분

쉼표로 칸을 나누는 파일입니다. 쉼표로만 잘라도 2,789줄이 모두 9칸이 될까요? 아니라면 몇 줄쯤 어긋날 것 같습니까 — 0줄, 몇 줄, 몇십 줄, 몇백 줄?

기본 코드 ④는 채워져 있습니다. 그대로 ▶ 실행해 두 숫자를 견주세요 — 손으로 자른 결과와 csv 모듈로 읽은 결과.

도전 어긋난 26줄이 어느 칸의 쉼표 때문인지 세어 보세요 (표 의 각 행에서 "," 가 든 칸을 찾으면 됩니다). 표 2 의 15 · 9 · 6 과 맞나요?

탐구 상세주소 칸을 분석에 쓰려면 어떤 칸(들)로 뽑아야 할까요 — 구 · 도로명 · 건물 번호 · '역 출구 앞인가'? 무엇을 칸으로 뽑을지는 무엇이 정합니까? (4차시에서 이어집니다)

이 파일에서 틀은 첫 줄에만 있는 것이 아니다 — 도 틀이다. 그리고 칸이 정해져 있어도 안의 글에는 틀이 없다.

확인 문제 1의 비교표를 채워 제출 →

상세주소 칸에 대한 조언은 확인 문제 5에 적습니다.

체크포인트 3

세 통을 직접 읽어 5 · 5 · 3 을 확인했고, 자료가 바뀌는 네 회차를 지나며 어느 읽는 법이 버티는지 보았다.

다음 정거장 · 정리·확인 10분 →
4
정리·확인 · 10분

오늘의 탐험 일지

  1. 찾은 것

    세 종류를 가르는 것은 담긴 내용이 아니라 틀이 적힌 곳이었다 — 첫 줄에(정형) · 값마다(반정형) · 어디에도 없이(비정형).

  2. 도구의 한계

    이름표는 이름만 말한다(자료형도 값의 뜻도 아니다). 정규식은 말투 하나에만 맞고, 정형 파일 안에도 틀 없는 칸이 있다.

  3. 보고의 규칙

    자료가 바뀔 때 코드가 멈추는지 조용히 틀리는지를 먼저 따진다. 이름으로 읽고, 값의 범위를 함께 찍어 둔다.

✍️ 문제마다 답을 쓰고 제출하기를 누르세요. 제출하면 모범 답안이 열리고, 제출한 답은 선생님께 전달됩니다.

1. 세 모양을 나란히 견주는 표를 채우시오. 그리고 담긴 내용이 같은데 종류가 갈리는 까닭을 한 문장으로 쓰시오.
봉투다섯 줄 가운데 읽은 행틀이 적힌 곳칸 자리·말투가 바뀌면'지금 자전거' 칸
① 파일
CSV
② 응답
JSON
③ 공지문
안내문
📖 모범 답안

① 파일(CSV) — 5/5 · 첫 줄 머리글 · 이름으로 읽으면 견디고, 자리로 읽으면 조용히 틀린다(거치대가 102개가 된다) · 없다(이 파일에는 그 칸이 아예 없다).

② 응답(JSON) — 5/5 · 값마다 붙은 이름표(키) · 키 차례가 바뀌어도 그대로(이름으로 찾으니까) · 있다(parkingBikeTotCnt).

③ 공지문 — 3/5 · 글 안에는 없고 우리가 쓴 규칙에만 있다 · 말투가 바뀌면 0/5 · 있다(다만 '없습니다' · '한 대'처럼 말로 올 때가 있다).

까닭 — 종류를 가르는 기준은 담긴 내용이 아니라 무엇이 어느 값인지를 적어 둔 글(틀)이 어디에 있느냐, 곧 형태이기 때문이다.

2. [B]에서 거치대 '15'와 자전거 '6'을 더했더니 '156'이 되었다. 무슨 일이 일어났는지, 공공 API 응답에서 이런 일이 흔한 까닭은 무엇인지, 그리고 이 응답을 표로 옮길 때 해야 할 일을 쓰시오.
📖 모범 답안

값이 글자(str)였기 때문에 + 가 더하기가 아니라 이어 붙이기로 돌았다. 코드 ②가 찍어 준 첫 행의 값 일곱 개가 모두 str 이었다.

JSON 은 수와 글자를 구별해 담을 수 있는데도, 공공 API 응답은 수를 따옴표에 넣어 글자로 보내는 경우가 많다. 보내는 쪽이 모든 값을 한결같이 글자로 다루면 빈 값·자릿수 0·아주 큰 수를 잃지 않기 때문이다.

표로 옮길 때는 int() · float() 로 바꾸되, 바꾸기 전에 모든 행이 수 모양인지 확인한다 (빈 값, '-', 쉼표가 든 수 같은 것). 그리고 대여소번호처럼 이름 글자 안에 박힌 값('102. 망원역 1번출구 앞')은 먼저 잘라 내야 한다 — stationId 의 'ST-4' 는 대여소번호가 아니다.

3. shared 는 다섯 곳에서 40 · 50 · 0 · 40 · 8, 직접 계산한 자전거 ÷ 거치대 × 100 은 40.0 · 50.0 · 0.0 · 40.0 · 8.3 이었다. shared 의 뜻을 추리하고, 이 다섯 줄로는 확정할 수 없는 것 두 가지와 확인 방법을 쓰시오.
📖 모범 답안

거치율(%)의 정수로 보인다 — 다섯 곳 모두 계산값의 정수 부분과 같다.

확정할 수 없는 것 ① 소수점을 반올림하는지 버리는지. 106번의 8.33 은 반올림해도 8, 버려도 8 이라 이 다섯 줄로는 가를 수 없다. ② 자전거가 거치대보다 많은 대여소에서 100 을 넘는지. 이 다섯 곳에는 그런 곳이 없다.

확인 방법 — API 설명서(메타데이터)에서 값의 정의를 읽는다. 그리고 가를 수 있는 줄을 찾는다 — 예를 들어 자전거 1 · 거치대 3 이면 계산값이 33.3 이라 반올림(33)과 버림(33)이 같지만, 자전거 2 · 거치대 3 이면 66.7 이라 반올림 67 · 버림 66 으로 갈린다. 다른 시각 · 다른 대여소의 응답을 더 받아 그런 줄을 찾으면 된다.

4. [C]의 정규식에서 (\d+) 는 무엇을 잡는 조각이고, 이 규칙이 놓친 두 줄은 왜 놓쳤는가? 규칙을 고쳐 5/5 를 만든다 해도 남는 문제를 쓰시오.
📖 모범 답안

(\d+) 는 이어진 숫자 한 덩어리를 잡아 따로 꺼내는 조각이다(\d 가 숫자 한 글자, + 가 하나 이상).

놓친 두 줄은 104번("자전거가 없습니다" — 0 을 말로 씀)과 106번("자전거 한 대가" — 1 을 글로 씀)이다. 숫자 덩어리가 아예 없으니 (\d+) 가 걸릴 자리가 없고, 그 앞뒤의 글자("에는 지금 자전거 " · "대가 있고")도 달라 규칙 전체가 맞지 않는다.

남는 문제 — 규칙을 하나 더 적으면 5/5 가 되지만, 말투가 바뀌면 다시 0 이 된다. 실제로 새 말투의 공지("망원역 1번출구 앞(102): 남은 자전거 6 / 거치대 15")에는 두 규칙 모두 걸리지 않는다. 비정형을 읽는 일은 사람이 규칙을 계속 늘려야 하는 일이고, 규칙이 늘수록 서로 부딪힌다. 그래서 어느 지점부터는 규칙을 손으로 쓰는 대신 많은 문장으로 학습시킨 자연어 처리를 쓴다 — 그때도 틀이 데이터 안에 없다는 사실 자체는 그대로다.

5. 정형 표의 상세주소 칸(괄호 설명이 붙은 주소 69곳 — 예: 당산로31길 32 서측(구청별관 주변))을 분석에 쓰려는 팀에게 조언하시오.
📖 모범 답안

칸은 정형이지만 칸 안은 비정형이다. 주소 칸 하나에 도로명 · 건물 번호 · 방향 · 괄호 설명이 사람마다 다른 방식으로 들어 있다.

쓰려면 묻고 싶은 물음에 맞춰 칸을 뽑아 정형화한다 — 예를 들어 '자치구별로 보고 싶다'면 자치구 칸이 이미 있으니 그것을 쓰고, '지하철역 앞 대여소인가'를 묻는다면 '역' · '번출구'가 들어 있는가를 참/거짓 칸으로 새로 만든다. 무엇을 칸으로 뽑을지는 분석하려는 물음이 정한다(→ 4차시).

읽을 때 주의 — 이 파일은 쉼표로 칸을 나누지만 칸 안에 쉼표가 든 줄이 있다. 쉼표로만 자르면 26줄이 어긋나므로 csv 모듈로(따옴표 규칙까지) 읽는다. 또 글자가 깨져 물음표가 섞인 주소도 두 곳(182번 · 457번) 있으니, 뽑기 전에 이상한 글자가 든 행을 먼저 세어 본다.

6. [A]의 가상 개편판에서 몇 번째 칸으로 읽은 코드는 102번 대여소의 거치대를 102개로 읽었지만 오류는 나지 않았다. 오류가 나지 않은 까닭과, 이런 '조용한 틀림'을 알아챌 방법 두 가지를 쓰시오.
📖 모범 답안

바뀐 자리에 들어온 값 '00102' 도 수로 바꿀 수 있는 글자라서 int() 가 그대로 통과했다. 코드는 뜻이 아니라 자리를 읽었고, 자리에는 언제나 무언가가 들어 있다. 파이썬이 알 수 있는 것은 '수로 바꿀 수 있나'까지이지 '그 수가 거치대 수로 말이 되나'가 아니다.

알아채는 법 ① 머리글 이름으로 읽는다(csv.DictReader). 칸 자리가 바뀌어도 이름으로 찾으니 값이 따라 움직이고, 칸 이름이 아예 사라지면 그 자리에서 오류가 난다 — 조용히 틀리는 대신 멈춘다.

② 값의 범위를 확인한다. 파일에서 가장 큰 대여소도 거치대 62개(LG유플러스 마곡사옥)인데 102개는 말이 되지 않는다. 읽자마자 가장 작은 값 · 가장 큰 값을 찍어 보는 습관이 이런 틀림을 잡는다(8차시에서 같은 습관을 다시 쓴다).

+
더 알아보기

모양을 둘러싼 이야기 셋

? 원 측정 무엇을 '데이터'로 세었나 · 어떻게 쟀나 업계 보고서 — "약 80%가 비정형" 기사 — "보고서에 따르면 80%" 발표 자료 — "잘 알려져 있듯 80%" 교과서 · 수업 자료 — "80%" 사슬을 거슬러 올라가면 맨 위 칸이 비어 있다
숫자를 의심하기

"세상 데이터의 80%는 비정형"— 그 숫자는 어디서 왔나

정형·반정형·비정형을 설명하는 자리에는 거의 언제나 이 문장이 따라붙습니다. 이 교과서의 기초판에도 같은 숫자가 실려 있어요. 그런데 잠깐 멈춰 생각해 보면 이상합니다 — '세상의 데이터'를 누가, 언제, 어떤 단위로 세었을까요? 바이트로 쟀을까요, 파일 수로 쟀을까요? 사진과 동영상은 바이트가 크니 바이트로 세면 비정형이 압도적일 수밖에 없고, 파일 수로 세면 또 다른 답이 나옵니다. 애초에 어디까지가 '세상'인지도 정해야 하지요.

이 숫자는 2000년대부터 업계 보고서와 기사, 발표 자료를 거치며 되풀이 인용돼 왔지만, 인용을 거슬러 올라가 보면 측정 방법을 밝힌 1차 자료에 좀처럼 닿지 못합니다. 그렇다고 이 말이 거짓이라는 뜻은 아닙니다. 비정형 자료가 매우 많다는 것은 누구나 실감하니까요. 다만 출처를 밝힐 수 없는 숫자는 근거가 아니라 인상입니다. 데이터 과학에서 숫자를 만나면 버릇처럼 물어야 합니다 — 누가 어떻게 쟀나. 오늘 우리가 쓴 '2,789곳' · '26줄' · '69곳'은 여러분이 직접 코드를 돌려 다시 셀 수 있는 숫자입니다. 그 차이가 핵심입니다.

도해: 널리 인용되는 수치가 보고서 → 기사 → 발표 자료 → 교과서로 이어지는 모습과, 그 맨 앞의 빈 칸을 그렸다. 특정 보고서를 가리키지 않는다.

같은 대여소 한 곳, 두 가지 적는 법 XML <rackTotCnt>15</rackTotCnt> <stationName>102. 망원역 …</stationName> <parkingBikeTotCnt>6</parkingBikeTotCnt> 이름을 열 때와 닫을 때 두 번 적는다 JSON "rackTotCnt":"15", "stationName":"102. 망원역 …", "parkingBikeTotCnt":"6" 이름은 한 번, 값은 콜론 뒤에 한 대여소를 다 적으면 — 글자 수 261 174 위 XML · 아래 JSON — 같은 일곱 값
역사

JSON 은 어떻게 세계의 API 언어가 되었나

JSON 은 새로 발명된 형식이 아닙니다. 자바스크립트에서 객체를 적던 문법 한 조각이 그대로 떨어져 나온 것이지요. 더글러스 크록퍼드가 2002년 json.org 도메인을 얻어 그 문법을 한 쪽짜리 규격으로 정리해 올린 것이 시작입니다. 2006년 RFC 4627 로 인터넷 문서가 되었고, 2013년 10월 ECMA-404 로 국제 표준이 되었으며, 2017년 12월 RFC 8259 로 인터넷 표준(STD 90)에 올랐습니다. 서로 다른 두 표준화 기구의 문서가 지금도 같은 문법을 가리키고 있습니다.

그전까지 자료를 주고받는 자리는 XML 의 것이었습니다. XML 은 이름을 열 때와 닫을 때 두 번 적습니다. 오른쪽 도해처럼 같은 대여소 한 곳(일곱 값)을 적어 보면 XML 은 261자, JSON 은 174자 — 약 1.5배 차이입니다. 대여소 2,789곳이면 이 차이가 그대로 쌓이지요. 게다가 브라우저의 자바스크립트는 JSON 을 따로 해석기를 만들지 않고 바로 값으로 바꿀 수 있었습니다. 가볍고 다루기 쉬웠던 것이 이유의 전부입니다. 다만 XML 이 사라진 것은 아닙니다 — XML 에는 '이 문서가 규칙에 맞는가'를 기계가 검사해 주는 스키마 언어가 있어서, 문서 형식이나 전자 문서 교환에서는 여전히 쓰입니다.

도해: 실시간 대여정보 응답의 첫 행(값 일곱 개)을 XML 과 JSON 으로 각각 적어 글자 수를 센 것이다(261자 대 174자, 집필 때 계산). · 연도·표준 번호: json.org(2002) · RFC 4627(2006) · ECMA-404(2013-10) · RFC 8259(2017-12, STD 90)

표 프로그램에 붙여 넣는 순간 붙여 넣기 전 SEPT2 MARCH1 2310009E13 붙여 넣은 뒤 2-Sep 1-Mar 2.31E+13 되돌릴 수 있을까 — 2-Sep 만 받은 사람은 SEPT2 인지 SEPTIN2 인지 알 수 없다 2020 — 이름 쪽을 고쳤다 SEPT2 → SEPTIN2 MARCH1 → MARCHF1 사람 유전자 27개의 이름이 바뀌었다 — 프로그램이 아니라 이름을 고치는 쪽을 골랐다
사고에서 배우기

표 프로그램이 유전자 이름을 날짜로 바꿨다

사람 유전자 가운데 SEPT2 · MARCH1 처럼 이름이 영어 달 이름 + 숫자 꼴인 것들이 있습니다. 이 이름을 표 프로그램에 붙여 넣으면, 프로그램은 친절하게도 "아, 날짜구나" 하고 2-Sep · 1-Mar 로 바꿔 버립니다. 2310009E13 같은 유전자 식별자는 지수 표기 숫자 2.31E+13 이 되고요. 문제는 이 변환이 경고 한 줄 없이 일어나고, 바뀐 파일만 받은 사람은 원래 이름으로 되돌릴 수 없다는 것입니다.

2016년 지만(Ziemann)과 동료들이 유전체학 학술지의 논문 부록 파일을 프로그램으로 훑어 보았더니, 유전자 목록이 엑셀 파일로 실린 논문 3,597편 가운데 약 5분의 1에서 이 변환 오류가 발견됐습니다(Genome Biology, 2016). 고칠 방법이 마땅치 않자 2020년 국제 유전자 명명 위원회(HGNC)는 유전자 이름 쪽을 바꾸기로 했습니다 — SEPT2 는 SEPTIN2 로, MARCH1 은 MARCHF1 로, 모두 27개입니다. 프로그램의 습관을 고치는 것보다 사람이 정한 이름을 고치는 쪽이 빨랐던 것이지요.

오늘 배운 말로 옮기면 이렇습니다. 엑셀 파일은 정형처럼 보이지만 칸의 자료형을 파일이 적어 두지 않습니다. 그래서 읽는 프로그램이 글자를 보고 짐작합니다. 짐작하는 순간 정형은 정형이 아니게 됩니다 — 오늘 '15' + '6' 이 '156' 이 된 것과 정반대 방향의 같은 사고입니다.

도해: 변환 전후의 목록 세 줄과 2020년의 이름 변경을 그렸다. · 출처: M. Ziemann, Y. Eren, A. El-Osta, "Gene name errors are widespread in the scientific literature", Genome Biology 17:177 (2016) · HUGO Gene Nomenclature Committee(HGNC) 2020년 유전자 기호 변경