Skip to main content

Pure-math text intelligence engine for Japanese — vocabulary richness, contamination detection, writing habit analysis, character voice fingerprinting

Project description

japhrase

辞書もLLMも使わずに、テキストからフレーズを見つける。

License: MIT Python 3.8+ PyPI

pip install japhrase

日本語テキストを入れると、繰り返し出現する 可変長フレーズ を頻度で見つけ出す。MeCab のような形態素解析器は単語単位で区切るが、japhrase は「機械学習」「自然言語処理」のような複合語・フレーズを丸ごと検出する。辞書も外部 AI も不要。

2つの使い方がある:


最初の一歩

from japhrase import PhraseExtractor

text = """機械学習の応用が広がっている。
自然言語処理は機械学習の重要な分野だ。
深層学習は機械学習の手法の一つである。
機械学習エンジニアの需要が高まっている。
機械学習のモデルを訓練するにはデータが必要だ。
深層学習の計算コストは高い。
自然言語処理の精度が向上している。
機械学習の倫理的課題も議論されている。""" * 3

pe = PhraseExtractor(verbose=0)
df = pe.extract(text)
print(df[["seqchar", "freq"]].head(3).to_string(index=False))
  seqchar  freq
    機械学習の  15.0
   自然言語処理   6.0
      ている  12.0

テキストを渡すだけ。「機械学習の」「自然言語処理」が辞書なしで見つかった。「機械学習」のように助詞が付くのは N-gram ベースの特徴 — use_pmi=Trueuse_branching_entropy=True を追加するとフレーズの境界が正確になる(→ 仕組み)。「ている」のようなノイズも PMI で消える。

次のステップ: デモで全機能を体験する自分の用途に合わせて使う

結果の使い方

df は pandas の DataFrame。

意味
seqchar 見つかったフレーズ
freq 出現回数
length フレーズの文字数
# ↑ の pe, df を使う

# 上位10フレーズ
print(df[["seqchar", "freq"]].head(10))

# 5回以上のフレーズだけ
frequent = df[df["freq"] >= 5]

# CSVに保存(Excelで開ける)
pe.export_csv(df, "results.csv")

# Pythonリストとして取り出す
phrases = df["seqchar"].tolist()

テキスト分析として使う

「大量のテキストの中身を数字で理解したい」人向け。 SNSの投稿、ニュース記事、論文、レビュー、アンケートの自由記述など。

キーワード抽出

テキスト種別ごとにプリセットがある:

from japhrase import PhraseExtractor

pe = PhraseExtractor.preset('sns')     # SNS/Twitter向け
pe = PhraseExtractor.preset('news')    # ニュース記事向け
pe = PhraseExtractor.preset('report')  # 論文/レポート向け

# ファイルから読み込む場合:
# df = pe.extract("input.txt")
# テキストを直接渡す場合:
df = pe.extract("あなたのテキスト。" * 50)
pe.export_csv(df, "keywords.csv")      # Excelで開ける

プリセットで足りないときは auto_tune=True で自動最適化:

from japhrase import PhraseExtractor

text = "機械学習の応用が広がっている。自然言語処理は機械学習の重要な分野だ。深層学習は機械学習の手法の一つである。" * 10
pe = PhraseExtractor(verbose=0)
df = pe.extract(text, auto_tune=True)   # テキストに合わせて自動チューニング
pe.show_params()                         # 最適化されたパラメータを表示
pe.save_params("my_params.json")         # 保存して次回から再利用

2つのテキストの違いを定量化

「テキスト A にだけ多い語は何か? B にだけ多い語は?」— レビュー比較や競合分析に。

from japhrase import DistributionComparator
from collections import Counter

# PhraseExtractor の結果から Counter を作ることもできる:
# freq_a = Counter(dict(zip(df_a["seqchar"], df_a["freq"])))

comp = DistributionComparator()
result = comp.compare(
    Counter({"騎士": 10, "剣": 8, "王城": 5}),
    Counter({"研究": 12, "実験": 9, "データ": 7}),
)
print(f"JS距離: {result.jsd:.4f}  (0=同一, 1=完全に別)")
JS距離: 1.0000  (0=同一, 1=完全に別)

