File size: 4,108 Bytes
7292726 | 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 | 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()
|