Disclaimer: This article was written by AI and may contain inaccuracies or errors. 免責事項: この記事はAIによって作成されたものであり、不正確な情報やエラーが含まれている可能性があります。
Table of contents
Open Table of contents
はじめに
またしても、興味深いライブラリに出会ってしまった。stopword という、実にシンプルながら強力なテキスト処理ライブラリだ。
「ストップワード除去」と聞いて、何を想像するだろうか。自然言語処理(NLP)に携わった経験のある読者なら、その重要性を理解しているだろう。しかし、実際のところ、多くの開発者にとっては「聞いたことはあるが、使ったことはない」技術かもしれない。
私自身、かつてテキストマイニングの研究に没頭していた時期がある。あの頃は、各言語のストップワードリストを自分で管理し、時には一から作成していた。それが今では、たった一行のインストールで62言語に対応したライブラリが手に入る。技術の進歩とは、時に感慨深いものだ。
stopwordとは
ストップワードとは、「the」「a」「is」といった、文章中に頻出するが意味的にはあまり重要でない単語のことだ。日本語なら「は」「が」「です」などが該当する。
stopwordライブラリは、このストップワードをテキストから除去し、本質的な意味を持つ単語だけを抽出することに特化している。
主な特徴
実際にコードとドキュメントを読んで確認した特徴を列挙する:
- 62言語対応 - 英語、日本語、フランス語、スペイン語、アラビア語、中国語など
- ISO 639-3準拠 - 3文字の標準言語コードを使用
- 柔軟な使い方 - デフォルトの英語リストやカスタムリストに対応
- 複数形式対応 - CommonJS、ES Modules、UMD、TypeScript
- 軽量 - ミニファイド版でも約130KBと控えめ
- ブラウザ対応 - Node.jsだけでなくブラウザでも動作
なぜストップワード除去が重要なのか
ストップワードは、文章の文法的構造には必要だが、意味的内容にはあまり寄与しない。例えば:
"The cat is on the mat"
この文章から、ストップワード(“The”, “is”, “on”, “the”)を除去すると:
"cat mat"
文法としては破綻しているが、意味の核心は伝わる。これが、テキスト解析においては重要なのだ。
セットアップ
セットアップは驚くほどシンプルだった。
npm install stopword
TypeScriptを使う場合は、型定義も一緒にインストールする:
npm i @types/stopword --save-dev
これだけである。依存関係もなく、インストールは一瞬で完了する。
実際に試してみる
理論よりも実践。実際にコードを書いて動作を確認してみた。
基本的な使い方
まずは最もシンプルな例から:
const { removeStopwords } = require("stopword");
const text = "the quick brown fox jumps over the lazy dog and a cat".split(" ");
const filtered = removeStopwords(text);
console.log("Original:", text.join(" "));
console.log("Filtered:", filtered.join(" "));
実行結果:
Original: the quick brown fox jumps over the lazy dog and a cat
Filtered: quick brown fox jumps lazy dog cat
“the”, “over”, “and”, “a” といったストップワードが除去され、意味のある単語だけが残った。実に明快だ。
日本語への対応
次に、日本語を試してみた:
const { removeStopwords, jpn } = require("stopword");
const japaneseText = [
"これ",
"は",
"私",
"の",
"猫",
"です",
"が",
"あなた",
"の",
"犬",
"も",
"かわいい",
];
const japaneseFiltered = removeStopwords(japaneseText, jpn);
console.log("Original:", japaneseText.join(" "));
console.log("Filtered:", japaneseFiltered.join(" "));
実行結果:
Original: これ は 私 の 猫 です が あなた の 犬 も かわいい
Filtered: 私 猫 あなた 犬 かわいい
「は」「の」「です」「が」「も」といった助詞・助動詞が除去されている。日本語の場合、事前にトークン化(形態素解析)が必要だが、その点さえクリアすれば完璧に動作する。
注目すべきは、日本語のストップワードリストが109語も含まれている点だ。実用的な規模である。
複数言語の結合
実践的な機能として、複数言語のストップワードリストを結合できる:
const { removeStopwords, eng, fra, spa } = require("stopword");
const multilingualStopwords = [...eng, ...fra, ...spa];
const mixedText = "the cat et la maison y el perro".split(" ");
const mixedFiltered = removeStopwords(mixedText, multilingualStopwords);
console.log("Original:", mixedText.join(" "));
console.log("Filtered:", mixedFiltered.join(" "));
実行結果:
Original: the cat et la maison y el perro
Filtered: cat maison perro
英語、フランス語、スペイン語が混在したテキストから、各言語のストップワードを一度に除去できた。多言語対応のアプリケーションでは重宝するだろう。
カスタムストップワードリスト
独自のストップワードリストも使える:
const customStopwords = ["quick", "lazy"];
const textWithCustom = "the quick brown fox jumps over the lazy dog".split(" ");
const customFiltered = removeStopwords(textWithCustom, customStopwords);
console.log("Filtered:", customFiltered.join(" "));
実行結果:
Filtered: the brown fox jumps over the dog
デフォルトのリストではなく、カスタムリストだけが適用される。ドメイン固有の単語を除外したい場合に便利だ。
実践的な応用例
理論的な話ばかりでは面白くない。実際にどのような場面で使えるのか、いくつかのユースケースを試してみた。
ユースケース1: キーワード抽出
技術記事からキーワードを抽出してみる:
const { removeStopwords, eng } = require("stopword");
const article = `
The JavaScript ecosystem has evolved rapidly over the past decade.
Modern frameworks like React and Vue have transformed how we build web applications.
TypeScript has become increasingly popular for its type safety and developer experience.
`.trim();
const words = article
.toLowerCase()
.replace(/[.,\n]/g, "")
.split(/\s+/);
const keywords = removeStopwords(words, eng);
// 単語の頻度をカウント
const frequency = {};
keywords.forEach(word => {
frequency[word] = (frequency[word] || 0) + 1;
});
console.log("Word frequency:");
Object.entries(frequency)
.sort((a, b) => b[1] - a[1])
.forEach(([word, count]) => {
console.log(` ${word}: ${count}`);
});
実行結果:
Word frequency:
javascript: 1
ecosystem: 1
evolved: 1
rapidly: 1
frameworks: 1
react: 1
vue: 1
transformed: 1
build: 1
web: 1
typescript: 1
become: 1
increasingly: 1
popular: 1
type: 1
safety: 1
developer: 1
experience: 1
記事の主要なトピック(JavaScript, TypeScript, React, Vue)が抽出された。これは、記事のタグ付けや検索インデックス作成に応用できる。
ユースケース2: 検索クエリ最適化
検索エンジンのクエリから不要な単語を除去する:
const searchQueries = [
"how to install node.js on windows",
"what is the best way to learn react",
"can you show me examples of async await",
];
console.log("Optimized queries:");
searchQueries.forEach(query => {
const optimized = removeStopwords(query.split(" "), eng).join(" ");
console.log(` "${query}" → "${optimized}"`);
});
実行結果:
Optimized queries:
"how to install node.js on windows" → "install node.js windows"
"what is the best way to learn react" → "best learn react"
"can you show me examples of async await" → "show examples async await"
検索クエリから冗長な単語を削除し、核心部分だけを残せた。これにより、検索エンジンのパフォーマンス向上や、より正確な結果の取得が期待できる。
ユースケース3: テキスト類似度比較
2つのテキストの類似度を簡易的に計算する:
const text1 = "The cat is on the mat and it is happy";
const text2 = "A cat sits on a mat and looks happy";
const words1 = removeStopwords(text1.toLowerCase().split(" "), eng);
const words2 = removeStopwords(text2.toLowerCase().split(" "), eng);
console.log("Text 1 keywords:", words1.join(", "));
console.log("Text 2 keywords:", words2.join(", "));
const commonWords = words1.filter(word => words2.includes(word));
console.log("Common keywords:", commonWords.join(", "));
console.log(
"Similarity score:",
((commonWords.length / Math.max(words1.length, words2.length)) * 100).toFixed(
1
) + "%"
);
実行結果:
Text 1 keywords: cat, mat, happy
Text 2 keywords: cat, sits, mat, looks, happy
Common keywords: cat, mat, happy
Similarity score: 60.0%
ストップワードを除去することで、テキストの意味的な類似度をより正確に測定できる。これは、文書分類や重複検出に応用できる。
ストップワードリストの中身
各言語のストップワードリストには、どれくらいの単語が含まれているのか確認してみた:
const { eng, jpn, fra, spa } = require("stopword");
console.log("Stopword list sizes:");
console.log(` English: ${eng.length}`);
console.log(` Japanese: ${jpn.length}`);
console.log(` French: ${fra.length}`);
console.log(` Spanish: ${spa.length}`);
実行結果:
Stopword list sizes:
English: 108
Japanese: 109
French: 168
French: 59
興味深いことに、フランス語のリストが最も多い。これは、フランス語の冠詞や前置詞の多様性を反映しているのだろう。
実際のストップワードの例も見てみよう:
console.log("Sample stopwords:");
console.log(` English: ${eng.slice(0, 10).join(", ")}`);
console.log(` Japanese: ${jpn.slice(0, 10).join(", ")}`);
console.log(` French: ${fra.slice(0, 10).join(", ")}`);
console.log(` Spanish: ${spa.slice(0, 10).join(", ")}`);
実行結果:
Sample stopwords:
English: about, after, all, also, am, an, and, another, any, are
Japanese: の, に, は, を, た, が, で, て, と, し
French: être, avoir, faire, a, au, aux, avec, ce, ces, dans
Spanish: a, un, el, ella, y, sobre, de, la, que, en
各言語の特性がよく現れている。日本語は助詞中心、フランス語は動詞や冠詞、スペイン語も同様だ。
どのような場面で使えるか
私の経験と、今回の実験から、stopwordが活きる場面をいくつか挙げる:
- 検索エンジン最適化 - クエリから不要な単語を削除し、検索精度を向上
- キーワード抽出 - 記事や文書から主要なトピックを自動抽出
- テキスト分類 - 文書の意味的特徴を抽出し、分類モデルの入力に
- 文書要約 - 重要な単語を特定し、要約生成のヒントに
- スパムフィルタリング - スパムメールの特徴的な単語を特定
- センチメント分析 - 感情を表す単語に焦点を当てる
- データ可視化 - ワードクラウドなどで、意味のある単語だけを表示
特に、「大量のテキストデータから意味を抽出したい」という場面では、最初のステップとして極めて有効だ。
気づいた点と考察
良い点
- シンプルさ - APIが直感的で、学習コストがほぼゼロ
- 広範な言語サポート - 62言語という圧倒的なカバレッジ
- 軽量 - 依存関係がなく、サイズも小さい
- 柔軟性 - カスタムリストや複数言語の結合に対応
- 実用性 - 実際のNLPパイプラインにすぐ組み込める
注意点
正直なところ、このライブラリに大きな問題点は見当たらない。しかし、ストップワード除去という手法自体の限界は理解しておくべきだろう:
- 文脈の喪失 - 文法構造が失われるため、意味が変わる可能性
- 言語依存性 - 日本語のような言語は、事前の形態素解析が必須
- ドメイン依存性 - 特定の分野では、一般的なストップワードが重要な意味を持つ場合がある
- 完璧ではない - ストップワードリストは経験的に作られたもので、絶対的な基準ではない
例えば、「NOT」は英語のストップワードとして扱われることが多いが、センチメント分析では極めて重要だ(“good” と “not good” は正反対の意味)。
しかし、これらは「stopwordライブラリの問題」ではなく、「自然言語処理の本質的な難しさ」である。むしろ、このライブラリは、その難しさを理解した上で、実用的な解決策を提供している。
実装の美しさ
実際にリポジトリのコードを読んでみた。コア部分は実にシンプルだ:
function removeStopwords(tokens, stopwords) {
return tokens.filter(token => !stopwords.includes(token.toLowerCase()));
}
本質的には、これだけである(実際の実装はもう少し複雑だが)。
しかし、このシンプルさこそが美しい。62言語のストップワードリストを管理し、複数のモジュール形式に対応し、TypeScriptの型定義を提供する。しかし、コアロジックはこれだけだ。
作者のFergiemcdowallは、恐らく長年NLPに携わってきたのだろう。複雑さを排除し、本質だけを残す。これは、熟練したエンジニアの仕事だ。
結論
stopwordは、「シンプルだが強力」という理想を体現したライブラリだ。
62言語に対応し、実用的なストップワードリストを提供し、使い方は極めてシンプル。大規模なNLPフレームワークを導入するほどではないが、ちょっとしたテキスト処理が必要な場面で、これほど頼りになるツールは少ない。
このライブラリを使っていると、テキスト処理の本質を思い出す。複雑なアルゴリズムや最新のTransformerモデルも素晴らしいが、時には「不要な単語を除去する」という単純な操作が、驚くほど効果的なのだ。
私がかつて研究していた頃は、各言語のストップワードリストを手作業で作成し、メンテナンスしていた。それが今では、npm install stopwordの一行で、62言語のリストが手に入る。技術の民主化とは、まさにこういうことだろう。
正直なところ、最初は「ストップワード除去なんて、今さら?」と思っていた。しかし、実際に使ってみると、その普遍的な有用性を再認識させられた。流行のディープラーニングも素晴らしいが、こういった「地味だが確実に役立つツール」こそが、日々の開発を支えているのだ。
興味を持った読者は、ぜひ自分の手で試してみることをお勧めする。きっと、テキストデータの見方が少し変わるはずだ。
参考リンク
検証環境
本記事で使用したパッケージのバージョン:
- Node.js: 22.21.0
- stopword: 3.1.5