語の結びつきの強さを測る

「機械学習」は偶然の並びか、意味のある結合か?

from japhrase import CollocationScorer

text = "機械学習の応用が広がっている。機械学習の技術が進んでいる。" * 50
scorer = CollocationScorer()
# score_phrase(フレーズ, 出現回数, テキスト全文)
# 出現回数は df["freq"] から取れる。ここでは直接指定:
cs = scorer.score_phrase("機械学習", 100, text)
print(f"PMI={cs.pmi:.1f}  t-score={cs.t_score:.1f}  Log-Dice={cs.log_dice:.1f}")
指標 得意なこと
PMI レアだが意味のある結合の発見
t-score 高頻度で安定した結合の発見
Log-Dice コーパスサイズに左右されない比較

テキスト類似度(コピペ・重複検出)

from japhrase import SimilarityAnalyzer

texts = [
    "機械学習の応用が広がっている。自然言語処理の進化が著しい。",
    "機械学習の技術が発展している。言語処理が急速に進化している。",
    "今日は天気がよい。散歩に出かけた。公園で花を見た。",
]
analyzer = SimilarityAnalyzer(method='jaccard')
matrix = analyzer.compare_texts(texts)
print(matrix)

text_1 と text_2 は類似、text_3 は完全に別。

時系列で語彙の変化を追う

章ごと・月ごとにテキストを並べて、語彙がどう変化しているか。

from japhrase import TemporalAnalyzer

ch1 = "騎士が剣を抜いた。王城の門が開いた。騎士は走った。" * 10
ch2 = "研究室でデータを確認した。実験の結果を記録した。" * 10
ch3 = "騎士は剣を収めた。王城に平和が戻った。研究が進んだ。" * 10

ta = TemporalAnalyzer()
result = ta.analyze_series([ch1, ch2, ch3], labels=["1章", "2章", "3章"])
print(f"語彙飽和度: {result['vocab_saturation']:.2f}")

執筆支援として使う

「自分の原稿を客観的に見たい」人向け。 小説、エッセイ、ブログ、ビジネス文書など。

語彙の豊かさを測る

「同じ言葉ばかり使っていないか?」を数字で確認。

from japhrase import StylometryAnalyzer

stylo = StylometryAnalyzer()
rich = stylo.analyze_advanced_diversity(
    "朝靄の中を歩いていると、足元で小さな花が揺れた。名前は知らない。淡い紫色の花弁が露を含んで光っている。")
poor = stylo.analyze_advanced_diversity(
    "男は歩いた。男は止まった。男はまた歩いた。男は見た。男は聞いた。")
print(f"豊かな文: Hapax比={rich['hapax_ratio']:.2f}")
print(f"単調な文: Hapax比={poor['hapax_ratio']:.2f}")
豊かな文: Hapax比=0.97
単調な文: Hapax比=0.76

Hapax比が高い = 一度しか使わなかった語が多い = 語彙が豊か。低い = 同じ語の繰り返し。

繰り返しの多さを測る

「同じことを何度も書いていないか?」を圧縮率で判定。

from japhrase import ComplexityAnalyzer

cx = ComplexityAnalyzer()
r1 = cx.analyze("朝靄の中を歩いていると、足元で小さな花が揺れた。名前は知らない。淡い紫色の花弁が露を含んで光っている。")
r2 = cx.analyze("男は歩いた。" * 20)
print(f"多様な文章: 圧縮率={r1['compression_ratio']:.3f}")
print(f"繰り返し:   圧縮率={r2['compression_ratio']:.3f}")
多様な文章: 圧縮率=0.869
繰り返し:   圧縮率=0.092

圧縮率が低い = 同じパターンが多い = 冗長。

書き癖を見つける

無意識に繰り返している表現を検出する。

from japhrase import WritingHabitDetector

text = """朝靄の中を歩いていた。しかし、名前は知らない。
しかし、気にはなった。しかし、立ち止まれなかった。
道の先に石橋が見えた。しかし、渡るのはためらわれた。
しかし、他に道はない。しかし、戻れない。
結局、渡った。しかし、足は震えていた。""" * 5

