package kr.co.swh.lecture.opensource.spark.recognition; 

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.mllib.classification.NaiveBayes;
import org.apache.spark.mllib.classification.NaiveBayesModel;
import org.apache.spark.mllib.classification.SVMModel;
import org.apache.spark.mllib.classification.SVMWithSGD;
import org.apache.spark.mllib.linalg.Vectors;
import org.apache.spark.mllib.regression.LabeledPoint;

import scala.Tuple2;

/**
 * <pre>
 * kr.co.swh.spark_ai.recognition.spark
 * SVM.java
 *
 * 설명 : C:\Users\tobby48\eclipse-workspace\spark-ai\src\main\resources\train C:\Users\tobby48\eclipse-workspace\spark-ai\src\main\resources\test 100
 * </pre>
 * 
 * @since : 2019. 4. 4.
 * @author : tobby48
 * @version : v1.0
 */
public class SVM {

	public static void main(String[] args) {

		System.setProperty("hadoop.home.dir", "C://hadoop-2.7.1");
		System.setProperty("SPARK_HOME", "C://spark-2.4");
		
		// Create Java spark context
		SparkConf conf = new SparkConf().setAppName("SVM vs Navie Bayes").setMaster("local[2]");
		JavaSparkContext sc = new JavaSparkContext(conf);

		//RDD training = MLUtils.loadLabeledData(sc, args[0]);
		//RDD test = MLUtils.loadLabeledData(sc, args[1]); // test set

		JavaRDD<LabeledPoint> training = sc.textFile(args[0]).cache().map(v1 -> {
			String filename = v1.substring(0, v1.indexOf("|"));
			double label = Double.parseDouble(v1.substring(v1.indexOf("|")+1, v1.indexOf(",")));
			String featureString[] = v1.substring(v1.indexOf(",") + 1).trim().split(" ");
			double[] v = new double[featureString.length];
			int i = 0;
			for (String s : featureString) {
				if (s.trim().equals(""))
					continue;
				v[i++] = Double.parseDouble(s.trim());
			}
			return new LabeledPoint(label, Vectors.dense(v));
		});
		System.out.println(training.count());
		JavaRDD<Tuple2<String, LabeledPoint>> test = sc.textFile(args[1]).cache().map(v1 -> {
			String filename = v1.substring(0, v1.indexOf("|"));
			double label = Double.parseDouble(v1.substring(v1.indexOf("|")+1, v1.indexOf(",")).trim());
			String featureString[] = v1.substring(v1.indexOf(",") + 1).trim().split(" ");
			double[] v = new double[featureString.length];
			int i = 0;
			for (String s : featureString) {
				if (s.trim().equals(""))
					continue;
				v[i++] = Double.parseDouble(s.trim());
			}
			return new Tuple2<String, LabeledPoint>(filename, new LabeledPoint(label, Vectors.dense(v)));
		});
		System.out.println(test.count());
		final NaiveBayesModel model = NaiveBayes.train(training.rdd(), 1.0);

		JavaPairRDD<String, Tuple2<Double, Double>> predictionAndLabel = test.mapToPair(p -> new Tuple2<String, Tuple2<Double, Double>>(p._1(), new Tuple2<Double, Double>(model.predict(p._2().features()), p._2().label())));
		System.out.println("navie bayes");
		for(Tuple2<String, Tuple2<Double, Double>> tuple2 : predictionAndLabel.collect()) {
			if(tuple2._2()._1().intValue() != tuple2._2()._2().intValue()) {
				System.out.println(tuple2._1() + " 예측값 : " + tuple2._2()._1().intValue() + "; 실제값 : " + tuple2._2()._2().intValue());
			}
		}
		double accuracy = 1.0
				* predictionAndLabel.filter(
						pl -> pl._2()._1().intValue() == pl._2()._2().intValue()).count() / (double)test.count();
		System.out.println("navie bayes accuracy : " + accuracy);

		
		final SVMModel svmModel = SVMWithSGD.train(training.rdd(), Integer.parseInt(args[2]));
		JavaPairRDD<String, Tuple2<Double, Double>> predictionAndLabelSVM = test.mapToPair(p -> new Tuple2<String, Tuple2<Double, Double>>(p._1(), new Tuple2<Double, Double>(svmModel.predict(p._2().features()), p._2().label())));
		System.out.println("svm");
		for(Tuple2<String, Tuple2<Double, Double>> tuple2 : predictionAndLabelSVM.collect()) {
			if(tuple2._2()._1().intValue() != tuple2._2()._2().intValue()) {
				System.out.println(tuple2._1() + " 예측값 : " + tuple2._2()._1().intValue() + "; 실제값 : " + tuple2._2()._2().intValue());
			}
		}
		double accuracySVM = 1.0
				* predictionAndLabelSVM.filter(
						pl -> pl._2()._1().intValue() == pl._2()._2().intValue()).count() / (double)test.count();
		System.out.println("svm accuracy : " + accuracySVM);

	}
}
