Class: VivlioStarter::CLI::Metrics::Analyzer
- Inherits:
-
Object
- Object
- VivlioStarter::CLI::Metrics::Analyzer
- Defined in:
- lib/vivlio_starter/cli/metrics/analyzer.rb
Overview
Markdown コンテンツを解析してメトリクスを算出する
Constant Summary collapse
- SENTENCE_DELIMITER =
/[。!?!?]+/- CLAUSE_DELIMITER =
'、'- KANJI_PATTERN =
/\p{Han}/- DEFAULT_MATTR_WINDOW =
MATTR(移動平均 TTR)の既定の窓幅。Covington & McFall (2010) 由来で 50〜100 が標準、定番実装 quanteda の既定も 100。設定で上書き可能。
100
Class Method Summary collapse
-
.prose_length(content) ⇒ Integer
地の文の文字数(空白をすべて除いた長さ)。
vocabulary_stats.total_char_countと同じ数え方——分母を揃えるため、 語彙分析(MeCab を伴う)を走らせずに分量だけ欲しいときはこちらを使う。. -
.prose_of(content) ⇒ String
地の文(コード片と Markdown 記法を除いた本文)だけを取り出す。 索引語数の目安を本文の分量から導く経路(IndexSizeEstimator)が使う。 同じ除去処理を索引側へ書き写すと
vs metricsの表示と黙ってずれるため、 ここを唯一の実装として公開する(index-term-selection-spec.md§3.3)。.
Instance Method Summary collapse
-
#basic_stats ⇒ Object
基本統計を算出する。 行数と chars / chars_no_newline は生の本文(コード込み)で数えるが、 文・節・読点は文章としての構造なので prose(コード除去後)で数える。.
-
#content_words ⇒ Object
内容語(名詞・固有名詞・形容動詞・動詞・形容詞)を品詞ラベルつきで返す。 MeCab が使えない環境では空配列(D セクションは表示しない)。.
-
#initialize(content, config = {}) ⇒ Analyzer
constructor
A new instance of Analyzer.
-
#prose ⇒ Object
コード片を除いた解析対象本文(文構造・語彙・読解で共通利用)。 索引の分量算出(
self.prose_length)からも使うため公開している。. -
#readability ⇒ Object
読解難度スコア(建石式 RS)を算出する.
-
#vocabulary_stats ⇒ Object
語彙分析を実行する(コード片は分析対象に含めない).
Constructor Details
#initialize(content, config = {}) ⇒ Analyzer
Returns a new instance of Analyzer.
108 109 110 111 112 |
# File 'lib/vivlio_starter/cli/metrics/analyzer.rb', line 108 def initialize(content, config = {}) @content = content @config = config @mecab_available = check_mecab_available end |
Class Method Details
.prose_length(content) ⇒ Integer
地の文の文字数(空白をすべて除いた長さ)。
vocabulary_stats.total_char_count と同じ数え方——分母を揃えるため、
語彙分析(MeCab を伴う)を走らせずに分量だけ欲しいときはこちらを使う。
106 |
# File 'lib/vivlio_starter/cli/metrics/analyzer.rb', line 106 def self.prose_length(content) = prose_of(content).gsub(/\s/, '').length |
.prose_of(content) ⇒ String
地の文(コード片と Markdown 記法を除いた本文)だけを取り出す。
索引語数の目安を本文の分量から導く経路(IndexSizeEstimator)が使う。
同じ除去処理を索引側へ書き写すと vs metrics の表示と黙ってずれるため、
ここを唯一の実装として公開する(index-term-selection-spec.md §3.3)。
99 |
# File 'lib/vivlio_starter/cli/metrics/analyzer.rb', line 99 def self.prose_of(content) = new(content).prose |
Instance Method Details
#basic_stats ⇒ Object
基本統計を算出する。 行数と chars / chars_no_newline は生の本文(コード込み)で数えるが、 文・節・読点は文章としての構造なので prose(コード除去後)で数える。
著者に見せる主役は本文で、コードと記法はその外数として添える
(chapter-volume-calibration-data.md §7.1)。本文そのものは語彙分析の分母と
同じ値なので VocabularyStats#total_char_count を唯一の持ち主にし、ここでは
外数だけを持つ。全空白を除いた基準に揃えてあるため、本文と足すと原文に一致する。
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/vivlio_starter/cli/metrics/analyzer.rb', line 122 def basic_stats sentences = sentence_segments(prose) clauses = clause_segments(prose) all_chars = content.gsub(/\s/, '').length without_code = Masking.strip_code(content).gsub(/\s/, '').length prose_chars = prose.gsub(/\s/, '').length BasicStats.new( chars: content.length, chars_no_newline: content.delete("\r\n").length, code_chars: all_chars - without_code, notation_chars: without_code - prose_chars, lines: content.empty? ? 0 : content.each_line.count, sentences: sentences.size, avg_sentence_len: safe_average(sentences.sum(&:length), sentences.size), clauses: clauses.size, avg_clause_len: safe_average(clauses.sum(&:length), clauses.size), commas: prose.count(CLAUSE_DELIMITER) ) end |
#content_words ⇒ Object
内容語(名詞・固有名詞・形容動詞・動詞・形容詞)を品詞ラベルつきで返す。 MeCab が使えない環境では空配列(D セクションは表示しない)。
174 175 176 177 178 |
# File 'lib/vivlio_starter/cli/metrics/analyzer.rb', line 174 def content_words return [] unless mecab_available extract_content_words(prose) end |
#prose ⇒ Object
コード片を除いた解析対象本文(文構造・語彙・読解で共通利用)。
索引の分量算出(self.prose_length)からも使うため公開している。
191 |
# File 'lib/vivlio_starter/cli/metrics/analyzer.rb', line 191 def prose = @prose ||= extract_prose(content) |
#readability ⇒ Object
読解難度スコア(建石式 RS)を算出する
181 182 183 184 185 186 187 |
# File 'lib/vivlio_starter/cli/metrics/analyzer.rb', line 181 def readability features = Readability.extract_features(prose) score = Readability.score(features) label = Readability.label(score, readability_thresholds) ReadabilityScore.new(score:, label:, features:) end |
#vocabulary_stats ⇒ Object
語彙分析を実行する(コード片は分析対象に含めない)
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/vivlio_starter/cli/metrics/analyzer.rb', line 145 def vocabulary_stats text = prose tokens = tokenize(text) unique = tokens.uniq tokens_map = build_token_frequencies(tokens) stripped = text.gsub(/\s/, '') total_chars = stripped.length kanji_count = stripped.scan(KANJI_PATTERN).size total_word_length = tokens.sum(&:length) VocabularyStats.new( kanji_ratio: calculate_kanji_ratio(kanji_count, total_chars), avg_word_length: safe_average(total_word_length, tokens.size), ttr: safe_average(unique.size.to_f, tokens.size), mattr: moving_average_ttr(tokens), total_tokens: tokens.size, unique_tokens: unique.size, kanji_char_count: kanji_count, hira_char_count: stripped.scan(/\p{Hiragana}/).size, kata_char_count: stripped.scan(/[\p{Katakana}ー]/).size, alpha_char_count: stripped.scan(/[A-Za-z]/).size, total_char_count: total_chars, total_word_length:, tokens_map: tokens_map ) end |