etuncer's picture
Rename src/streamlit_app.py to src/app.py
7292726 verified
Raw
History Blame Contribute Delete
4.11 kB
import streamlit as st
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import neattext as ntx
# -----------------------------
# Helpers
# -----------------------------
def clean_text(text: str) -> str:
if not isinstance(text, str):
return ""
text = text.lower()
text = ntx.remove_stopwords(text)
text = ntx.remove_multiple_spaces(text)
text = ntx.normalize(text)
return text
@st.cache_data(show_spinner=False)
def load_data(path: str) -> pd.DataFrame:
df = pd.read_csv(path, encoding="ISO-8859-1")
df = df.dropna(subset=["Title", "Article"]).copy()
df["article_clean"] = df["Article"].apply(clean_text)
return df
@st.cache_resource(show_spinner=False)
def build_vectorizer(corpus: pd.Series):
vectorizer = TfidfVectorizer()
matrix = vectorizer.fit_transform(corpus)
return vectorizer, matrix
def find_similar_by_text(
input_text: str,
df: pd.DataFrame,
vectorizer: TfidfVectorizer,
article_matrix,
top_n: int = 5,
):
query_clean = clean_text(input_text)
if not query_clean.strip():
return []
query_vec = vectorizer.transform([query_clean])
sims = cosine_similarity(query_vec, article_matrix).flatten()
top_idx = np.argsort(-sims)[:top_n]
results = []
for i in top_idx:
results.append({
"title": df.iloc[i]["Title"],
"article": df.iloc[i]["Article"],
"score": float(sims[i]),
})
return results
def find_similar_by_title(
title: str,
df: pd.DataFrame,
vectorizer: TfidfVectorizer,
article_matrix,
top_n: int = 5,
):
matches = df.index[df["Title"] == title].tolist()
if matches:
idx = matches[0]
vec = article_matrix[idx]
sims = cosine_similarity(vec, article_matrix).flatten()
sims[idx] = -np.inf
top_idx = np.argsort(-sims)[:top_n]
results = []
for i in top_idx:
results.append({
"title": df.iloc[i]["Title"],
"article": df.iloc[i]["Article"],
"score": float(sims[i]),
})
return results
return find_similar_by_text(title, df, vectorizer, article_matrix, top_n)
# -----------------------------
# UI
# -----------------------------
def main():
st.set_page_config(page_title="Article Recommender", page_icon="📰", layout="centered")
st.title("📰 Article Recommendation System")
st.caption("Content-based recommendations using TF-IDF and cosine similarity")
df = load_data("https://raw.githubusercontent.com/amankharwal/Website-data/master/articles.csv")
vectorizer, article_matrix = build_vectorizer(df["article_clean"])
with st.sidebar:
st.header("Settings")
top_n = st.number_input("Top N results", min_value=1, max_value=20, value=5, step=1)
mode = st.radio("Input type", ("Title", "Article content"))
results = []
if mode == "Title":
title = st.selectbox("Select title", options=sorted(df["Title"].unique().tolist()))
if title:
st.subheader(title)
st.write(df.loc[df["Title"] == title, "Article"].iloc[0])
if st.button("Find similar articles", type="primary"):
results = find_similar_by_title(title, df, vectorizer, article_matrix, top_n)
else:
article_text = st.text_area("Paste article content", height=200)
if st.button("Find similar articles", type="primary"):
if not article_text.strip():
st.warning("Please paste some article content.")
else:
results = find_similar_by_text(article_text, df, vectorizer, article_matrix, top_n)
if results:
st.subheader("Similar Articles")
for i, item in enumerate(results, start=1):
with st.expander(f"{i}. {item['title']} :yellow-badge[:material/star: {item['score']:.3f}]", expanded=False):
st.write(item["article"])
if __name__ == "__main__":
main()