Spaces:
Sleeping
Sleeping
File size: 7,228 Bytes
5a2d956 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | """
NLTK Text Processing Playground (Gradio)
Features
- Type text or drag-and-drop a .txt or .docx file
- Menu of steps:
1) Install & download required NLTK resources
2) Tokenize text
3) Remove stopwords
4) Stem words (Porter)
5) Lemmatize words (WordNet)
6) Tag parts of speech
7) Extract named entities
- Prints results to screen for only the steps you select
"""
import io
import os
import re
from typing import List, Tuple, Optional
import gradio as gr
# --- NLTK imports are inside functions so the app can start even if resources aren't ready ---
SUPPORTED_EXTS = {".txt", ".docx"}
def read_text_from_inputs(text_input: str, file_obj: Optional[gr.File]) -> str:
"""
Returns a single text string from either the text box or the uploaded file.
If both are provided, file content takes precedence.
"""
if file_obj is not None:
name = getattr(file_obj, "name", None) or ""
ext = os.path.splitext(name)[1].lower()
file_bytes = file_obj.read()
if ext == ".txt":
try:
return file_bytes.decode("utf-8")
except Exception:
# Fallback: best-effort decode
return file_bytes.decode(errors="ignore")
elif ext == ".docx":
from docx import Document # python-docx
with io.BytesIO(file_bytes) as buf:
doc = Document(buf)
return "\n".join(p.text for p in doc.paragraphs)
else:
raise gr.Error(f"Unsupported file type: {ext}. Use one of: {', '.join(SUPPORTED_EXTS)}.")
# fallback to text area
return text_input or ""
def setup_nltk() -> str:
"""
Installs/downloads the corpora and models needed for the lab.
Safe to run multiple times; NLTK skips existing files.
"""
import nltk
downloaded = []
for pkg in [
"punkt",
"stopwords",
"wordnet",
"averaged_perceptron_tagger",
"maxent_ne_chunker",
"words",
]:
try:
nltk.download(pkg, quiet=True)
downloaded.append(pkg)
except Exception as e:
downloaded.append(f"{pkg} (error: {e})")
return "NLTK resources ready:\n- " + "\n- ".join(downloaded)
def pipeline(text: str, steps: List[str]) -> str:
"""
Runs the selected steps in a fixed logical order and returns a Markdown report.
"""
if not text.strip():
return "⚠️ **No input text found.** Type text or upload a .txt/.docx file."
# Import here (after optional setup) to avoid early failures
import nltk
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer, WordNetLemmatizer
from nltk import pos_tag
from nltk.chunk import ne_chunk
report_sections = []
current_tokens = []
filtered_tokens = []
stemmed_tokens = []
lemmatized_tokens = []
# 1) Tokenize
if "Tokenize text" in steps:
current_tokens = word_tokenize(text)
report_sections.append(
"### 1) Tokens\n```\n" + repr(current_tokens) + "\n```"
)
# 2) Stopword removal (case-insensitive)
if "Remove stopwords" in steps:
if not current_tokens:
current_tokens = word_tokenize(text)
stop_words = set(stopwords.words("english"))
filtered_tokens = [w for w in current_tokens if w.lower() not in stop_words]
report_sections.append(
"### 2) Filtered (stopwords removed)\n```\n" + repr(filtered_tokens) + "\n```"
)
# 3) Stem
if "Stem words" in steps:
if not filtered_tokens:
# If user skipped stopwords, stem tokens directly
base = current_tokens or word_tokenize(text)
else:
base = filtered_tokens
stemmer = PorterStemmer()
stemmed_tokens = [stemmer.stem(w) for w in base]
report_sections.append(
"### 3) Stemmed (Porter)\n```\n" + repr(stemmed_tokens) + "\n```"
)
# 4) Lemmatize
if "Lemmatize words" in steps:
if not filtered_tokens:
base = current_tokens or word_tokenize(text)
else:
base = filtered_tokens
lemmatizer = WordNetLemmatizer()
lemmatized_tokens = [lemmatizer.lemmatize(w) for w in base]
report_sections.append(
"### 4) Lemmatized (WordNet, default POS=noun)\n```\n"
+ repr(lemmatized_tokens)
+ "\n```"
)
# Choose a reasonable token sequence for downstream steps
downstream = (
lemmatized_tokens
or stemmed_tokens
or filtered_tokens
or current_tokens
or word_tokenize(text)
)
# 5) POS tagging
if "Tag parts of speech" in steps:
tags = pos_tag(downstream)
report_sections.append("### 5) POS Tags\n```\n" + repr(tags) + "\n```")
# 6) Named entities
if "Extract named entities" in steps:
# ne_chunk expects POS-tagged input
tagged = pos_tag(downstream)
tree = ne_chunk(tagged)
# Pretty-print the chunk tree as text
report_sections.append(
"### 6) Named Entities (chunk tree)\n```\n" + tree.pformat() + "\n```"
)
if not report_sections:
return "ℹ️ **No steps selected.** Choose at least one item from the menu."
header = "# NLTK Processing Report\n"
return header + "\n\n".join(report_sections)
with gr.Blocks(title="NLTK Text Processing Playground") as demo:
gr.Markdown(
"""
# NLTK Text Processing Playground
**Type text** _or_ **drop a `.txt` / `.docx` file**.
Select the steps you want and click **Process**.
If this is your first time, click **Prepare NLTK Resources**.
"""
)
with gr.Row():
text_in = gr.Textbox(
label="Text input",
placeholder="Type or paste text here (or upload a file instead)…",
lines=8,
)
file_in = gr.File(
label="Optional: upload a .txt or .docx file",
file_types=[".txt", ".docx"],
file_count="single",
)
steps = gr.CheckboxGroup(
choices=[
"Tokenize text",
"Remove stopwords",
"Stem words",
"Lemmatize words",
"Tag parts of speech",
"Extract named entities",
],
value=["Tokenize text", "Remove stopwords", "Lemmatize words", "Tag parts of speech", "Extract named entities"],
label="Menu — select one or more steps",
)
with gr.Row():
setup_btn = gr.Button("🧰 Prepare NLTK Resources")
run_btn = gr.Button("▶️ Process")
output = gr.Markdown(label="Output")
# Wire up actions
setup_btn.click(fn=lambda: setup_nltk(), outputs=output)
def run_pipeline(text_input, file_input, selected_steps):
text = read_text_from_inputs(text_input, file_input)
return pipeline(text, selected_steps)
run_btn.click(
fn=run_pipeline,
inputs=[text_in, file_in, steps],
outputs=output,
)
if __name__ == "__main__":
# Launch Gradio app. Share=False by default; set share=True if you want a public link.
demo.launch()
|