Keywords
The keywords and key phrases of a text by RAKE, TextRank or TF-IDF, with stop words of 58 languages left out: tags, SEO and related posts.
Version 1.0.0 · 3 functions · 2 crates
Functions
What use keywords.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use keywords_rake, keywords_textrank from keywords (Quillo 0.6).
keywords_rake(text: str, n: int, language: str) → listor an errorThe n best key phrases of text by RAKE, as [{ phrase, score }], best first: runs of words between stop words and punctuation, scored by how their words are used together. Quick, good for one article. language is a two-letter code for the stop words.
keywords_textrank(text: str, n: int, language: str) → listor an errorThe n best key phrases of text by TextRank, as [{ phrase, score }]: words ranked by the words around them, as PageRank ranks pages. Slower than RAKE, often better on long texts.
keywords_tfidf(documents: list, n: int, language: str) → listor an errorThe n words that best set documents apart from one another, by TF-IDF, as [{ word, score }]: common in some documents, rare in the rest. Give it a set of posts, products or tickets. Up to 10,000 documents.
Get it
$ quillo install keywords # keywords.qwer into this folder$ quillo install keywords --example # and its example, keywords.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/keywords.qwer$ curl -O https://quillolang.org/plugins/files/keywords.qweThen, in your program, all of it or only what you call:
use keywords.qweruse keywords_rake, keywords_textrank from keywords // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
keyword_extraction = { version = "1.5", default-features = false, features = ["tf_idf", "rake", "text_rank"] }stop-words = "0.10"Example
// Tags suggested for a blog post, and the words that set a shop's products apart.//// quillo run keywords.qwe then open http://localhost:8080/tagsuse "keywords.qwer"let post = "Solar panels on the roof of a house turn sunlight into electricity. A solar panel works best facing south, without shade. Battery storage keeps the electricity for the evening, so a house with solar panels and battery storage buys less electricity from the grid. The price of solar panels has fallen every year for a decade."route GET /tags = { rake: keywords_rake(post, 5, "en")?, textrank: keywords_textrank(post, 5, "en")? }route GET /products = keywords_tfidf([ "Leather wallet with coin pocket, brown leather, hand stitched", "Canvas backpack with laptop pocket, waterproof canvas", "Leather belt, black leather, brass buckle", "Waterproof jacket with hood, breathable",], 5, "en")?$ quillo run keywords.qwe$ curl 'localhost:8080/tags'$ curl 'localhost:8080/products'Source
//! The keywords and key phrases of a text, for tags, SEO and "related posts": RAKE, TextRank and TF-IDF, with stop words of 58 languages left out.//! version: 1.0.0//!//! [dependencies]//! keyword_extraction = { version = "1.5", default-features = false, features = ["tf_idf", "rake", "text_rank"] }//! stop-words = "0.10"use keyword_extraction::rake::{Rake, RakeParams};use keyword_extraction::text_rank::{TextRank, TextRankParams};use keyword_extraction::tf_idf::{TfIdf, TfIdfParams};use serde_json::{json, Value};const MAX: usize = 1_000_000;fn stops(language: &str) -> Result<Vec<String>, String> { let l = language.trim().to_lowercase(); let l = if l.is_empty() { "en".to_string() } else { l }; let mut w: Vec<String> = stop_words::lookup(&l).map(|w| w.iter().map(|s| s.to_string()).collect()).ok_or_else(|| format!("no stop words for {:?}", language))?; w.push(BREAK.to_string()); Ok(w)}/// The crate skips punctuation instead of ending a phrase there ("shade. Battery" would be one/// phrase), so each mark that ends a phrase becomes a stop word of our own.const BREAK: &str = "qqbreakqq";fn breaks(text: &str) -> String { let mut out = String::with_capacity(text.len() + 64); for c in text.chars() { if ".,;:!?()[]{}\"“”«»—–|/\n".contains(c) { out.push(' '); out.push_str(BREAK); out.push(' '); } else { out.push(c); } } out}fn scored(v: Vec<(String, f32)>, key: &str) -> Vec<Value> { v.into_iter().map(|(w, s)| json!({ key: w, "score": ((s as f64) * 1000.0).round() / 1000.0 })).collect()}fn check(text: &str) -> Result<(), String> { if text.len() > MAX { Err("text up to 1 MB".into()) } else { Ok(()) }}/// The `n` best key phrases of `text` by RAKE, as `[{ phrase, score }]`, best first: runs of words between stop words and punctuation, scored by how their words are used together. Quick, good for one article. `language` is a two-letter code for the stop words.pub fn keywords_rake(text: &str, n: i64, language: &str) -> Result<Vec<Value>, String> { check(text)?; let sw = stops(language)?; let r = Rake::new(RakeParams::WithDefaultsAndPhraseLength(&breaks(text), &sw, Some(4))); Ok(scored(r.get_ranked_phrases_scores(n.clamp(1, 100) as usize), "phrase"))}/// The `n` best key phrases of `text` by TextRank, as `[{ phrase, score }]`: words ranked by the words around them, as PageRank ranks pages. Slower than RAKE, often better on long texts.pub fn keywords_textrank(text: &str, n: i64, language: &str) -> Result<Vec<Value>, String> { check(text)?; let sw = stops(language)?; let r = TextRank::new(TextRankParams::WithDefaultsAndPhraseLength(&breaks(text), &sw, Some(3))); Ok(scored(r.get_ranked_phrase_scores(n.clamp(1, 100) as usize), "phrase"))}/// The `n` words that best set `documents` apart from one another, by TF-IDF, as `[{ word, score }]`: common in some documents, rare in the rest. Give it a set of posts, products or tickets. Up to 10,000 documents.pub fn keywords_tfidf(documents: Vec<String>, n: i64, language: &str) -> Result<Vec<Value>, String> { if documents.len() > 10_000 || documents.iter().map(|d| d.len()).sum::<usize>() > 20 * MAX { return Err("up to 10,000 documents and 20 MB".into()); } let sw = stops(language)?; let t = TfIdf::new(TfIdfParams::UnprocessedDocuments(&documents, &sw, None)); Ok(scored(t.get_ranked_word_scores(n.clamp(1, 1000) as usize), "word"))}Related
stemming.qwer · 1.0.0Words cut to their stems in 18 languages, so running, runs and run count as one word in a search or a tag cloud.7 views · 8 downloadsChinese words chinese_segment.qwer · 1.0.0Chinese text split into words for search indexes and counts, with parts of speech, and written in pinyin with tones, tone numbers or initials for sorting and finding names.7 views · 7 downloadsFuzzy search fuzzy.qwer · 1.0.0Rank names against what someone typed, typos and all, edit distances, and did-you-mean.7 views · 8 downloadsN-grams ngrams.qwer · 1.0.0N-grams and shingles: word and character n-grams, the phrases a text uses most, how alike two texts are by shared shingles despite typos, and near-duplicates found among thousands of texts with MinHash.6 views · 9 downloadsPhonetic matching phonetics.qwer · 1.0.0Names matched by how they sound for search and duplicate checks: Soundex, Metaphone, Double Metaphone, NYSIIS, Caverphone and Cologne codes, accents ignored.8 views · 9 downloadsSentences sentences.qwer · 1.0.0Text split into sentences as a reader would: "Dr.", "p.m.", initials, decimals, "e.g." and quoted questions handled, with abbreviations for seven languages; positions for highlighting, and previews of whole sentences that fit a length.7 views · 9 downloadsThread 0 posts
On Keywords: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.