├── Photos ├── photo-1488554378835-f7acf46e6c98.jpeg ├── photo-1501526029524-a8ea952b15be.jpeg ├── photo-1523961131990-5ea7c61b2107.jpeg ├── photo-1529236183275-4fdcf2bc987e.jpeg ├── photo-1545987796-200677ee1011.jpeg ├── photo-1574012716378-0ca6f4c18c08.jpeg └── sdfsf.png ├── README.md ├── Scrape.py ├── Sentiment.py ├── __pycache__ ├── Scrape.cpython-310.pyc ├── Sentiment.cpython-310.pyc ├── chart.cpython-310.pyc └── corpora.cpython-310.pyc ├── app.py ├── chart.py ├── corpora.py └── requirements.txt /Photos/photo-1488554378835-f7acf46e6c98.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/Photos/photo-1488554378835-f7acf46e6c98.jpeg -------------------------------------------------------------------------------- /Photos/photo-1501526029524-a8ea952b15be.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/Photos/photo-1501526029524-a8ea952b15be.jpeg -------------------------------------------------------------------------------- /Photos/photo-1523961131990-5ea7c61b2107.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/Photos/photo-1523961131990-5ea7c61b2107.jpeg -------------------------------------------------------------------------------- /Photos/photo-1529236183275-4fdcf2bc987e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/Photos/photo-1529236183275-4fdcf2bc987e.jpeg -------------------------------------------------------------------------------- /Photos/photo-1545987796-200677ee1011.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/Photos/photo-1545987796-200677ee1011.jpeg -------------------------------------------------------------------------------- /Photos/photo-1574012716378-0ca6f4c18c08.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/Photos/photo-1574012716378-0ca6f4c18c08.jpeg -------------------------------------------------------------------------------- /Photos/sdfsf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/Photos/sdfsf.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Streamlit-Interactive-Twitter-Sentiment-Analyzer 2 | A streamlit powered web app to perform basic sentiment analysis on your text or tweets 3 | 4 | -------------------------------------------------------------------------------- /Scrape.py: -------------------------------------------------------------------------------- 1 | import re 2 | import snscrape.modules.twitter as sntwitter 3 | import pandas as pd 4 | from Sentiment import * 5 | 6 | 7 | 8 | def clean(tweet): 9 | return ' '.join(re.sub("(@[A-Za-z0-9]+)|([^0-9A-Za-z \t])|(\w+:\/\/\S+)", " ", "{}".format(tweet)).split()) 10 | 11 | 12 | def scrape_tweets(search_query, max_tweets_count=10,start=0, end=0): 13 | 14 | tweets_list2 = [] 15 | tweets_list3 = [] 16 | 17 | # Using TwitterSearchScraper to scrape data and append tweets to list 18 | for fetched_tweets_no, tweet in enumerate(sntwitter.TwitterSearchScraper('{} since:{} until:{} lang:en'.format(search_query, start, end)).get_items()): 19 | 20 | if fetched_tweets_no>=max_tweets_count: 21 | break 22 | 23 | tweets_list2.append([tweet.date, tweet.id, tweet.content, tweet.user.username]) 24 | 25 | po, su = sentiment(tweet.content) 26 | tweets_list3.append([tweet.date, tweet.id, clean(tweet.content), po, su, tweet.user.username]) 27 | 28 | # Creating a dataframe from the tweets list above 29 | tweets_df2 = pd.DataFrame(tweets_list2, columns=['Datetime', 'Tweet Id', 'Text', 'Username']) 30 | tweets_df3 = pd.DataFrame(tweets_list3, columns=['Datetime', 'Tweet Id', 'Text', 'Polarity', 'Subjectivity', 'Username']) 31 | 32 | return tweets_df2, tweets_df3 33 | -------------------------------------------------------------------------------- /Sentiment.py: -------------------------------------------------------------------------------- 1 | from numpy import negative, positive 2 | import streamlit as st 3 | from textblob import TextBlob 4 | import pandas as pd 5 | from textblob.en import polarity, subjectivity 6 | from chart import * 7 | 8 | 9 | #Sentiment analyser 10 | def sentiment(text, corrspell=False): 11 | blob = TextBlob(text) 12 | 13 | #Spelling Corrector 14 | if corrspell : 15 | blob=blob.correct() 16 | st.markdown("#### Corrected to") 17 | st.markdown("**"+str(blob)+"**") 18 | 19 | return blob.sentiment.polarity, blob.sentiment.subjectivity 20 | 21 | def show_stats2(positive, negative, polarity_list, subjectivity_list): 22 | avg_pol = sum(polarity_list)/len(polarity_list) 23 | avg_subj = sum(subjectivity_list)/len(subjectivity_list) 24 | subj2 = [subjectivity_list[i] for i in range(len(subjectivity_list)) if not polarity_list[i]==0] 25 | avg_subj2 = sum(subj2)/len(subj2) 26 | st.markdown("## Statistics") 27 | st.markdown("---") 28 | 29 | 30 | col1, col2 = st.columns([1, 1]) 31 | 32 | with col1: 33 | if avg_subj<0.3: 34 | delta1 = "- low" 35 | elif avg_subj>0.5: 36 | delta1 = "+ high" 37 | else: 38 | delta1 = "" 39 | 40 | if avg_subj2<0.5: 41 | delta2 = "- low" 42 | else: 43 | delta2 = "+ high" 44 | 45 | st.metric(label = "Average Subjectivity", value = "%0.5f"%(avg_subj), delta =delta1) 46 | st.metric(label = "Average Average Subjectivity Of Non Neutral Tweets", value = "%0.5f"%(avg_subj2), delta = delta2) 47 | 48 | with col2: 49 | 50 | if avg_pol<-0.25: 51 | delta1 = "- low" 52 | 53 | elif avg_pol>0.25: 54 | delta1 = "+ high" 55 | else: 56 | delta1 = "" 57 | 58 | if negative==0 or positive/negative>10: 59 | delta2 = "+high" 60 | else: 61 | delta2 = "- low" 62 | 63 | st.metric(label = "Average Polarity", value = "%0.5f"%(avg_pol), delta = delta1) 64 | 65 | if negative!=0: 66 | st.metric(label="+ve/-ve Ratio", value = "%.5f"%(positive/negative), delta = delta2) 67 | #st.write(positive/negative) 68 | 69 | else: 70 | #st.write("All positive") 71 | st.metric("+ve/-ve Ratio", value = "All Positive", delta = delta2) 72 | 73 | return avg_pol, avg_subj 74 | 75 | 76 | def show_stats(positive, negative, polarity_list, subjectivity_list): 77 | avg_pol = sum(polarity_list)/len(polarity_list) 78 | avg_subj = sum(subjectivity_list)/len(subjectivity_list) 79 | subj2 = [subjectivity_list[i] for i in range(len(subjectivity_list)) if not polarity_list[i]==0] 80 | avg_subj2 = sum(subj2)/len(subj2) 81 | st.markdown("## Statistics") 82 | st.markdown("---") 83 | col1, col2 = st.columns([1, 1]) 84 | with col1: 85 | st.markdown("###### Average Polarity") 86 | st.markdown("###### [-1 to 1]") 87 | st.markdown("") 88 | 89 | st.markdown("###### Average Subjectivity") 90 | st.markdown("###### [0 to 1]") 91 | st.markdown("") 92 | 93 | st.markdown("###### Average Subjectivity") 94 | st.markdown("###### Of Non-NeutralTweets ") 95 | st.markdown("") 96 | 97 | st.markdown("###### Positive / Negative Ratio") 98 | 99 | with col2: 100 | st.write(avg_pol) 101 | st.markdown("") 102 | st.markdown("") 103 | 104 | st.write(avg_subj) 105 | st.markdown("") 106 | st.markdown("") 107 | st.markdown("") 108 | 109 | st.write(avg_subj2) 110 | st.markdown("") 111 | st.markdown("") 112 | 113 | 114 | if negative!=0: 115 | st.write(positive/negative) 116 | else: 117 | st.write("All positive") 118 | 119 | return avg_pol, avg_subj 120 | 121 | def show_analysis(avg_pol, avg_subj): 122 | st.markdown("## Analysis") 123 | st.markdown("---") 124 | 125 | st.markdown("##### Level of Emotion") 126 | if avg_pol>=0.25: 127 | st.markdown(''' 128 | High positive average polarity indicates that most tweets have strong and positive opinions about the searched topic 129 | ''') 130 | 131 | elif avg_pol<=-0.25: 132 | st.markdown(''' 133 | High negative average polarity indicates that most tweets have strong and negative opinions about the searched topic 134 | ''') 135 | 136 | else: 137 | st.markdown(''' 138 | Moderate average polarity indicates that most tweets are somewhat neutral about the searched topic and do not exhibit strong opinions about it 139 | ''') 140 | 141 | st.markdown("##### Level of Subjectivity") 142 | if avg_subj>=0.35: 143 | st.markdown(''' 144 | High average subjectivity indicates that most tweets are highly subjective to the person tweeting about the searched topic 145 | ''') 146 | 147 | else: 148 | st.markdown(''' 149 | Moderate average subjectivity indicates that most tweets are more in general sense rather than being subjective about the searched topic 150 | ''') 151 | 152 | 153 | def tweetSentiment(tweet_df): 154 | 155 | polarity_list=[] 156 | subjectivity_list=[] 157 | 158 | positive=0 159 | negative=0 160 | neutral=0 161 | 162 | for i in tweet_df["Polarity"]: 163 | if i<0: 164 | negative += 1 165 | 166 | elif i>0: 167 | positive += 1 168 | 169 | else: 170 | neutral += 1 171 | 172 | polarity_list.append(i) 173 | 174 | for i in tweet_df["Subjectivity"]: 175 | subjectivity_list.append(i) 176 | 177 | st.markdown("### Tweet Distribution") 178 | tweetChart(positive, negative, neutral) 179 | st.markdown("### Tweet propotions") 180 | tweet_pie(positive, negative, neutral) 181 | 182 | #Statistics 183 | avg_pol, avg_subj = show_stats2(positive, negative, polarity_list, subjectivity_list) 184 | 185 | #st.markdown("---") 186 | st.markdown("") 187 | st.markdown("") 188 | st.markdown("") 189 | st.markdown("") 190 | st.markdown("") 191 | 192 | #Analysis 193 | show_analysis(avg_pol, avg_subj) -------------------------------------------------------------------------------- /__pycache__/Scrape.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/__pycache__/Scrape.cpython-310.pyc -------------------------------------------------------------------------------- /__pycache__/Sentiment.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/__pycache__/Sentiment.cpython-310.pyc -------------------------------------------------------------------------------- /__pycache__/chart.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/__pycache__/chart.cpython-310.pyc -------------------------------------------------------------------------------- /__pycache__/corpora.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MainakRepositor/Healthyments/423f5cdb15a8cd415ec807399cda2641d945d892/__pycache__/corpora.cpython-310.pyc -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from Sentiment import * 3 | from chart import * 4 | from Scrape import * 5 | import datetime 6 | 7 | from PIL import Image 8 | import corpora 9 | #Page Config 10 | st.set_page_config(page_title="ITSA") 11 | st.markdown("# Interactive Text Sentiment Analysis System") 12 | st.markdown("") 13 | st.sidebar.markdown("# Navigation Pane") 14 | show_page = st.sidebar.selectbox(label="Choose page", options=["Welcome", "Type In Prompt", "Scrape From Twitter"]) 15 | 16 | 17 | 18 | if show_page=="Welcome": 19 | ''' 20 | ##### Welcome, this interactive web app is aimed to demonstrate the working of text sentiment analysis 21 | ''' 22 | image = Image.open('Photos/photo-1523961131990-5ea7c61b2107.jpeg') 23 | st.image(image) 24 | 25 | ''' 26 | --- 27 | ##### To begin: 28 | - ###### Open the navigation pane 29 | - ###### Chose the method to be used for sentiment analysis [Select the desired page from the drop down] 30 | - ###### Play around and view analysis 31 | --- 32 | ##### Few things to keep in mind 33 | 34 | - ###### Polarity refers to the strength of the emotion 35 | - -1 being strongly negative and 36 | - +1 being strongly positive 37 | - ###### Subjectivity as the name suggests, refers to how subjective the text is 38 | - 0 being completely objective [for eg facts] 39 | - 1 being completely subjective 40 | - ###### As with all machine learning systems, it will not be 100% accurate and might have biases 41 | --- 42 | ''' 43 | 44 | elif show_page=="Type In Prompt": 45 | 46 | image = Image.open("Photos/photo-1529236183275-4fdcf2bc987e.jpeg") 47 | st.image(image) 48 | 49 | st.markdown("### Evaluate Your Custom Text") 50 | 51 | txt = st.text_area(label="Enter text") 52 | corrspell = st.checkbox(label="Use spelling corrector") 53 | evaluate = st.button(label="Compute Sentiment") 54 | 55 | #main 56 | if evaluate and txt!="": 57 | 58 | with st.spinner("Computing Sentiment"): 59 | 60 | po,su = sentiment(txt, corrspell) 61 | col1, col2 = st.columns([1, 1]) 62 | 63 | with col1: 64 | st.write("Polarity : ") 65 | st.write(po) 66 | 67 | with col2: 68 | st.write("Subjectivity") 69 | st.write(su) 70 | 71 | chart(po, su) 72 | 73 | else: 74 | 75 | image = Image.open('Photos/sdfsf.png') 76 | st.image(image) 77 | 78 | st.markdown("### Evaluate Twitter Fetched Text") 79 | txt = st.text_area(label="Enter what you want to search") 80 | tweetCount = st.slider(label="Choose no of tweets to scrape", min_value=1, value=10, max_value=1150) 81 | 82 | 83 | #Date Picker 84 | col1, col2 = st.columns([1, 1]) 85 | with col1: 86 | begin=st.date_input(label="Starting Date", value=datetime.date.today()-datetime.timedelta(days=1), min_value=datetime.date.today()-datetime.timedelta(days=1000), max_value=datetime.date.today()-datetime.timedelta(days=1)) 87 | 88 | with col2: 89 | finish=st.date_input(label="Finishing Date", value=datetime.date.today(), min_value=datetime.date.today()-datetime.timedelta(days=999), max_value=datetime.date.today()) 90 | 91 | #Ensure starting date before finishing date 92 | if finish<=begin: 93 | st.error("Error \nStarting date must be < Finishing Date") 94 | st.stop() 95 | get_tweet = st.button(label="Scrape and Analyze tweets") 96 | 97 | if get_tweet: 98 | 99 | if txt=="": 100 | st.error("Error \nEmpty search string") 101 | st.stop() 102 | 103 | with st.spinner("Loading"): 104 | 105 | 106 | data, data_clean = scrape_tweets(txt, tweetCount, begin, finish) 107 | 108 | if len(data)==0: 109 | st.error("No data") 110 | st.stop() 111 | 112 | st.markdown("") 113 | st.markdown("") 114 | st.markdown("") 115 | 116 | st.markdown("## Result") 117 | st.markdown("---") 118 | 119 | tweetSentiment(data_clean) 120 | 121 | with st.expander("Show scraped Twitter tweets"): 122 | 123 | st.markdown("#### Raw") 124 | st.write(data) 125 | 126 | st.markdown("#### Processed") 127 | st.write(data_clean) 128 | 129 | 130 | -------------------------------------------------------------------------------- /chart.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import plotly.express as px 3 | import streamlit as st 4 | 5 | def chart(po, su): 6 | 7 | #Define Colors 8 | if po>=0: 9 | c1="#42f598" 10 | else: 11 | c1="#fa7066" 12 | 13 | #Convert lists to Pandas Datafram 14 | data = pd.DataFrame( 15 | [[po,0],[0,su]], 16 | index = ["Polarity", "Subjectivity"], 17 | columns = ["Polarity", "Subjectivity"] 18 | ) 19 | 20 | #Make Figure 21 | fig = px.bar( 22 | data, 23 | labels = { 24 | "index" : "", 25 | "value" : "value" 26 | }, 27 | color_discrete_sequence=[c1,"#16d3f5"] 28 | ) 29 | fig.update_layout(legend=dict( 30 | orientation="v", 31 | yanchor="bottom", 32 | y=-0.7, 33 | xanchor="left", 34 | x=0.01 35 | )) 36 | 37 | #Plot Fig 38 | st.plotly_chart(fig, use_container_width=True) 39 | 40 | def tweetChart(positive, negative, neutral): 41 | data = pd.DataFrame( 42 | [ 43 | [positive,0, 0], 44 | [0,negative, 0], 45 | [0, 0, neutral] 46 | ], 47 | index = ["Positive", "Negative", "Neutral"], 48 | columns = ["No Of Positive Tweets", "No Of Negative Tweets", "No Of Neutral Tweets"] 49 | ) 50 | 51 | #Make Figure 52 | fig = px.bar( 53 | data, 54 | labels = { 55 | "index" : "Sentiment", 56 | "value" : "Number Of Tweets" 57 | }, 58 | color_discrete_sequence=["#42f598", "#fa7066", "#16d3f5"] 59 | ) 60 | fig.update_layout(legend=dict( 61 | orientation="v", 62 | yanchor="bottom", 63 | y=-0.7, 64 | xanchor="left", 65 | x=0.01 66 | )) 67 | #Plot Fig 68 | st.plotly_chart(fig, use_container_width=True) 69 | 70 | def tweet_pie(positive, negative, neutral): 71 | data = { 72 | "polarity": pd.Series([positive, negative, neutral], index=["positive", "negative", "neutral"]), 73 | } 74 | data = pd.DataFrame(data) 75 | 76 | fig = px.pie(data, values='polarity', names=data.index, color=data.index, 77 | color_discrete_map={'positive':'#42f598', 78 | 'negative':'#fa7066', 79 | 'neutral':'#16d3f5'}) 80 | fig.update_layout(legend=dict( 81 | orientation="v", 82 | yanchor="bottom", 83 | y=-0.7, 84 | xanchor="left", 85 | x=0.01 86 | )) 87 | st.plotly_chart(fig, use_container_width=True) -------------------------------------------------------------------------------- /corpora.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | cmd = ['python3','-m','textblob.download_corpora'] 3 | subprocess.run(cmd) 4 | print("Working") -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | plotly 2 | git+https://github.com/JustAnotherArchivist/snscrape.git 3 | Pillow 4 | textblob 5 | click==7.1.2 --------------------------------------------------------------------------------