在數據分析和挖掘的過程中,為了知道個體間差異的大小,我們需要去評價個體之間的相似性,數據的挖掘方法可以分為分類和聚類,如KNN和KMeans.
而衡量個體差異的方法主要分為兩種,距離度量——歐式距離,相似度度量——余弦距離。
1、歐式距離
衡量個體在空間上存在的距離,距離越遠說明個體間的差異越大。
根據歐幾里得公式: 計算出每個點之間的絕對距離,對於歐式距離公式,求其倒數將范圍規定與(0,1),對於值越靠近與1的則相似度越高

2、余弦距離
余弦距離也稱為余弦相似度,是用向量空間中兩個向量夾角的余弦值作為衡量兩個個體間差異的大小的度量,
當兩個向量的方向接近於一致,夾角趨於0則兩個向量就相近,也可以稱兩個個體之間相似。
將三角形的兩邊a,b當作向量:

求其夾角公式如下:

關於相似度和余弦距離的關系如下圖:角度越小則相似度越高

3、舉例分析
假設5個用戶對喜歡的水果進行打分如下形成一個DataFrame:
val spark = SparkSession.builder().appName("euclidean").master("local[*]").getOrCreate() val schema = StructType(Seq( StructField("userid",StringType,false), StructField("apple",DoubleType,false), StructField("banana",DoubleType,false), StructField("orange",DoubleType,false), StructField("watermelon",DoubleType,false), StructField("melon",DoubleType,false) )) val df = spark.createDataFrame(spark.sparkContext.parallelize(Seq( Row("nol",4.5,5.0,0.0,3.0,0.0), Row("no2",0.0,0.0,5.0,4.0,0.0), Row("no3",4.0,3.0,4.5,0.0,4.0), Row("no4",0.0,0.0,0.0,0.0,5.0), Row("no5",4.0,3.0,2.0,1.0,0.0) )),schema)
求歐式距離:
每一個用戶需要對每一個水果都要求解歐式距離,假設當前用戶:arr1,比較用戶:arr2
定義函數如下:
//歐氏距離公式 def calcEuc(arr1:Array[Double],arr2:Array[Double])={ val lst:ListBuffer[Double] = ListBuffer[Double]() for (num1<-0 until arr1.length;num2<-0 until arr2.length;if num1==num2){ lst.append(math.pow(arr1(num1)-arr2(num2),2)) } 1/(1+math.sqrt(lst.sum)) //求出接近於1則用戶之間更相似 } //查相似度(每兩個用戶間相似度) def eucli(rdd:List[Array[Double]])={ val lst:ListBuffer[ListBuffer[Double]] = ListBuffer[ListBuffer[Double]]() for (arr<-rdd){ val sec:ListBuffer[Double]=ListBuffer[Double]() for(a1<-rdd){ //計算歐式距離公式 sec.append(calcEuc(arr,a1)) } lst.append(sec) } lst }
求解如下:
val cls = df.columns.filter(x => x != "userid").map(x=>col(x)) val rdd = df.select(concat_ws(",", cls: _*).alias("feature")).rdd.toLocalIterator.toList.map(r => { val arr = r(0).toString.split(",").map(x => x.toDouble) arr }) val res =eucli(rdd) res.foreach(x=>x.foreach(println))
1.0 0.10592130260593688 0.12444584824196174 0.10098718092501505 0.2222222222222222 0.10592130260593688 1.0 0.1167355195593027 0.10960059084055324 0.13231996486433337 0.12444584824196174 0.1167355195593027 1.0 0.12819304429541925 0.17176743283130236 0.10098718092501505 0.10960059084055324 0.12819304429541925 1.0 0.11881849050177154 0.2222222222222222 0.13231996486433337 0.17176743283130236 0.11881849050177154 1.0
求余弦距離:
1#求點積
//點積 def dj(arr1:Array[Double],arr2:Array[Double])={ val lst:ListBuffer[Double]=ListBuffer[Double]() for(num1<-0 until arr1.length;num2<- 0 until arr2.length;if num1==num2){ lst.append(arr1(num1)*arr2(num2)) } lst.sum }
2#求模
//向量求模 def mod(vec:Array[Double])={ math.sqrt(vec.map(math.pow(_,2)).sum) }
3#余弦相似度公式
def calcCos(arr1:Array[Double],arr2:Array[Double])={ dj(arr1,arr2)/(mod(arr1)*mod(arr2)) }
4#相似度計算
//查相似度(每兩個用戶間相似度) def eucli(rdd:List[Array[Double]])={ val lst:ListBuffer[ListBuffer[Double]] = ListBuffer[ListBuffer[Double]]() for (arr<-rdd){ val sec:ListBuffer[Double]=ListBuffer[Double]() for(a1<-rdd){ //計算余弦相似度 sec.append(calcCos(arr,a1)) } lst.append(sec) } lst }
求解如下:
val cls = df.columns.filter(x => x != "userid").map(x=>col(x)) val rdd = df.select(concat_ws(",", cls: _*).alias("feature")).rdd.toLocalIterator.toList.map(r => { val arr = r(0).toString.split(",").map(x => x.toDouble) arr }) val res =eucli(rdd) res.foreach(x=>x.foreach(println))
1.0000000000000002
0.25444237836283273
0.5724809269635496
0.0
0.892363919068504
0.25444237836283273
1.0
0.44899090442330025
0.0
0.39918616395854073
0.5724809269635496
0.44899090442330025
0.9999999999999999
0.5111012519999519
0.7931681071869339
0.0
0.0
0.5111012519999519
1.0
0.0
0.892363919068504
0.39918616395854073
0.7931681071869339
0.0
1.0
