番組の言葉を年表にする。正規表現とSQLを組み合わせたら静かに0件になった
130万行の集計をページに置かない、表記ゆれを機械で束ねない、言い方が毎回変わる語をどう拾うか。載せない判断も含めて。
結論から
- 130万行の集計はページに置かない。事前に数えて JSON に書き出しておく
- 表記ゆれは機械で束ねない。手で書く。別の語が混ざる
- 正規表現とSQLを組み合わせるときは、先頭が取り出せなかったら止める。静かに0件になるのが一番困る
- 作れるものを全部載せない。載せる基準を決めるほうが読めるものになる
作ったもの
語ごとに、年ごとの出現数を並べた年表です。番組の造語がいつ生まれて定着したかを見たくて作りました。 いまは11語あります。
うんちく 1036回 変種3
エウレーカ 427回 変種1
クリシェ 383回 変種1
言語化 224回 変種1
母語話者 116回 変種1
機械音痴 109回 変種2
ディバイド&コンカー 88回 変種4
月額会員制村作りサービス 73回 変種1
理科ができない 53回 正規表現
デカルトみ 42回 変種1
ミスター堀元 36回 変種6
ページで数えない
1語ぶん数えるのに LIKE で130万行を舐めるので、1秒前後かかります。
以前、別のページで同じことをやって初回の表示が7.2秒になったことがありました。
$ python3 scripts/build_word_timeline.py # 全部作り直す
$ python3 scripts/build_word_timeline.py kikai-onchi # 1語だけ
出力: data/words/<slug>.json と data/words/_index.json
表記ゆれは手で書く
"unchiku": ("うんちく", ["うんちく", "蘊蓄", "ウンチク"], "..."),
"kikai-onchi": ("機械音痴", ["機械音痴", "機械オンチ"], "..."),
読みが同じものを自動で束ねる案も考えたのですが、 たとえば「クロード」を束ねると「クロードモネ」が混ざります。 語の数だけ例外があるので、変種は1語ずつ手で書くことにしました。11語ぶんなら手で足ります。
言い方が毎回変わる語がある
困ったのが「理科ができない」でした。番組の定番なのですが、言い方が毎回違います。
理科全然できない
理科できなさが
理科弱すぎる
理科が苦手
「理科ができない」という文字列そのままでは2件しか取れません。 それで、この語だけ正規表現で拾うようにしました。2件が53回になりました。
"rika-dekinai": ("理科ができない", [], "...",
r"理科[^。]{0,12}?(でき|出来|わか|分か|苦手|弱|ダメ|だめ)"),
ハマったところ: 正規表現の前段で静かに0件になる
正規表現はSQLでは使えないので、まずLIKEで粗く絞ってから、 取ってきた行にPython側で正規表現をかけています。 その粗い絞り込みに、正規表現の先頭のリテラル部分を使っていました。
# ❌ 最初の書き方
head = rx.pattern.split("[")[0]
cond, args = "(... LIKE ?)", [f"%{head}%"]
# 「理科[^。]{0,12}?(でき|...)」 → head は「理科」。ここまでは動く
ところが、括弧で始まる正規表現を足した瞬間に0件になりました。
[ で切っているので、先頭が ( の場合は何も切れず、
パターンの文字列がまるごとLIKEに渡ります。
LIKE '%(ミスター|Mr\.)堀元%' ← こんな文字列は本文に無い
厄介なのは、エラーにならず静かに0件になることです。 「この語、まだ言ってないのかな」と一瞬思いました。
# ✅ いまの書き方
head = re.match(r"[^\[\(\{\.\*\+\?\|\]+", rx.pattern)
head = head.group(0) if head else ""
if not head:
raise SystemExit(f"{slug}: 正規表現の先頭が取り出せない")
0件で素通りするより、止まってくれたほうがいいです。
載せない判断もする
デカルトみ 42回 / 多数の回
ソシュールみ 7回 / 2本
堀元み 2回 / 2本
年表は「いつ増えたか」を見るものなので、2回では線になりません。 作れるものを全部載せると、見る側は「どれが本当に定着した言葉なのか」が分からなくなります。
表記ゆれの多くは、こちらが作っている
音声には表記がありません。「むらづくり」と言われた音を、 ひらがなで書くか漢字で書くかを決めているのは機械のほうです。 つまり文字起こしに現れる割れは、番組の表記ゆれではなくこちらの産物です。
「村作り」がそうでした。本文ではひらがなと漢字で割れていましたが、 番組の概要欄に「月額会員制村作りサービス」と書いてあります。 番組自身の表記は1つに決まっているので、こちらが作った割れを揃えました。
「ミスター堀元」も同じです。本文では6通りに割れています。
ミスター堀元 17件
ミスター・堀元 5件
ミスター・ホリモト 7件
Mr.ホリモト 5件
ミスターホリモト 1件
Mr.堀元 1件
これも番組が6通りに書き分けたのではなく、機械が6通りに書いただけです。 では番組はどう書いているか、概要欄を引きました。
Why Mr.ホリモト? (2本)
Why ? Mr.堀元 ? (1本)
オープニング。Mr.Horimoto. (1本)
カタカナの「ミスター」は0件でした。番組は Mr. と書いています。
本文にある22件の「ミスター〜」は、番組が一度も書いていない形です。
ただし後ろが「ホリモト」「堀元」「Horimoto」で割れていて、 こちらは番組自身の書き分けなので、どれかに寄せる判断はできません。 いまは揃えずに置いていますが、それは「人の呼び名だから触らない」からではなく、 寄せ先の後半が決まらないからです。
基準にしたこと
番組自身の表記が1つある → こちらの割れを揃える (村作り)
番組自身の表記が割れている → 寄せ先を決められない
番組自身の表記が無い → 触らない
最初、この節を「人が名乗った名前だから直さない」と書いていました。 2つ間違えています。概要欄を引かずに「番組の表記は無い」と決めつけたのと、 自分が作った割れを番組の表記ゆれとして扱ったことです。
後者のほうが根が深いと思っています。 文字起こしを長く触っていると、目の前の文字列が「そう書かれていたもの」に見えてきます。 実際には、そこにあるのは自分たちが選んだ表記です。 揃えるかどうかを考える前に、その割れを作ったのが誰かを確かめる必要がありました。