2015-01-05 13 views
10

Tôi muốn sử dụng thư viện mllib.recommendation của Spark để xây dựng một hệ thống giới thiệu mẫu thử nghiệm. Tuy nhiên, định dạng của dữ liệu người dùng Tôi có một cái gì đó của định dạng sau:Làm thế nào để sử dụng mllib.recommendation nếu id người dùng là chuỗi thay vì các số nguyên tiếp giáp?

AB123XY45678 
CD234WZ12345 
EF345OOO1234 
GH456XY98765 
.... 

Nếu tôi muốn sử dụng thư viện mllib.recommendation, theo API của lớp Rating, id người dùng phải số nguyên (cũng phải tiếp giáp?)

Dường như một số loại chuyển đổi giữa id người dùng thực và các loại số được sử dụng bởi Spark phải được thực hiện. Nhưng làm thế nào tôi nên làm điều này?

Trả lời

10

Spark không thực sự yêu cầu id số, nó chỉ cần ong một số giá trị duy nhất, nhưng để thực hiện họ chọn Int.

Bạn có thể làm lại đơn giản và ra chuyển đổi cho userId:

case class MyRating(userId: String, product: Int, rating: Double) 

    val data: RDD[MyRating] = ??? 

    // Assign unique Long id for each userId 
    val userIdToInt: RDD[(String, Long)] = 
    data.map(_.userId).distinct().zipWithUniqueId() 

    // Reverse mapping from generated id to original 
    val reverseMapping: RDD[(Long, String)] 
    userIdToInt map { case (l, r) => (r, l) } 

    // Depends on data size, maybe too big to keep 
    // on single machine 
    val map: Map[String, Int] = 
    userIdToInt.collect().toMap.mapValues(_.toInt) 

    // Transform to MLLib rating 
    val rating: RDD[Rating] = data.map { r => 
    Rating(userIdToInt.lookup(r.userId).head.toInt, r.product, r.rating) 
    // -- or 
    Rating(map(r.userId), r.product, r.rating) 
    } 

    // ... train model 

    // ... get back to MyRating userId from Int 

    val someUserId: String = reverseMapping.lookup(123).head 

Bạn cũng có thể thử 'data.zipWithUniqueId()' nhưng tôi không chắc chắn rằng trong trường hợp này .toInt sẽ chuyển đổi an toàn ngay cả nếu kích thước tập dữ liệu nhỏ.

+1

Điều này không chỉ định một chỉ mục duy nhất cho từng xếp hạng, chứ không phải từng người dùng? Tôi không nghĩ rằng nó sẽ hoạt động nếu người dùng có nhiều xếp hạng. – PBJ

+0

@PBJ, vâng, bạn nói đúng, tôi đã cập nhật mã trong câu trả lời –

+1

phương pháp 'tra cứu' không phải là mã Spark hợp lệ. Nó sẽ biên dịch nhưng thổi vào thời gian chạy. Bạn có thể sửa chữa (loại bỏ) nó? – zero323

1

Giải pháp trên có thể không phải lúc nào cũng hoạt động như tôi đã khám phá. Spark không thể thực hiện phép biến đổi RDD từ bên trong RDD khác. Lỗi đầu ra:

org.apache.spark.SparkException: biến đổi và hành động RDD thể chỉ được nhập mã hereinvoked bởi người lái xe, không phải bên trong biến đổi khác; ví dụ, rdd1.map (x => rdd2.values.count() * x) không hợp lệ vì việc chuyển đổi giá trị và đếm hành động không được được thực hiện bên trong phép chuyển đổi rdd1.map. Để biết thêm thông tin , xem SPARK-5063.

Là giải pháp bạn có thể tham gia userIdToInt RDD với dữ liệu gốc RDD để lưu trữ mối quan hệ giữa userId và uniqueId. Sau đó, bạn có thể tham gia kết quả RDD với RDD này một lần nữa.

// Create RDD with the unique id included 
val dataWithUniqueUserId: RDD[(String, Int, Int, Double)] = 
    data.keyBy(_.userId).join(userIdToInt).map(r => 
     (r._2._1.userId, r._2._2.toInt, r._2._1.productId, 1)) 
3

Bạn cần chạy StringIndexer trên người dùng của mình để chuyển đổi chuỗi thành chỉ số nguyên duy nhất. Họ không phải liên tục.

Chúng tôi sử dụng này cho động cơ khuyến mục của chúng tôi trong https://www.aihello.com

df là (user: String, sản phẩm, đánh giá)

val stringindexer = new StringIndexer() 
     .setInputCol("user") 
     .setOutputCol("userNumber") 
    val modelc = stringindexer.fit(df) 
    val df = modelc.transform(df) 
1

@Ganesh Krishnan là đúng, StringIndexer giải quyết vấn đề này.

from pyspark.ml.feature import OneHotEncoder, StringIndexer 
from pyspark.sql import SQLContext 
>>> spark = SQLContext(sc)                    
>>> df = spark.createDataFrame(
...  [(0, "a"), (1, "b"), (2, "c"), (3, "a"), (4, "a"), (5, "c")], 
...  ["id", "category"]) 

| id|category| 
+---+--------+ 
| 0|  a| 
| 1|  b| 
| 2|  c| 
| 3|  a| 
| 4|  a| 
| 5|  c| 
+---+--------+ 
>>> stringIndexer = StringIndexer(inputCol="category", outputCol="categoryIndex") 
>>> model = stringIndexer.fit(df) 
>>> indexed = model.transform(df) 
>>> indexed.show() 
+---+--------+-------------+ 
| id|category|categoryIndex| 
+---+--------+-------------+ 
| 0|  a|   0.0| 
| 1|  b|   2.0| 
| 2|  c|   1.0| 
| 3|  a|   0.0| 
| 4|  a|   0.0| 
| 5|  c|   1.0| 
+---+--------+-------------+ 

>>> converter = IndexToString(inputCol="categoryIndex", outputCol="originalCategory") 
>>> converted = converter.transform(indexed) 
>>> converted.show() 
+---+--------+-------------+----------------+ 
| id|category|categoryIndex|originalCategory| 
+---+--------+-------------+----------------+ 
| 0|  a|   0.0|    a| 
| 1|  b|   2.0|    b| 
| 2|  c|   1.0|    c| 
| 3|  a|   0.0|    a| 
| 4|  a|   0.0|    a| 
| 5|  c|   1.0|    c| 
+---+--------+-------------+----------------+ 

>>> converted.select("id", "originalCategory").show() 
+---+----------------+ 
| id|originalCategory| 
+---+----------------+ 
| 0|    a| 
| 1|    b| 
| 2|    c| 
| 3|    a| 
| 4|    a| 
| 5|    c| 
+---+----------------+ 
Các vấn đề liên quan