detector = WritingHabitDetector()
habits = detector.detect(text)
print(habits[["phrase", "count"]].to_string(index=False))
phrase  count
    。し     20
    い。     15
  た。しか     10

「。し」=「しかし」の文頭パターン、「た。しか」=「〜た。しかし」の接続パターン。N-gram ベースなので句読点を含む断片として検出される。繰り返しパターンが数字で見える。

原稿の汚染を検出

文字化け・コピペ重複・括弧の閉じ忘れを入稿前にチェック。

from japhrase.contamination import scan

profile = scan("正常な文章。\n’文字化け。\n「閉じない台詞")
print(f"汚染スコア: {profile.overall}/100")
print(profile.explain())

章ごとに一括チェック:

from japhrase.contamination import batch_scan

result = batch_scan({
    "1章": "正常なテキスト。問題のない文章が続きます。特に異常はありません。",
    "2章": "’文字化け。「閉じない括弧。重複文。重複文。重複文。重複文。重複文。",
    "3章": "正常なテキスト。こちらも問題ありません。普通の文章です。",
})
for key, p in result.profiles.items():
    print(f"{key}: スコア={p.overall}/100")
1章: スコア=0/100
2章: スコア=23/100
3章: スコア=0/100

さらに使える機能

機能 何をするか 使い方
TextVariantDetector 表記ゆれ検出 サーバー/サーバ、出来る/できる を自動発見
PhraseExtractor.preset('novel') 小説向け抽出 繰り返し表現・情緒的フレーズに最適化
japhrase.applied 執筆ワークフロー 公開前品質ゲート・話数間推移・キャラ文体指紋

各機能の詳細: help(TextVariantDetector) / help(japhrase.applied) で確認。


デモ(2行で全機能体験)

import japhrase
japhrase.run_demo()

内蔵の例文で全機能が順に実行される。何も用意しなくていい。

個別のデモ:

from japhrase import PhraseExtractor
PhraseExtractor.demo()

仕組み — N-gram + PMI + 分岐エントロピー

japhrase は3層でフレーズを絞り込む。

第1層: N-gram(繰り返しを広く拾う)

テキストを「連続する N 文字」に分解し、出現頻度を数える。

from japhrase import PhraseExtractor

text = """ChatGPTの登場により生成AIが注目を集めている。
生成AIはテキストだけでなく画像生成にも使われる。
大規模言語モデルは生成AIの中核技術である。
生成AIの倫理的課題について議論が活発化している。
企業における生成AIの導入事例が増加している。
生成AIの品質向上が求められている。
生成AIと人間の協調が進んでいる。
生成AIの応用範囲は広がり続けている。
生成AIによるコスト削減が期待されている。
生成AIの安全性が議論されている。""" * 5

pe = PhraseExtractor(min_count=3, min_length=2, max_length=8, verbose=0)
df = pe.extract(text)
print(df[["seqchar", "freq"]].head(4).to_string(index=False))
  seqchar  freq
     生成AI  50.0
      ている  35.0
ChatGPTの登   5.0
生成AIはテキスト   5.0

「生成AI」は見つかった。しかし「ている」(35回) も一緒に出てくる。N-gram だけでは意味のある結合とノイズを区別できない。

第2層: PMI(ノイズを落とす)

PMI(自己相互情報量)は「偶然一緒に現れる確率」と「実際の共起頻度」の比。フラグ1個追加するだけ:

pe = PhraseExtractor(min_count=3, min_length=2, max_length=8, use_pmi=True, verbose=0)
df = pe.extract(text)  # ↑ 第1層と同じ text
print(df[["seqchar", "freq"]].head(4).to_string(index=False))
    seqchar  freq
    生成AIの  30.0
    されている  10.0
ChatGPTの登   5.0
生成AIはテキスト   5.0

「ている」(35回) が消えた。「生成AI」の構成文字は偶然以上に一緒に現れている — PMI が高い。「ている」はどんな文章にも現れるので PMI が低く、脱落する。

第3層: 分岐エントロピー(切れ目を正確にする)

「あるフレーズの次にどんな文字が来るか」の多様性を測る。多様性が急に上がる位置 = フレーズの境界。use_branching_entropy=True を追加:

