Add Sentiment Analysis lab
This commit is contained in:
33
SentimentAnalysisLab/build.gradle
Normal file
33
SentimentAnalysisLab/build.gradle
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import org.springframework.boot.gradle.plugin.SpringBootPlugin
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'org.springframework.boot'
|
||||||
|
apply plugin: 'io.spring.dependency-management'
|
||||||
|
|
||||||
|
description = "Sentiment Analysis Lab"
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_21
|
||||||
|
targetCompatibility = JavaVersion.VERSION_21
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyManagement {
|
||||||
|
imports {
|
||||||
|
mavenBom SpringBootPlugin.BOM_COORDINATES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
|
||||||
|
implementation 'com.opencsv:opencsv:5.7.1'
|
||||||
|
implementation 'edu.stanford.nlp:stanford-corenlp:4.5.9:models'
|
||||||
|
implementation 'edu.stanford.nlp:stanford-corenlp:4.5.9'
|
||||||
|
|
||||||
|
runtimeOnly 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package com.example.senti;
|
||||||
|
|
||||||
|
import edu.stanford.nlp.ling.CoreAnnotations;
|
||||||
|
import edu.stanford.nlp.neural.rnn.RNNCoreAnnotations;
|
||||||
|
import edu.stanford.nlp.pipeline.*;
|
||||||
|
import edu.stanford.nlp.sentiment.SentimentCoreAnnotations;
|
||||||
|
import edu.stanford.nlp.util.CoreMap;
|
||||||
|
import com.opencsv.CSVReader;
|
||||||
|
import com.opencsv.exceptions.CsvException;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ProductReviewAnalyzer {
|
||||||
|
|
||||||
|
private StanfordCoreNLP pipeline;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ProductReviewAnalyzer analyzer = new ProductReviewAnalyzer();
|
||||||
|
analyzer.initialize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Load reviews from CSV
|
||||||
|
List<String[]> reviews = analyzer.loadReviews("/product_reviews.csv");
|
||||||
|
Map<String, Integer> sentimentDistribution = new LinkedHashMap<>();
|
||||||
|
sentimentDistribution.put("Very Positive", 0);
|
||||||
|
sentimentDistribution.put("Positive", 0);
|
||||||
|
sentimentDistribution.put("Neutral", 0);
|
||||||
|
sentimentDistribution.put("Negative", 0);
|
||||||
|
sentimentDistribution.put("Very Negative", 0);
|
||||||
|
|
||||||
|
System.out.println("=== Review Sentiment Analysis ===");
|
||||||
|
for (String[] review : reviews) {
|
||||||
|
if (review[0].equals("review_id")) continue; // Skip header
|
||||||
|
|
||||||
|
String sentiment = analyzer.analyzeSentiment(review[1]);
|
||||||
|
sentimentDistribution.put(sentiment, sentimentDistribution.get(sentiment) + 1);
|
||||||
|
|
||||||
|
System.out.printf("Review %2s: %-60s - %s\n",
|
||||||
|
review[0],
|
||||||
|
shortenText(review[1], 55),
|
||||||
|
sentiment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate report
|
||||||
|
analyzer.generateReport(sentimentDistribution, reviews.size() - 1);
|
||||||
|
|
||||||
|
} catch (IOException | CsvException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize() {
|
||||||
|
// Set up pipeline properties
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.setProperty("annotators", "tokenize, ssplit, parse, sentiment");
|
||||||
|
props.setProperty("coref.algorithm", "neural");
|
||||||
|
this.pipeline = new StanfordCoreNLP(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String[]> loadReviews(String filePath) throws IOException, CsvException {
|
||||||
|
try (CSVReader reader = new CSVReader(new InputStreamReader(new ClassPathResource(filePath).getInputStream()))) {
|
||||||
|
return reader.readAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String analyzeSentiment(String text) {
|
||||||
|
int mainSentiment = 0;
|
||||||
|
int longest = 0;
|
||||||
|
|
||||||
|
Annotation annotation = pipeline.process(text);
|
||||||
|
for (CoreMap sentence : annotation.get(CoreAnnotations.SentencesAnnotation.class)) {
|
||||||
|
int sentiment = RNNCoreAnnotations.getPredictedClass(sentence.get(SentimentCoreAnnotations.SentimentAnnotatedTree.class));
|
||||||
|
String partText = sentence.toString();
|
||||||
|
|
||||||
|
if (partText.length() > longest) {
|
||||||
|
mainSentiment = sentiment;
|
||||||
|
longest = partText.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert numeric sentiment to text
|
||||||
|
return switch (mainSentiment) {
|
||||||
|
case 0 -> "Very Negative";
|
||||||
|
case 1 -> "Negative";
|
||||||
|
case 2 -> "Neutral";
|
||||||
|
case 3 -> "Positive";
|
||||||
|
case 4 -> "Very Positive";
|
||||||
|
default -> "Neutral";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateReport(Map<String, Integer> sentimentCounts, int totalReviews) {
|
||||||
|
System.out.println("\n=== Sentiment Analysis Report ===");
|
||||||
|
System.out.printf("Total Reviews Analyzed: %d\n\n", totalReviews);
|
||||||
|
|
||||||
|
System.out.println("Sentiment Distribution:");
|
||||||
|
for (Map.Entry<String, Integer> entry : sentimentCounts.entrySet()) {
|
||||||
|
double percentage = (entry.getValue() * 100.0) / totalReviews;
|
||||||
|
System.out.printf("%-12s: %2d reviews (%5.1f%%) %s\n",
|
||||||
|
entry.getKey(),
|
||||||
|
entry.getValue(),
|
||||||
|
percentage,
|
||||||
|
generateBar(percentage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateBar(double percentage) {
|
||||||
|
int bars = (int) (percentage / 5);
|
||||||
|
return "[" + new String(new char[bars]).replace("\0", "=") + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String shortenText(String text, int maxLength) {
|
||||||
|
return text.length() > maxLength ? text.substring(0, maxLength - 3) + "..." : text;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
spring.application.name=senti
|
||||||
11
SentimentAnalysisLab/src/main/resources/product_reviews.csv
Normal file
11
SentimentAnalysisLab/src/main/resources/product_reviews.csv
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
review_id,review_text
|
||||||
|
1,"This product is absolutely amazing! Works perfectly."
|
||||||
|
2,"Terrible quality. Broke after 2 days of use."
|
||||||
|
3,"Good value for the price. Very satisfied."
|
||||||
|
4,"Not what I expected. Poor packaging."
|
||||||
|
5,"Excellent customer service and fast shipping."
|
||||||
|
6,"The item arrived damaged. Very disappointed."
|
||||||
|
7,"Works well but instructions could be better."
|
||||||
|
8,"Best purchase I've made this year!"
|
||||||
|
9,"Overpriced for what you get."
|
||||||
|
10,"Highly recommend this to all my friends."
|
||||||
|
@@ -8,3 +8,4 @@ include 'InventoryManagementSystem'
|
|||||||
include 'SmartClinicManagementSystem:app'
|
include 'SmartClinicManagementSystem:app'
|
||||||
include 'SoftwareDevChatbot'
|
include 'SoftwareDevChatbot'
|
||||||
include 'RegressionPredictionLab'
|
include 'RegressionPredictionLab'
|
||||||
|
include 'SentimentAnalysisLab'
|
||||||
Reference in New Issue
Block a user