pe = PhraseExtractor(min_count=3, min_length=2, max_length=8,
                     use_pmi=True, use_branching_entropy=True, verbose=0)
df = pe.extract(text)  # ↑ 第1層と同じ text
print(df[["seqchar", "freq"]].head(4).to_string(index=False))

この例文では第2層と同じ結果になる。分岐エントロピーの効果は、フレーズの境界が曖昧な長文(小説など)で大きくなる。

まとめ: N-gram が広く拾い → PMI がノイズを落とし → 分岐エントロピーが切れ目を正確にする。この3層が japhrase のコア。


ファイルから読み込み

from japhrase import PhraseExtractor

df = PhraseExtractor.from_file("input.txt")                  # エンコーディング自動検出
df = PhraseExtractor.from_file("data.csv", column="text")    # CSV(列指定)

パラメータ一覧

from japhrase import PhraseExtractor

pe = PhraseExtractor(
    min_count=6,                # 最小出現回数
    max_length=16,              # フレーズの最大文字数
    min_length=4,               # フレーズの最小文字数
    threshold_originality=0.5,  # 類似フレーズ除去の閾値
    use_pmi=True,               # PMI で意味的結合を評価
    use_branching_entropy=True, # 分岐エントロピーで境界検出
    knowns=["既知語1"],         # 優先的に抽出したい語
    verbose=1,                  # 進捗表示(0:非表示)
)

CLI

japhrase --help           # コマンド一覧
japhrase extract --help   # フレーズ抽出
japhrase check --help     # 文書品質チェック

help() で調べる

from japhrase import PhraseExtractor
help(PhraseExtractor.extract)

from japhrase.contamination import scan
help(scan)

テスト

pytest

English

japhrase extracts phrases from Japanese text using statistics alone — no dictionary, no morphological analyzer, no LLM.

pip install japhrase
from japhrase import PhraseExtractor

pe = PhraseExtractor(verbose=0)
df = pe.extract("機械学習の応用が広がっている。自然言語処理は機械学習の重要な分野だ。" * 10)
print(df[["seqchar", "freq"]].head())

How it works: N-grams find recurring character sequences. PMI (pointwise mutual information) filters out meaningless combinations — "機械学習" scores high because its components co-occur far more than chance predicts, while "ている" scores low. Branching entropy then identifies natural phrase boundaries.

Two use cases:

  • Text analysis (researchers, marketers): keyword extraction, distribution comparison (JSD), collocation scoring (6 metrics), vocabulary richness (7 metrics), temporal analysis
  • Writing support (authors, editors): writing habit detection, contamination scanning (encoding errors, duplicates, bracket mismatches), complexity metrics, quality gates

Pure math, no external AI. Python 3.8+, numpy/scipy.


ライセンス

MIT License — Takeshi SHIMIZU

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

japhrase-0.3.13.tar.gz (336.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

japhrase-0.3.13-py3-none-any.whl (295.4 kB view details)

Uploaded Python 3

File details

Details for the file japhrase-0.3.13.tar.gz.

File metadata

  • Download URL: japhrase-0.3.13.tar.gz
  • Upload date:
  • Size: 336.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for japhrase-0.3.13.tar.gz
Algorithm Hash digest
SHA256 4c3762a442a34d4c5e498f8a8728f3c00ef8c3b6fa5df093ba2976c6d9727787
MD5 1c9ff00ed95ed5e78ceb49cd07ddcf00
BLAKE2b-256 2af8ce6d36a3c037c07e9aacb5486640208c979bed72e9446c183a72c4a774ee

See more details on using hashes here.

File details

Details for the file japhrase-0.3.13-py3-none-any.whl.

File metadata

  • Download URL: japhrase-0.3.13-py3-none-any.whl
  • Upload date:
  • Size: 295.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for japhrase-0.3.13-py3-none-any.whl
Algorithm Hash digest
SHA256 8abedb53447e1543d9f3a378b25ab91016bc4d28ef0038036fc1bec7362dca79
MD5 316630ab2ba4bd566c16a143f10af0ca
BLAKE2b-256 ce1343c2b8d9184d11817e655b91a838a0d1b75dc0f32973bca681cba13729c7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page